diff --git a/.gitignore b/.gitignore index 796b96d1..189b414f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /build +/CMakeLists.txt.user diff --git a/CHANGELOG.md b/CHANGELOG.md index 2005d74d..8a40a859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,85 @@ +# v2.4.3 + - [#184](https://github.com/xmrig/xmrig/issues/184) Fixed cache size detection for CPUs with disabled Hyper-Threading. + +# v2.4.2 + - [#60](https://github.com/xmrig/xmrig/issues/60) Added FreeBSD support, thanks [vcambur](https://github.com/vcambur). + - [#153](https://github.com/xmrig/xmrig/issues/153) Fixed issues with dwarfpool.com. + +# v2.4.1 + - [#147](https://github.com/xmrig/xmrig/issues/147) Fixed comparability with monero-stratum. + +# v2.4.0 + - Added [HTTP API](https://github.com/xmrig/xmrig/wiki/API). + - Added comments support in config file. + - libjansson replaced to rapidjson. + - [#98](https://github.com/xmrig/xmrig/issues/98) Ignore `keepalive` option with minergate.com and nicehash.com. + - [#101](https://github.com/xmrig/xmrig/issues/101) Fixed MSVC 2017 (15.3) compile time version detection. + - [#108](https://github.com/xmrig/xmrig/issues/108) Silently ignore invalid values for `donate-level` option. + - [#111](https://github.com/xmrig/xmrig/issues/111) Fixed build without AEON support. + +# v2.3.1 +- [#68](https://github.com/xmrig/xmrig/issues/68) Fixed compatibility with Docker containers, was nothing print on console. + +# v2.3.0 +- Added `--cpu-priority` option (0 idle, 2 normal to 5 highest). +- Added `--user-agent` option, to set custom user-agent string for pool. For example `cpuminer-multi/0.1`. +- Added `--no-huge-pages` option, to disable huge pages support. +- [#62](https://github.com/xmrig/xmrig/issues/62) Don't send the login to the dev pool. +- Force reconnect if pool block miner IP address. helps switch to backup pool. +- Fixed: failed open default config file if path contains non English characters. +- Fixed: error occurred if try use unavailable stdin or stdout, regression since version 2.2.0. +- Fixed: message about huge pages support successfully enabled on Windows was not shown in release builds. + +# v2.2.1 +- Fixed [terminal issues](https://github.com/xmrig/xmrig-proxy/issues/2#issuecomment-319914085) after exit on Linux and OS X. + +# v2.2.0 +- [#46](https://github.com/xmrig/xmrig/issues/46) Restored config file support. Now possible use multiple config files and combine with command line options also added support for default config. +- Improved colors support on Windows, now used uv_tty, legacy code removed. +- QuickEdit Mode now disabled on Windows. +- Added interactive commands in console window:: **h**ashrate, **p**ause, **r**esume. +- Fixed autoconf mode for AMD FX CPUs. + +# v2.1.0 +- [#40](https://github.com/xmrig/xmrig/issues/40) +Improved miner shutdown, fixed crash on exit for Linux and OS X. +- Fixed, login request was contain malformed JSON if username or password has some special characters for example `\`. +- [#220](https://github.com/fireice-uk/xmr-stak-cpu/pull/220) Better support for Round Robin DNS, IP address now always chosen randomly instead of stuck on first one. +- Changed donation address, new [xmrig-proxy](https://github.com/xmrig/xmrig-proxy) is coming soon. + +# v2.0.2 +- Better deal with possible duplicate jobs from pool, show warning and ignore duplicates. +- For Windows builds libuv updated to version 1.13.1 and gcc to 7.1.0. + +# v2.0.1 + - [#27](https://github.com/xmrig/xmrig/issues/27) Fixed possibility crash on 32bit systems. + +# v2.0.0 + - Option `--backup-url` removed, instead now possibility specify multiple pools for example: `-o example1.com:3333 -u user1 -p password1 -k -o example2.com:5555 -u user2 -o example3.com:4444 -u user3` + - [#15](https://github.com/xmrig/xmrig/issues/15) Added option `-l, --log-file=FILE` to write log to file. + - [#15](https://github.com/xmrig/xmrig/issues/15) Added option `-S, --syslog` to use syslog for logging, Linux only. + - [#18](https://github.com/xmrig/xmrig/issues/18) Added nice messages for accepted/rejected shares with diff and network latency. + - [#20](https://github.com/xmrig/xmrig/issues/20) Fixed `--cpu-affinity` for more than 32 threads. + - Fixed Windows XP support. + - Fixed regression, option `--no-color` was not fully disable colored output. + - Show resolved pool IP address in miner output. + +# v1.0.1 +- Fix broken software AES implementation, app has crashed if CPU not support AES-NI, only version 1.0.0 affected. + +# v1.0.0 +- Miner complete rewritten in C++ with libuv. +- This version should be fully compatible (except config file) with previos versions, many new nice features will come in next versions. +- This is still beta. If you found regression, stability or perfomance issues or have an idea for new feature please fell free to open new [issue](https://github.com/xmrig/xmrig/issues/new). +- Added new option `--print-time=N`, print hashrate report every N seconds. +- New hashrate reports, by default every 60 secons. +- Added Microsoft Visual C++ 2015 and 2017 support. +- Removed dependency on libcurl. +- To compile this version from source please switch to [dev](https://github.com/xmrig/xmrig/tree/dev) branch. + +# v0.8.2 +- Fixed L2 cache size detection for AMD CPUs (Bulldozer/Piledriver/Steamroller/Excavator architecture). + # v0.8.2 - Fixed L2 cache size detection for AMD CPUs (Bulldozer/Piledriver/Steamroller/Excavator architecture). - Fixed gcc 7.1 support. diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d30acd3..c86fe953 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,146 +1,239 @@ cmake_minimum_required(VERSION 3.0) -project(xmrig C) +project(xmrig) option(WITH_LIBCPUID "Use Libcpuid" ON) option(WITH_AEON "CryptoNight-Lite support" ON) +option(WITH_HTTPD "HTTP REST API" ON) + +include (CheckIncludeFile) + set(HEADERS - compat.h - algo/cryptonight/cryptonight.h - algo/cryptonight/cryptonight_aesni.h - algo/cryptonight/cryptonight_softaes.h - elist.h - xmrig.h - version.h - options.h - cpu.h - persistent_memory.h - stratum.h - stats.h - util.h - donate.h + src/3rdparty/align.h + src/api/Api.h + src/api/ApiState.h + src/api/NetworkState.h + src/App.h + src/Console.h + src/Cpu.h + src/interfaces/IClientListener.h + src/interfaces/IConsoleListener.h + src/interfaces/IJobResultListener.h + src/interfaces/ILogBackend.h + src/interfaces/IStrategy.h + src/interfaces/IStrategyListener.h + src/interfaces/IWorker.h + src/log/ConsoleLog.h + src/log/FileLog.h + src/log/Log.h + src/Mem.h + src/net/Client.h + src/net/Job.h + src/net/JobId.h + src/net/JobResult.h + src/net/Network.h + src/net/strategies/DonateStrategy.h + src/net/strategies/FailoverStrategy.h + src/net/strategies/SinglePoolStrategy.h + src/net/SubmitResult.h + src/net/Url.h + src/Options.h + src/Platform.h + src/Summary.h + src/version.h + src/workers/DoubleWorker.h + src/workers/Handle.h + src/workers/Hashrate.h + src/workers/SingleWorker.h + src/workers/Worker.h + src/workers/Workers.h ) set(HEADERS_CRYPTO - crypto/c_groestl.h - crypto/c_blake256.h - crypto/c_jh.h - crypto/c_skein.h - ) - -set(HEADERS_COMPAT - compat/winansi.h - ) - -set(HEADERS_UTILS - utils/applog.h - utils/threads.h - utils/summary.h + src/crypto/c_blake256.h + src/crypto/c_groestl.h + src/crypto/c_jh.h + src/crypto/c_keccak.h + src/crypto/c_skein.h + src/crypto/CryptoNight.h + src/crypto/CryptoNight_p.h + src/crypto/CryptoNight_test.h + src/crypto/groestl_tables.h + src/crypto/hash.h + src/crypto/skein_port.h + src/crypto/soft_aes.h ) set(SOURCES - xmrig.c - algo/cryptonight/cryptonight.c - algo/cryptonight/cryptonight_av1_aesni.c - algo/cryptonight/cryptonight_av2_aesni_double.c - algo/cryptonight/cryptonight_av3_softaes.c - algo/cryptonight/cryptonight_av4_softaes_double.c - util.c - options.c - stratum.c - stats.c - memory.c + src/api/Api.cpp + src/api/ApiState.cpp + src/api/NetworkState.cpp + src/App.cpp + src/Console.cpp + src/log/ConsoleLog.cpp + src/log/FileLog.cpp + src/log/Log.cpp + src/Mem.cpp + src/net/Client.cpp + src/net/Job.cpp + src/net/Network.cpp + src/net/strategies/DonateStrategy.cpp + src/net/strategies/FailoverStrategy.cpp + src/net/strategies/SinglePoolStrategy.cpp + src/net/SubmitResult.cpp + src/net/Url.cpp + src/Options.cpp + src/Platform.cpp + src/Summary.cpp + src/workers/DoubleWorker.cpp + src/workers/Handle.cpp + src/workers/Hashrate.cpp + src/workers/SingleWorker.cpp + src/workers/Worker.cpp + src/workers/Workers.cpp + src/xmrig.cpp ) set(SOURCES_CRYPTO - crypto/c_keccak.c - crypto/c_groestl.c - crypto/c_blake256.c - crypto/c_jh.c - crypto/c_skein.c - crypto/soft_aes.c - ) - -set(SOURCES_UTILS - utils/applog.c - utils/summary.c + src/crypto/c_keccak.c + src/crypto/c_groestl.c + src/crypto/c_blake256.c + src/crypto/c_jh.c + src/crypto/c_skein.c + src/crypto/CryptoNight.cpp ) if (WIN32) - set(SOURCES_OS win/cpu_win.c win/memory_win.c win/xmrig_win.c win/app.rc compat/winansi.c) - set(EXTRA_LIBS ws2_32) - add_definitions(/D_WIN32_WINNT=0x600) + set(SOURCES_OS + res/app.rc + src/App_win.cpp + src/Cpu_win.cpp + src/Mem_win.cpp + src/Platform_win.cpp + ) + + add_definitions(/DWIN32) + set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv) elseif (APPLE) - set(SOURCES_OS mac/cpu_mac.c mac/memory_mac.c mac/xmrig_mac.c) + set(SOURCES_OS + src/App_unix.cpp + src/Cpu_mac.cpp + src/Mem_unix.cpp + src/Platform_mac.cpp + ) else() - set(SOURCES_OS unix/cpu_unix.c unix/memory_unix.c unix/xmrig_unix.c) - set(EXTRA_LIBS pthread) + set(SOURCES_OS + src/App_unix.cpp + src/Cpu_unix.cpp + src/Mem_unix.cpp + src/Platform_unix.cpp + ) + + set(EXTRA_LIBS pthread rt) endif() -include_directories(.) -add_definitions(/DUSE_NATIVE_THREADS) -add_definitions(/D_GNU_SOURCE) +if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + set(EXTRA_LIBS ${EXTRA_LIBS} kvm) +endif() + +add_definitions(/D__STDC_FORMAT_MACROS) add_definitions(/DUNICODE) +add_definitions(/DRAPIDJSON_SSE2) +#add_definitions(/DAPP_DEBUG) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") + +find_package(UV REQUIRED) if ("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -Wno-pointer-to-int-cast") -if (CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -funroll-loops -fvariable-expansion-in-unroller -fmerge-all-constants") -else() +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 11) + + +# https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html +if (CMAKE_CXX_COMPILER_ID MATCHES GNU) + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -Wall -Wno-strict-aliasing") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -Wall -fno-exceptions -fno-rtti") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -s -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2") + + if (WIN32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") + endif() + + add_definitions(/D_GNU_SOURCE) + + if (${CMAKE_VERSION} VERSION_LESS "3.1.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() + + #set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-2") + +elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox /Ot /Oi /MT /GL") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /Ot /Oi /MT /GL") + add_definitions(/D_CRT_SECURE_NO_WARNINGS) + add_definitions(/D_CRT_NONSTDC_NO_WARNINGS) + add_definitions(/DNOMINMAX) + +elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -Wall") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -funroll-loops -fmerge-all-constants") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -Wall -fno-exceptions -fno-rtti -Wno-missing-braces") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -funroll-loops -fmerge-all-constants") + endif() -#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-2") -#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fprofile-generate") -#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fprofile-use -fprofile-correction") - -if (WIN32) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") -endif() - -include_directories(compat/jansson) -add_subdirectory(compat/jansson) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") - -find_package(CURL REQUIRED) -include_directories(${CURL_INCLUDE_DIRS}) -add_definitions(/DCURL_STATICLIB) -link_directories(${CURL_LIBRARIES}) - if (WITH_LIBCPUID) - add_subdirectory(compat/libcpuid) + add_subdirectory(src/3rdparty/libcpuid) - include_directories(compat/libcpuid) + include_directories(src/3rdparty/libcpuid) set(CPUID_LIB cpuid) - set(SOURCES_CPUID cpu.c) + set(SOURCES_CPUID src/Cpu.cpp) else() add_definitions(/DXMRIG_NO_LIBCPUID) - set(SOURCES_CPUID cpu_stub.c) + set(SOURCES_CPUID src/Cpu_stub.cpp) endif() -if (WITH_AEON) - set(SOURCES_AEON - algo/cryptonight-lite/cryptonight_lite_av1_aesni.c - algo/cryptonight-lite/cryptonight_lite_av2_aesni_double.c - algo/cryptonight-lite/cryptonight_lite_av3_softaes.c - algo/cryptonight-lite/cryptonight_lite_av4_softaes_double.c - algo/cryptonight-lite/cryptonight_lite_aesni.h - algo/cryptonight-lite/cryptonight_lite_softaes.h - ) -else() +CHECK_INCLUDE_FILE (syslog.h HAVE_SYSLOG_H) +if (HAVE_SYSLOG_H) + add_definitions(/DHAVE_SYSLOG_H) + set(SOURCES_SYSLOG src/log/SysLog.h src/log/SysLog.cpp) +endif() + +if (NOT WITH_AEON) add_definitions(/DXMRIG_NO_AEON) endif() -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - add_executable(xmrig ${HEADERS} ${HEADERS_CRYPTO} ${SOURCES} ${SOURCES_CRYPTO} ${HEADERS_UTILS} ${SOURCES_UTILS} ${HEADERS_COMPAT} ${SOURCES_COMPAT} ${SOURCES_OS} ${SOURCES_CPUID} ${SOURCES_AEON}) - target_link_libraries(xmrig jansson curl ${CPUID_LIB} ${EXTRA_LIBS}) +if (WITH_HTTPD) + find_package(MHD) + + if (MHD_FOUND) + include_directories(${MHD_INCLUDE_DIRS}) + set(HTTPD_SOURCES src/api/Httpd.h src/api/Httpd.cpp) + else() + message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_HTTPD=OFF` to build without http deamon support") + endif() else() - add_executable(xmrig32 ${HEADERS} ${HEADERS_CRYPTO} ${SOURCES} ${SOURCES_CRYPTO} ${HEADERS_UTILS} ${SOURCES_UTILS} ${HEADERS_COMPAT} ${SOURCES_COMPAT} ${SOURCES_OS} ${SOURCES_CPUID} ${SOURCES_AEON}) - target_link_libraries(xmrig32 jansson curl ${CPUID_LIB} ${EXTRA_LIBS}) + add_definitions(/DXMRIG_NO_HTTPD) + add_definitions(/DXMRIG_NO_API) endif() +include_directories(src) +include_directories(src/3rdparty) +include_directories(${UV_INCLUDE_DIR}) + +add_executable(xmrig ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES}) +target_link_libraries(xmrig ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) diff --git a/README.md b/README.md index fb3f44e2..50ab830b 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,28 @@ # XMRig XMRig is high performance Monero (XMR) CPU miner, with the official full Windows support. -Based on cpuminer-multi with heavy optimizations/rewrites and removing a lot of legacy code. +Originally based on cpuminer-multi with heavy optimizations/rewrites and removing a lot of legacy code, since version 1.0.0 complete rewritten from scratch on C++. - +* This is the **CPU-mining** version, there is also a [NVIDIA GPU version](https://github.com/xmrig/xmrig-nvidia) and [AMD GPU version]( https://github.com/xmrig/xmrig-amd). +* [Roadmap](https://github.com/xmrig/xmrig/issues/106) for next releases. + + #### Table of contents * [Features](#features) * [Download](#download) * [Usage](#usage) * [Algorithm variations](#algorithm-variations) -* [Build](#build) +* [Build](https://github.com/xmrig/xmrig/wiki/Build) * [Common Issues](#common-issues) * [Other information](#other-information) * [Donations](#donations) * [Contacts](#contacts) ## Features -* High performance (290+ H/s on i7 6700). +* High performance. * Official Windows support. -* Small Windows executable, only 535 KB without dependencies. +* Small Windows executable, without dependencies. +* x86/x64 support. * Support for backup (failover) mining server. * keepalived support. * Command line options compatible with cpuminer. @@ -30,19 +34,24 @@ Based on cpuminer-multi with heavy optimizations/rewrites and removing a lot of ## Download * Binary releases: https://github.com/xmrig/xmrig/releases * Git tree: https://github.com/xmrig/xmrig.git - * Clone with `git clone https://github.com/xmrig/xmrig.git` + * Clone with `git clone https://github.com/xmrig/xmrig.git` :hammer: [Build instructions](https://github.com/xmrig/xmrig/wiki/Build). ## Usage ### Basic example ``` -xmrig.exe -o xmr-eu.dwarfpool.com:8005 -u YOUR_WALLET -p x -k +xmrig.exe -o pool.monero.hashvault.pro:5555 -u YOUR_WALLET -p x -k ``` +### Failover +``` +xmrig.exe -o pool.monero.hashvault.pro:5555 -u YOUR_WALLET1 -p x -k -o pool.supportxmr.com:5555 -u YOUR_WALLET2 -p x -k +``` +For failover you can add multiple pools, maximum count not limited. + ### Options ``` -a, --algo=ALGO cryptonight (default) or cryptonight-lite -o, --url=URL URL of mining server - -b, --backup-url=URL URL of backup 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 @@ -51,18 +60,25 @@ xmrig.exe -o xmr-eu.dwarfpool.com:8005 -u YOUR_WALLET -p x -k -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) - --cpu-affinity set process affinity to cpu core(s), mask 0x3 for cores 0 and 1 + --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1 + --cpu-priority set process priority (0 idle, 2 normal to 5 highest) + --no-huge-pages disable huge pages support --no-color disable colored output --donate-level=N donate level, default 5% (5 minutes in 100 minutes) + --user-agent set custom user-agent string for pool -B, --background run the miner in the background -c, --config=FILE load a JSON-format configuration file - --max-cpu-usage=N maximum cpu usage for automatic threads mode (default 75) - --safe safe adjust threads and av settings for current cpu + -l, --log-file=FILE log all output to a file + --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75) + --safe safe adjust threads and av settings for current CPU --nicehash enable nicehash support + --print-time=N print hashrate report every N seconds -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**. You can load multiple config files and combine it with command line options. + ## Algorithm variations Since version 0.8.0. * `--av=1` For CPUs with hardware AES. @@ -70,42 +86,6 @@ Since version 0.8.0. * `--av=3` Software AES implementation. * `--av=4` Lower power mode (double hash) of `3`. -## Build -### Ubuntu (Debian-based distros) -``` -sudo apt-get install git build-essential cmake libcurl4-openssl-dev -git clone https://github.com/xmrig/xmrig.git -cd xmrig -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -make -``` - -### Windows -It's complicated, you need [MSYS2](http://www.msys2.org/), custom libcurl build, and of course CMake too. - -Necessary MSYS2 packages: -``` -pacman -Sy -pacman -S mingw-w64-x86_64-gcc -pacman -S make -pacman -S mingw-w64-x86_64-cmake -pacman -S mingw-w64-x86_64-pkg-config -``` -Configure options for libcurl: -``` -./configure --disable-shared --enable-optimize --enable-threaded-resolver --disable-libcurl-option --disable-ares --disable-rt --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-manual --disable-ipv6 --disable-sspi --disable-crypto-auth --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --without-zlib --without-winssl --without-ssl --without-libssh2 --without-nghttp2 --disable-cookies --without-ca-bundle --without-librtmp -``` -CMake options: -``` -cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCURL_INCLUDE_DIR="c:\\curl-7.53.1\include" -DCURL_LIBRARY="c:\\curl-7.53.1\lib\.libs" -``` - -### Optional features -`-DWITH_LIBCPUID=OFF` Disable libcpuid. Auto configuration of CPU after this will be very limited. -`-DWITH_AEON=OFF` Disable CryptoNight-Lite support. - ## Common Issues ### HUGE PAGES unavailable * Run XMRig as Administrator. @@ -118,8 +98,8 @@ cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCURL_INCLUDE_DIR="c:\< ### CPU mining performance -* **i7-6700** - 290+ H/s (4 threads, cpu affinity 0xAA) -* **Dual E5620** - 377 H/s (12 threads, cpu affinity 0xEEEE) +* **Intel i7-7700** - 307 H/s (4 threads) +* **AMD Ryzen 7 1700X** - 560 H/s (8 threads) Please note performance is highly dependent on system load. The numbers above are obtained on an idle system. Tasks heavily using a processor cache, such as video playback, can greatly degrade hashrate. Optimal number of threads depends on the size of the L3 cache of a processor, 1 thread requires 2 MB of cache. diff --git a/algo/cryptonight-lite/cryptonight_lite_aesni.h b/algo/cryptonight-lite/cryptonight_lite_aesni.h deleted file mode 100644 index bb528cfb..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_aesni.h +++ /dev/null @@ -1,256 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __CRYPTONIGHT_LITE_AESNI_H__ -#define __CRYPTONIGHT_LITE_AESNI_H__ - -#include - - -#define aes_genkey_sub(imm8) \ - __m128i xout1 = _mm_aeskeygenassist_si128(*xout2, (imm8)); \ - xout1 = _mm_shuffle_epi32(xout1, 0xFF); \ - *xout0 = sl_xor(*xout0); \ - *xout0 = _mm_xor_si128(*xout0, xout1); \ - xout1 = _mm_aeskeygenassist_si128(*xout0, 0x00);\ - xout1 = _mm_shuffle_epi32(xout1, 0xAA); \ - *xout2 = sl_xor(*xout2); \ - *xout2 = _mm_xor_si128(*xout2, xout1); \ - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -static inline void aes_genkey_sub1(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x1) -} - - -static inline void aes_genkey_sub2(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x2) -} - - -static inline void aes_genkey_sub4(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x4) -} - - -static inline void aes_genkey_sub8(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x8) -} - - -static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) -{ - *x0 = _mm_aesenc_si128(*x0, key); - *x1 = _mm_aesenc_si128(*x1, key); - *x2 = _mm_aesenc_si128(*x2, key); - *x3 = _mm_aesenc_si128(*x3, key); - *x4 = _mm_aesenc_si128(*x4, key); - *x5 = _mm_aesenc_si128(*x5, key); - *x6 = _mm_aesenc_si128(*x6, key); - *x7 = _mm_aesenc_si128(*x7, key); -} - - -static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - aes_genkey_sub1(&xout0, &xout2); - *k2 = xout0; - *k3 = xout2; - - aes_genkey_sub2(&xout0, &xout2); - *k4 = xout0; - *k5 = xout2; - - aes_genkey_sub4(&xout0, &xout2); - *k6 = xout0; - *k7 = xout2; - - aes_genkey_sub8(&xout0, &xout2); - *k8 = xout0; - *k9 = xout2; -} - - -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY_LITE / sizeof(__m128i), 1); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - - -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY_LITE / sizeof(__m128i), 1); i += 8) - { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - - -#if defined(__x86_64__) -# define EXTRACT64(X) _mm_cvtsi128_si64(X) - -static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#elif defined(__i386__) -# define HI32(X) \ - _mm_srli_si128((X), 4) - - -# define EXTRACT64(X) \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) - -static inline uint64_t _umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - - -#endif /* __CRYPTONIGHT_LITE_AESNI_H__ */ diff --git a/algo/cryptonight-lite/cryptonight_lite_av1_aesni.c b/algo/cryptonight-lite/cryptonight_lite_av1_aesni.c deleted file mode 100644 index 80110fb2..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_av1_aesni.c +++ /dev/null @@ -1,77 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "algo/cryptonight/cryptonight.h" -#include "cryptonight_lite_aesni.h" -#include "crypto/c_keccak.h" - - -void cryptonight_lite_av1_aesni(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - - cn_explode_scratchpad((__m128i*) ctx->state0, (__m128i*) ctx->memory); - - const uint8_t* l0 = ctx->memory; - uint64_t* h0 = (uint64_t*) ctx->state0; - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - - for (size_t i = 0; __builtin_expect(i < 0x40000, 1); i++) { - __m128i cx; - cx = _mm_load_si128((__m128i *) &l0[idx0 & 0xFFFF0]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah0, al0)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0xFFFF0], _mm_xor_si128(bx0, cx)); - idx0 = EXTRACT64(cx); - bx0 = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0xFFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0xFFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*)&l0[idx0 & 0xFFFF0])[0] = al0; - ((uint64_t*)&l0[idx0 & 0xFFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - } - - cn_implode_scratchpad((__m128i*) ctx->memory, (__m128i*) ctx->state0); - - keccakf(h0, 24); - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); -} diff --git a/algo/cryptonight-lite/cryptonight_lite_av2_aesni_double.c b/algo/cryptonight-lite/cryptonight_lite_av2_aesni_double.c deleted file mode 100644 index 055435c6..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_av2_aesni_double.c +++ /dev/null @@ -1,111 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "algo/cryptonight/cryptonight.h" -#include "cryptonight_lite_aesni.h" -#include "crypto/c_keccak.h" - - -void cryptonight_lite_av2_aesni_double(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - keccak((const uint8_t *) input + size, size, ctx->state1, 200); - - const uint8_t* l0 = ctx->memory; - const uint8_t* l1 = ctx->memory + MEMORY_LITE; - uint64_t* h0 = (uint64_t*) ctx->state0; - uint64_t* h1 = (uint64_t*) ctx->state1; - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t al1 = h1[0] ^ h1[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - uint64_t ah1 = h1[1] ^ h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - uint64_t idx1 = h1[0] ^ h1[4]; - - for (size_t i = 0; __builtin_expect(i < 0x40000, 1); i++) { - __m128i cx0 = _mm_load_si128((__m128i *) &l0[idx0 & 0xFFFF0]); - __m128i cx1 = _mm_load_si128((__m128i *) &l1[idx1 & 0xFFFF0]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0xFFFF0], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i *) &l1[idx1 & 0xFFFF0], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0xFFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0xFFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & 0xFFFF0])[0] = al0; - ((uint64_t*) &l0[idx0 & 0xFFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & 0xFFFF0])[0]; - ch = ((uint64_t*) &l1[idx1 & 0xFFFF0])[1]; - lo = _umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & 0xFFFF0])[0] = al1; - ((uint64_t*) &l1[idx1 & 0xFFFF0])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); - extra_hashes[ctx->state1[0] & 3](ctx->state1, 200, (char*) output + 32); -} diff --git a/algo/cryptonight-lite/cryptonight_lite_av3_softaes.c b/algo/cryptonight-lite/cryptonight_lite_av3_softaes.c deleted file mode 100644 index 3dec6e33..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_av3_softaes.c +++ /dev/null @@ -1,77 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "algo/cryptonight/cryptonight.h" -#include "cryptonight_lite_softaes.h" -#include "crypto/c_keccak.h" - - -void cryptonight_lite_av3_softaes(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - - cn_explode_scratchpad((__m128i*) ctx->state0, (__m128i*) ctx->memory); - - const uint8_t* l0 = ctx->memory; - uint64_t* h0 = (uint64_t*) ctx->state0; - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - - for (size_t i = 0; __builtin_expect(i < 0x40000, 1); i++) { - __m128i cx; - cx = _mm_load_si128((__m128i *)&l0[idx0 & 0xFFFF0]); - cx = soft_aesenc(cx, _mm_set_epi64x(ah0, al0)); - - _mm_store_si128((__m128i *)&l0[idx0 & 0xFFFF0], _mm_xor_si128(bx0, cx)); - idx0 = EXTRACT64(cx); - bx0 = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*)&l0[idx0 & 0xFFFF0])[0]; - ch = ((uint64_t*)&l0[idx0 & 0xFFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*)&l0[idx0 & 0xFFFF0])[0] = al0; - ((uint64_t*)&l0[idx0 & 0xFFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - } - - cn_implode_scratchpad((__m128i*) ctx->memory, (__m128i*) ctx->state0); - - keccakf(h0, 24); - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); -} diff --git a/algo/cryptonight-lite/cryptonight_lite_av4_softaes_double.c b/algo/cryptonight-lite/cryptonight_lite_av4_softaes_double.c deleted file mode 100644 index 873b8cac..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_av4_softaes_double.c +++ /dev/null @@ -1,111 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "algo/cryptonight/cryptonight.h" -#include "cryptonight_lite_softaes.h" -#include "crypto/c_keccak.h" - - -void cryptonight_lite_av4_softaes_double(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - keccak((const uint8_t *) input + size, size, ctx->state1, 200); - - const uint8_t* l0 = ctx->memory; - const uint8_t* l1 = ctx->memory + MEMORY_LITE; - uint64_t* h0 = (uint64_t*) ctx->state0; - uint64_t* h1 = (uint64_t*) ctx->state1; - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t al1 = h1[0] ^ h1[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - uint64_t ah1 = h1[1] ^ h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - uint64_t idx1 = h1[0] ^ h1[4]; - - for (size_t i = 0; __builtin_expect(i < 0x40000, 1); i++) { - __m128i cx0 = _mm_load_si128((__m128i *) &l0[idx0 & 0xFFFF0]); - __m128i cx1 = _mm_load_si128((__m128i *) &l1[idx1 & 0xFFFF0]); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0xFFFF0], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i *) &l1[idx1 & 0xFFFF0], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0xFFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0xFFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & 0xFFFF0])[0] = al0; - ((uint64_t*) &l0[idx0 & 0xFFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & 0xFFFF0])[0]; - ch = ((uint64_t*) &l1[idx1 & 0xFFFF0])[1]; - lo = _umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & 0xFFFF0])[0] = al1; - ((uint64_t*) &l1[idx1 & 0xFFFF0])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); - extra_hashes[ctx->state1[0] & 3](ctx->state1, 200, (char*) output + 32); -} diff --git a/algo/cryptonight-lite/cryptonight_lite_softaes.h b/algo/cryptonight-lite/cryptonight_lite_softaes.h deleted file mode 100644 index bab3dcaf..00000000 --- a/algo/cryptonight-lite/cryptonight_lite_softaes.h +++ /dev/null @@ -1,237 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __CRYPTONIGHT_LITE_SOFTAES_H__ -#define __CRYPTONIGHT_LITE_SOFTAES_H__ - -#include - -extern __m128i soft_aesenc(__m128i in, __m128i key); -extern __m128i soft_aeskeygenassist(__m128i key, uint8_t rcon); - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -static inline void aes_genkey_sub(__m128i* xout0, __m128i* xout2, uint8_t rcon) -{ - __m128i xout1 = soft_aeskeygenassist(*xout2, rcon); - xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem - *xout0 = sl_xor(*xout0); - *xout0 = _mm_xor_si128(*xout0, xout1); - xout1 = soft_aeskeygenassist(*xout0, 0x00); - xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem - *xout2 = sl_xor(*xout2); - *xout2 = _mm_xor_si128(*xout2, xout1); -} - - -static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) -{ - *x0 = soft_aesenc(*x0, key); - *x1 = soft_aesenc(*x1, key); - *x2 = soft_aesenc(*x2, key); - *x3 = soft_aesenc(*x3, key); - *x4 = soft_aesenc(*x4, key); - *x5 = soft_aesenc(*x5, key); - *x6 = soft_aesenc(*x6, key); - *x7 = soft_aesenc(*x7, key); -} - - -static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x1); - *k2 = xout0; - *k3 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x2); - *k4 = xout0; - *k5 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x4); - *k6 = xout0; - *k7 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x8); - *k8 = xout0; - *k9 = xout2; -} - - -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < MEMORY_LITE / sizeof(__m128i); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - - -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY_LITE / sizeof(__m128i), 1); i += 8) - { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - - -#if defined(__x86_64__) -# define EXTRACT64(X) _mm_cvtsi128_si64(X) - -static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#elif defined(__i386__) -# define HI32(X) \ - _mm_srli_si128((X), 4) - - -# define EXTRACT64(X) \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) - -static inline uint64_t _umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - - -#endif /* __CRYPTONIGHT_LITE_SOFTAES_H__ */ diff --git a/algo/cryptonight/cryptonight.c b/algo/cryptonight/cryptonight.c deleted file mode 100644 index 73018728..00000000 --- a/algo/cryptonight/cryptonight.c +++ /dev/null @@ -1,244 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include -#include -#include - -#ifndef BUILD_TEST -# include "xmrig.h" -#endif - -#include "crypto/c_groestl.h" -#include "crypto/c_blake256.h" -#include "crypto/c_jh.h" -#include "crypto/c_skein.h" -#include "cryptonight.h" -#include "options.h" - - -const static char test_input[152] = { - 0x01, 0x00, 0xFB, 0x8E, 0x8A, 0xC8, 0x05, 0x89, 0x93, 0x23, 0x37, 0x1B, 0xB7, 0x90, 0xDB, 0x19, - 0x21, 0x8A, 0xFD, 0x8D, 0xB8, 0xE3, 0x75, 0x5D, 0x8B, 0x90, 0xF3, 0x9B, 0x3D, 0x55, 0x06, 0xA9, - 0xAB, 0xCE, 0x4F, 0xA9, 0x12, 0x24, 0x45, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x81, 0x46, 0xD4, 0x9F, - 0xA9, 0x3E, 0xE7, 0x24, 0xDE, 0xB5, 0x7D, 0x12, 0xCB, 0xC6, 0xC6, 0xF3, 0xB9, 0x24, 0xD9, 0x46, - 0x12, 0x7C, 0x7A, 0x97, 0x41, 0x8F, 0x93, 0x48, 0x82, 0x8F, 0x0F, 0x02, - 0x03, 0x05, 0xA0, 0xDB, 0xD6, 0xBF, 0x05, 0xCF, 0x16, 0xE5, 0x03, 0xF3, 0xA6, 0x6F, 0x78, 0x00, - 0x7C, 0xBF, 0x34, 0x14, 0x43, 0x32, 0xEC, 0xBF, 0xC2, 0x2E, 0xD9, 0x5C, 0x87, 0x00, 0x38, 0x3B, - 0x30, 0x9A, 0xCE, 0x19, 0x23, 0xA0, 0x96, 0x4B, 0x00, 0x00, 0x00, 0x08, 0xBA, 0x93, 0x9A, 0x62, - 0x72, 0x4C, 0x0D, 0x75, 0x81, 0xFC, 0xE5, 0x76, 0x1E, 0x9D, 0x8A, 0x0E, 0x6A, 0x1C, 0x3F, 0x92, - 0x4F, 0xDD, 0x84, 0x93, 0xD1, 0x11, 0x56, 0x49, 0xC0, 0x5E, 0xB6, 0x01 -}; - - -const static char test_output0[64] = { - 0x1B, 0x60, 0x6A, 0x3F, 0x4A, 0x07, 0xD6, 0x48, 0x9A, 0x1B, 0xCD, 0x07, 0x69, 0x7B, 0xD1, 0x66, - 0x96, 0xB6, 0x1C, 0x8A, 0xE9, 0x82, 0xF6, 0x1A, 0x90, 0x16, 0x0F, 0x4E, 0x52, 0x82, 0x8A, 0x7F, - 0x1A, 0x3F, 0xFB, 0xEE, 0x90, 0x9B, 0x42, 0x0D, 0x91, 0xF7, 0xBE, 0x6E, 0x5F, 0xB5, 0x6D, 0xB7, - 0x1B, 0x31, 0x10, 0xD8, 0x86, 0x01, 0x1E, 0x87, 0x7E, 0xE5, 0x78, 0x6A, 0xFD, 0x08, 0x01, 0x00 -}; - - -void cryptonight_av1_aesni(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_av2_aesni_double(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_av3_softaes(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_av4_softaes_double(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); - -#ifndef XMRIG_NO_AEON -const static char test_output1[64] = { - 0x28, 0xA2, 0x2B, 0xAD, 0x3F, 0x93, 0xD1, 0x40, 0x8F, 0xCA, 0x47, 0x2E, 0xB5, 0xAD, 0x1C, 0xBE, - 0x75, 0xF2, 0x1D, 0x05, 0x3C, 0x8C, 0xE5, 0xB3, 0xAF, 0x10, 0x5A, 0x57, 0x71, 0x3E, 0x21, 0xDD, - 0x36, 0x95, 0xB4, 0xB5, 0x3B, 0xB0, 0x03, 0x58, 0xB0, 0xAD, 0x38, 0xDC, 0x16, 0x0F, 0xEB, 0x9E, - 0x00, 0x4E, 0xEC, 0xE0, 0x9B, 0x83, 0xA7, 0x2E, 0xF6, 0xBA, 0x98, 0x64, 0xD3, 0x51, 0x0C, 0x88, -}; - -void cryptonight_lite_av1_aesni(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_lite_av2_aesni_double(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_lite_av3_softaes(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -void cryptonight_lite_av4_softaes_double(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx); -#endif - -void (*cryptonight_hash_ctx)(const void* input, size_t size, void* output, struct cryptonight_ctx* ctx) = NULL; - - -static bool self_test() { - if (cryptonight_hash_ctx == NULL) { - return false; - } - - char output[64]; - - struct cryptonight_ctx *ctx = (struct cryptonight_ctx*) _mm_malloc(sizeof(struct cryptonight_ctx), 16); - ctx->memory = (uint8_t *) _mm_malloc(MEMORY * 2, 16); - - cryptonight_hash_ctx(test_input, 76, output, ctx); - - _mm_free(ctx->memory); - _mm_free(ctx); - -# ifndef XMRIG_NO_AEON - if (opt_algo == ALGO_CRYPTONIGHT_LITE) { - return memcmp(output, test_output1, (opt_double_hash ? 64 : 32)) == 0; - } -# endif - - return memcmp(output, test_output0, (opt_double_hash ? 64 : 32)) == 0; -} - - -#ifndef XMRIG_NO_AEON -bool cryptonight_lite_init(int variant) { - switch (variant) { - case AEON_AV1_AESNI: - cryptonight_hash_ctx = cryptonight_lite_av1_aesni; - break; - - case AEON_AV2_AESNI_DOUBLE: - opt_double_hash = true; - cryptonight_hash_ctx = cryptonight_lite_av2_aesni_double; - break; - - case AEON_AV3_SOFT_AES: - cryptonight_hash_ctx = cryptonight_lite_av3_softaes; - break; - - case AEON_AV4_SOFT_AES_DOUBLE: - opt_double_hash = true; - cryptonight_hash_ctx = cryptonight_lite_av4_softaes_double; - break; - - default: - break; - } - - return self_test(); -} -#endif - - -bool cryptonight_init(int variant) -{ -# ifndef XMRIG_NO_AEON - if (opt_algo == ALGO_CRYPTONIGHT_LITE) { - return cryptonight_lite_init(variant); - } -# endif - - switch (variant) { - case XMR_AV1_AESNI: - cryptonight_hash_ctx = cryptonight_av1_aesni; - break; - - case XMR_AV2_AESNI_DOUBLE: - opt_double_hash = true; - cryptonight_hash_ctx = cryptonight_av2_aesni_double; - break; - - case XMR_AV3_SOFT_AES: - cryptonight_hash_ctx = cryptonight_av3_softaes; - break; - - case XMR_AV4_SOFT_AES_DOUBLE: - opt_double_hash = true; - cryptonight_hash_ctx = cryptonight_av4_softaes_double; - break; - - default: - break; - } - - return self_test(); -} - - -static inline void do_blake_hash(const void* input, size_t len, char* output) { - blake256_hash((uint8_t*)output, input, len); -} - - -static inline void do_groestl_hash(const void* input, size_t len, char* output) { - groestl(input, len * 8, (uint8_t*)output); -} - - -static inline void do_jh_hash(const void* input, size_t len, char* output) { - jh_hash(32 * 8, input, 8 * len, (uint8_t*)output); -} - - -static inline void do_skein_hash(const void* input, size_t len, char* output) { - skein_hash(8 * 32, input, 8 * len, (uint8_t*)output); -} - - -void (* const extra_hashes[4])(const void *, size_t, char *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; - - -#ifndef BUILD_TEST -int scanhash_cryptonight(int thr_id, uint32_t *hash, uint32_t *restrict blob, size_t blob_size, uint32_t target, uint32_t max_nonce, unsigned long *restrict hashes_done, struct cryptonight_ctx *restrict ctx) { - uint32_t *nonceptr = (uint32_t*) (((char*) blob) + 39); - - do { - cryptonight_hash_ctx(blob, blob_size, hash, ctx); - (*hashes_done)++; - - if (unlikely(hash[7] < target)) { - return 1; - } - - (*nonceptr)++; - } while (likely(((*nonceptr) < max_nonce && !work_restart[thr_id].restart))); - - return 0; -} - - -int scanhash_cryptonight_double(int thr_id, uint32_t *hash, uint8_t *restrict blob, size_t blob_size, uint32_t target, uint32_t max_nonce, unsigned long *restrict hashes_done, struct cryptonight_ctx *restrict ctx) { - int rc = 0; - uint32_t *nonceptr0 = (uint32_t*) (((char*) blob) + 39); - uint32_t *nonceptr1 = (uint32_t*) (((char*) blob) + 39 + blob_size); - - do { - cryptonight_hash_ctx(blob, blob_size, hash, ctx); - (*hashes_done) += 2; - - if (unlikely(hash[7] < target)) { - return rc |= 1; - } - - if (unlikely(hash[15] < target)) { - return rc |= 2; - } - - if (rc) { - break; - } - - (*nonceptr0)++; - (*nonceptr1)++; - } while (likely(((*nonceptr0) < max_nonce && !work_restart[thr_id].restart))); - - return rc; -} -#endif diff --git a/algo/cryptonight/cryptonight_aesni.h b/algo/cryptonight/cryptonight_aesni.h deleted file mode 100644 index e4d6d42f..00000000 --- a/algo/cryptonight/cryptonight_aesni.h +++ /dev/null @@ -1,256 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __CRYPTONIGHT_AESNI_H__ -#define __CRYPTONIGHT_AESNI_H__ - -#include - - -#define aes_genkey_sub(imm8) \ - __m128i xout1 = _mm_aeskeygenassist_si128(*xout2, (imm8)); \ - xout1 = _mm_shuffle_epi32(xout1, 0xFF); \ - *xout0 = sl_xor(*xout0); \ - *xout0 = _mm_xor_si128(*xout0, xout1); \ - xout1 = _mm_aeskeygenassist_si128(*xout0, 0x00);\ - xout1 = _mm_shuffle_epi32(xout1, 0xAA); \ - *xout2 = sl_xor(*xout2); \ - *xout2 = _mm_xor_si128(*xout2, xout1); \ - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -static inline void aes_genkey_sub1(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x1) -} - - -static inline void aes_genkey_sub2(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x2) -} - - -static inline void aes_genkey_sub4(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x4) -} - - -static inline void aes_genkey_sub8(__m128i* xout0, __m128i* xout2) -{ - aes_genkey_sub(0x8) -} - - -static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) -{ - *x0 = _mm_aesenc_si128(*x0, key); - *x1 = _mm_aesenc_si128(*x1, key); - *x2 = _mm_aesenc_si128(*x2, key); - *x3 = _mm_aesenc_si128(*x3, key); - *x4 = _mm_aesenc_si128(*x4, key); - *x5 = _mm_aesenc_si128(*x5, key); - *x6 = _mm_aesenc_si128(*x6, key); - *x7 = _mm_aesenc_si128(*x7, key); -} - - -static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - aes_genkey_sub1(&xout0, &xout2); - *k2 = xout0; - *k3 = xout2; - - aes_genkey_sub2(&xout0, &xout2); - *k4 = xout0; - *k5 = xout2; - - aes_genkey_sub4(&xout0, &xout2); - *k6 = xout0; - *k7 = xout2; - - aes_genkey_sub8(&xout0, &xout2); - *k8 = xout0; - *k9 = xout2; -} - - -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY / sizeof(__m128i), 1); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - - -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY / sizeof(__m128i), 1); i += 8) - { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - - -#if defined(__x86_64__) -# define EXTRACT64(X) _mm_cvtsi128_si64(X) - -static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#elif defined(__i386__) -# define HI32(X) \ - _mm_srli_si128((X), 4) - - -# define EXTRACT64(X) \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) - -static inline uint64_t _umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - - -#endif /* __CRYPTONIGHT_AESNI_H__ */ diff --git a/algo/cryptonight/cryptonight_av1_aesni.c b/algo/cryptonight/cryptonight_av1_aesni.c deleted file mode 100644 index 3f30544e..00000000 --- a/algo/cryptonight/cryptonight_av1_aesni.c +++ /dev/null @@ -1,77 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "cryptonight.h" -#include "cryptonight_aesni.h" -#include "crypto/c_keccak.h" - - -void cryptonight_av1_aesni(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - - cn_explode_scratchpad((__m128i*) ctx->state0, (__m128i*) ctx->memory); - - const uint8_t* l0 = ctx->memory; - uint64_t* h0 = (uint64_t*) ctx->state0; - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - - for (size_t i = 0; __builtin_expect(i < 0x80000, 1); i++) { - __m128i cx; - cx = _mm_load_si128((__m128i *) &l0[idx0 & 0x1FFFF0]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah0, al0)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0x1FFFF0], _mm_xor_si128(bx0, cx)); - idx0 = EXTRACT64(cx); - bx0 = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*)&l0[idx0 & 0x1FFFF0])[0] = al0; - ((uint64_t*)&l0[idx0 & 0x1FFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - } - - cn_implode_scratchpad((__m128i*) ctx->memory, (__m128i*) ctx->state0); - - keccakf(h0, 24); - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); -} diff --git a/algo/cryptonight/cryptonight_av2_aesni_double.c b/algo/cryptonight/cryptonight_av2_aesni_double.c deleted file mode 100644 index 779b9bc3..00000000 --- a/algo/cryptonight/cryptonight_av2_aesni_double.c +++ /dev/null @@ -1,111 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "cryptonight.h" -#include "cryptonight_aesni.h" -#include "crypto/c_keccak.h" - - -void cryptonight_av2_aesni_double(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - keccak((const uint8_t *) input + size, size, ctx->state1, 200); - - const uint8_t* l0 = ctx->memory; - const uint8_t* l1 = ctx->memory + MEMORY; - uint64_t* h0 = (uint64_t*) ctx->state0; - uint64_t* h1 = (uint64_t*) ctx->state1; - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t al1 = h1[0] ^ h1[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - uint64_t ah1 = h1[1] ^ h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - uint64_t idx1 = h1[0] ^ h1[4]; - - for (size_t i = 0; __builtin_expect(i < 0x80000, 1); i++) { - __m128i cx0 = _mm_load_si128((__m128i *) &l0[idx0 & 0x1FFFF0]); - __m128i cx1 = _mm_load_si128((__m128i *) &l1[idx1 & 0x1FFFF0]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0x1FFFF0], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i *) &l1[idx1 & 0x1FFFF0], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & 0x1FFFF0])[0] = al0; - ((uint64_t*) &l0[idx0 & 0x1FFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & 0x1FFFF0])[0]; - ch = ((uint64_t*) &l1[idx1 & 0x1FFFF0])[1]; - lo = _umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & 0x1FFFF0])[0] = al1; - ((uint64_t*) &l1[idx1 & 0x1FFFF0])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); - extra_hashes[ctx->state1[0] & 3](ctx->state1, 200, (char*) output + 32); -} diff --git a/algo/cryptonight/cryptonight_av3_softaes.c b/algo/cryptonight/cryptonight_av3_softaes.c deleted file mode 100644 index 22be894d..00000000 --- a/algo/cryptonight/cryptonight_av3_softaes.c +++ /dev/null @@ -1,77 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "cryptonight.h" -#include "cryptonight_softaes.h" -#include "crypto/c_keccak.h" - - -void cryptonight_av3_softaes(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - - cn_explode_scratchpad((__m128i*) ctx->state0, (__m128i*) ctx->memory); - - const uint8_t* l0 = ctx->memory; - uint64_t* h0 = (uint64_t*) ctx->state0; - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - - for (size_t i = 0; __builtin_expect(i < 0x80000, 1); i++) { - __m128i cx; - cx = _mm_load_si128((__m128i *)&l0[idx0 & 0x1FFFF0]); - cx = soft_aesenc(cx, _mm_set_epi64x(ah0, al0)); - - _mm_store_si128((__m128i *)&l0[idx0 & 0x1FFFF0], _mm_xor_si128(bx0, cx)); - idx0 = EXTRACT64(cx); - bx0 = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*)&l0[idx0 & 0x1FFFF0])[0]; - ch = ((uint64_t*)&l0[idx0 & 0x1FFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*)&l0[idx0 & 0x1FFFF0])[0] = al0; - ((uint64_t*)&l0[idx0 & 0x1FFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - } - - cn_implode_scratchpad((__m128i*) ctx->memory, (__m128i*) ctx->state0); - - keccakf(h0, 24); - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); -} diff --git a/algo/cryptonight/cryptonight_av4_softaes_double.c b/algo/cryptonight/cryptonight_av4_softaes_double.c deleted file mode 100644 index afd4bebe..00000000 --- a/algo/cryptonight/cryptonight_av4_softaes_double.c +++ /dev/null @@ -1,111 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "cryptonight.h" -#include "cryptonight_softaes.h" -#include "crypto/c_keccak.h" - - -void cryptonight_av4_softaes_double(const void *restrict input, size_t size, void *restrict output, struct cryptonight_ctx *restrict ctx) -{ - keccak((const uint8_t *) input, size, ctx->state0, 200); - keccak((const uint8_t *) input + size, size, ctx->state1, 200); - - const uint8_t* l0 = ctx->memory; - const uint8_t* l1 = ctx->memory + MEMORY; - uint64_t* h0 = (uint64_t*) ctx->state0; - uint64_t* h1 = (uint64_t*) ctx->state1; - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t al1 = h1[0] ^ h1[4]; - uint64_t ah0 = h0[1] ^ h0[5]; - uint64_t ah1 = h1[1] ^ h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^ h0[4]; - uint64_t idx1 = h1[0] ^ h1[4]; - - for (size_t i = 0; __builtin_expect(i < 0x80000, 1); i++) { - __m128i cx0 = _mm_load_si128((__m128i *) &l0[idx0 & 0x1FFFF0]); - __m128i cx1 = _mm_load_si128((__m128i *) &l1[idx1 & 0x1FFFF0]); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - - _mm_store_si128((__m128i *) &l0[idx0 & 0x1FFFF0], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i *) &l1[idx1 & 0x1FFFF0], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[0]; - ch = ((uint64_t*) &l0[idx0 & 0x1FFFF0])[1]; - lo = _umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & 0x1FFFF0])[0] = al0; - ((uint64_t*) &l0[idx0 & 0x1FFFF0])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & 0x1FFFF0])[0]; - ch = ((uint64_t*) &l1[idx1 & 0x1FFFF0])[1]; - lo = _umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & 0x1FFFF0])[0] = al1; - ((uint64_t*) &l1[idx1 & 0x1FFFF0])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, output); - extra_hashes[ctx->state1[0] & 3](ctx->state1, 200, (char*) output + 32); -} diff --git a/algo/cryptonight/cryptonight_softaes.h b/algo/cryptonight/cryptonight_softaes.h deleted file mode 100644 index f12ab8c6..00000000 --- a/algo/cryptonight/cryptonight_softaes.h +++ /dev/null @@ -1,237 +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 2017 fireice-uk - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __CRYPTONIGHT_SOFTAES_H__ -#define __CRYPTONIGHT_SOFTAES_H__ - -#include - -extern __m128i soft_aesenc(__m128i in, __m128i key); -extern __m128i soft_aeskeygenassist(__m128i key, uint8_t rcon); - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -static inline void aes_genkey_sub(__m128i* xout0, __m128i* xout2, uint8_t rcon) -{ - __m128i xout1 = soft_aeskeygenassist(*xout2, rcon); - xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem - *xout0 = sl_xor(*xout0); - *xout0 = _mm_xor_si128(*xout0, xout1); - xout1 = soft_aeskeygenassist(*xout0, 0x00); - xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem - *xout2 = sl_xor(*xout2); - *xout2 = _mm_xor_si128(*xout2, xout1); -} - - -static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) -{ - *x0 = soft_aesenc(*x0, key); - *x1 = soft_aesenc(*x1, key); - *x2 = soft_aesenc(*x2, key); - *x3 = soft_aesenc(*x3, key); - *x4 = soft_aesenc(*x4, key); - *x5 = soft_aesenc(*x5, key); - *x6 = soft_aesenc(*x6, key); - *x7 = soft_aesenc(*x7, key); -} - - -static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x1); - *k2 = xout0; - *k3 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x2); - *k4 = xout0; - *k5 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x4); - *k6 = xout0; - *k7 = xout2; - - aes_genkey_sub(&xout0, &xout2, 0x8); - *k8 = xout0; - *k9 = xout2; -} - - -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < MEMORY / sizeof(__m128i); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - - -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - // This is more than we have registers, compiler will assign 2 keys on the stack - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; __builtin_expect(i < MEMORY / sizeof(__m128i), 1); i += 8) - { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - - -#if defined(__x86_64__) -# define EXTRACT64(X) _mm_cvtsi128_si64(X) - -static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#elif defined(__i386__) -# define HI32(X) \ - _mm_srli_si128((X), 4) - - -# define EXTRACT64(X) \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) - -inline uint64_t _umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - - -#endif /* __CRYPTONIGHT_SOFTAES_H__ */ diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake new file mode 100644 index 00000000..8505b337 --- /dev/null +++ b/cmake/FindMHD.cmake @@ -0,0 +1,39 @@ +# - Try to find MHD +# Once done this will define +# +# MHD_FOUND - system has MHD +# MHD_INCLUDE_DIRS - the MHD include directory +# MHD_LIBRARY - Link these to use MHD + +find_path( + MHD_INCLUDE_DIR + NAMES microhttpd.h + DOC "microhttpd include dir" +) + +find_library( + MHD_LIBRARY + NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll + DOC "microhttpd library" +) + +set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) +set(MHD_LIBRARIES ${MHD_LIBRARY}) + +# debug library on windows +# same naming convention as in qt (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +# official MHD project actually uses _d suffix +if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) + find_library( + MHD_LIBRARY_DEBUG + NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d + DOC "mhd debug library" + ) + set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY) +mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) + diff --git a/cmake/FindUV.cmake b/cmake/FindUV.cmake new file mode 100644 index 00000000..e3c22d28 --- /dev/null +++ b/cmake/FindUV.cmake @@ -0,0 +1,8 @@ +find_path(UV_INCLUDE_DIR NAMES uv.h) +find_library(UV_LIBRARY NAMES uv libuv) + +set(UV_LIBRARIES ${UV_LIBRARY}) +set(UV_INCLUDE_DIRS ${UV_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UV DEFAULT_MSG UV_LIBRARY UV_INCLUDE_DIR) diff --git a/compat/jansson/CMakeLists.txt b/compat/jansson/CMakeLists.txt deleted file mode 100644 index 7bd74c67..00000000 --- a/compat/jansson/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -cmake_minimum_required (VERSION 2.8) -project (jansson C) - -add_definitions(-DHAVE_CONFIG_H) - -# Add the lib sources. -file(GLOB JANSSON_SRC *.c) - -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Os") - -set(JANSSON_HDR_PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/hashtable.h - ${CMAKE_CURRENT_SOURCE_DIR}/jansson_private.h - ${CMAKE_CURRENT_SOURCE_DIR}/strbuffer.h - ${CMAKE_CURRENT_SOURCE_DIR}/utf.h - ${CMAKE_CURRENT_SOURCE_DIR}/jansson_private_config.h) - -set(JANSSON_HDR_PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/jansson_config.h - ${CMAKE_CURRENT_SOURCE_DIR}/jansson.h) - -add_library(jansson STATIC - ${JANSSON_SRC} - ${JANSSON_HDR_PRIVATE} - ${JANSSON_HDR_PUBLIC}) diff --git a/compat/jansson/LICENSE b/compat/jansson/LICENSE deleted file mode 100644 index 8ae43e02..00000000 --- a/compat/jansson/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2009-2014 Petri Lehtinen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/compat/jansson/dump.c b/compat/jansson/dump.c deleted file mode 100644 index a23fabb7..00000000 --- a/compat/jansson/dump.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include "jansson_private.h" - -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "jansson.h" -#include "strbuffer.h" -#include "utf.h" - -#define MAX_INTEGER_STR_LENGTH 100 -#define MAX_REAL_STR_LENGTH 100 - -#define FLAGS_TO_INDENT(f) ((f) & 0x1F) -#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) - -struct buffer { - const size_t size; - size_t used; - char *data; -}; - -static int dump_to_strbuffer(const char *buffer, size_t size, void *data) -{ - return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); -} - -static int dump_to_buffer(const char *buffer, size_t size, void *data) -{ - struct buffer *buf = (struct buffer *)data; - - if(buf->used + size <= buf->size) - memcpy(&buf->data[buf->used], buffer, size); - - buf->used += size; - return 0; -} - -static int dump_to_file(const char *buffer, size_t size, void *data) -{ - FILE *dest = (FILE *)data; - if(fwrite(buffer, size, 1, dest) != 1) - return -1; - return 0; -} - -static int dump_to_fd(const char *buffer, size_t size, void *data) -{ - int *dest = (int *)data; -#ifdef HAVE_UNISTD_H - if(write(*dest, buffer, size) == (ssize_t)size) - return 0; -#endif - return -1; -} - -/* 32 spaces (the maximum indentation size) */ -static const char whitespace[] = " "; - -static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) -{ - if(FLAGS_TO_INDENT(flags) > 0) - { - unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count; - - if(dump("\n", 1, data)) - return -1; - - while(n_spaces > 0) - { - int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1; - - if(dump(whitespace, cur_n, data)) - return -1; - - n_spaces -= cur_n; - } - } - else if(space && !(flags & JSON_COMPACT)) - { - return dump(" ", 1, data); - } - return 0; -} - -static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags) -{ - const char *pos, *end, *lim; - int32_t codepoint; - - if(dump("\"", 1, data)) - return -1; - - end = pos = str; - lim = str + len; - while(1) - { - const char *text; - char seq[13]; - int length; - - while(end < lim) - { - end = utf8_iterate(pos, lim - pos, &codepoint); - if(!end) - return -1; - - /* mandatory escape or control char */ - if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) - break; - - /* slash */ - if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') - break; - - /* non-ASCII */ - if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) - break; - - pos = end; - } - - if(pos != str) { - if(dump(str, pos - str, data)) - return -1; - } - - if(end == pos) - break; - - /* handle \, /, ", and control codes */ - length = 2; - switch(codepoint) - { - case '\\': text = "\\\\"; break; - case '\"': text = "\\\""; break; - case '\b': text = "\\b"; break; - case '\f': text = "\\f"; break; - case '\n': text = "\\n"; break; - case '\r': text = "\\r"; break; - case '\t': text = "\\t"; break; - case '/': text = "\\/"; break; - default: - { - /* codepoint is in BMP */ - if(codepoint < 0x10000) - { - snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint); - length = 6; - } - - /* not in BMP -> construct a UTF-16 surrogate pair */ - else - { - int32_t first, last; - - codepoint -= 0x10000; - first = 0xD800 | ((codepoint & 0xffc00) >> 10); - last = 0xDC00 | (codepoint & 0x003ff); - - snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last); - length = 12; - } - - text = seq; - break; - } - } - - if(dump(text, length, data)) - return -1; - - str = pos = end; - } - - return dump("\"", 1, data); -} - -static int compare_keys(const void *key1, const void *key2) -{ - return strcmp(*(const char **)key1, *(const char **)key2); -} - -static int do_dump(const json_t *json, size_t flags, int depth, - json_dump_callback_t dump, void *data) -{ - int embed = flags & JSON_EMBED; - - flags &= ~JSON_EMBED; - - if(!json) - return -1; - - switch(json_typeof(json)) { - case JSON_NULL: - return dump("null", 4, data); - - case JSON_TRUE: - return dump("true", 4, data); - - case JSON_FALSE: - return dump("false", 5, data); - - case JSON_INTEGER: - { - char buffer[MAX_INTEGER_STR_LENGTH]; - int size; - - size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, - "%" JSON_INTEGER_FORMAT, - json_integer_value(json)); - if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) - return -1; - - return dump(buffer, size, data); - } - - case JSON_REAL: - { - char buffer[MAX_REAL_STR_LENGTH]; - int size; - double value = json_real_value(json); - - size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, - FLAGS_TO_PRECISION(flags)); - if(size < 0) - return -1; - - return dump(buffer, size, data); - } - - case JSON_STRING: - return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); - - case JSON_ARRAY: - { - size_t n; - size_t i; - - json_array_t *array; - - /* detect circular references */ - array = json_to_array(json); - if(array->visited) - goto array_error; - array->visited = 1; - - n = json_array_size(json); - - if(!embed && dump("[", 1, data)) - goto array_error; - if(n == 0) { - array->visited = 0; - return embed ? 0 : dump("]", 1, data); - } - if(dump_indent(flags, depth + 1, 0, dump, data)) - goto array_error; - - for(i = 0; i < n; ++i) { - if(do_dump(json_array_get(json, i), flags, depth + 1, - dump, data)) - goto array_error; - - if(i < n - 1) - { - if(dump(",", 1, data) || - dump_indent(flags, depth + 1, 1, dump, data)) - goto array_error; - } - else - { - if(dump_indent(flags, depth, 0, dump, data)) - goto array_error; - } - } - - array->visited = 0; - return embed ? 0 : dump("]", 1, data); - - array_error: - array->visited = 0; - return -1; - } - - case JSON_OBJECT: - { - json_object_t *object; - void *iter; - const char *separator; - int separator_length; - - if(flags & JSON_COMPACT) { - separator = ":"; - separator_length = 1; - } - else { - separator = ": "; - separator_length = 2; - } - - /* detect circular references */ - object = json_to_object(json); - if(object->visited) - goto object_error; - object->visited = 1; - - iter = json_object_iter((json_t *)json); - - if(!embed && dump("{", 1, data)) - goto object_error; - if(!iter) { - object->visited = 0; - return embed ? 0 : dump("}", 1, data); - } - if(dump_indent(flags, depth + 1, 0, dump, data)) - goto object_error; - - if(flags & JSON_SORT_KEYS) - { - const char **keys; - size_t size, i; - - size = json_object_size(json); - keys = jsonp_malloc(size * sizeof(const char *)); - if(!keys) - goto object_error; - - i = 0; - while(iter) - { - keys[i] = json_object_iter_key(iter); - iter = json_object_iter_next((json_t *)json, iter); - i++; - } - assert(i == size); - - qsort(keys, size, sizeof(const char *), compare_keys); - - for(i = 0; i < size; i++) - { - const char *key; - json_t *value; - - key = keys[i]; - value = json_object_get(json, key); - assert(value); - - dump_string(key, strlen(key), dump, data, flags); - if(dump(separator, separator_length, data) || - do_dump(value, flags, depth + 1, dump, data)) - { - jsonp_free(keys); - goto object_error; - } - - if(i < size - 1) - { - if(dump(",", 1, data) || - dump_indent(flags, depth + 1, 1, dump, data)) - { - jsonp_free(keys); - goto object_error; - } - } - else - { - if(dump_indent(flags, depth, 0, dump, data)) - { - jsonp_free(keys); - goto object_error; - } - } - } - - jsonp_free(keys); - } - else - { - /* Don't sort keys */ - - while(iter) - { - void *next = json_object_iter_next((json_t *)json, iter); - const char *key = json_object_iter_key(iter); - - dump_string(key, strlen(key), dump, data, flags); - if(dump(separator, separator_length, data) || - do_dump(json_object_iter_value(iter), flags, depth + 1, - dump, data)) - goto object_error; - - if(next) - { - if(dump(",", 1, data) || - dump_indent(flags, depth + 1, 1, dump, data)) - goto object_error; - } - else - { - if(dump_indent(flags, depth, 0, dump, data)) - goto object_error; - } - - iter = next; - } - } - - object->visited = 0; - return embed ? 0 : dump("}", 1, data); - - object_error: - object->visited = 0; - return -1; - } - - default: - /* not reached */ - return -1; - } -} - -char *json_dumps(const json_t *json, size_t flags) -{ - strbuffer_t strbuff; - char *result; - - if(strbuffer_init(&strbuff)) - return NULL; - - if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) - result = NULL; - else - result = jsonp_strdup(strbuffer_value(&strbuff)); - - strbuffer_close(&strbuff); - return result; -} - -size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) -{ - struct buffer buf = { size, 0, buffer }; - - if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags)) - return 0; - - return buf.used; -} - -int json_dumpf(const json_t *json, FILE *output, size_t flags) -{ - return json_dump_callback(json, dump_to_file, (void *)output, flags); -} - -int json_dumpfd(const json_t *json, int output, size_t flags) -{ - return json_dump_callback(json, dump_to_fd, (void *)&output, flags); -} - -int json_dump_file(const json_t *json, const char *path, size_t flags) -{ - int result; - - FILE *output = fopen(path, "w"); - if(!output) - return -1; - - result = json_dumpf(json, output, flags); - - fclose(output); - return result; -} - -int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) -{ - if(!(flags & JSON_ENCODE_ANY)) { - if(!json_is_array(json) && !json_is_object(json)) - return -1; - } - - return do_dump(json, flags, 0, callback, data); -} diff --git a/compat/jansson/error.c b/compat/jansson/error.c deleted file mode 100644 index 58c83790..00000000 --- a/compat/jansson/error.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include "jansson_private.h" - -void jsonp_error_init(json_error_t *error, const char *source) -{ - if(error) - { - error->text[0] = '\0'; - error->line = -1; - error->column = -1; - error->position = 0; - if(source) - jsonp_error_set_source(error, source); - else - error->source[0] = '\0'; - } -} - -void jsonp_error_set_source(json_error_t *error, const char *source) -{ - size_t length; - - if(!error || !source) - return; - - length = strlen(source); - if(length < JSON_ERROR_SOURCE_LENGTH) - strncpy(error->source, source, length + 1); - else { - size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; - strncpy(error->source, "...", 3); - strncpy(error->source + 3, source + extra, length - extra + 1); - } -} - -void jsonp_error_set(json_error_t *error, int line, int column, - size_t position, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - jsonp_error_vset(error, line, column, position, msg, ap); - va_end(ap); -} - -void jsonp_error_vset(json_error_t *error, int line, int column, - size_t position, const char *msg, va_list ap) -{ - if(!error) - return; - - if(error->text[0] != '\0') { - /* error already set */ - return; - } - - error->line = line; - error->column = column; - error->position = (int)position; - - vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); - error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; -} diff --git a/compat/jansson/hashtable.c b/compat/jansson/hashtable.c deleted file mode 100644 index c819319a..00000000 --- a/compat/jansson/hashtable.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#if HAVE_CONFIG_H -#include -#endif - -#include -#include - -#if HAVE_STDINT_H -#include -#endif - -#include /* for JSON_INLINE */ -#include "jansson_private.h" /* for container_of() */ -#include "hashtable.h" - -#ifndef INITIAL_HASHTABLE_ORDER -#define INITIAL_HASHTABLE_ORDER 3 -#endif - -typedef struct hashtable_list list_t; -typedef struct hashtable_pair pair_t; -typedef struct hashtable_bucket bucket_t; - -extern volatile uint32_t hashtable_seed; - -/* Implementation of the hash function */ -#include "lookup3.h" - -#define list_to_pair(list_) container_of(list_, pair_t, list) -#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list) -#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed)) - -static JSON_INLINE void list_init(list_t *list) -{ - list->next = list; - list->prev = list; -} - -static JSON_INLINE void list_insert(list_t *list, list_t *node) -{ - node->next = list; - node->prev = list->prev; - list->prev->next = node; - list->prev = node; -} - -static JSON_INLINE void list_remove(list_t *list) -{ - list->prev->next = list->next; - list->next->prev = list->prev; -} - -static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) -{ - return bucket->first == &hashtable->list && bucket->first == bucket->last; -} - -static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, - list_t *list) -{ - if(bucket_is_empty(hashtable, bucket)) - { - list_insert(&hashtable->list, list); - bucket->first = bucket->last = list; - } - else - { - list_insert(bucket->first, list); - bucket->first = list; - } -} - -static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, - const char *key, size_t hash) -{ - list_t *list; - pair_t *pair; - - if(bucket_is_empty(hashtable, bucket)) - return NULL; - - list = bucket->first; - while(1) - { - pair = list_to_pair(list); - if(pair->hash == hash && strcmp(pair->key, key) == 0) - return pair; - - if(list == bucket->last) - break; - - list = list->next; - } - - return NULL; -} - -/* returns 0 on success, -1 if key was not found */ -static int hashtable_do_del(hashtable_t *hashtable, - const char *key, size_t hash) -{ - pair_t *pair; - bucket_t *bucket; - size_t index; - - index = hash & hashmask(hashtable->order); - bucket = &hashtable->buckets[index]; - - pair = hashtable_find_pair(hashtable, bucket, key, hash); - if(!pair) - return -1; - - if(&pair->list == bucket->first && &pair->list == bucket->last) - bucket->first = bucket->last = &hashtable->list; - - else if(&pair->list == bucket->first) - bucket->first = pair->list.next; - - else if(&pair->list == bucket->last) - bucket->last = pair->list.prev; - - list_remove(&pair->list); - list_remove(&pair->ordered_list); - json_decref(pair->value); - - jsonp_free(pair); - hashtable->size--; - - return 0; -} - -static void hashtable_do_clear(hashtable_t *hashtable) -{ - list_t *list, *next; - pair_t *pair; - - for(list = hashtable->list.next; list != &hashtable->list; list = next) - { - next = list->next; - pair = list_to_pair(list); - json_decref(pair->value); - jsonp_free(pair); - } -} - -static int hashtable_do_rehash(hashtable_t *hashtable) -{ - list_t *list, *next; - pair_t *pair; - size_t i, index, new_size, new_order; - struct hashtable_bucket *new_buckets; - - new_order = hashtable->order + 1; - new_size = hashsize(new_order); - - new_buckets = jsonp_malloc(new_size * sizeof(bucket_t)); - if(!new_buckets) - return -1; - - jsonp_free(hashtable->buckets); - hashtable->buckets = new_buckets; - hashtable->order = new_order; - - for(i = 0; i < hashsize(hashtable->order); i++) - { - hashtable->buckets[i].first = hashtable->buckets[i].last = - &hashtable->list; - } - - list = hashtable->list.next; - list_init(&hashtable->list); - - for(; list != &hashtable->list; list = next) { - next = list->next; - pair = list_to_pair(list); - index = pair->hash % new_size; - insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); - } - - return 0; -} - - -int hashtable_init(hashtable_t *hashtable) -{ - size_t i; - - hashtable->size = 0; - hashtable->order = INITIAL_HASHTABLE_ORDER; - hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t)); - if(!hashtable->buckets) - return -1; - - list_init(&hashtable->list); - list_init(&hashtable->ordered_list); - - for(i = 0; i < hashsize(hashtable->order); i++) - { - hashtable->buckets[i].first = hashtable->buckets[i].last = - &hashtable->list; - } - - return 0; -} - -void hashtable_close(hashtable_t *hashtable) -{ - hashtable_do_clear(hashtable); - jsonp_free(hashtable->buckets); -} - -int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) -{ - pair_t *pair; - bucket_t *bucket; - size_t hash, index; - - /* rehash if the load ratio exceeds 1 */ - if(hashtable->size >= hashsize(hashtable->order)) - if(hashtable_do_rehash(hashtable)) - return -1; - - hash = hash_str(key); - index = hash & hashmask(hashtable->order); - bucket = &hashtable->buckets[index]; - pair = hashtable_find_pair(hashtable, bucket, key, hash); - - if(pair) - { - json_decref(pair->value); - pair->value = value; - } - else - { - /* offsetof(...) returns the size of pair_t without the last, - flexible member. This way, the correct amount is - allocated. */ - - size_t len = strlen(key); - if(len >= (size_t)-1 - offsetof(pair_t, key)) { - /* Avoid an overflow if the key is very long */ - return -1; - } - - pair = jsonp_malloc(offsetof(pair_t, key) + len + 1); - if(!pair) - return -1; - - pair->hash = hash; - strncpy(pair->key, key, len + 1); - pair->value = value; - list_init(&pair->list); - list_init(&pair->ordered_list); - - insert_to_bucket(hashtable, bucket, &pair->list); - list_insert(&hashtable->ordered_list, &pair->ordered_list); - - hashtable->size++; - } - return 0; -} - -void *hashtable_get(hashtable_t *hashtable, const char *key) -{ - pair_t *pair; - size_t hash; - bucket_t *bucket; - - hash = hash_str(key); - bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; - - pair = hashtable_find_pair(hashtable, bucket, key, hash); - if(!pair) - return NULL; - - return pair->value; -} - -int hashtable_del(hashtable_t *hashtable, const char *key) -{ - size_t hash = hash_str(key); - return hashtable_do_del(hashtable, key, hash); -} - -void hashtable_clear(hashtable_t *hashtable) -{ - size_t i; - - hashtable_do_clear(hashtable); - - for(i = 0; i < hashsize(hashtable->order); i++) - { - hashtable->buckets[i].first = hashtable->buckets[i].last = - &hashtable->list; - } - - list_init(&hashtable->list); - list_init(&hashtable->ordered_list); - hashtable->size = 0; -} - -void *hashtable_iter(hashtable_t *hashtable) -{ - return hashtable_iter_next(hashtable, &hashtable->ordered_list); -} - -void *hashtable_iter_at(hashtable_t *hashtable, const char *key) -{ - pair_t *pair; - size_t hash; - bucket_t *bucket; - - hash = hash_str(key); - bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; - - pair = hashtable_find_pair(hashtable, bucket, key, hash); - if(!pair) - return NULL; - - return &pair->ordered_list; -} - -void *hashtable_iter_next(hashtable_t *hashtable, void *iter) -{ - list_t *list = (list_t *)iter; - if(list->next == &hashtable->ordered_list) - return NULL; - return list->next; -} - -void *hashtable_iter_key(void *iter) -{ - pair_t *pair = ordered_list_to_pair((list_t *)iter); - return pair->key; -} - -void *hashtable_iter_value(void *iter) -{ - pair_t *pair = ordered_list_to_pair((list_t *)iter); - return pair->value; -} - -void hashtable_iter_set(void *iter, json_t *value) -{ - pair_t *pair = ordered_list_to_pair((list_t *)iter); - - json_decref(pair->value); - pair->value = value; -} diff --git a/compat/jansson/hashtable.h b/compat/jansson/hashtable.h deleted file mode 100644 index d4c32ae0..00000000 --- a/compat/jansson/hashtable.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef HASHTABLE_H -#define HASHTABLE_H - -#include -#include "jansson.h" - -struct hashtable_list { - struct hashtable_list *prev; - struct hashtable_list *next; -}; - -/* "pair" may be a bit confusing a name, but think of it as a - key-value pair. In this case, it just encodes some extra data, - too */ -struct hashtable_pair { - struct hashtable_list list; - struct hashtable_list ordered_list; - size_t hash; - json_t *value; - char key[1]; -}; - -struct hashtable_bucket { - struct hashtable_list *first; - struct hashtable_list *last; -}; - -typedef struct hashtable { - size_t size; - struct hashtable_bucket *buckets; - size_t order; /* hashtable has pow(2, order) buckets */ - struct hashtable_list list; - struct hashtable_list ordered_list; -} hashtable_t; - - -#define hashtable_key_to_iter(key_) \ - (&(container_of(key_, struct hashtable_pair, key)->ordered_list)) - - -/** - * hashtable_init - Initialize a hashtable object - * - * @hashtable: The (statically allocated) hashtable object - * - * Initializes a statically allocated hashtable object. The object - * should be cleared with hashtable_close when it's no longer used. - * - * Returns 0 on success, -1 on error (out of memory). - */ -int hashtable_init(hashtable_t *hashtable); - -/** - * hashtable_close - Release all resources used by a hashtable object - * - * @hashtable: The hashtable - * - * Destroys a statically allocated hashtable object. - */ -void hashtable_close(hashtable_t *hashtable); - -/** - * hashtable_set - Add/modify value in hashtable - * - * @hashtable: The hashtable object - * @key: The key - * @serial: For addition order of keys - * @value: The value - * - * If a value with the given key already exists, its value is replaced - * with the new value. Value is "stealed" in the sense that hashtable - * doesn't increment its refcount but decreases the refcount when the - * value is no longer needed. - * - * Returns 0 on success, -1 on failure (out of memory). - */ -int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value); - -/** - * hashtable_get - Get a value associated with a key - * - * @hashtable: The hashtable object - * @key: The key - * - * Returns value if it is found, or NULL otherwise. - */ -void *hashtable_get(hashtable_t *hashtable, const char *key); - -/** - * hashtable_del - Remove a value from the hashtable - * - * @hashtable: The hashtable object - * @key: The key - * - * Returns 0 on success, or -1 if the key was not found. - */ -int hashtable_del(hashtable_t *hashtable, const char *key); - -/** - * hashtable_clear - Clear hashtable - * - * @hashtable: The hashtable object - * - * Removes all items from the hashtable. - */ -void hashtable_clear(hashtable_t *hashtable); - -/** - * hashtable_iter - Iterate over hashtable - * - * @hashtable: The hashtable object - * - * Returns an opaque iterator to the first element in the hashtable. - * The iterator should be passed to hashtable_iter_* functions. - * The hashtable items are not iterated over in any particular order. - * - * There's no need to free the iterator in any way. The iterator is - * valid as long as the item that is referenced by the iterator is not - * deleted. Other values may be added or deleted. In particular, - * hashtable_iter_next() may be called on an iterator, and after that - * the key/value pair pointed by the old iterator may be deleted. - */ -void *hashtable_iter(hashtable_t *hashtable); - -/** - * hashtable_iter_at - Return an iterator at a specific key - * - * @hashtable: The hashtable object - * @key: The key that the iterator should point to - * - * Like hashtable_iter() but returns an iterator pointing to a - * specific key. - */ -void *hashtable_iter_at(hashtable_t *hashtable, const char *key); - -/** - * hashtable_iter_next - Advance an iterator - * - * @hashtable: The hashtable object - * @iter: The iterator - * - * Returns a new iterator pointing to the next element in the - * hashtable or NULL if the whole hastable has been iterated over. - */ -void *hashtable_iter_next(hashtable_t *hashtable, void *iter); - -/** - * hashtable_iter_key - Retrieve the key pointed by an iterator - * - * @iter: The iterator - */ -void *hashtable_iter_key(void *iter); - -/** - * hashtable_iter_value - Retrieve the value pointed by an iterator - * - * @iter: The iterator - */ -void *hashtable_iter_value(void *iter); - -/** - * hashtable_iter_set - Set the value pointed by an iterator - * - * @iter: The iterator - * @value: The value to set - */ -void hashtable_iter_set(void *iter, json_t *value); - -#endif diff --git a/compat/jansson/hashtable_seed.c b/compat/jansson/hashtable_seed.c deleted file mode 100644 index 8aed5406..00000000 --- a/compat/jansson/hashtable_seed.c +++ /dev/null @@ -1,277 +0,0 @@ -/* Generate sizeof(uint32_t) bytes of as random data as possible to seed - the hash function. -*/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#ifdef HAVE_STDINT_H -#include -#endif - -#ifdef HAVE_FCNTL_H -#include -#endif - -#ifdef HAVE_SCHED_H -#include -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#if defined(_WIN32) -/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */ -#include -#endif - -#include "jansson.h" - - -static uint32_t buf_to_uint32(char *data) { - size_t i; - uint32_t result = 0; - - for (i = 0; i < sizeof(uint32_t); i++) - result = (result << 8) | (unsigned char)data[i]; - - return result; -} - - - -/* /dev/urandom */ -#if !defined(_WIN32) && defined(USE_URANDOM) -static int seed_from_urandom(uint32_t *seed) { - /* Use unbuffered I/O if we have open(), close() and read(). Otherwise - fall back to fopen() */ - - char data[sizeof(uint32_t)]; - int ok; - -#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) - int urandom; - urandom = open("/dev/urandom", O_RDONLY); - if (urandom == -1) - return 1; - - ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); - close(urandom); -#else - FILE *urandom; - - urandom = fopen("/dev/urandom", "rb"); - if (!urandom) - return 1; - - ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); - fclose(urandom); -#endif - - if (!ok) - return 1; - - *seed = buf_to_uint32(data); - return 0; -} -#endif - -/* Windows Crypto API */ -#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) -#include - -typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags); -typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); -typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); - -static int seed_from_windows_cryptoapi(uint32_t *seed) -{ - HINSTANCE hAdvAPI32 = NULL; - CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; - CRYPTGENRANDOM pCryptGenRandom = NULL; - CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; - HCRYPTPROV hCryptProv = 0; - BYTE data[sizeof(uint32_t)]; - int ok; - - hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); - if(hAdvAPI32 == NULL) - return 1; - - pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); - if (!pCryptAcquireContext) - return 1; - - pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); - if (!pCryptGenRandom) - return 1; - - pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); - if (!pCryptReleaseContext) - return 1; - - if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - return 1; - - ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); - pCryptReleaseContext(hCryptProv, 0); - - if (!ok) - return 1; - - *seed = buf_to_uint32((char *)data); - return 0; -} -#endif - -/* gettimeofday() and getpid() */ -static int seed_from_timestamp_and_pid(uint32_t *seed) { -#ifdef HAVE_GETTIMEOFDAY - /* XOR of seconds and microseconds */ - struct timeval tv; - gettimeofday(&tv, NULL); - *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; -#else - /* Seconds only */ - *seed = (uint32_t)time(NULL); -#endif - - /* XOR with PID for more randomness */ -#if defined(_WIN32) - *seed ^= (uint32_t)GetCurrentProcessId(); -#elif defined(HAVE_GETPID) - *seed ^= (uint32_t)getpid(); -#endif - - return 0; -} - -static uint32_t generate_seed() { - uint32_t seed; - int done = 0; - -#if !defined(_WIN32) && defined(USE_URANDOM) - if (seed_from_urandom(&seed) == 0) - done = 1; -#endif - -#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) - if (seed_from_windows_cryptoapi(&seed) == 0) - done = 1; -#endif - - if (!done) { - /* Fall back to timestamp and PID if no better randomness is - available */ - seed_from_timestamp_and_pid(&seed); - } - - /* Make sure the seed is never zero */ - if (seed == 0) - seed = 1; - - return seed; -} - - -volatile uint32_t hashtable_seed = 0; - -#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) -static volatile char seed_initialized = 0; - -void json_object_seed(size_t seed) { - uint32_t new_seed = (uint32_t)seed; - - if (hashtable_seed == 0) { - if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { - /* Do the seeding ourselves */ - if (new_seed == 0) - new_seed = generate_seed(); - - __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); - } else { - /* Wait for another thread to do the seeding */ - do { -#ifdef HAVE_SCHED_YIELD - sched_yield(); -#endif - } while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); - } - } -} -#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) -void json_object_seed(size_t seed) { - uint32_t new_seed = (uint32_t)seed; - - if (hashtable_seed == 0) { - if (new_seed == 0) { - /* Explicit synchronization fences are not supported by the - __sync builtins, so every thread getting here has to - generate the seed value. - */ - new_seed = generate_seed(); - } - - do { - if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { - /* We were the first to seed */ - break; - } else { - /* Wait for another thread to do the seeding */ -#ifdef HAVE_SCHED_YIELD - sched_yield(); -#endif - } - } while(hashtable_seed == 0); - } -} -#elif defined(_WIN32) -static long seed_initialized = 0; -void json_object_seed(size_t seed) { - uint32_t new_seed = (uint32_t)seed; - - if (hashtable_seed == 0) { - if (InterlockedIncrement(&seed_initialized) == 1) { - /* Do the seeding ourselves */ - if (new_seed == 0) - new_seed = generate_seed(); - - hashtable_seed = new_seed; - } else { - /* Wait for another thread to do the seeding */ - do { - SwitchToThread(); - } while (hashtable_seed == 0); - } - } -} -#else -/* Fall back to a thread-unsafe version */ -void json_object_seed(size_t seed) { - uint32_t new_seed = (uint32_t)seed; - - if (hashtable_seed == 0) { - if (new_seed == 0) - new_seed = generate_seed(); - - hashtable_seed = new_seed; - } -} -#endif diff --git a/compat/jansson/jansson.h b/compat/jansson/jansson.h deleted file mode 100644 index a5927bd6..00000000 --- a/compat/jansson/jansson.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef JANSSON_H -#define JANSSON_H - -#include -#include /* for size_t */ -#include - -#include "jansson_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* version */ - -#define JANSSON_MAJOR_VERSION 2 -#define JANSSON_MINOR_VERSION 10 -#define JANSSON_MICRO_VERSION 0 - -/* Micro version is omitted if it's 0 */ -#define JANSSON_VERSION "2.10" - -/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this - for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ -#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ - (JANSSON_MINOR_VERSION << 8) | \ - (JANSSON_MICRO_VERSION << 0)) - - -/* types */ - -typedef enum { - JSON_OBJECT, - JSON_ARRAY, - JSON_STRING, - JSON_INTEGER, - JSON_REAL, - JSON_TRUE, - JSON_FALSE, - JSON_NULL -} json_type; - -typedef struct json_t { - json_type type; - size_t refcount; -} json_t; - -#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ -#if JSON_INTEGER_IS_LONG_LONG -#ifdef _WIN32 -#define JSON_INTEGER_FORMAT "I64d" -#else -#define JSON_INTEGER_FORMAT "lld" -#endif -typedef long long json_int_t; -#else -#define JSON_INTEGER_FORMAT "ld" -typedef long json_int_t; -#endif /* JSON_INTEGER_IS_LONG_LONG */ -#endif - -#define json_typeof(json) ((json)->type) -#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) -#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) -#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) -#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) -#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) -#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) -#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) -#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) -#define json_boolean_value json_is_true -#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) -#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) - -/* construction, destruction, reference counting */ - -json_t *json_object(void); -json_t *json_array(void); -json_t *json_string(const char *value); -json_t *json_stringn(const char *value, size_t len); -json_t *json_string_nocheck(const char *value); -json_t *json_stringn_nocheck(const char *value, size_t len); -json_t *json_integer(json_int_t value); -json_t *json_real(double value); -json_t *json_true(void); -json_t *json_false(void); -#define json_boolean(val) ((val) ? json_true() : json_false()) -json_t *json_null(void); - -static JSON_INLINE -json_t *json_incref(json_t *json) -{ - if(json && json->refcount != (size_t)-1) - ++json->refcount; - return json; -} - -/* do not call json_delete directly */ -void json_delete(json_t *json); - -static JSON_INLINE -void json_decref(json_t *json) -{ - if(json && json->refcount != (size_t)-1 && --json->refcount == 0) - json_delete(json); -} - -#if defined(__GNUC__) || defined(__clang__) -static JSON_INLINE -void json_decrefp(json_t **json) -{ - if(json) { - json_decref(*json); - *json = NULL; - } -} - -#define json_auto_t json_t __attribute__((cleanup(json_decrefp))) -#endif - - -/* error reporting */ - -#define JSON_ERROR_TEXT_LENGTH 160 -#define JSON_ERROR_SOURCE_LENGTH 80 - -typedef struct { - int line; - int column; - int position; - char source[JSON_ERROR_SOURCE_LENGTH]; - char text[JSON_ERROR_TEXT_LENGTH]; -} json_error_t; - - -/* getters, setters, manipulation */ - -void json_object_seed(size_t seed); -size_t json_object_size(const json_t *object); -json_t *json_object_get(const json_t *object, const char *key); -int json_object_set_new(json_t *object, const char *key, json_t *value); -int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); -int json_object_del(json_t *object, const char *key); -int json_object_clear(json_t *object); -int json_object_update(json_t *object, json_t *other); -int json_object_update_existing(json_t *object, json_t *other); -int json_object_update_missing(json_t *object, json_t *other); -void *json_object_iter(json_t *object); -void *json_object_iter_at(json_t *object, const char *key); -void *json_object_key_to_iter(const char *key); -void *json_object_iter_next(json_t *object, void *iter); -const char *json_object_iter_key(void *iter); -json_t *json_object_iter_value(void *iter); -int json_object_iter_set_new(json_t *object, void *iter, json_t *value); - -#define json_object_foreach(object, key, value) \ - for(key = json_object_iter_key(json_object_iter(object)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) - -#define json_object_foreach_safe(object, n, key, value) \ - for(key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) - -#define json_array_foreach(array, index, value) \ - for(index = 0; \ - index < json_array_size(array) && (value = json_array_get(array, index)); \ - index++) - -static JSON_INLINE -int json_object_set(json_t *object, const char *key, json_t *value) -{ - return json_object_set_new(object, key, json_incref(value)); -} - -static JSON_INLINE -int json_object_set_nocheck(json_t *object, const char *key, json_t *value) -{ - return json_object_set_new_nocheck(object, key, json_incref(value)); -} - -static JSON_INLINE -int json_object_iter_set(json_t *object, void *iter, json_t *value) -{ - return json_object_iter_set_new(object, iter, json_incref(value)); -} - -size_t json_array_size(const json_t *array); -json_t *json_array_get(const json_t *array, size_t index); -int json_array_set_new(json_t *array, size_t index, json_t *value); -int json_array_append_new(json_t *array, json_t *value); -int json_array_insert_new(json_t *array, size_t index, json_t *value); -int json_array_remove(json_t *array, size_t index); -int json_array_clear(json_t *array); -int json_array_extend(json_t *array, json_t *other); - -static JSON_INLINE -int json_array_set(json_t *array, size_t ind, json_t *value) -{ - return json_array_set_new(array, ind, json_incref(value)); -} - -static JSON_INLINE -int json_array_append(json_t *array, json_t *value) -{ - return json_array_append_new(array, json_incref(value)); -} - -static JSON_INLINE -int json_array_insert(json_t *array, size_t ind, json_t *value) -{ - return json_array_insert_new(array, ind, json_incref(value)); -} - -const char *json_string_value(const json_t *string); -size_t json_string_length(const json_t *string); -json_int_t json_integer_value(const json_t *integer); -double json_real_value(const json_t *real); -double json_number_value(const json_t *json); - -int json_string_set(json_t *string, const char *value); -int json_string_setn(json_t *string, const char *value, size_t len); -int json_string_set_nocheck(json_t *string, const char *value); -int json_string_setn_nocheck(json_t *string, const char *value, size_t len); -int json_integer_set(json_t *integer, json_int_t value); -int json_real_set(json_t *real, double value); - -/* pack, unpack */ - -json_t *json_pack(const char *fmt, ...); -json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); -json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); - -#define JSON_VALIDATE_ONLY 0x1 -#define JSON_STRICT 0x2 - -int json_unpack(json_t *root, const char *fmt, ...); -int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); -int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); - - -/* equality */ - -int json_equal(json_t *value1, json_t *value2); - - -/* copying */ - -json_t *json_copy(json_t *value); -json_t *json_deep_copy(const json_t *value); - - -/* decoding */ - -#define JSON_REJECT_DUPLICATES 0x1 -#define JSON_DISABLE_EOF_CHECK 0x2 -#define JSON_DECODE_ANY 0x4 -#define JSON_DECODE_INT_AS_REAL 0x8 -#define JSON_ALLOW_NUL 0x10 - -typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); - -json_t *json_loads(const char *input, size_t flags, json_error_t *error); -json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); -json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); -json_t *json_loadfd(int input, size_t flags, json_error_t *error); -json_t *json_load_file(const char *path, size_t flags, json_error_t *error); -json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); - - -/* encoding */ - -#define JSON_MAX_INDENT 0x1F -#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT) -#define JSON_COMPACT 0x20 -#define JSON_ENSURE_ASCII 0x40 -#define JSON_SORT_KEYS 0x80 -#define JSON_PRESERVE_ORDER 0x100 -#define JSON_ENCODE_ANY 0x200 -#define JSON_ESCAPE_SLASH 0x400 -#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) -#define JSON_EMBED 0x10000 - -typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); - -char *json_dumps(const json_t *json, size_t flags); -size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); -int json_dumpf(const json_t *json, FILE *output, size_t flags); -int json_dumpfd(const json_t *json, int output, size_t flags); -int json_dump_file(const json_t *json, const char *path, size_t flags); -int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); - -/* custom memory allocation */ - -typedef void *(*json_malloc_t)(size_t); -typedef void (*json_free_t)(void *); - -void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); -void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/compat/jansson/jansson_config.h b/compat/jansson/jansson_config.h deleted file mode 100644 index f1a5ddd2..00000000 --- a/compat/jansson/jansson_config.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2010-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - * - * - * This file specifies a part of the site-specific configuration for - * Jansson, namely those things that affect the public API in - * jansson.h. - * - * The configure script copies this file to jansson_config.h and - * replaces @var@ substitutions by values that fit your system. If you - * cannot run the configure script, you can do the value substitution - * by hand. - */ - -#ifndef JANSSON_CONFIG_H -#define JANSSON_CONFIG_H - -/* If your compiler supports the inline keyword in C, JSON_INLINE is - defined to `inline', otherwise empty. In C++, the inline is always - supported. */ -#ifdef __cplusplus -#define JSON_INLINE inline -#else -#define JSON_INLINE inline -#endif - -/* If your compiler supports the `long long` type and the strtoll() - library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, - otherwise to 0. */ -#define JSON_INTEGER_IS_LONG_LONG 1 - -/* If locale.h and localeconv() are available, define to 1, - otherwise to 0. */ -#define JSON_HAVE_LOCALECONV 1 - -/* Maximum recursion depth for parsing JSON input. - This limits the depth of e.g. array-within-array constructions. */ -#define JSON_PARSER_MAX_DEPTH 2048 - -#endif diff --git a/compat/jansson/jansson_private.h b/compat/jansson/jansson_private.h deleted file mode 100644 index 5ed96158..00000000 --- a/compat/jansson/jansson_private.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef JANSSON_PRIVATE_H -#define JANSSON_PRIVATE_H - -#include "jansson_private_config.h" -#include -#include "jansson.h" -#include "hashtable.h" -#include "strbuffer.h" - -#define container_of(ptr_, type_, member_) \ - ((type_ *)((char *)ptr_ - offsetof(type_, member_))) - -/* On some platforms, max() may already be defined */ -#ifndef max -#define max(a, b) ((a) > (b) ? (a) : (b)) -#endif - -/* va_copy is a C99 feature. In C89 implementations, it's sometimes - available as __va_copy. If not, memcpy() should do the trick. */ -#ifndef va_copy -#ifdef __va_copy -#define va_copy __va_copy -#else -#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) -#endif -#endif - -typedef struct { - json_t json; - hashtable_t hashtable; - int visited; -} json_object_t; - -typedef struct { - json_t json; - size_t size; - size_t entries; - json_t **table; - int visited; -} json_array_t; - -typedef struct { - json_t json; - char *value; - size_t length; -} json_string_t; - -typedef struct { - json_t json; - double value; -} json_real_t; - -typedef struct { - json_t json; - json_int_t value; -} json_integer_t; - -#define json_to_object(json_) container_of(json_, json_object_t, json) -#define json_to_array(json_) container_of(json_, json_array_t, json) -#define json_to_string(json_) container_of(json_, json_string_t, json) -#define json_to_real(json_) container_of(json_, json_real_t, json) -#define json_to_integer(json_) container_of(json_, json_integer_t, json) - -/* Create a string by taking ownership of an existing buffer */ -json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); - -/* Error message formatting */ -void jsonp_error_init(json_error_t *error, const char *source); -void jsonp_error_set_source(json_error_t *error, const char *source); -void jsonp_error_set(json_error_t *error, int line, int column, - size_t position, const char *msg, ...); -void jsonp_error_vset(json_error_t *error, int line, int column, - size_t position, const char *msg, va_list ap); - -/* Locale independent string<->double conversions */ -int jsonp_strtod(strbuffer_t *strbuffer, double *out); -int jsonp_dtostr(char *buffer, size_t size, double value, int prec); - -/* Wrappers for custom memory functions */ -void* jsonp_malloc(size_t size); -void jsonp_free(void *ptr); -char *jsonp_strndup(const char *str, size_t length); -char *jsonp_strdup(const char *str); -char *jsonp_strndup(const char *str, size_t len); - - -/* Windows compatibility */ -#if defined(_WIN32) || defined(WIN32) -# if defined(_MSC_VER) /* MS compiller */ -# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */ -# define snprintf _snprintf -# endif -# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */ -# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) -# endif -# else /* Other Windows compiller, old definition */ -# define snprintf _snprintf -# define vsnprintf _vsnprintf -# endif -#endif - -#endif diff --git a/compat/jansson/jansson_private_config.h b/compat/jansson/jansson_private_config.h deleted file mode 100644 index 2b17f182..00000000 --- a/compat/jansson/jansson_private_config.h +++ /dev/null @@ -1,159 +0,0 @@ -/* jansson_private_config.h. Generated from jansson_private_config.h.in by configure. */ -/* jansson_private_config.h.in. Generated from configure.ac by autoheader. */ - -/* Define to 1 if gcc's __atomic builtins are available */ -#define HAVE_ATOMIC_BUILTINS 1 - -/* Define to 1 if you have the `close' function. */ -#define HAVE_CLOSE 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ENDIAN_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* Define to 1 if you have the `getpid' function. */ -#define HAVE_GETPID 1 - -/* Define to 1 if you have the `gettimeofday' function. */ -#define HAVE_GETTIMEOFDAY 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the `localeconv' function. */ -#define HAVE_LOCALECONV 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_LOCALE_H 1 - -/* Define to 1 if the system has the type 'long long int'. */ -#define HAVE_LONG_LONG_INT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the `open' function. */ -#define HAVE_OPEN 1 - -/* Define to 1 if you have the `read' function. */ -#define HAVE_READ 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SCHED_H 1 - -/* Define to 1 if you have the `sched_yield' function. */ -#define HAVE_SCHED_YIELD 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strtoll' function. */ -#define HAVE_STRTOLL 1 - -/* Define to 1 if gcc's __sync builtins are available */ -#define HAVE_SYNC_BUILTINS 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if the system has the type 'unsigned long long int'. */ -#define HAVE_UNSIGNED_LONG_LONG_INT 1 - -/* Number of buckets new object hashtables contain is 2 raised to this power. - E.g. 3 -> 2^3 = 8. */ -#define INITIAL_HASHTABLE_ORDER 3 - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" - -/* Name of package */ -#define PACKAGE "jansson" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "petri@digip.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "jansson" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "jansson 2.9" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "jansson" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "2.9" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if /dev/urandom should be used for seeding the hash function */ -#define USE_URANDOM 1 - -/* Define to 1 if CryptGenRandom should be used for seeding the hash function - */ -#define USE_WINDOWS_CRYPTOAPI 1 - -/* Version number of package */ -#define VERSION "2.9" - -/* Define for Solaris 2.5.1 so the uint32_t typedef from , - , or is not used. If the typedef were allowed, the - #define below would cause a syntax error. */ -/* #undef _UINT32_T */ - -/* Define for Solaris 2.5.1 so the uint8_t typedef from , - , or is not used. If the typedef were allowed, the - #define below would cause a syntax error. */ -/* #undef _UINT8_T */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - -/* Define to the type of a signed integer type of width exactly 32 bits if - such a type exists and the standard includes do not define it. */ -/* #undef int32_t */ - -/* Define to the type of an unsigned integer type of width exactly 16 bits if - such a type exists and the standard includes do not define it. */ -/* #undef uint16_t */ - -/* Define to the type of an unsigned integer type of width exactly 32 bits if - such a type exists and the standard includes do not define it. */ -/* #undef uint32_t */ - -/* Define to the type of an unsigned integer type of width exactly 8 bits if - such a type exists and the standard includes do not define it. */ -/* #undef uint8_t */ diff --git a/compat/jansson/load.c b/compat/jansson/load.c deleted file mode 100644 index c212489a..00000000 --- a/compat/jansson/load.c +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include "jansson_private.h" - -#include -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "jansson.h" -#include "strbuffer.h" -#include "utf.h" - -#define STREAM_STATE_OK 0 -#define STREAM_STATE_EOF -1 -#define STREAM_STATE_ERROR -2 - -#define TOKEN_INVALID -1 -#define TOKEN_EOF 0 -#define TOKEN_STRING 256 -#define TOKEN_INTEGER 257 -#define TOKEN_REAL 258 -#define TOKEN_TRUE 259 -#define TOKEN_FALSE 260 -#define TOKEN_NULL 261 - -/* Locale independent versions of isxxx() functions */ -#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') -#define l_islower(c) ('a' <= (c) && (c) <= 'z') -#define l_isalpha(c) (l_isupper(c) || l_islower(c)) -#define l_isdigit(c) ('0' <= (c) && (c) <= '9') -#define l_isxdigit(c) \ - (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) - -/* Read one byte from stream, convert to unsigned char, then int, and - return. return EOF on end of file. This corresponds to the - behaviour of fgetc(). */ -typedef int (*get_func)(void *data); - -typedef struct { - get_func get; - void *data; - char buffer[5]; - size_t buffer_pos; - int state; - int line; - int column, last_column; - size_t position; -} stream_t; - -typedef struct { - stream_t stream; - strbuffer_t saved_text; - size_t flags; - size_t depth; - int token; - union { - struct { - char *val; - size_t len; - } string; - json_int_t integer; - double real; - } value; -} lex_t; - -#define stream_to_lex(stream) container_of(stream, lex_t, stream) - - -/*** error reporting ***/ - -static void error_set(json_error_t *error, const lex_t *lex, - const char *msg, ...) -{ - va_list ap; - char msg_text[JSON_ERROR_TEXT_LENGTH]; - char msg_with_context[JSON_ERROR_TEXT_LENGTH]; - - int line = -1, col = -1; - size_t pos = 0; - const char *result = msg_text; - - if(!error) - return; - - va_start(ap, msg); - vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); - msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; - va_end(ap); - - if(lex) - { - const char *saved_text = strbuffer_value(&lex->saved_text); - - line = lex->stream.line; - col = lex->stream.column; - pos = lex->stream.position; - - if(saved_text && saved_text[0]) - { - if(lex->saved_text.length <= 20) { - snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, - "%s near '%s'", msg_text, saved_text); - msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; - result = msg_with_context; - } - } - else - { - if(lex->stream.state == STREAM_STATE_ERROR) { - /* No context for UTF-8 decoding errors */ - result = msg_text; - } - else { - snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, - "%s near end of file", msg_text); - msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; - result = msg_with_context; - } - } - } - - jsonp_error_set(error, line, col, pos, "%s", result); -} - - -/*** lexical analyzer ***/ - -static void -stream_init(stream_t *stream, get_func get, void *data) -{ - stream->get = get; - stream->data = data; - stream->buffer[0] = '\0'; - stream->buffer_pos = 0; - - stream->state = STREAM_STATE_OK; - stream->line = 1; - stream->column = 0; - stream->position = 0; -} - -static int stream_get(stream_t *stream, json_error_t *error) -{ - int c; - - if(stream->state != STREAM_STATE_OK) - return stream->state; - - if(!stream->buffer[stream->buffer_pos]) - { - c = stream->get(stream->data); - if(c == EOF) { - stream->state = STREAM_STATE_EOF; - return STREAM_STATE_EOF; - } - - stream->buffer[0] = c; - stream->buffer_pos = 0; - - if(0x80 <= c && c <= 0xFF) - { - /* multi-byte UTF-8 sequence */ - size_t i, count; - - count = utf8_check_first(c); - if(!count) - goto out; - - assert(count >= 2); - - for(i = 1; i < count; i++) - stream->buffer[i] = stream->get(stream->data); - - if(!utf8_check_full(stream->buffer, count, NULL)) - goto out; - - stream->buffer[count] = '\0'; - } - else - stream->buffer[1] = '\0'; - } - - c = stream->buffer[stream->buffer_pos++]; - - stream->position++; - if(c == '\n') { - stream->line++; - stream->last_column = stream->column; - stream->column = 0; - } - else if(utf8_check_first(c)) { - /* track the Unicode character column, so increment only if - this is the first character of a UTF-8 sequence */ - stream->column++; - } - - return c; - -out: - stream->state = STREAM_STATE_ERROR; - error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c); - return STREAM_STATE_ERROR; -} - -static void stream_unget(stream_t *stream, int c) -{ - if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) - return; - - stream->position--; - if(c == '\n') { - stream->line--; - stream->column = stream->last_column; - } - else if(utf8_check_first(c)) - stream->column--; - - assert(stream->buffer_pos > 0); - stream->buffer_pos--; - assert(stream->buffer[stream->buffer_pos] == c); -} - - -static int lex_get(lex_t *lex, json_error_t *error) -{ - return stream_get(&lex->stream, error); -} - -static void lex_save(lex_t *lex, int c) -{ - strbuffer_append_byte(&lex->saved_text, c); -} - -static int lex_get_save(lex_t *lex, json_error_t *error) -{ - int c = stream_get(&lex->stream, error); - if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) - lex_save(lex, c); - return c; -} - -static void lex_unget(lex_t *lex, int c) -{ - stream_unget(&lex->stream, c); -} - -static void lex_unget_unsave(lex_t *lex, int c) -{ - if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { - /* Since we treat warnings as errors, when assertions are turned - * off the "d" variable would be set but never used. Which is - * treated as an error by GCC. - */ - #ifndef NDEBUG - char d; - #endif - stream_unget(&lex->stream, c); - #ifndef NDEBUG - d = - #endif - strbuffer_pop(&lex->saved_text); - assert(c == d); - } -} - -static void lex_save_cached(lex_t *lex) -{ - while(lex->stream.buffer[lex->stream.buffer_pos] != '\0') - { - lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); - lex->stream.buffer_pos++; - lex->stream.position++; - } -} - -static void lex_free_string(lex_t *lex) -{ - jsonp_free(lex->value.string.val); - lex->value.string.val = NULL; - lex->value.string.len = 0; -} - -/* assumes that str points to 'u' plus at least 4 valid hex digits */ -static int32_t decode_unicode_escape(const char *str) -{ - int i; - int32_t value = 0; - - assert(str[0] == 'u'); - - for(i = 1; i <= 4; i++) { - char c = str[i]; - value <<= 4; - if(l_isdigit(c)) - value += c - '0'; - else if(l_islower(c)) - value += c - 'a' + 10; - else if(l_isupper(c)) - value += c - 'A' + 10; - else - return -1; - } - - return value; -} - -static void lex_scan_string(lex_t *lex, json_error_t *error) -{ - int c; - const char *p; - char *t; - int i; - - lex->value.string.val = NULL; - lex->token = TOKEN_INVALID; - - c = lex_get_save(lex, error); - - while(c != '"') { - if(c == STREAM_STATE_ERROR) - goto out; - - else if(c == STREAM_STATE_EOF) { - error_set(error, lex, "premature end of input"); - goto out; - } - - else if(0 <= c && c <= 0x1F) { - /* control character */ - lex_unget_unsave(lex, c); - if(c == '\n') - error_set(error, lex, "unexpected newline"); - else - error_set(error, lex, "control character 0x%x", c); - goto out; - } - - else if(c == '\\') { - c = lex_get_save(lex, error); - if(c == 'u') { - c = lex_get_save(lex, error); - for(i = 0; i < 4; i++) { - if(!l_isxdigit(c)) { - error_set(error, lex, "invalid escape"); - goto out; - } - c = lex_get_save(lex, error); - } - } - else if(c == '"' || c == '\\' || c == '/' || c == 'b' || - c == 'f' || c == 'n' || c == 'r' || c == 't') - c = lex_get_save(lex, error); - else { - error_set(error, lex, "invalid escape"); - goto out; - } - } - else - c = lex_get_save(lex, error); - } - - /* the actual value is at most of the same length as the source - string, because: - - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte - - a single \uXXXX escape (length 6) is converted to at most 3 bytes - - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair - are converted to 4 bytes - */ - t = jsonp_malloc(lex->saved_text.length + 1); - if(!t) { - /* this is not very nice, since TOKEN_INVALID is returned */ - goto out; - } - lex->value.string.val = t; - - /* + 1 to skip the " */ - p = strbuffer_value(&lex->saved_text) + 1; - - while(*p != '"') { - if(*p == '\\') { - p++; - if(*p == 'u') { - size_t length; - int32_t value; - - value = decode_unicode_escape(p); - if(value < 0) { - error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1); - goto out; - } - p += 5; - - if(0xD800 <= value && value <= 0xDBFF) { - /* surrogate pair */ - if(*p == '\\' && *(p + 1) == 'u') { - int32_t value2 = decode_unicode_escape(++p); - if(value2 < 0) { - error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1); - goto out; - } - p += 5; - - if(0xDC00 <= value2 && value2 <= 0xDFFF) { - /* valid second surrogate */ - value = - ((value - 0xD800) << 10) + - (value2 - 0xDC00) + - 0x10000; - } - else { - /* invalid second surrogate */ - error_set(error, lex, - "invalid Unicode '\\u%04X\\u%04X'", - value, value2); - goto out; - } - } - else { - /* no second surrogate */ - error_set(error, lex, "invalid Unicode '\\u%04X'", - value); - goto out; - } - } - else if(0xDC00 <= value && value <= 0xDFFF) { - error_set(error, lex, "invalid Unicode '\\u%04X'", value); - goto out; - } - - if(utf8_encode(value, t, &length)) - assert(0); - t += length; - } - else { - switch(*p) { - case '"': case '\\': case '/': - *t = *p; break; - case 'b': *t = '\b'; break; - case 'f': *t = '\f'; break; - case 'n': *t = '\n'; break; - case 'r': *t = '\r'; break; - case 't': *t = '\t'; break; - default: assert(0); - } - t++; - p++; - } - } - else - *(t++) = *(p++); - } - *t = '\0'; - lex->value.string.len = t - lex->value.string.val; - lex->token = TOKEN_STRING; - return; - -out: - lex_free_string(lex); -} - -#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ -#if JSON_INTEGER_IS_LONG_LONG -#ifdef _MSC_VER /* Microsoft Visual Studio */ -#define json_strtoint _strtoi64 -#else -#define json_strtoint strtoll -#endif -#else -#define json_strtoint strtol -#endif -#endif - -static int lex_scan_number(lex_t *lex, int c, json_error_t *error) -{ - const char *saved_text; - char *end; - double doubleval; - - lex->token = TOKEN_INVALID; - - if(c == '-') - c = lex_get_save(lex, error); - - if(c == '0') { - c = lex_get_save(lex, error); - if(l_isdigit(c)) { - lex_unget_unsave(lex, c); - goto out; - } - } - else if(l_isdigit(c)) { - do - c = lex_get_save(lex, error); - while(l_isdigit(c)); - } - else { - lex_unget_unsave(lex, c); - goto out; - } - - if(!(lex->flags & JSON_DECODE_INT_AS_REAL) && - c != '.' && c != 'E' && c != 'e') - { - json_int_t intval; - - lex_unget_unsave(lex, c); - - saved_text = strbuffer_value(&lex->saved_text); - - errno = 0; - intval = json_strtoint(saved_text, &end, 10); - if(errno == ERANGE) { - if(intval < 0) - error_set(error, lex, "too big negative integer"); - else - error_set(error, lex, "too big integer"); - goto out; - } - - assert(end == saved_text + lex->saved_text.length); - - lex->token = TOKEN_INTEGER; - lex->value.integer = intval; - return 0; - } - - if(c == '.') { - c = lex_get(lex, error); - if(!l_isdigit(c)) { - lex_unget(lex, c); - goto out; - } - lex_save(lex, c); - - do - c = lex_get_save(lex, error); - while(l_isdigit(c)); - } - - if(c == 'E' || c == 'e') { - c = lex_get_save(lex, error); - if(c == '+' || c == '-') - c = lex_get_save(lex, error); - - if(!l_isdigit(c)) { - lex_unget_unsave(lex, c); - goto out; - } - - do - c = lex_get_save(lex, error); - while(l_isdigit(c)); - } - - lex_unget_unsave(lex, c); - - if(jsonp_strtod(&lex->saved_text, &doubleval)) { - error_set(error, lex, "real number overflow"); - goto out; - } - - lex->token = TOKEN_REAL; - lex->value.real = doubleval; - return 0; - -out: - return -1; -} - -static int lex_scan(lex_t *lex, json_error_t *error) -{ - int c; - - strbuffer_clear(&lex->saved_text); - - if(lex->token == TOKEN_STRING) - lex_free_string(lex); - - do - c = lex_get(lex, error); - while(c == ' ' || c == '\t' || c == '\n' || c == '\r'); - - if(c == STREAM_STATE_EOF) { - lex->token = TOKEN_EOF; - goto out; - } - - if(c == STREAM_STATE_ERROR) { - lex->token = TOKEN_INVALID; - goto out; - } - - lex_save(lex, c); - - if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') - lex->token = c; - - else if(c == '"') - lex_scan_string(lex, error); - - else if(l_isdigit(c) || c == '-') { - if(lex_scan_number(lex, c, error)) - goto out; - } - - else if(l_isalpha(c)) { - /* eat up the whole identifier for clearer error messages */ - const char *saved_text; - - do - c = lex_get_save(lex, error); - while(l_isalpha(c)); - lex_unget_unsave(lex, c); - - saved_text = strbuffer_value(&lex->saved_text); - - if(strcmp(saved_text, "true") == 0) - lex->token = TOKEN_TRUE; - else if(strcmp(saved_text, "false") == 0) - lex->token = TOKEN_FALSE; - else if(strcmp(saved_text, "null") == 0) - lex->token = TOKEN_NULL; - else - lex->token = TOKEN_INVALID; - } - - else { - /* save the rest of the input UTF-8 sequence to get an error - message of valid UTF-8 */ - lex_save_cached(lex); - lex->token = TOKEN_INVALID; - } - -out: - return lex->token; -} - -static char *lex_steal_string(lex_t *lex, size_t *out_len) -{ - char *result = NULL; - if(lex->token == TOKEN_STRING) { - result = lex->value.string.val; - *out_len = lex->value.string.len; - lex->value.string.val = NULL; - lex->value.string.len = 0; - } - return result; -} - -static int lex_init(lex_t *lex, get_func get, size_t flags, void *data) -{ - stream_init(&lex->stream, get, data); - if(strbuffer_init(&lex->saved_text)) - return -1; - - lex->flags = flags; - lex->token = TOKEN_INVALID; - return 0; -} - -static void lex_close(lex_t *lex) -{ - if(lex->token == TOKEN_STRING) - lex_free_string(lex); - strbuffer_close(&lex->saved_text); -} - - -/*** parser ***/ - -static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); - -static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) -{ - json_t *object = json_object(); - if(!object) - return NULL; - - lex_scan(lex, error); - if(lex->token == '}') - return object; - - while(1) { - char *key; - size_t len; - json_t *value; - - if(lex->token != TOKEN_STRING) { - error_set(error, lex, "string or '}' expected"); - goto error; - } - - key = lex_steal_string(lex, &len); - if(!key) - return NULL; - if (memchr(key, '\0', len)) { - jsonp_free(key); - error_set(error, lex, "NUL byte in object key not supported"); - goto error; - } - - if(flags & JSON_REJECT_DUPLICATES) { - if(json_object_get(object, key)) { - jsonp_free(key); - error_set(error, lex, "duplicate object key"); - goto error; - } - } - - lex_scan(lex, error); - if(lex->token != ':') { - jsonp_free(key); - error_set(error, lex, "':' expected"); - goto error; - } - - lex_scan(lex, error); - value = parse_value(lex, flags, error); - if(!value) { - jsonp_free(key); - goto error; - } - - if(json_object_set_nocheck(object, key, value)) { - jsonp_free(key); - json_decref(value); - goto error; - } - - json_decref(value); - jsonp_free(key); - - lex_scan(lex, error); - if(lex->token != ',') - break; - - lex_scan(lex, error); - } - - if(lex->token != '}') { - error_set(error, lex, "'}' expected"); - goto error; - } - - return object; - -error: - json_decref(object); - return NULL; -} - -static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) -{ - json_t *array = json_array(); - if(!array) - return NULL; - - lex_scan(lex, error); - if(lex->token == ']') - return array; - - while(lex->token) { - json_t *elem = parse_value(lex, flags, error); - if(!elem) - goto error; - - if(json_array_append(array, elem)) { - json_decref(elem); - goto error; - } - json_decref(elem); - - lex_scan(lex, error); - if(lex->token != ',') - break; - - lex_scan(lex, error); - } - - if(lex->token != ']') { - error_set(error, lex, "']' expected"); - goto error; - } - - return array; - -error: - json_decref(array); - return NULL; -} - -static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) -{ - json_t *json; - - lex->depth++; - if(lex->depth > JSON_PARSER_MAX_DEPTH) { - error_set(error, lex, "maximum parsing depth reached"); - return NULL; - } - - switch(lex->token) { - case TOKEN_STRING: { - const char *value = lex->value.string.val; - size_t len = lex->value.string.len; - - if(!(flags & JSON_ALLOW_NUL)) { - if(memchr(value, '\0', len)) { - error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL"); - return NULL; - } - } - - json = jsonp_stringn_nocheck_own(value, len); - if(json) { - lex->value.string.val = NULL; - lex->value.string.len = 0; - } - break; - } - - case TOKEN_INTEGER: { - json = json_integer(lex->value.integer); - break; - } - - case TOKEN_REAL: { - json = json_real(lex->value.real); - break; - } - - case TOKEN_TRUE: - json = json_true(); - break; - - case TOKEN_FALSE: - json = json_false(); - break; - - case TOKEN_NULL: - json = json_null(); - break; - - case '{': - json = parse_object(lex, flags, error); - break; - - case '[': - json = parse_array(lex, flags, error); - break; - - case TOKEN_INVALID: - error_set(error, lex, "invalid token"); - return NULL; - - default: - error_set(error, lex, "unexpected token"); - return NULL; - } - - if(!json) - return NULL; - - lex->depth--; - return json; -} - -static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) -{ - json_t *result; - - lex->depth = 0; - - lex_scan(lex, error); - if(!(flags & JSON_DECODE_ANY)) { - if(lex->token != '[' && lex->token != '{') { - error_set(error, lex, "'[' or '{' expected"); - return NULL; - } - } - - result = parse_value(lex, flags, error); - if(!result) - return NULL; - - if(!(flags & JSON_DISABLE_EOF_CHECK)) { - lex_scan(lex, error); - if(lex->token != TOKEN_EOF) { - error_set(error, lex, "end of file expected"); - json_decref(result); - return NULL; - } - } - - if(error) { - /* Save the position even though there was no error */ - error->position = (int)lex->stream.position; - } - - return result; -} - -typedef struct -{ - const char *data; - size_t pos; -} string_data_t; - -static int string_get(void *data) -{ - char c; - string_data_t *stream = (string_data_t *)data; - c = stream->data[stream->pos]; - if(c == '\0') - return EOF; - else - { - stream->pos++; - return (unsigned char)c; - } -} - -json_t *json_loads(const char *string, size_t flags, json_error_t *error) -{ - lex_t lex; - json_t *result; - string_data_t stream_data; - - jsonp_error_init(error, ""); - - if (string == NULL) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - stream_data.data = string; - stream_data.pos = 0; - - if(lex_init(&lex, string_get, flags, (void *)&stream_data)) - return NULL; - - result = parse_json(&lex, flags, error); - - lex_close(&lex); - return result; -} - -typedef struct -{ - const char *data; - size_t len; - size_t pos; -} buffer_data_t; - -static int buffer_get(void *data) -{ - char c; - buffer_data_t *stream = data; - if(stream->pos >= stream->len) - return EOF; - - c = stream->data[stream->pos]; - stream->pos++; - return (unsigned char)c; -} - -json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) -{ - lex_t lex; - json_t *result; - buffer_data_t stream_data; - - jsonp_error_init(error, ""); - - if (buffer == NULL) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - stream_data.data = buffer; - stream_data.pos = 0; - stream_data.len = buflen; - - if(lex_init(&lex, buffer_get, flags, (void *)&stream_data)) - return NULL; - - result = parse_json(&lex, flags, error); - - lex_close(&lex); - return result; -} - -json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) -{ - lex_t lex; - const char *source; - json_t *result; - - if(input == stdin) - source = ""; - else - source = ""; - - jsonp_error_init(error, source); - - if (input == NULL) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - if(lex_init(&lex, (get_func)fgetc, flags, input)) - return NULL; - - result = parse_json(&lex, flags, error); - - lex_close(&lex); - return result; -} - -static int fd_get_func(int *fd) -{ - uint8_t c; -#ifdef HAVE_UNISTD_H - if (read(*fd, &c, 1) == 1) - return c; -#endif - return EOF; -} - -json_t *json_loadfd(int input, size_t flags, json_error_t *error) -{ - lex_t lex; - const char *source; - json_t *result; - -#ifdef HAVE_UNISTD_H - if(input == STDIN_FILENO) - source = ""; - else -#endif - source = ""; - - jsonp_error_init(error, source); - - if (input < 0) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - if(lex_init(&lex, (get_func)fd_get_func, flags, &input)) - return NULL; - - result = parse_json(&lex, flags, error); - - lex_close(&lex); - return result; -} - -json_t *json_load_file(const char *path, size_t flags, json_error_t *error) -{ - json_t *result; - FILE *fp; - - jsonp_error_init(error, path); - - if (path == NULL) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - fp = fopen(path, "rb"); - if(!fp) - { - error_set(error, NULL, "unable to open %s: %s", - path, strerror(errno)); - return NULL; - } - - result = json_loadf(fp, flags, error); - - fclose(fp); - return result; -} - -#define MAX_BUF_LEN 1024 - -typedef struct -{ - char data[MAX_BUF_LEN]; - size_t len; - size_t pos; - json_load_callback_t callback; - void *arg; -} callback_data_t; - -static int callback_get(void *data) -{ - char c; - callback_data_t *stream = data; - - if(stream->pos >= stream->len) { - stream->pos = 0; - stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); - if(stream->len == 0 || stream->len == (size_t)-1) - return EOF; - } - - c = stream->data[stream->pos]; - stream->pos++; - return (unsigned char)c; -} - -json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) -{ - lex_t lex; - json_t *result; - - callback_data_t stream_data; - - memset(&stream_data, 0, sizeof(stream_data)); - stream_data.callback = callback; - stream_data.arg = arg; - - jsonp_error_init(error, ""); - - if (callback == NULL) { - error_set(error, NULL, "wrong arguments"); - return NULL; - } - - if(lex_init(&lex, (get_func)callback_get, flags, &stream_data)) - return NULL; - - result = parse_json(&lex, flags, error); - - lex_close(&lex); - return result; -} diff --git a/compat/jansson/lookup3.h b/compat/jansson/lookup3.h deleted file mode 100644 index 522a41ae..00000000 --- a/compat/jansson/lookup3.h +++ /dev/null @@ -1,381 +0,0 @@ -/* -------------------------------------------------------------------------------- -lookup3.c, by Bob Jenkins, May 2006, Public Domain. - -These are functions for producing 32-bit hashes for hash table lookup. -hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() -are externally useful functions. Routines to test the hash are included -if SELF_TEST is defined. You can use this free for any purpose. It's in -the public domain. It has no warranty. - -You probably want to use hashlittle(). hashlittle() and hashbig() -hash byte arrays. hashlittle() is is faster than hashbig() on -little-endian machines. Intel and AMD are little-endian machines. -On second thought, you probably want hashlittle2(), which is identical to -hashlittle() except it returns two 32-bit hashes for the price of one. -You could implement hashbig2() if you wanted but I haven't bothered here. - -If you want to find a hash of, say, exactly 7 integers, do - a = i1; b = i2; c = i3; - mix(a,b,c); - a += i4; b += i5; c += i6; - mix(a,b,c); - a += i7; - final(a,b,c); -then use c as the hash value. If you have a variable length array of -4-byte integers to hash, use hashword(). If you have a byte array (like -a character string), use hashlittle(). If you have several byte arrays, or -a mix of things, see the comments above hashlittle(). - -Why is this so big? I read 12 bytes at a time into 3 4-byte integers, -then mix those integers. This is fast (you can do a lot more thorough -mixing with 12*3 instructions on 3 integers than you can with 3 instructions -on 1 byte), but shoehorning those bytes into integers efficiently is messy. -------------------------------------------------------------------------------- -*/ - -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_STDINT_H -#include /* defines uint32_t etc */ -#endif - -#ifdef HAVE_SYS_PARAM_H -#include /* attempt to define endianness */ -#endif - -#ifdef HAVE_ENDIAN_H -# include /* attempt to define endianness */ -#endif - -/* - * My best guess at if you are big-endian or little-endian. This may - * need adjustment. - */ -#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ - __BYTE_ORDER == __LITTLE_ENDIAN) || \ - (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) -# define HASH_LITTLE_ENDIAN 1 -# define HASH_BIG_ENDIAN 0 -#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ - __BYTE_ORDER == __BIG_ENDIAN) || \ - (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 1 -#else -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 0 -#endif - -#define hashsize(n) ((uint32_t)1<<(n)) -#define hashmask(n) (hashsize(n)-1) -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -/* -------------------------------------------------------------------------------- -mix -- mix 3 32-bit values reversibly. - -This is reversible, so any information in (a,b,c) before mix() is -still in (a,b,c) after mix(). - -If four pairs of (a,b,c) inputs are run through mix(), or through -mix() in reverse, there are at least 32 bits of the output that -are sometimes the same for one pair and different for another pair. -This was tested for: -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - is commonly produced by subtraction) look like a single 1-bit - difference. -* the base values were pseudorandom, all zero but one bit set, or - all zero plus a counter that starts at zero. - -Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that -satisfy this are - 4 6 8 16 19 4 - 9 15 3 18 27 15 - 14 9 3 7 17 3 -Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing -for "differ" defined as + with a one-bit base and a two-bit delta. I -used http://burtleburtle.net/bob/hash/avalanche.html to choose -the operations, constants, and arrangements of the variables. - -This does not achieve avalanche. There are input bits of (a,b,c) -that fail to affect some output bits of (a,b,c), especially of a. The -most thoroughly mixed value is c, but it doesn't really even achieve -avalanche in c. - -This allows some parallelism. Read-after-writes are good at doubling -the number of bits affected, so the goal of mixing pulls in the opposite -direction as the goal of parallelism. I did what I could. Rotates -seem to cost as much as shifts on every machine I could lay my hands -on, and rotates are much kinder to the top and bottom bits, so I used -rotates. -------------------------------------------------------------------------------- -*/ -#define mix(a,b,c) \ -{ \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c,16); c += b; \ - b -= a; b ^= rot(a,19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} - -/* -------------------------------------------------------------------------------- -final -- final mixing of 3 32-bit values (a,b,c) into c - -Pairs of (a,b,c) values differing in only a few bits will usually -produce values of c that look totally different. This was tested for -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - is commonly produced by subtraction) look like a single 1-bit - difference. -* the base values were pseudorandom, all zero but one bit set, or - all zero plus a counter that starts at zero. - -These constants passed: - 14 11 25 16 4 14 24 - 12 14 25 16 4 14 24 -and these came close: - 4 8 15 26 3 22 24 - 10 8 15 26 3 22 24 - 11 8 15 26 3 22 24 -------------------------------------------------------------------------------- -*/ -#define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - -/* -------------------------------------------------------------------------------- -hashlittle() -- hash a variable-length key into a 32-bit value - k : the key (the unaligned variable-length array of bytes) - length : the length of the key, counting by bytes - initval : can be any 4-byte value -Returns a 32-bit value. Every bit of the key affects every bit of -the return value. Two keys differing by one or two bits will have -totally different hash values. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 32 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. - -If you are hashing n strings (uint8_t **)k, do it like this: - for (i=0, h=0; i 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). - */ -#ifndef NO_MASKING_TRICK - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : return c; /* zero length strings require no mixing */ - } - -#else /* make valgrind happy */ - - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ - case 1 : a+=k8[0]; break; - case 0 : return c; - } - -#endif /* !valgrind */ - - } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) - { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=k[0]; - break; - case 1 : a+=k8[0]; - break; - case 0 : return c; /* zero length requires no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch(length) /* all the case statements fall through */ - { - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9 : c+=k[8]; - case 8 : b+=((uint32_t)k[7])<<24; - case 7 : b+=((uint32_t)k[6])<<16; - case 6 : b+=((uint32_t)k[5])<<8; - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3])<<24; - case 3 : a+=((uint32_t)k[2])<<16; - case 2 : a+=((uint32_t)k[1])<<8; - case 1 : a+=k[0]; - break; - case 0 : return c; - } - } - - final(a,b,c); - return c; -} diff --git a/compat/jansson/memory.c b/compat/jansson/memory.c deleted file mode 100644 index a2be5d23..00000000 --- a/compat/jansson/memory.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * Copyright (c) 2011-2012 Basile Starynkevitch - * - * Jansson is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include - -#include "jansson.h" -#include "jansson_private.h" - -/* C89 allows these to be macros */ -#undef malloc -#undef free - -/* memory function pointers */ -static json_malloc_t do_malloc = malloc; -static json_free_t do_free = free; - -void *jsonp_malloc(size_t size) -{ - if(!size) - return NULL; - - return (*do_malloc)(size); -} - -void jsonp_free(void *ptr) -{ - if(!ptr) - return; - - (*do_free)(ptr); -} - -char *jsonp_strdup(const char *str) -{ - return jsonp_strndup(str, strlen(str)); -} - -char *jsonp_strndup(const char *str, size_t len) -{ - char *new_str; - - new_str = jsonp_malloc(len + 1); - if(!new_str) - return NULL; - - memcpy(new_str, str, len); - new_str[len] = '\0'; - return new_str; -} - -void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) -{ - do_malloc = malloc_fn; - do_free = free_fn; -} - -void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) -{ - if (malloc_fn) - *malloc_fn = do_malloc; - if (free_fn) - *free_fn = do_free; -} diff --git a/compat/jansson/pack_unpack.c b/compat/jansson/pack_unpack.c deleted file mode 100644 index 2a2da353..00000000 --- a/compat/jansson/pack_unpack.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * Copyright (c) 2011-2012 Graeme Smecher - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include "jansson.h" -#include "jansson_private.h" -#include "utf.h" - -typedef struct { - int line; - int column; - size_t pos; - char token; -} token_t; - -typedef struct { - const char *start; - const char *fmt; - token_t prev_token; - token_t token; - token_t next_token; - json_error_t *error; - size_t flags; - int line; - int column; - size_t pos; -} scanner_t; - -#define token(scanner) ((scanner)->token.token) - -static const char * const type_names[] = { - "object", - "array", - "string", - "integer", - "real", - "true", - "false", - "null" -}; - -#define type_name(x) type_names[json_typeof(x)] - -static const char unpack_value_starters[] = "{[siIbfFOon"; - -static void scanner_init(scanner_t *s, json_error_t *error, - size_t flags, const char *fmt) -{ - s->error = error; - s->flags = flags; - s->fmt = s->start = fmt; - memset(&s->prev_token, 0, sizeof(token_t)); - memset(&s->token, 0, sizeof(token_t)); - memset(&s->next_token, 0, sizeof(token_t)); - s->line = 1; - s->column = 0; - s->pos = 0; -} - -static void next_token(scanner_t *s) -{ - const char *t; - s->prev_token = s->token; - - if(s->next_token.line) { - s->token = s->next_token; - s->next_token.line = 0; - return; - } - - t = s->fmt; - s->column++; - s->pos++; - - /* skip space and ignored chars */ - while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { - if(*t == '\n') { - s->line++; - s->column = 1; - } - else - s->column++; - - s->pos++; - t++; - } - - s->token.token = *t; - s->token.line = s->line; - s->token.column = s->column; - s->token.pos = s->pos; - - t++; - s->fmt = t; -} - -static void prev_token(scanner_t *s) -{ - s->next_token = s->token; - s->token = s->prev_token; -} - -static void set_error(scanner_t *s, const char *source, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - - jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, - fmt, ap); - - jsonp_error_set_source(s->error, source); - - va_end(ap); -} - -static json_t *pack(scanner_t *s, va_list *ap); - - -/* ours will be set to 1 if jsonp_free() must be called for the result - afterwards */ -static char *read_string(scanner_t *s, va_list *ap, - const char *purpose, size_t *out_len, int *ours) -{ - char t; - strbuffer_t strbuff; - const char *str; - size_t length; - - next_token(s); - t = token(s); - prev_token(s); - - if(t != '#' && t != '%' && t != '+') { - /* Optimize the simple case */ - str = va_arg(*ap, const char *); - - if(!str) { - set_error(s, "", "NULL string argument"); - return NULL; - } - - length = strlen(str); - - if(!utf8_check_string(str, length)) { - set_error(s, "", "Invalid UTF-8 %s", purpose); - return NULL; - } - - *out_len = length; - *ours = 0; - return (char *)str; - } - - strbuffer_init(&strbuff); - - while(1) { - str = va_arg(*ap, const char *); - if(!str) { - set_error(s, "", "NULL string argument"); - strbuffer_close(&strbuff); - return NULL; - } - - next_token(s); - - if(token(s) == '#') { - length = va_arg(*ap, int); - } - else if(token(s) == '%') { - length = va_arg(*ap, size_t); - } - else { - prev_token(s); - length = strlen(str); - } - - if(strbuffer_append_bytes(&strbuff, str, length) == -1) { - set_error(s, "", "Out of memory"); - strbuffer_close(&strbuff); - return NULL; - } - - next_token(s); - if(token(s) != '+') { - prev_token(s); - break; - } - } - - if(!utf8_check_string(strbuff.value, strbuff.length)) { - set_error(s, "", "Invalid UTF-8 %s", purpose); - strbuffer_close(&strbuff); - return NULL; - } - - *out_len = strbuff.length; - *ours = 1; - return strbuffer_steal_value(&strbuff); -} - -static json_t *pack_object(scanner_t *s, va_list *ap) -{ - json_t *object = json_object(); - next_token(s); - - while(token(s) != '}') { - char *key; - size_t len; - int ours; - json_t *value; - - if(!token(s)) { - set_error(s, "", "Unexpected end of format string"); - goto error; - } - - if(token(s) != 's') { - set_error(s, "", "Expected format 's', got '%c'", token(s)); - goto error; - } - - key = read_string(s, ap, "object key", &len, &ours); - if(!key) - goto error; - - next_token(s); - - value = pack(s, ap); - if(!value) { - if(ours) - jsonp_free(key); - - goto error; - } - - if(json_object_set_new_nocheck(object, key, value)) { - set_error(s, "", "Unable to add key \"%s\"", key); - if(ours) - jsonp_free(key); - - goto error; - } - - if(ours) - jsonp_free(key); - - next_token(s); - } - - return object; - -error: - json_decref(object); - return NULL; -} - -static json_t *pack_array(scanner_t *s, va_list *ap) -{ - json_t *array = json_array(); - next_token(s); - - while(token(s) != ']') { - json_t *value; - - if(!token(s)) { - set_error(s, "", "Unexpected end of format string"); - goto error; - } - - value = pack(s, ap); - if(!value) - goto error; - - if(json_array_append_new(array, value)) { - set_error(s, "", "Unable to append to array"); - goto error; - } - - next_token(s); - } - return array; - -error: - json_decref(array); - return NULL; -} - -static json_t *pack_string(scanner_t *s, va_list *ap) -{ - char *str; - size_t len; - int ours; - int nullable; - - next_token(s); - nullable = token(s) == '?'; - if (!nullable) - prev_token(s); - - str = read_string(s, ap, "string", &len, &ours); - if (!str) { - return nullable ? json_null() : NULL; - } else if (ours) { - return jsonp_stringn_nocheck_own(str, len); - } else { - return json_stringn_nocheck(str, len); - } -} - -static json_t *pack(scanner_t *s, va_list *ap) -{ - switch(token(s)) { - case '{': - return pack_object(s, ap); - - case '[': - return pack_array(s, ap); - - case 's': /* string */ - return pack_string(s, ap); - - case 'n': /* null */ - return json_null(); - - case 'b': /* boolean */ - return va_arg(*ap, int) ? json_true() : json_false(); - - case 'i': /* integer from int */ - return json_integer(va_arg(*ap, int)); - - case 'I': /* integer from json_int_t */ - return json_integer(va_arg(*ap, json_int_t)); - - case 'f': /* real */ - return json_real(va_arg(*ap, double)); - - case 'O': /* a json_t object; increments refcount */ - { - int nullable; - json_t *json; - - next_token(s); - nullable = token(s) == '?'; - if (!nullable) - prev_token(s); - - json = va_arg(*ap, json_t *); - if (!json && nullable) { - return json_null(); - } else { - return json_incref(json); - } - } - - case 'o': /* a json_t object; doesn't increment refcount */ - { - int nullable; - json_t *json; - - next_token(s); - nullable = token(s) == '?'; - if (!nullable) - prev_token(s); - - json = va_arg(*ap, json_t *); - if (!json && nullable) { - return json_null(); - } else { - return json; - } - } - - default: - set_error(s, "", "Unexpected format character '%c'", - token(s)); - return NULL; - } -} - -static int unpack(scanner_t *s, json_t *root, va_list *ap); - -static int unpack_object(scanner_t *s, json_t *root, va_list *ap) -{ - int ret = -1; - int strict = 0; - int gotopt = 0; - - /* Use a set (emulated by a hashtable) to check that all object - keys are accessed. Checking that the correct number of keys - were accessed is not enough, as the same key can be unpacked - multiple times. - */ - hashtable_t key_set; - - if(hashtable_init(&key_set)) { - set_error(s, "", "Out of memory"); - return -1; - } - - if(root && !json_is_object(root)) { - set_error(s, "", "Expected object, got %s", - type_name(root)); - goto out; - } - next_token(s); - - while(token(s) != '}') { - const char *key; - json_t *value; - int opt = 0; - - if(strict != 0) { - set_error(s, "", "Expected '}' after '%c', got '%c'", - (strict == 1 ? '!' : '*'), token(s)); - goto out; - } - - if(!token(s)) { - set_error(s, "", "Unexpected end of format string"); - goto out; - } - - if(token(s) == '!' || token(s) == '*') { - strict = (token(s) == '!' ? 1 : -1); - next_token(s); - continue; - } - - if(token(s) != 's') { - set_error(s, "", "Expected format 's', got '%c'", token(s)); - goto out; - } - - key = va_arg(*ap, const char *); - if(!key) { - set_error(s, "", "NULL object key"); - goto out; - } - - next_token(s); - - if(token(s) == '?') { - opt = gotopt = 1; - next_token(s); - } - - if(!root) { - /* skipping */ - value = NULL; - } - else { - value = json_object_get(root, key); - if(!value && !opt) { - set_error(s, "", "Object item not found: %s", key); - goto out; - } - } - - if(unpack(s, value, ap)) - goto out; - - hashtable_set(&key_set, key, json_null()); - next_token(s); - } - - if(strict == 0 && (s->flags & JSON_STRICT)) - strict = 1; - - if(root && strict == 1) { - /* We need to check that all non optional items have been parsed */ - const char *key; - int have_unrecognized_keys = 0; - strbuffer_t unrecognized_keys; - json_t *value; - long unpacked = 0; - if (gotopt) { - /* We have optional keys, we need to iter on each key */ - json_object_foreach(root, key, value) { - if(!hashtable_get(&key_set, key)) { - unpacked++; - - /* Save unrecognized keys for the error message */ - if (!have_unrecognized_keys) { - strbuffer_init(&unrecognized_keys); - have_unrecognized_keys = 1; - } else { - strbuffer_append_bytes(&unrecognized_keys, ", ", 2); - } - strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); - } - } - } else { - /* No optional keys, we can just compare the number of items */ - unpacked = (long)json_object_size(root) - (long)key_set.size; - } - if (unpacked) { - if (!gotopt) { - /* Save unrecognized keys for the error message */ - json_object_foreach(root, key, value) { - if(!hashtable_get(&key_set, key)) { - if (!have_unrecognized_keys) { - strbuffer_init(&unrecognized_keys); - have_unrecognized_keys = 1; - } else { - strbuffer_append_bytes(&unrecognized_keys, ", ", 2); - } - strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); - } - } - } - set_error(s, "", - "%li object item(s) left unpacked: %s", - unpacked, strbuffer_value(&unrecognized_keys)); - strbuffer_close(&unrecognized_keys); - goto out; - } - } - - ret = 0; - -out: - hashtable_close(&key_set); - return ret; -} - -static int unpack_array(scanner_t *s, json_t *root, va_list *ap) -{ - size_t i = 0; - int strict = 0; - - if(root && !json_is_array(root)) { - set_error(s, "", "Expected array, got %s", type_name(root)); - return -1; - } - next_token(s); - - while(token(s) != ']') { - json_t *value; - - if(strict != 0) { - set_error(s, "", "Expected ']' after '%c', got '%c'", - (strict == 1 ? '!' : '*'), - token(s)); - return -1; - } - - if(!token(s)) { - set_error(s, "", "Unexpected end of format string"); - return -1; - } - - if(token(s) == '!' || token(s) == '*') { - strict = (token(s) == '!' ? 1 : -1); - next_token(s); - continue; - } - - if(!strchr(unpack_value_starters, token(s))) { - set_error(s, "", "Unexpected format character '%c'", - token(s)); - return -1; - } - - if(!root) { - /* skipping */ - value = NULL; - } - else { - value = json_array_get(root, i); - if(!value) { - set_error(s, "", "Array index %lu out of range", - (unsigned long)i); - return -1; - } - } - - if(unpack(s, value, ap)) - return -1; - - next_token(s); - i++; - } - - if(strict == 0 && (s->flags & JSON_STRICT)) - strict = 1; - - if(root && strict == 1 && i != json_array_size(root)) { - long diff = (long)json_array_size(root) - (long)i; - set_error(s, "", "%li array item(s) left unpacked", diff); - return -1; - } - - return 0; -} - -static int unpack(scanner_t *s, json_t *root, va_list *ap) -{ - switch(token(s)) - { - case '{': - return unpack_object(s, root, ap); - - case '[': - return unpack_array(s, root, ap); - - case 's': - if(root && !json_is_string(root)) { - set_error(s, "", "Expected string, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - const char **str_target; - size_t *len_target = NULL; - - str_target = va_arg(*ap, const char **); - if(!str_target) { - set_error(s, "", "NULL string argument"); - return -1; - } - - next_token(s); - - if(token(s) == '%') { - len_target = va_arg(*ap, size_t *); - if(!len_target) { - set_error(s, "", "NULL string length argument"); - return -1; - } - } - else - prev_token(s); - - if(root) { - *str_target = json_string_value(root); - if(len_target) - *len_target = json_string_length(root); - } - } - return 0; - - case 'i': - if(root && !json_is_integer(root)) { - set_error(s, "", "Expected integer, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - int *target = va_arg(*ap, int*); - if(root) - *target = (int)json_integer_value(root); - } - - return 0; - - case 'I': - if(root && !json_is_integer(root)) { - set_error(s, "", "Expected integer, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - json_int_t *target = va_arg(*ap, json_int_t*); - if(root) - *target = json_integer_value(root); - } - - return 0; - - case 'b': - if(root && !json_is_boolean(root)) { - set_error(s, "", "Expected true or false, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - int *target = va_arg(*ap, int*); - if(root) - *target = json_is_true(root); - } - - return 0; - - case 'f': - if(root && !json_is_real(root)) { - set_error(s, "", "Expected real, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - double *target = va_arg(*ap, double*); - if(root) - *target = json_real_value(root); - } - - return 0; - - case 'F': - if(root && !json_is_number(root)) { - set_error(s, "", "Expected real or integer, got %s", - type_name(root)); - return -1; - } - - if(!(s->flags & JSON_VALIDATE_ONLY)) { - double *target = va_arg(*ap, double*); - if(root) - *target = json_number_value(root); - } - - return 0; - - case 'O': - if(root && !(s->flags & JSON_VALIDATE_ONLY)) - json_incref(root); - /* Fall through */ - - case 'o': - if(!(s->flags & JSON_VALIDATE_ONLY)) { - json_t **target = va_arg(*ap, json_t**); - if(root) - *target = root; - } - - return 0; - - case 'n': - /* Never assign, just validate */ - if(root && !json_is_null(root)) { - set_error(s, "", "Expected null, got %s", - type_name(root)); - return -1; - } - return 0; - - default: - set_error(s, "", "Unexpected format character '%c'", - token(s)); - return -1; - } -} - -json_t *json_vpack_ex(json_error_t *error, size_t flags, - const char *fmt, va_list ap) -{ - scanner_t s; - va_list ap_copy; - json_t *value; - - if(!fmt || !*fmt) { - jsonp_error_init(error, ""); - jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); - return NULL; - } - jsonp_error_init(error, NULL); - - scanner_init(&s, error, flags, fmt); - next_token(&s); - - va_copy(ap_copy, ap); - value = pack(&s, &ap_copy); - va_end(ap_copy); - - if(!value) - return NULL; - - next_token(&s); - if(token(&s)) { - json_decref(value); - set_error(&s, "", "Garbage after format string"); - return NULL; - } - - return value; -} - -json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) -{ - json_t *value; - va_list ap; - - va_start(ap, fmt); - value = json_vpack_ex(error, flags, fmt, ap); - va_end(ap); - - return value; -} - -json_t *json_pack(const char *fmt, ...) -{ - json_t *value; - va_list ap; - - va_start(ap, fmt); - value = json_vpack_ex(NULL, 0, fmt, ap); - va_end(ap); - - return value; -} - -int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, - const char *fmt, va_list ap) -{ - scanner_t s; - va_list ap_copy; - - if(!root) { - jsonp_error_init(error, ""); - jsonp_error_set(error, -1, -1, 0, "NULL root value"); - return -1; - } - - if(!fmt || !*fmt) { - jsonp_error_init(error, ""); - jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); - return -1; - } - jsonp_error_init(error, NULL); - - scanner_init(&s, error, flags, fmt); - next_token(&s); - - va_copy(ap_copy, ap); - if(unpack(&s, root, &ap_copy)) { - va_end(ap_copy); - return -1; - } - va_end(ap_copy); - - next_token(&s); - if(token(&s)) { - set_error(&s, "", "Garbage after format string"); - return -1; - } - - return 0; -} - -int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) -{ - int ret; - va_list ap; - - va_start(ap, fmt); - ret = json_vunpack_ex(root, error, flags, fmt, ap); - va_end(ap); - - return ret; -} - -int json_unpack(json_t *root, const char *fmt, ...) -{ - int ret; - va_list ap; - - va_start(ap, fmt); - ret = json_vunpack_ex(root, NULL, 0, fmt, ap); - va_end(ap); - - return ret; -} diff --git a/compat/jansson/strbuffer.c b/compat/jansson/strbuffer.c deleted file mode 100644 index 5e8c0039..00000000 --- a/compat/jansson/strbuffer.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include "jansson_private.h" -#include "strbuffer.h" - -#define STRBUFFER_MIN_SIZE 16 -#define STRBUFFER_FACTOR 2 -#define STRBUFFER_SIZE_MAX ((size_t)-1) - -int strbuffer_init(strbuffer_t *strbuff) -{ - strbuff->size = STRBUFFER_MIN_SIZE; - strbuff->length = 0; - - strbuff->value = jsonp_malloc(strbuff->size); - if(!strbuff->value) - return -1; - - /* initialize to empty */ - strbuff->value[0] = '\0'; - return 0; -} - -void strbuffer_close(strbuffer_t *strbuff) -{ - if(strbuff->value) - jsonp_free(strbuff->value); - - strbuff->size = 0; - strbuff->length = 0; - strbuff->value = NULL; -} - -void strbuffer_clear(strbuffer_t *strbuff) -{ - strbuff->length = 0; - strbuff->value[0] = '\0'; -} - -const char *strbuffer_value(const strbuffer_t *strbuff) -{ - return strbuff->value; -} - -char *strbuffer_steal_value(strbuffer_t *strbuff) -{ - char *result = strbuff->value; - strbuff->value = NULL; - return result; -} - -int strbuffer_append_byte(strbuffer_t *strbuff, char byte) -{ - return strbuffer_append_bytes(strbuff, &byte, 1); -} - -int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) -{ - if(size >= strbuff->size - strbuff->length) - { - size_t new_size; - char *new_value; - - /* avoid integer overflow */ - if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR - || size > STRBUFFER_SIZE_MAX - 1 - || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) - return -1; - - new_size = max(strbuff->size * STRBUFFER_FACTOR, - strbuff->length + size + 1); - - new_value = jsonp_malloc(new_size); - if(!new_value) - return -1; - - memcpy(new_value, strbuff->value, strbuff->length); - - jsonp_free(strbuff->value); - strbuff->value = new_value; - strbuff->size = new_size; - } - - memcpy(strbuff->value + strbuff->length, data, size); - strbuff->length += size; - strbuff->value[strbuff->length] = '\0'; - - return 0; -} - -char strbuffer_pop(strbuffer_t *strbuff) -{ - if(strbuff->length > 0) { - char c = strbuff->value[--strbuff->length]; - strbuff->value[strbuff->length] = '\0'; - return c; - } - else - return '\0'; -} diff --git a/compat/jansson/strbuffer.h b/compat/jansson/strbuffer.h deleted file mode 100644 index 615b7f5f..00000000 --- a/compat/jansson/strbuffer.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef STRBUFFER_H -#define STRBUFFER_H - -#include - -typedef struct { - char *value; - size_t length; /* bytes used */ - size_t size; /* bytes allocated */ -} strbuffer_t; - -int strbuffer_init(strbuffer_t *strbuff); -void strbuffer_close(strbuffer_t *strbuff); - -void strbuffer_clear(strbuffer_t *strbuff); - -const char *strbuffer_value(const strbuffer_t *strbuff); - -/* Steal the value and close the strbuffer */ -char *strbuffer_steal_value(strbuffer_t *strbuff); - -int strbuffer_append_byte(strbuffer_t *strbuff, char byte); -int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); - -char strbuffer_pop(strbuffer_t *strbuff); - -#endif diff --git a/compat/jansson/strconv.c b/compat/jansson/strconv.c deleted file mode 100644 index 8075481e..00000000 --- a/compat/jansson/strconv.c +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include -#include -#include -#ifdef __MINGW32__ -#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */ -#endif -#include "jansson_private.h" -#include "strbuffer.h" - -/* need jansson_private_config.h to get the correct snprintf */ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef __MINGW32__ -#define strtod __strtod -#endif - -#if JSON_HAVE_LOCALECONV -#include - -/* - - This code assumes that the decimal separator is exactly one - character. - - - If setlocale() is called by another thread between the call to - localeconv() and the call to sprintf() or strtod(), the result may - be wrong. setlocale() is not thread-safe and should not be used - this way. Multi-threaded programs should use uselocale() instead. -*/ - -static void to_locale(strbuffer_t *strbuffer) -{ - const char *point; - char *pos; - - point = localeconv()->decimal_point; - if(*point == '.') { - /* No conversion needed */ - return; - } - - pos = strchr(strbuffer->value, '.'); - if(pos) - *pos = *point; -} - -static void from_locale(char *buffer) -{ - const char *point; - char *pos; - - point = localeconv()->decimal_point; - if(*point == '.') { - /* No conversion needed */ - return; - } - - pos = strchr(buffer, *point); - if(pos) - *pos = '.'; -} -#endif - -int jsonp_strtod(strbuffer_t *strbuffer, double *out) -{ - double value; - char *end; - -#if JSON_HAVE_LOCALECONV - to_locale(strbuffer); -#endif - - errno = 0; - value = strtod(strbuffer->value, &end); - assert(end == strbuffer->value + strbuffer->length); - - if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { - /* Overflow */ - return -1; - } - - *out = value; - return 0; -} - -int jsonp_dtostr(char *buffer, size_t size, double value, int precision) -{ - int ret; - char *start, *end; - size_t length; - - if (precision == 0) - precision = 17; - - ret = snprintf(buffer, size, "%.*g", precision, value); - if(ret < 0) - return -1; - - length = (size_t)ret; - if(length >= size) - return -1; - -#if JSON_HAVE_LOCALECONV - from_locale(buffer); -#endif - - /* Make sure there's a dot or 'e' in the output. Otherwise - a real is converted to an integer when decoding */ - if(strchr(buffer, '.') == NULL && - strchr(buffer, 'e') == NULL) - { - if(length + 3 >= size) { - /* No space to append ".0" */ - return -1; - } - buffer[length] = '.'; - buffer[length + 1] = '0'; - buffer[length + 2] = '\0'; - length += 2; - } - - /* Remove leading '+' from positive exponent. Also remove leading - zeros from exponents (added by some printf() implementations) */ - start = strchr(buffer, 'e'); - if(start) { - start++; - end = start + 1; - - if(*start == '-') - start++; - - while(*end == '0') - end++; - - if(end != start) { - memmove(start, end, length - (size_t)(end - buffer)); - length -= (size_t)(end - start); - } - } - - return (int)length; -} diff --git a/compat/jansson/utf.c b/compat/jansson/utf.c deleted file mode 100644 index be966cbd..00000000 --- a/compat/jansson/utf.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include "utf.h" - -int utf8_encode(int32_t codepoint, char *buffer, size_t *size) -{ - if(codepoint < 0) - return -1; - else if(codepoint < 0x80) - { - buffer[0] = (char)codepoint; - *size = 1; - } - else if(codepoint < 0x800) - { - buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); - buffer[1] = 0x80 + ((codepoint & 0x03F)); - *size = 2; - } - else if(codepoint < 0x10000) - { - buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); - buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); - buffer[2] = 0x80 + ((codepoint & 0x003F)); - *size = 3; - } - else if(codepoint <= 0x10FFFF) - { - buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); - buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); - buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); - buffer[3] = 0x80 + ((codepoint & 0x00003F)); - *size = 4; - } - else - return -1; - - return 0; -} - -size_t utf8_check_first(char byte) -{ - unsigned char u = (unsigned char)byte; - - if(u < 0x80) - return 1; - - if(0x80 <= u && u <= 0xBF) { - /* second, third or fourth byte of a multi-byte - sequence, i.e. a "continuation byte" */ - return 0; - } - else if(u == 0xC0 || u == 0xC1) { - /* overlong encoding of an ASCII byte */ - return 0; - } - else if(0xC2 <= u && u <= 0xDF) { - /* 2-byte sequence */ - return 2; - } - - else if(0xE0 <= u && u <= 0xEF) { - /* 3-byte sequence */ - return 3; - } - else if(0xF0 <= u && u <= 0xF4) { - /* 4-byte sequence */ - return 4; - } - else { /* u >= 0xF5 */ - /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid - UTF-8 */ - return 0; - } -} - -size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) -{ - size_t i; - int32_t value = 0; - unsigned char u = (unsigned char)buffer[0]; - - if(size == 2) - { - value = u & 0x1F; - } - else if(size == 3) - { - value = u & 0xF; - } - else if(size == 4) - { - value = u & 0x7; - } - else - return 0; - - for(i = 1; i < size; i++) - { - u = (unsigned char)buffer[i]; - - if(u < 0x80 || u > 0xBF) { - /* not a continuation byte */ - return 0; - } - - value = (value << 6) + (u & 0x3F); - } - - if(value > 0x10FFFF) { - /* not in Unicode range */ - return 0; - } - - else if(0xD800 <= value && value <= 0xDFFF) { - /* invalid code point (UTF-16 surrogate halves) */ - return 0; - } - - else if((size == 2 && value < 0x80) || - (size == 3 && value < 0x800) || - (size == 4 && value < 0x10000)) { - /* overlong encoding */ - return 0; - } - - if(codepoint) - *codepoint = value; - - return 1; -} - -const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) -{ - size_t count; - int32_t value; - - if(!bufsize) - return buffer; - - count = utf8_check_first(buffer[0]); - if(count <= 0) - return NULL; - - if(count == 1) - value = (unsigned char)buffer[0]; - else - { - if(count > bufsize || !utf8_check_full(buffer, count, &value)) - return NULL; - } - - if(codepoint) - *codepoint = value; - - return buffer + count; -} - -int utf8_check_string(const char *string, size_t length) -{ - size_t i; - - for(i = 0; i < length; i++) - { - size_t count = utf8_check_first(string[i]); - if(count == 0) - return 0; - else if(count > 1) - { - if(count > length - i) - return 0; - - if(!utf8_check_full(&string[i], count, NULL)) - return 0; - - i += count - 1; - } - } - - return 1; -} diff --git a/compat/jansson/utf.h b/compat/jansson/utf.h deleted file mode 100644 index e182df78..00000000 --- a/compat/jansson/utf.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef UTF_H -#define UTF_H - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_STDINT_H -#include -#endif - -int utf8_encode(int32_t codepoint, char *buffer, size_t *size); - -size_t utf8_check_first(char byte); -size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint); -const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint); - -int utf8_check_string(const char *string, size_t length); - -#endif diff --git a/compat/jansson/value.c b/compat/jansson/value.c deleted file mode 100644 index 82e60a5f..00000000 --- a/compat/jansson/value.c +++ /dev/null @@ -1,1045 +0,0 @@ -/* - * Copyright (c) 2009-2016 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#ifdef HAVE_STDINT_H -#include -#endif - -#include "jansson.h" -#include "hashtable.h" -#include "jansson_private.h" -#include "utf.h" - -/* Work around nonstandard isnan() and isinf() implementations */ -#ifndef isnan -#ifndef __sun -static JSON_INLINE int isnan(double x) { return x != x; } -#endif -#endif -#ifndef isinf -static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } -#endif - -static JSON_INLINE void json_init(json_t *json, json_type type) -{ - json->type = type; - json->refcount = 1; -} - - -/*** object ***/ - -extern volatile uint32_t hashtable_seed; - -json_t *json_object(void) -{ - json_object_t *object = jsonp_malloc(sizeof(json_object_t)); - if(!object) - return NULL; - - if (!hashtable_seed) { - /* Autoseed */ - json_object_seed(0); - } - - json_init(&object->json, JSON_OBJECT); - - if(hashtable_init(&object->hashtable)) - { - jsonp_free(object); - return NULL; - } - - object->visited = 0; - - return &object->json; -} - -static void json_delete_object(json_object_t *object) -{ - hashtable_close(&object->hashtable); - jsonp_free(object); -} - -size_t json_object_size(const json_t *json) -{ - json_object_t *object; - - if(!json_is_object(json)) - return 0; - - object = json_to_object(json); - return object->hashtable.size; -} - -json_t *json_object_get(const json_t *json, const char *key) -{ - json_object_t *object; - - if(!key || !json_is_object(json)) - return NULL; - - object = json_to_object(json); - return hashtable_get(&object->hashtable, key); -} - -int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) -{ - json_object_t *object; - - if(!value) - return -1; - - if(!key || !json_is_object(json) || json == value) - { - json_decref(value); - return -1; - } - object = json_to_object(json); - - if(hashtable_set(&object->hashtable, key, value)) - { - json_decref(value); - return -1; - } - - return 0; -} - -int json_object_set_new(json_t *json, const char *key, json_t *value) -{ - if(!key || !utf8_check_string(key, strlen(key))) - { - json_decref(value); - return -1; - } - - return json_object_set_new_nocheck(json, key, value); -} - -int json_object_del(json_t *json, const char *key) -{ - json_object_t *object; - - if(!key || !json_is_object(json)) - return -1; - - object = json_to_object(json); - return hashtable_del(&object->hashtable, key); -} - -int json_object_clear(json_t *json) -{ - json_object_t *object; - - if(!json_is_object(json)) - return -1; - - object = json_to_object(json); - hashtable_clear(&object->hashtable); - - return 0; -} - -int json_object_update(json_t *object, json_t *other) -{ - const char *key; - json_t *value; - - if(!json_is_object(object) || !json_is_object(other)) - return -1; - - json_object_foreach(other, key, value) { - if(json_object_set_nocheck(object, key, value)) - return -1; - } - - return 0; -} - -int json_object_update_existing(json_t *object, json_t *other) -{ - const char *key; - json_t *value; - - if(!json_is_object(object) || !json_is_object(other)) - return -1; - - json_object_foreach(other, key, value) { - if(json_object_get(object, key)) - json_object_set_nocheck(object, key, value); - } - - return 0; -} - -int json_object_update_missing(json_t *object, json_t *other) -{ - const char *key; - json_t *value; - - if(!json_is_object(object) || !json_is_object(other)) - return -1; - - json_object_foreach(other, key, value) { - if(!json_object_get(object, key)) - json_object_set_nocheck(object, key, value); - } - - return 0; -} - -void *json_object_iter(json_t *json) -{ - json_object_t *object; - - if(!json_is_object(json)) - return NULL; - - object = json_to_object(json); - return hashtable_iter(&object->hashtable); -} - -void *json_object_iter_at(json_t *json, const char *key) -{ - json_object_t *object; - - if(!key || !json_is_object(json)) - return NULL; - - object = json_to_object(json); - return hashtable_iter_at(&object->hashtable, key); -} - -void *json_object_iter_next(json_t *json, void *iter) -{ - json_object_t *object; - - if(!json_is_object(json) || iter == NULL) - return NULL; - - object = json_to_object(json); - return hashtable_iter_next(&object->hashtable, iter); -} - -const char *json_object_iter_key(void *iter) -{ - if(!iter) - return NULL; - - return hashtable_iter_key(iter); -} - -json_t *json_object_iter_value(void *iter) -{ - if(!iter) - return NULL; - - return (json_t *)hashtable_iter_value(iter); -} - -int json_object_iter_set_new(json_t *json, void *iter, json_t *value) -{ - if(!json_is_object(json) || !iter || !value) - return -1; - - hashtable_iter_set(iter, value); - return 0; -} - -void *json_object_key_to_iter(const char *key) -{ - if(!key) - return NULL; - - return hashtable_key_to_iter(key); -} - -static int json_object_equal(json_t *object1, json_t *object2) -{ - const char *key; - json_t *value1, *value2; - - if(json_object_size(object1) != json_object_size(object2)) - return 0; - - json_object_foreach(object1, key, value1) { - value2 = json_object_get(object2, key); - - if(!json_equal(value1, value2)) - return 0; - } - - return 1; -} - -static json_t *json_object_copy(json_t *object) -{ - json_t *result; - - const char *key; - json_t *value; - - result = json_object(); - if(!result) - return NULL; - - json_object_foreach(object, key, value) - json_object_set_nocheck(result, key, value); - - return result; -} - -static json_t *json_object_deep_copy(const json_t *object) -{ - json_t *result; - void *iter; - - result = json_object(); - if(!result) - return NULL; - - /* Cannot use json_object_foreach because object has to be cast - non-const */ - iter = json_object_iter((json_t *)object); - while(iter) { - const char *key; - const json_t *value; - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); - - json_object_set_new_nocheck(result, key, json_deep_copy(value)); - iter = json_object_iter_next((json_t *)object, iter); - } - - return result; -} - - -/*** array ***/ - -json_t *json_array(void) -{ - json_array_t *array = jsonp_malloc(sizeof(json_array_t)); - if(!array) - return NULL; - json_init(&array->json, JSON_ARRAY); - - array->entries = 0; - array->size = 8; - - array->table = jsonp_malloc(array->size * sizeof(json_t *)); - if(!array->table) { - jsonp_free(array); - return NULL; - } - - array->visited = 0; - - return &array->json; -} - -static void json_delete_array(json_array_t *array) -{ - size_t i; - - for(i = 0; i < array->entries; i++) - json_decref(array->table[i]); - - jsonp_free(array->table); - jsonp_free(array); -} - -size_t json_array_size(const json_t *json) -{ - if(!json_is_array(json)) - return 0; - - return json_to_array(json)->entries; -} - -json_t *json_array_get(const json_t *json, size_t index) -{ - json_array_t *array; - if(!json_is_array(json)) - return NULL; - array = json_to_array(json); - - if(index >= array->entries) - return NULL; - - return array->table[index]; -} - -int json_array_set_new(json_t *json, size_t index, json_t *value) -{ - json_array_t *array; - - if(!value) - return -1; - - if(!json_is_array(json) || json == value) - { - json_decref(value); - return -1; - } - array = json_to_array(json); - - if(index >= array->entries) - { - json_decref(value); - return -1; - } - - json_decref(array->table[index]); - array->table[index] = value; - - return 0; -} - -static void array_move(json_array_t *array, size_t dest, - size_t src, size_t count) -{ - memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); -} - -static void array_copy(json_t **dest, size_t dpos, - json_t **src, size_t spos, - size_t count) -{ - memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); -} - -static json_t **json_array_grow(json_array_t *array, - size_t amount, - int copy) -{ - size_t new_size; - json_t **old_table, **new_table; - - if(array->entries + amount <= array->size) - return array->table; - - old_table = array->table; - - new_size = max(array->size + amount, array->size * 2); - new_table = jsonp_malloc(new_size * sizeof(json_t *)); - if(!new_table) - return NULL; - - array->size = new_size; - array->table = new_table; - - if(copy) { - array_copy(array->table, 0, old_table, 0, array->entries); - jsonp_free(old_table); - return array->table; - } - - return old_table; -} - -int json_array_append_new(json_t *json, json_t *value) -{ - json_array_t *array; - - if(!value) - return -1; - - if(!json_is_array(json) || json == value) - { - json_decref(value); - return -1; - } - array = json_to_array(json); - - if(!json_array_grow(array, 1, 1)) { - json_decref(value); - return -1; - } - - array->table[array->entries] = value; - array->entries++; - - return 0; -} - -int json_array_insert_new(json_t *json, size_t index, json_t *value) -{ - json_array_t *array; - json_t **old_table; - - if(!value) - return -1; - - if(!json_is_array(json) || json == value) { - json_decref(value); - return -1; - } - array = json_to_array(json); - - if(index > array->entries) { - json_decref(value); - return -1; - } - - old_table = json_array_grow(array, 1, 0); - if(!old_table) { - json_decref(value); - return -1; - } - - if(old_table != array->table) { - array_copy(array->table, 0, old_table, 0, index); - array_copy(array->table, index + 1, old_table, index, - array->entries - index); - jsonp_free(old_table); - } - else - array_move(array, index + 1, index, array->entries - index); - - array->table[index] = value; - array->entries++; - - return 0; -} - -int json_array_remove(json_t *json, size_t index) -{ - json_array_t *array; - - if(!json_is_array(json)) - return -1; - array = json_to_array(json); - - if(index >= array->entries) - return -1; - - json_decref(array->table[index]); - - /* If we're removing the last element, nothing has to be moved */ - if(index < array->entries - 1) - array_move(array, index, index + 1, array->entries - index - 1); - - array->entries--; - - return 0; -} - -int json_array_clear(json_t *json) -{ - json_array_t *array; - size_t i; - - if(!json_is_array(json)) - return -1; - array = json_to_array(json); - - for(i = 0; i < array->entries; i++) - json_decref(array->table[i]); - - array->entries = 0; - return 0; -} - -int json_array_extend(json_t *json, json_t *other_json) -{ - json_array_t *array, *other; - size_t i; - - if(!json_is_array(json) || !json_is_array(other_json)) - return -1; - array = json_to_array(json); - other = json_to_array(other_json); - - if(!json_array_grow(array, other->entries, 1)) - return -1; - - for(i = 0; i < other->entries; i++) - json_incref(other->table[i]); - - array_copy(array->table, array->entries, other->table, 0, other->entries); - - array->entries += other->entries; - return 0; -} - -static int json_array_equal(json_t *array1, json_t *array2) -{ - size_t i, size; - - size = json_array_size(array1); - if(size != json_array_size(array2)) - return 0; - - for(i = 0; i < size; i++) - { - json_t *value1, *value2; - - value1 = json_array_get(array1, i); - value2 = json_array_get(array2, i); - - if(!json_equal(value1, value2)) - return 0; - } - - return 1; -} - -static json_t *json_array_copy(json_t *array) -{ - json_t *result; - size_t i; - - result = json_array(); - if(!result) - return NULL; - - for(i = 0; i < json_array_size(array); i++) - json_array_append(result, json_array_get(array, i)); - - return result; -} - -static json_t *json_array_deep_copy(const json_t *array) -{ - json_t *result; - size_t i; - - result = json_array(); - if(!result) - return NULL; - - for(i = 0; i < json_array_size(array); i++) - json_array_append_new(result, json_deep_copy(json_array_get(array, i))); - - return result; -} - -/*** string ***/ - -static json_t *string_create(const char *value, size_t len, int own) -{ - char *v; - json_string_t *string; - - if(!value) - return NULL; - - if(own) - v = (char *)value; - else { - v = jsonp_strndup(value, len); - if(!v) - return NULL; - } - - string = jsonp_malloc(sizeof(json_string_t)); - if(!string) { - if(!own) - jsonp_free(v); - return NULL; - } - json_init(&string->json, JSON_STRING); - string->value = v; - string->length = len; - - return &string->json; -} - -json_t *json_string_nocheck(const char *value) -{ - if(!value) - return NULL; - - return string_create(value, strlen(value), 0); -} - -json_t *json_stringn_nocheck(const char *value, size_t len) -{ - return string_create(value, len, 0); -} - -/* this is private; "steal" is not a public API concept */ -json_t *jsonp_stringn_nocheck_own(const char *value, size_t len) -{ - return string_create(value, len, 1); -} - -json_t *json_string(const char *value) -{ - if(!value) - return NULL; - - return json_stringn(value, strlen(value)); -} - -json_t *json_stringn(const char *value, size_t len) -{ - if(!value || !utf8_check_string(value, len)) - return NULL; - - return json_stringn_nocheck(value, len); -} - -const char *json_string_value(const json_t *json) -{ - if(!json_is_string(json)) - return NULL; - - return json_to_string(json)->value; -} - -size_t json_string_length(const json_t *json) -{ - if(!json_is_string(json)) - return 0; - - return json_to_string(json)->length; -} - -int json_string_set_nocheck(json_t *json, const char *value) -{ - if(!value) - return -1; - - return json_string_setn_nocheck(json, value, strlen(value)); -} - -int json_string_setn_nocheck(json_t *json, const char *value, size_t len) -{ - char *dup; - json_string_t *string; - - if(!json_is_string(json) || !value) - return -1; - - dup = jsonp_strndup(value, len); - if(!dup) - return -1; - - string = json_to_string(json); - jsonp_free(string->value); - string->value = dup; - string->length = len; - - return 0; -} - -int json_string_set(json_t *json, const char *value) -{ - if(!value) - return -1; - - return json_string_setn(json, value, strlen(value)); -} - -int json_string_setn(json_t *json, const char *value, size_t len) -{ - if(!value || !utf8_check_string(value, len)) - return -1; - - return json_string_setn_nocheck(json, value, len); -} - -static void json_delete_string(json_string_t *string) -{ - jsonp_free(string->value); - jsonp_free(string); -} - -static int json_string_equal(json_t *string1, json_t *string2) -{ - json_string_t *s1, *s2; - - if(!json_is_string(string1) || !json_is_string(string2)) - return 0; - - s1 = json_to_string(string1); - s2 = json_to_string(string2); - return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length); -} - -static json_t *json_string_copy(const json_t *string) -{ - json_string_t *s; - - if(!json_is_string(string)) - return NULL; - - s = json_to_string(string); - return json_stringn_nocheck(s->value, s->length); -} - - -/*** integer ***/ - -json_t *json_integer(json_int_t value) -{ - json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); - if(!integer) - return NULL; - json_init(&integer->json, JSON_INTEGER); - - integer->value = value; - return &integer->json; -} - -json_int_t json_integer_value(const json_t *json) -{ - if(!json_is_integer(json)) - return 0; - - return json_to_integer(json)->value; -} - -int json_integer_set(json_t *json, json_int_t value) -{ - if(!json_is_integer(json)) - return -1; - - json_to_integer(json)->value = value; - - return 0; -} - -static void json_delete_integer(json_integer_t *integer) -{ - jsonp_free(integer); -} - -static int json_integer_equal(json_t *integer1, json_t *integer2) -{ - return json_integer_value(integer1) == json_integer_value(integer2); -} - -static json_t *json_integer_copy(const json_t *integer) -{ - return json_integer(json_integer_value(integer)); -} - - -/*** real ***/ - -json_t *json_real(double value) -{ - json_real_t *real; - - if(isnan(value) || isinf(value)) - return NULL; - - real = jsonp_malloc(sizeof(json_real_t)); - if(!real) - return NULL; - json_init(&real->json, JSON_REAL); - - real->value = value; - return &real->json; -} - -double json_real_value(const json_t *json) -{ - if(!json_is_real(json)) - return 0; - - return json_to_real(json)->value; -} - -int json_real_set(json_t *json, double value) -{ - if(!json_is_real(json) || isnan(value) || isinf(value)) - return -1; - - json_to_real(json)->value = value; - - return 0; -} - -static void json_delete_real(json_real_t *real) -{ - jsonp_free(real); -} - -static int json_real_equal(json_t *real1, json_t *real2) -{ - return json_real_value(real1) == json_real_value(real2); -} - -static json_t *json_real_copy(const json_t *real) -{ - return json_real(json_real_value(real)); -} - - -/*** number ***/ - -double json_number_value(const json_t *json) -{ - if(json_is_integer(json)) - return (double)json_integer_value(json); - else if(json_is_real(json)) - return json_real_value(json); - else - return 0.0; -} - - -/*** simple values ***/ - -json_t *json_true(void) -{ - static json_t the_true = {JSON_TRUE, (size_t)-1}; - return &the_true; -} - - -json_t *json_false(void) -{ - static json_t the_false = {JSON_FALSE, (size_t)-1}; - return &the_false; -} - - -json_t *json_null(void) -{ - static json_t the_null = {JSON_NULL, (size_t)-1}; - return &the_null; -} - - -/*** deletion ***/ - -void json_delete(json_t *json) -{ - if (!json) - return; - - switch(json_typeof(json)) { - case JSON_OBJECT: - json_delete_object(json_to_object(json)); - break; - case JSON_ARRAY: - json_delete_array(json_to_array(json)); - break; - case JSON_STRING: - json_delete_string(json_to_string(json)); - break; - case JSON_INTEGER: - json_delete_integer(json_to_integer(json)); - break; - case JSON_REAL: - json_delete_real(json_to_real(json)); - break; - default: - return; - } - - /* json_delete is not called for true, false or null */ -} - - -/*** equality ***/ - -int json_equal(json_t *json1, json_t *json2) -{ - if(!json1 || !json2) - return 0; - - if(json_typeof(json1) != json_typeof(json2)) - return 0; - - /* this covers true, false and null as they are singletons */ - if(json1 == json2) - return 1; - - switch(json_typeof(json1)) { - case JSON_OBJECT: - return json_object_equal(json1, json2); - case JSON_ARRAY: - return json_array_equal(json1, json2); - case JSON_STRING: - return json_string_equal(json1, json2); - case JSON_INTEGER: - return json_integer_equal(json1, json2); - case JSON_REAL: - return json_real_equal(json1, json2); - default: - return 0; - } -} - - -/*** copying ***/ - -json_t *json_copy(json_t *json) -{ - if(!json) - return NULL; - - switch(json_typeof(json)) { - case JSON_OBJECT: - return json_object_copy(json); - case JSON_ARRAY: - return json_array_copy(json); - case JSON_STRING: - return json_string_copy(json); - case JSON_INTEGER: - return json_integer_copy(json); - case JSON_REAL: - return json_real_copy(json); - case JSON_TRUE: - case JSON_FALSE: - case JSON_NULL: - return json; - default: - return NULL; - } - - return NULL; -} - -json_t *json_deep_copy(const json_t *json) -{ - if(!json) - return NULL; - - switch(json_typeof(json)) { - case JSON_OBJECT: - return json_object_deep_copy(json); - case JSON_ARRAY: - return json_array_deep_copy(json); - /* for the rest of the types, deep copying doesn't differ from - shallow copying */ - case JSON_STRING: - return json_string_copy(json); - case JSON_INTEGER: - return json_integer_copy(json); - case JSON_REAL: - return json_real_copy(json); - case JSON_TRUE: - case JSON_FALSE: - case JSON_NULL: - return (json_t *)json; - default: - return NULL; - } - - return NULL; -} diff --git a/compat/libcpuid/libcpuid_util.c b/compat/libcpuid/libcpuid_util.c deleted file mode 100644 index 4de1871a..00000000 --- a/compat/libcpuid/libcpuid_util.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2008 Veselin Georgiev, - * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include "libcpuid.h" -#include "libcpuid_util.h" - -int _current_verboselevel; - -void match_features(const struct feature_map_t* matchtable, int count, uint32_t reg, struct cpu_id_t* data) -{ - int i; - for (i = 0; i < count; i++) - if (reg & (1u << matchtable[i].bit)) - data->flags[matchtable[i].feature] = 1; -} - -static void default_warn(const char *msg) -{ - fprintf(stderr, "%s", msg); -} - -libcpuid_warn_fn_t _warn_fun = default_warn; - -#if defined(_MSC_VER) -# define vsnprintf _vsnprintf -#endif -void warnf(const char* format, ...) -{ - char buff[1024]; - va_list va; - if (!_warn_fun) return; - va_start(va, format); - vsnprintf(buff, sizeof(buff), format, va); - va_end(va); - _warn_fun(buff); -} - -void debugf(int verboselevel, const char* format, ...) -{ - char buff[1024]; - va_list va; - if (verboselevel > _current_verboselevel) return; - va_start(va, format); - vsnprintf(buff, sizeof(buff), format, va); - va_end(va); - _warn_fun(buff); -} - -static int popcount64(uint64_t mask) -{ - int num_set_bits = 0; - - while (mask) { - mask &= mask - 1; - num_set_bits++; - } - - return num_set_bits; -} - -static int score(const struct match_entry_t* entry, const struct cpu_id_t* data, - int brand_code, uint64_t bits, int model_code) -{ - int res = 0; - if (entry->family == data->family ) res += 2; - if (entry->model == data->model ) res += 2; - if (entry->stepping == data->stepping ) res += 2; - if (entry->ext_family == data->ext_family) res += 2; - if (entry->ext_model == data->ext_model ) res += 2; - if (entry->ncores == data->num_cores ) res += 2; - if (entry->l2cache == data->l2_cache ) res += 1; - if (entry->l3cache == data->l3_cache ) res += 1; - if (entry->brand_code == brand_code ) res += 2; - if (entry->model_code == model_code ) res += 2; - - res += popcount64(entry->model_bits & bits) * 2; - return res; -} - -int match_cpu_codename(const struct match_entry_t* matchtable, int count, - struct cpu_id_t* data, int brand_code, uint64_t bits, - int model_code) -{ - int bestscore = -1; - int bestindex = 0; - int i, t; - - debugf(3, "Matching cpu f:%d, m:%d, s:%d, xf:%d, xm:%d, ncore:%d, l2:%d, bcode:%d, bits:%llu, code:%d\n", - data->family, data->model, data->stepping, data->ext_family, - data->ext_model, data->num_cores, data->l2_cache, brand_code, (unsigned long long) bits, model_code); - - for (i = 0; i < count; i++) { - t = score(&matchtable[i], data, brand_code, bits, model_code); - debugf(3, "Entry %d, `%s', score %d\n", i, matchtable[i].name, t); - if (t > bestscore) { - debugf(2, "Entry `%s' selected - best score so far (%d)\n", matchtable[i].name, t); - bestscore = t; - bestindex = i; - } - } - strcpy(data->cpu_codename, matchtable[bestindex].name); - return bestscore; -} - -void generic_get_cpu_list(const struct match_entry_t* matchtable, int count, - struct cpu_list_t* list) -{ - int i, j, n, good; - n = 0; - list->names = (char**) malloc(sizeof(char*) * count); - for (i = 0; i < count; i++) { - if (strstr(matchtable[i].name, "Unknown")) continue; - good = 1; - for (j = n - 1; j >= 0; j--) - if (!strcmp(list->names[j], matchtable[i].name)) { - good = 0; - break; - } - if (!good) continue; -#if defined(_MSC_VER) - list->names[n++] = _strdup(matchtable[i].name); -#else - list->names[n++] = strdup(matchtable[i].name); -#endif - } - list->num_entries = n; -} - -static int xmatch_entry(char c, const char* p) -{ - int i, j; - if (c == 0) return -1; - if (c == p[0]) return 1; - if (p[0] == '.') return 1; - if (p[0] == '#' && isdigit(c)) return 1; - if (p[0] == '[') { - j = 1; - while (p[j] && p[j] != ']') j++; - if (!p[j]) return -1; - for (i = 1; i < j; i++) - if (p[i] == c) return j + 1; - } - return -1; -} - -int match_pattern(const char* s, const char* p) -{ - int i, j, dj, k, n, m; - n = (int) strlen(s); - m = (int) strlen(p); - for (i = 0; i < n; i++) { - if (xmatch_entry(s[i], p) != -1) { - j = 0; - k = 0; - while (j < m && ((dj = xmatch_entry(s[i + k], p + j)) != -1)) { - k++; - j += dj; - } - if (j == m) return i + 1; - } - } - return 0; -} - -struct cpu_id_t* get_cached_cpuid(void) -{ - static int initialized = 0; - static struct cpu_id_t id; - if (initialized) return &id; - if (cpu_identify(NULL, &id)) - memset(&id, 0, sizeof(id)); - initialized = 1; - return &id; -} - -int match_all(uint64_t bits, uint64_t mask) -{ - return (bits & mask) == mask; -} - -void debug_print_lbits(int debuglevel, uint64_t mask) -{ - int i, first = 0; - for (i = 0; i < 64; i++) if (mask & (((uint64_t) 1) << i)) { - if (first) first = 0; - else debugf(2, " + "); - debugf(2, "LBIT(%d)", i); - } - debugf(2, "\n"); -} diff --git a/compat/libcpuid/recog_amd.c b/compat/libcpuid/recog_amd.c deleted file mode 100644 index b735c400..00000000 --- a/compat/libcpuid/recog_amd.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * Copyright 2008 Veselin Georgiev, - * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include "libcpuid.h" -#include "libcpuid_util.h" -#include "libcpuid_internal.h" -#include "recog_amd.h" - -const struct amd_code_str { amd_code_t code; char *str; } amd_code_str[] = { - #define CODE(x) { x, #x } - #define CODE2(x, y) CODE(x) - #include "amd_code_t.h" - #undef CODE -}; - -struct amd_code_and_bits_t { - int code; - uint64_t bits; -}; - -enum _amd_bits_t { - ATHLON_ = LBIT( 0 ), - _XP_ = LBIT( 1 ), - _M_ = LBIT( 2 ), - _MP_ = LBIT( 3 ), - MOBILE_ = LBIT( 4 ), - DURON_ = LBIT( 5 ), - SEMPRON_ = LBIT( 6 ), - OPTERON_ = LBIT( 7 ), - TURION_ = LBIT( 8 ), - _LV_ = LBIT( 9 ), - _64_ = LBIT( 10 ), - _X2 = LBIT( 11 ), - _X3 = LBIT( 12 ), - _X4 = LBIT( 13 ), - _X6 = LBIT( 14 ), - _FX = LBIT( 15 ), -}; -typedef enum _amd_bits_t amd_bits_t; - -enum _amd_model_codes_t { - // Only for Ryzen CPUs: - _1400, - _1500, - _1600, -}; - - -const struct match_entry_t cpudb_amd[] = { - { -1, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD CPU" }, - - /* 486 and the likes */ - { 4, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD 486" }, - { 4, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX2" }, - { 4, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX2WB" }, - { 4, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX4" }, - { 4, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX4WB" }, - - /* Pentia clones */ - { 5, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD 586" }, - { 5, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, - { 5, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, - { 5, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, - { 5, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, - - /* The K6 */ - { 5, 6, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6" }, - { 5, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6" }, - - { 5, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-2" }, - { 5, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-III" }, - { 5, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, - { 5, 11, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, - { 5, 12, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, - { 5, 13, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-2+" }, - - /* Athlon et al. */ - { 6, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (Slot-A)" }, - { 6, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (Slot-A)" }, - { 6, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Duron (Spitfire)" }, - { 6, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (ThunderBird)" }, - - { 6, 6, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Athlon" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_ , 0, "Athlon (Palomino)" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Palomino)" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Palomino)" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP" }, - - { 6, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Athlon XP" }, - { 6, 7, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Morgan)" }, - - { 6, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon XP" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_ , 0, "Athlon XP (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Applebred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Thoroughbred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_ , 0, "Mobile Athlon (T-Bred)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_|_LV_, 0, "Mobile Athlon (T-Bred)" }, - - { 6, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon XP (Barton)" }, - { 6, 10, -1, -1, -1, 1, 512, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Barton)" }, - { 6, 10, -1, -1, -1, 1, 512, -1, NC, SEMPRON_ , 0, "Sempron (Barton)" }, - { 6, 10, -1, -1, -1, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron (Thorton)" }, - { 6, 10, -1, -1, -1, 1, 256, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Thorton)" }, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Barton)" }, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_ , 0, "Mobile Athlon (Barton)" }, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_|_LV_, 0, "Mobile Athlon (Barton)" }, - - /* K8 Architecture */ - { 15, -1, -1, 15, -1, 1, -1, -1, NC, 0 , 0, "Unknown K8" }, - { 15, -1, -1, 16, -1, 1, -1, -1, NC, 0 , 0, "Unknown K9" }, - - { 15, -1, -1, 15, -1, 1, -1, -1, NC, 0 , 0, "Unknown A64" }, - { 15, -1, -1, 15, -1, 1, -1, -1, NC, OPTERON_ , 0, "Opteron" }, - { 15, -1, -1, 15, -1, 2, -1, -1, NC, OPTERON_|_X2 , 0, "Opteron (Dual Core)" }, - { 15, 3, -1, 15, -1, 1, -1, -1, NC, OPTERON_ , 0, "Opteron" }, - { 15, 3, -1, 15, -1, 2, -1, -1, NC, OPTERON_|_X2 , 0, "Opteron (Dual Core)" }, - { 15, -1, -1, 15, -1, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (512K)" }, - { 15, -1, -1, 15, -1, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (1024K)" }, - { 15, -1, -1, 15, -1, 1, -1, -1, NC, ATHLON_|_FX , 0, "Athlon FX" }, - { 15, -1, -1, 15, -1, 1, -1, -1, NC, ATHLON_|_64_|_FX , 0, "Athlon 64 FX" }, - { 15, 3, -1, 15, 35, 2, -1, -1, NC, ATHLON_|_64_|_FX , 0, "Athlon 64 FX X2 (Toledo)" }, - { 15, -1, -1, 15, -1, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (512K)" }, - { 15, -1, -1, 15, -1, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (1024K)" }, - { 15, -1, -1, 15, -1, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (512K)" }, - { 15, -1, -1, 15, -1, 1, 1024, -1, NC, TURION_|_64_ , 0, "Turion 64 (1024K)" }, - { 15, -1, -1, 15, -1, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion 64 X2 (512K)" }, - { 15, -1, -1, 15, -1, 2, 1024, -1, NC, TURION_|_X2 , 0, "Turion 64 X2 (1024K)" }, - { 15, -1, -1, 15, -1, 1, 128, -1, NC, SEMPRON_ , 0, "A64 Sempron (128K)" }, - { 15, -1, -1, 15, -1, 1, 256, -1, NC, SEMPRON_ , 0, "A64 Sempron (256K)" }, - { 15, -1, -1, 15, -1, 1, 512, -1, NC, SEMPRON_ , 0, "A64 Sempron (512K)" }, - { 15, -1, -1, 15, 0x4f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/512K)" }, - { 15, -1, -1, 15, 0x5f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/512K)" }, - { 15, -1, -1, 15, 0x2f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Venice/512K)" }, - { 15, -1, -1, 15, 0x2c, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Venice/512K)" }, - { 15, -1, -1, 15, 0x1f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Winchester/512K)" }, - { 15, -1, -1, 15, 0x0c, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Newcastle/512K)" }, - { 15, -1, -1, 15, 0x27, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/512K)" }, - { 15, -1, -1, 15, 0x37, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/512K)" }, - { 15, -1, -1, 15, 0x04, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (ClawHammer/512K)" }, - - { 15, -1, -1, 15, 0x5f, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/1024K)" }, - { 15, -1, -1, 15, 0x27, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/1024K)" }, - { 15, -1, -1, 15, 0x04, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (ClawHammer/1024K)" }, - - { 15, -1, -1, 15, 0x4b, 2, 256, -1, NC, SEMPRON_ , 0, "Athlon 64 X2 (Windsor/256K)" }, - - { 15, -1, -1, 15, 0x23, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Toledo/512K)" }, - { 15, -1, -1, 15, 0x4b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/512K)" }, - { 15, -1, -1, 15, 0x43, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/512K)" }, - { 15, -1, -1, 15, 0x6b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Brisbane/512K)" }, - { 15, -1, -1, 15, 0x2b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Manchester/512K)"}, - - { 15, -1, -1, 15, 0x23, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Toledo/1024K)" }, - { 15, -1, -1, 15, 0x43, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/1024K)" }, - - { 15, -1, -1, 15, 0x08, 1, 128, -1, NC, MOBILE_|SEMPRON_ , 0, "Mobile Sempron 64 (Dublin/128K)"}, - { 15, -1, -1, 15, 0x08, 1, 256, -1, NC, MOBILE_|SEMPRON_ , 0, "Mobile Sempron 64 (Dublin/256K)"}, - { 15, -1, -1, 15, 0x0c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Paris)" }, - { 15, -1, -1, 15, 0x1c, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, - { 15, -1, -1, 15, 0x1c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, - { 15, -1, -1, 15, 0x1c, 1, 128, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Sonora/128K)"}, - { 15, -1, -1, 15, 0x1c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Sonora/256K)"}, - { 15, -1, -1, 15, 0x2c, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, - { 15, -1, -1, 15, 0x2c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, - { 15, -1, -1, 15, 0x2c, 1, 128, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Albany/128K)"}, - { 15, -1, -1, 15, 0x2c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Albany/256K)"}, - { 15, -1, -1, 15, 0x2f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, - { 15, -1, -1, 15, 0x2f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, - { 15, -1, -1, 15, 0x4f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/128K)" }, - { 15, -1, -1, 15, 0x4f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/256K)" }, - { 15, -1, -1, 15, 0x5f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/128K)" }, - { 15, -1, -1, 15, 0x5f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/256K)" }, - { 15, -1, -1, 15, 0x6b, 2, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/256K)"}, - { 15, -1, -1, 15, 0x6b, 2, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/512K)"}, - { 15, -1, -1, 15, 0x7f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/256K)" }, - { 15, -1, -1, 15, 0x7f, 1, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/512K)" }, - { 15, -1, -1, 15, 0x4c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Keene/256K)"}, - { 15, -1, -1, 15, 0x4c, 1, 512, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Keene/512K)"}, - { 15, -1, -1, 15, -1, 2, -1, -1, NC, SEMPRON_ , 0, "Sempron Dual Core" }, - - { 15, -1, -1, 15, 0x24, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (Lancaster/512K)" }, - { 15, -1, -1, 15, 0x24, 1, 1024, -1, NC, TURION_|_64_ , 0, "Turion 64 (Lancaster/1024K)" }, - { 15, -1, -1, 15, 0x48, 2, 256, -1, NC, TURION_|_X2 , 0, "Turion X2 (Taylor)" }, - { 15, -1, -1, 15, 0x48, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Trinidad)" }, - { 15, -1, -1, 15, 0x4c, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (Richmond)" }, - { 15, -1, -1, 15, 0x68, 2, 256, -1, NC, TURION_|_X2 , 0, "Turion X2 (Tyler/256K)" }, - { 15, -1, -1, 15, 0x68, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Tyler/512K)" }, - { 15, -1, -1, 17, 3, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Griffin/512K)" }, - { 15, -1, -1, 17, 3, 2, 1024, -1, NC, TURION_|_X2 , 0, "Turion X2 (Griffin/1024K)" }, - - /* K10 Architecture (2007) */ - { 15, -1, -1, 16, -1, 1, -1, -1, PHENOM, 0 , 0, "Unknown AMD Phenom" }, - { 15, 2, -1, 16, -1, 1, -1, -1, PHENOM, 0 , 0, "Phenom" }, - { 15, 2, -1, 16, -1, 3, -1, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman)" }, - { 15, 2, -1, 16, -1, 4, -1, -1, PHENOM, 0 , 0, "Phenom X4 (Agena)" }, - { 15, 2, -1, 16, -1, 3, 512, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman/256K)" }, - { 15, 2, -1, 16, -1, 3, 512, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman/512K)" }, - { 15, 2, -1, 16, -1, 4, 128, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/128K)" }, - { 15, 2, -1, 16, -1, 4, 256, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/256K)" }, - { 15, 2, -1, 16, -1, 4, 512, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/512K)" }, - { 15, 2, -1, 16, -1, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon X2 (Kuma)" }, - /* Phenom II derivates: */ - { 15, 4, -1, 16, -1, 4, -1, -1, NC, 0 , 0, "Phenom (Deneb-based)" }, - { 15, 4, -1, 16, -1, 1, 1024, -1, NC, SEMPRON_ , 0, "Sempron (Sargas)" }, - { 15, 4, -1, 16, -1, 2, 512, -1, PHENOM2, 0 , 0, "Phenom II X2 (Callisto)" }, - { 15, 4, -1, 16, -1, 3, 512, -1, PHENOM2, 0 , 0, "Phenom II X3 (Heka)" }, - { 15, 4, -1, 16, -1, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4" }, - { 15, 4, -1, 16, 4, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Deneb)" }, - { 15, 5, -1, 16, 5, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Deneb)" }, - { 15, 4, -1, 16, 10, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Zosma)" }, - { 15, 4, -1, 16, 10, 6, 512, -1, PHENOM2, 0 , 0, "Phenom II X6 (Thuban)" }, - /* Athlon II derivates: */ - { 15, 6, -1, 16, 6, 2, 512, -1, NC, ATHLON_|_X2 , 0, "Athlon II (Champlain)" }, - { 15, 6, -1, 16, 6, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon II X2 (Regor)" }, - { 15, 6, -1, 16, 6, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon II X2 (Regor)" }, - { 15, 5, -1, 16, 5, 3, 512, -1, NC, ATHLON_|_64_|_X3 , 0, "Athlon II X3 (Rana)" }, - { 15, 5, -1, 16, 5, 4, 512, -1, NC, ATHLON_|_64_|_X4 , 0, "Athlon II X4 (Propus)" }, - /* Llano APUs (2011): */ - { 15, 1, -1, 18, 1, 2, -1, -1, FUSION_EA, 0 , 0, "Llano X2" }, - { 15, 1, -1, 18, 1, 3, -1, -1, FUSION_EA, 0 , 0, "Llano X3" }, - { 15, 1, -1, 18, 1, 4, -1, -1, FUSION_EA, 0 , 0, "Llano X4" }, - - /* Family 14h: Bobcat Architecture (2011) */ - { 15, 2, -1, 20, -1, 1, -1, -1, FUSION_C, 0 , 0, "Brazos Ontario" }, - { 15, 2, -1, 20, -1, 2, -1, -1, FUSION_C, 0 , 0, "Brazos Ontario (Dual-core)" }, - { 15, 1, -1, 20, -1, 1, -1, -1, FUSION_E, 0 , 0, "Brazos Zacate" }, - { 15, 1, -1, 20, -1, 2, -1, -1, FUSION_E, 0 , 0, "Brazos Zacate (Dual-core)" }, - { 15, 2, -1, 20, -1, 2, -1, -1, FUSION_Z, 0 , 0, "Brazos Desna (Dual-core)" }, - - /* Family 15h: Bulldozer Architecture (2011) */ - { 15, -1, -1, 21, 0, 4, -1, -1, NC, 0 , 0, "Bulldozer X2" }, - { 15, -1, -1, 21, 1, 4, -1, -1, NC, 0 , 0, "Bulldozer X2" }, - { 15, -1, -1, 21, 1, 6, -1, -1, NC, 0 , 0, "Bulldozer X3" }, - { 15, -1, -1, 21, 1, 8, -1, -1, NC, 0 , 0, "Bulldozer X4" }, - /* 2nd-gen, Piledriver core (2012): */ - { 15, -1, -1, 21, 2, 4, -1, -1, NC, 0 , 0, "Vishera X2" }, - { 15, -1, -1, 21, 2, 6, -1, -1, NC, 0 , 0, "Vishera X3" }, - { 15, -1, -1, 21, 2, 8, -1, -1, NC, 0 , 0, "Vishera X4" }, - { 15, 0, -1, 21, 16, 2, -1, -1, FUSION_A, 0 , 0, "Trinity X2" }, - { 15, 0, -1, 21, 16, 4, -1, -1, FUSION_A, 0 , 0, "Trinity X4" }, - { 15, 3, -1, 21, 19, 2, -1, -1, FUSION_A, 0 , 0, "Richland X2" }, - { 15, 3, -1, 21, 19, 4, -1, -1, FUSION_A, 0 , 0, "Richland X4" }, - /* 3rd-gen, Steamroller core (2014): */ - { 15, 0, -1, 21, 48, 2, -1, -1, FUSION_A, 0 , 0, "Kaveri X2" }, - { 15, 0, -1, 21, 48, 4, -1, -1, FUSION_A, 0 , 0, "Kaveri X4" }, - { 15, 8, -1, 21, 56, 4, -1, -1, FUSION_A, 0 , 0, "Godavari X4" }, - /* 4th-gen, Excavator core (2015): */ - { 15, 1, -1, 21, 96, 2, -1, -1, FUSION_A, 0 , 0, "Carrizo X2" }, - { 15, 1, -1, 21, 96, 4, -1, -1, FUSION_A, 0 , 0, "Carrizo X4" }, - { 15, 5, -1, 21, 101, 2, -1, -1, FUSION_A, 0 , 0, "Bristol Ridge X2" }, - { 15, 5, -1, 21, 101, 4, -1, -1, FUSION_A, 0 , 0, "Bristol Ridge X4" }, - { 15, 0, -1, 21, 112, 2, -1, -1, FUSION_A, 0 , 0, "Stoney Ridge X2" }, - { 15, 0, -1, 21, 112, 2, -1, -1, FUSION_E, 0 , 0, "Stoney Ridge X2" }, - - /* Family 16h: Jaguar Architecture (2013) */ - { 15, 0, -1, 22, 0, 2, -1, -1, FUSION_A, 0 , 0, "Kabini X2" }, - { 15, 0, -1, 22, 0, 4, -1, -1, FUSION_A, 0 , 0, "Kabini X4" }, - /* 2nd-gen, Puma core (2013): */ - { 15, 0, -1, 22, 48, 2, -1, -1, FUSION_E, 0 , 0, "Mullins X2" }, - { 15, 0, -1, 22, 48, 4, -1, -1, FUSION_A, 0 , 0, "Mullins X4" }, - - /* Family 17h: Zen Architecture (2017) */ - { 15, -1, -1, 23, 1, 8, -1, -1, NC, 0 , 0, "Ryzen 7" }, - { 15, -1, -1, 23, 1, 6, -1, -1, NC, 0 , _1600, "Ryzen 5" }, - { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , _1500, "Ryzen 5" }, - { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , _1400, "Ryzen 5" }, - { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , 0, "Ryzen 3" }, - //{ 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , 0, "Raven Ridge" }, //TBA - - /* Newer Opterons: */ - { 15, 9, -1, 22, 9, 8, -1, -1, NC, OPTERON_ , 0, "Magny-Cours Opteron" }, -}; - - -static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - const struct feature_map_t matchtable_edx81[] = { - { 20, CPU_FEATURE_NX }, - { 22, CPU_FEATURE_MMXEXT }, - { 25, CPU_FEATURE_FXSR_OPT }, - { 30, CPU_FEATURE_3DNOWEXT }, - { 31, CPU_FEATURE_3DNOW }, - }; - const struct feature_map_t matchtable_ecx81[] = { - { 1, CPU_FEATURE_CMP_LEGACY }, - { 2, CPU_FEATURE_SVM }, - { 5, CPU_FEATURE_ABM }, - { 6, CPU_FEATURE_SSE4A }, - { 7, CPU_FEATURE_MISALIGNSSE }, - { 8, CPU_FEATURE_3DNOWPREFETCH }, - { 9, CPU_FEATURE_OSVW }, - { 10, CPU_FEATURE_IBS }, - { 11, CPU_FEATURE_XOP }, - { 12, CPU_FEATURE_SKINIT }, - { 13, CPU_FEATURE_WDT }, - { 16, CPU_FEATURE_FMA4 }, - { 21, CPU_FEATURE_TBM }, - }; - const struct feature_map_t matchtable_edx87[] = { - { 0, CPU_FEATURE_TS }, - { 1, CPU_FEATURE_FID }, - { 2, CPU_FEATURE_VID }, - { 3, CPU_FEATURE_TTP }, - { 4, CPU_FEATURE_TM_AMD }, - { 5, CPU_FEATURE_STC }, - { 6, CPU_FEATURE_100MHZSTEPS }, - { 7, CPU_FEATURE_HWPSTATE }, - /* id 8 is handled in common */ - { 9, CPU_FEATURE_CPB }, - { 10, CPU_FEATURE_APERFMPERF }, - { 11, CPU_FEATURE_PFI }, - { 12, CPU_FEATURE_PA }, - }; - if (raw->ext_cpuid[0][0] >= 0x80000001) { - match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); - match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data); - } - if (raw->ext_cpuid[0][0] >= 0x80000007) - match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data); - if (raw->ext_cpuid[0][0] >= 0x8000001a) { - /* We have the extended info about SSE unit size */ - data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] = 1; - data->sse_size = (raw->ext_cpuid[0x1a][0] & 1) ? 128 : 64; - } -} - -static void decode_amd_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - int l3_result; - const int assoc_table[16] = { - 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255 - }; - unsigned n = raw->ext_cpuid[0][0]; - - if (n >= 0x80000005) { - data->l1_data_cache = (raw->ext_cpuid[5][2] >> 24) & 0xff; - data->l1_assoc = (raw->ext_cpuid[5][2] >> 16) & 0xff; - data->l1_cacheline = (raw->ext_cpuid[5][2]) & 0xff; - data->l1_instruction_cache = (raw->ext_cpuid[5][3] >> 24) & 0xff; - } - if (n >= 0x80000006) { - data->l2_cache = (raw->ext_cpuid[6][2] >> 16) & 0xffff; - data->l2_assoc = assoc_table[(raw->ext_cpuid[6][2] >> 12) & 0xf]; - data->l2_cacheline = (raw->ext_cpuid[6][2]) & 0xff; - - l3_result = (raw->ext_cpuid[6][3] >> 18); - if (l3_result > 0) { - l3_result = 512 * l3_result; /* AMD spec says it's a range, - but we take the lower bound */ - data->l3_cache = l3_result; - data->l3_assoc = assoc_table[(raw->ext_cpuid[6][3] >> 12) & 0xf]; - data->l3_cacheline = (raw->ext_cpuid[6][3]) & 0xff; - } else { - data->l3_cache = 0; - } - } -} - -static void decode_amd_number_of_cores(struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - int logical_cpus = -1, num_cores = -1; - - if (raw->basic_cpuid[0][0] >= 1) { - logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; - if (raw->ext_cpuid[0][0] >= 8) { - num_cores = 1 + (raw->ext_cpuid[8][2] & 0xff); - } - } - if (data->flags[CPU_FEATURE_HT]) { - if (num_cores > 1) { - if (data->ext_family >= 23) - num_cores /= 2; // e.g., Ryzen 7 reports 16 "real" cores, but they are really just 8. - data->num_cores = num_cores; - data->num_logical_cpus = logical_cpus; - } else { - data->num_cores = 1; - data->num_logical_cpus = (logical_cpus >= 2 ? logical_cpus : 2); - } - } else { - data->num_cores = data->num_logical_cpus = 1; - } -} - -static int amd_has_turion_modelname(const char *bs) -{ - /* We search for something like TL-60. Ahh, I miss regexes...*/ - int i, l, k; - char code[3] = {0}; - const char* codes[] = { "ML", "MT", "MK", "TK", "TL", "RM", "ZM", "" }; - l = (int) strlen(bs); - for (i = 3; i < l - 2; i++) { - if (bs[i] == '-' && - isupper(bs[i-1]) && isupper(bs[i-2]) && !isupper(bs[i-3]) && - isdigit(bs[i+1]) && isdigit(bs[i+2]) && !isdigit(bs[i+3])) - { - code[0] = bs[i-2]; - code[1] = bs[i-1]; - for (k = 0; codes[k][0]; k++) - if (!strcmp(codes[k], code)) return 1; - } - } - return 0; -} - -static struct amd_code_and_bits_t decode_amd_codename_part1(const char *bs) -{ - amd_code_t code = NC; - uint64_t bits = 0; - struct amd_code_and_bits_t result; - - if (strstr(bs, "Dual Core") || - strstr(bs, "Dual-Core") || - strstr(bs, " X2 ")) - bits |= _X2; - if (strstr(bs, " X4 ")) bits |= _X4; - if (strstr(bs, " X3 ")) bits |= _X3; - if (strstr(bs, "Opteron")) bits |= OPTERON_; - if (strstr(bs, "Phenom")) { - code = (strstr(bs, "II")) ? PHENOM2 : PHENOM; - } - if (amd_has_turion_modelname(bs)) { - bits |= TURION_; - } - if (strstr(bs, "Athlon(tm)")) bits |= ATHLON_; - if (strstr(bs, "Sempron(tm)")) bits |= SEMPRON_; - if (strstr(bs, "Duron")) bits |= DURON_; - if (strstr(bs, " 64 ")) bits |= _64_; - if (strstr(bs, " FX")) bits |= _FX; - if (strstr(bs, " MP")) bits |= _MP_; - if (strstr(bs, "Athlon(tm) 64") || strstr(bs, "Athlon(tm) II X") || match_pattern(bs, "Athlon(tm) X#")) { - bits |= ATHLON_ | _64_; - } - if (strstr(bs, "Turion")) bits |= TURION_; - - if (strstr(bs, "mobile") || strstr(bs, "Mobile")) { - bits |= MOBILE_; - } - - if (strstr(bs, "XP")) bits |= _XP_; - if (strstr(bs, "XP-M")) bits |= _M_; - if (strstr(bs, "(LV)")) bits |= _LV_; - - if (match_pattern(bs, "C-##")) code = FUSION_C; - if (match_pattern(bs, "E-###")) code = FUSION_E; - if (match_pattern(bs, "Z-##")) code = FUSION_Z; - if (match_pattern(bs, "E#-####") || match_pattern(bs, "A#-####")) code = FUSION_EA; - - result.code = code; - result.bits = bits; - return result; -} - -static int decode_amd_ryzen_model_code(const char* bs) -{ - const struct { - int model_code; - const char* match_str; - } patterns[] = { - { _1600, "1600" }, - { _1500, "1500" }, - { _1400, "1400" }, - }; - int i; - - for (i = 0; i < COUNT_OF(patterns); i++) - if (strstr(bs, patterns[i].match_str)) - return patterns[i].model_code; - // - return 0; -} - -static void decode_amd_codename(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) -{ - struct amd_code_and_bits_t code_and_bits = decode_amd_codename_part1(data->brand_str); - int i = 0; - char* code_str = NULL; - int model_code; - - for (i = 0; i < COUNT_OF(amd_code_str); i++) { - if (code_and_bits.code == amd_code_str[i].code) { - code_str = amd_code_str[i].str; - break; - } - } - if (/*code == ATHLON_64_X2*/ match_all(code_and_bits.bits, ATHLON_|_64_|_X2) && data->l2_cache < 512) { - code_and_bits.bits &= ~(ATHLON_ | _64_); - code_and_bits.bits |= SEMPRON_; - } - if (code_str) - debugf(2, "Detected AMD brand code: %d (%s)\n", code_and_bits.code, code_str); - else - debugf(2, "Detected AMD brand code: %d\n", code_and_bits.code); - - if (code_and_bits.bits) { - debugf(2, "Detected AMD bits: "); - debug_print_lbits(2, code_and_bits.bits); - } - // is it Ryzen? if so, we need to detect discern between the four-core 1400/1500 (Ryzen 5) and the four-core Ryzen 3: - model_code = (data->ext_family == 23) ? decode_amd_ryzen_model_code(data->brand_str) : 0; - - internal->code.amd = code_and_bits.code; - internal->bits = code_and_bits.bits; - internal->score = match_cpu_codename(cpudb_amd, COUNT_OF(cpudb_amd), data, code_and_bits.code, - code_and_bits.bits, model_code); -} - -int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) -{ - load_amd_features(raw, data); - decode_amd_cache_info(raw, data); - decode_amd_number_of_cores(raw, data); - decode_amd_codename(raw, data, internal); - return 0; -} - -void cpuid_get_list_amd(struct cpu_list_t* list) -{ - generic_get_cpu_list(cpudb_amd, COUNT_OF(cpudb_amd), list); -} diff --git a/compat/libcpuid/recog_intel.c b/compat/libcpuid/recog_intel.c deleted file mode 100644 index 1d6c6a84..00000000 --- a/compat/libcpuid/recog_intel.c +++ /dev/null @@ -1,935 +0,0 @@ -/* - * Copyright 2008 Veselin Georgiev, - * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include "libcpuid.h" -#include "libcpuid_util.h" -#include "libcpuid_internal.h" -#include "recog_intel.h" - -const struct intel_bcode_str { intel_code_t code; char *str; } intel_bcode_str[] = { - #define CODE(x) { x, #x } - #define CODE2(x, y) CODE(x) - #include "intel_code_t.h" - #undef CODE -}; - -typedef struct { - int code; - uint64_t bits; -} intel_code_and_bits_t; - -enum _intel_model_t { - UNKNOWN = -1, - _3000 = 100, - _3100, - _3200, - X3200, - _3300, - X3300, - _5100, - _5200, - _5300, - _5400, - _2xxx, /* Core i[357] 2xxx */ - _3xxx, /* Core i[357] 3xxx */ -}; -typedef enum _intel_model_t intel_model_t; - -enum _intel_bits_t { - PENTIUM_ = LBIT( 0 ), - CELERON_ = LBIT( 1 ), - MOBILE_ = LBIT( 2 ), - CORE_ = LBIT( 3 ), - _I_ = LBIT( 4 ), - _M_ = LBIT( 5 ), - _3 = LBIT( 6 ), - _5 = LBIT( 7 ), - _7 = LBIT( 8 ), - XEON_ = LBIT( 9 ), - _MP = LBIT( 10 ), - ATOM_ = LBIT( 11 ), - -}; -typedef enum _intel_bits_t intel_bits_t; - -const struct match_entry_t cpudb_intel[] = { - { -1, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Intel CPU" }, - - /* i486 */ - { 4, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown i486" }, - { 4, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX-25/33" }, - { 4, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX-50" }, - { 4, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SX" }, - { 4, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX2" }, - { 4, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SL" }, - { 4, 5, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SX2" }, - { 4, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX2 WriteBack" }, - { 4, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX4" }, - { 4, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX4 WriteBack" }, - - /* All Pentia: - Pentium 1 */ - { 5, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium" }, - { 5, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium A-Step" }, - { 5, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.8u)" }, - { 5, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, - { 5, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium OverDrive" }, - { 5, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, - { 5, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, - { 5, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium MMX (0.25u)" }, - - /* Pentium 2 / 3 / M / Conroe / whatsnext - all P6 based. */ - { 6, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown P6" }, - { 6, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium Pro" }, - { 6, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium Pro" }, - { 6, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium II (Klamath)" }, - { 6, 5, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium II (Deschutes)" }, - { 6, 5, -1, -1, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile Pentium II (Tonga)"}, - { 6, 6, -1, -1, -1, 1, -1, -1, NC,0 , 0, "Pentium II (Dixon)" }, - - { 6, 3, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Klamath)" }, - { 6, 5, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Drake)" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Dixon)" }, - - { 6, 5, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-II Celeron (Covington)" }, - { 6, 6, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-II Celeron (Mendocino)" }, - - /* -------------------------------------------------- */ - - { 6, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Katmai)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Coppermine)"}, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Coppermine)"}, - { 6, 11, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Tualatin)" }, - - { 6, 7, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Tanner)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Cascades)" }, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Cascades)" }, - { 6, 11, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Tualatin)" }, - - { 6, 7, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Katmai)" }, - { 6, 8, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Coppermine)" }, - { 6, 10, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Coppermine)" }, - { 6, 11, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Tualatin)" }, - - /* Netburst based (Pentium 4 and later) - classic P4s */ - { 15, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium 4" }, - { 15, -1, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "Unknown P-4 Celeron" }, - { 15, -1, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Unknown Xeon" }, - - { 15, 0, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Willamette)" }, - { 15, 1, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Willamette)" }, - { 15, 2, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Northwood)" }, - { 15, 3, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Prescott)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Prescott)" }, - { 15, 6, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Cedar Mill)" }, - { 15, 0, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Willamette)" }, - { 15, 1, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Willamette)" }, - { 15, 2, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Northwood)" }, - { 15, 3, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Prescott)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Prescott)" }, - { 15, 6, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Cedar Mill)" }, - - /* server CPUs */ - { 15, 0, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Foster)" }, - { 15, 1, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Foster)" }, - { 15, 2, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Prestonia)" }, - { 15, 2, -1, 15, -1, 1, -1, -1, NC, XEON_|_MP , 0, "Xeon (Gallatin)" }, - { 15, 3, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Nocona)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Nocona)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, IRWIN, XEON_ , 0, "Xeon (Irwindale)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, NC, XEON_|_MP , 0, "Xeon (Cranford)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, POTOMAC, XEON_ , 0, "Xeon (Potomac)" }, - { 15, 6, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Dempsey)" }, - - /* Pentium Ds */ - { 15, 4, 4, 15, -1, 1, -1, -1, NC, 0 , 0, "Pentium D (SmithField)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, PENTIUM_D, 0 , 0, "Pentium D (SmithField)" }, - { 15, 4, 7, 15, -1, 1, -1, -1, NC, 0 , 0, "Pentium D (SmithField)" }, - { 15, 6, -1, 15, -1, 1, -1, -1, PENTIUM_D, 0 , 0, "Pentium D (Presler)" }, - - /* Celeron and Celeron Ds */ - { 15, 1, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron (Willamette)" }, - { 15, 2, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron (Northwood)" }, - { 15, 3, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Prescott)" }, - { 15, 4, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Prescott)" }, - { 15, 6, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Cedar Mill)" }, - - /* -------------------------------------------------- */ - /* Intel Core microarchitecture - P6-based */ - - { 6, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium M" }, - { 6, 9, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Unknown Pentium M" }, - { 6, 9, -1, -1, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium M (Banias)" }, - { 6, 9, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Pentium M (Banias)" }, - { 6, 9, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "Celeron M" }, - { 6, 13, -1, -1, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium M (Dothan)" }, - { 6, 13, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Pentium M (Dothan)" }, - { 6, 13, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "Celeron M" }, - - { 6, 12, -1, -1, -1, -1, -1, -1, NC, ATOM_ , 0, "Unknown Atom" }, - { 6, 12, -1, -1, -1, -1, -1, -1, DIAMONDVILLE,ATOM_, 0, "Atom (Diamondville)" }, - { 6, 12, -1, -1, -1, -1, -1, -1, SILVERTHORNE,ATOM_, 0, "Atom (Silverthorne)" }, - { 6, 12, -1, -1, -1, -1, -1, -1, CEDARVIEW, ATOM_ , 0, "Atom (Cedarview)" }, - { 6, 6, -1, -1, -1, -1, -1, -1, CEDARVIEW, ATOM_ , 0, "Atom (Cedarview)" }, - { 6, 12, -1, -1, -1, -1, -1, -1, PINEVIEW, ATOM_ , 0, "Atom (Pineview)" }, - - /* -------------------------------------------------- */ - - { 6, 14, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Yonah" }, - { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, 0 , 0, "Yonah (Core Solo)" }, - { 6, 14, -1, -1, -1, 2, -1, -1, CORE_DUO, 0 , 0, "Yonah (Core Duo)" }, - { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, MOBILE_, 0, "Yonah (Core Solo)" }, - { 6, 14, -1, -1, -1, 2, -1, -1, CORE_DUO , MOBILE_, 0, "Yonah (Core Duo)" }, - { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, 0 , 0, "Yonah (Core Solo)" }, - - { 6, 15, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Core 2" }, - { 6, 15, -1, -1, -1, 2, 4096, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo)" }, - { 6, 15, -1, -1, -1, 2, 1024, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo) 1024K" }, - { 6, 15, -1, -1, -1, 2, 512, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo) 512K" }, - { 6, 15, -1, -1, -1, 4, -1, -1, QUAD_CORE, 0 , 0, "Kentsfield (Core 2 Quad)" }, - { 6, 15, -1, -1, -1, 4, 4096, -1, QUAD_CORE, 0 , 0, "Kentsfield (Core 2 Quad)" }, - { 6, 15, -1, -1, -1, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, - { 6, 15, -1, -1, -1, 2, 2048, -1, CORE_DUO, 0 , 0, "Allendale (Core 2 Duo)" }, - { 6, 15, -1, -1, -1, 2, -1, -1, MOBILE_CORE_DUO, 0, 0, "Merom (Core 2 Duo)" }, - { 6, 15, -1, -1, -1, 2, 2048, -1, MEROM, 0 , 0, "Merom (Core 2 Duo) 2048K" }, - { 6, 15, -1, -1, -1, 2, 4096, -1, MEROM, 0 , 0, "Merom (Core 2 Duo) 4096K" }, - - { 6, 15, -1, -1, 15, 1, -1, -1, NC, CELERON_ , 0, "Conroe-L (Celeron)" }, - { 6, 6, -1, -1, 22, 1, -1, -1, NC, CELERON_ , 0, "Conroe-L (Celeron)" }, - { 6, 15, -1, -1, 15, 2, -1, -1, NC, CELERON_ , 0, "Conroe-L (Allendale)" }, - { 6, 6, -1, -1, 22, 2, -1, -1, NC, CELERON_ , 0, "Conroe-L (Allendale)" }, - - - { 6, 6, -1, -1, 22, 1, -1, -1, NC, 0 , 0, "Unknown Core ?" }, - { 6, 7, -1, -1, 23, 1, -1, -1, NC, 0 , 0, "Unknown Core ?" }, - { 6, 6, -1, -1, 22, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, - { 6, 7, -1, -1, 23, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, - - { 6, 7, -1, -1, 23, 1, -1, -1, CORE_SOLO , 0, 0, "Unknown Core 45nm" }, - { 6, 7, -1, -1, 23, 1, -1, -1, CORE_DUO , 0, 0, "Unknown Core 45nm" }, - { 6, 7, -1, -1, 23, 2, 1024, -1, WOLFDALE , 0, 0, "Celeron Wolfdale 1M" }, - { 6, 7, -1, -1, 23, 2, 2048, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 2M" }, - { 6, 7, -1, -1, 23, 2, 3072, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 3M" }, - { 6, 7, -1, -1, 23, 2, 6144, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 6M" }, - { 6, 7, -1, -1, 23, 1, -1, -1, MOBILE_CORE_DUO , 0, 0, "Penryn (Core 2 Duo)" }, - { 6, 7, -1, -1, 23, 2, 1024, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo)" }, - { 6, 7, -1, -1, 23, 2, 3072, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo) 3M" }, - { 6, 7, -1, -1, 23, 2, 6144, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo) 6M" }, - { 6, 7, -1, -1, 23, 4, 2048, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 2M"}, - { 6, 7, -1, -1, 23, 4, 3072, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 3M"}, - { 6, 7, -1, -1, 23, 4, 6144, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 6M"}, - - /* Core microarchitecture-based Xeons: */ - { 6, 14, -1, -1, 14, 1, -1, -1, NC, XEON_ , 0, "Xeon LV" }, - { 6, 15, -1, -1, 15, 2, 4096, -1, NC, XEON_ , _5100, "Xeon (Woodcrest)" }, - { 6, 15, -1, -1, 15, 2, 2048, -1, NC, XEON_ , _3000, "Xeon (Conroe/2M)" }, - { 6, 15, -1, -1, 15, 2, 4096, -1, NC, XEON_ , _3000, "Xeon (Conroe/4M)" }, - { 6, 15, -1, -1, 15, 4, 4096, -1, NC, XEON_ , X3200, "Xeon (Kentsfield)" }, - { 6, 15, -1, -1, 15, 4, 4096, -1, NC, XEON_ , _5300, "Xeon (Clovertown)" }, - { 6, 7, -1, -1, 23, 2, 6144, -1, NC, XEON_ , _3100, "Xeon (Wolfdale)" }, - { 6, 7, -1, -1, 23, 2, 6144, -1, NC, XEON_ , _5200, "Xeon (Wolfdale DP)" }, - { 6, 7, -1, -1, 23, 4, 6144, -1, NC, XEON_ , _5400, "Xeon (Harpertown)" }, - { 6, 7, -1, -1, 23, 4, 3072, -1, NC, XEON_ , X3300, "Xeon (Yorkfield/3M)" }, - { 6, 7, -1, -1, 23, 4, 6144, -1, NC, XEON_ , X3300, "Xeon (Yorkfield/6M)" }, - - /* Nehalem CPUs (45nm): */ - { 6, 10, -1, -1, 26, 4, -1, -1, GAINESTOWN, XEON_ , 0, "Gainestown (Xeon)" }, - { 6, 10, -1, -1, 26, 4, -1, 4096, GAINESTOWN, XEON_ , 0, "Gainestown 4M (Xeon)" }, - { 6, 10, -1, -1, 26, 4, -1, 8192, GAINESTOWN, XEON_ , 0, "Gainestown 8M (Xeon)" }, - { 6, 10, -1, -1, 26, 4, -1, -1, NC, XEON_|_7 , 0, "Bloomfield (Xeon)" }, - { 6, 10, -1, -1, 26, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Bloomfield (Core i7)" }, - { 6, 10, -1, -1, 30, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Lynnfield (Core i7)" }, - { 6, 5, -1, -1, 37, 4, -1, 8192, NC, CORE_|_I_|_5 , 0, "Lynnfield (Core i5)" }, - - /* Westmere CPUs (32nm): */ - { 6, 5, -1, -1, 37, 2, -1, -1, NC, 0 , 0, "Unknown Core i3/i5" }, - { 6, 12, -1, -1, 44, -1, -1, -1, WESTMERE, XEON_ , 0, "Westmere (Xeon)" }, - { 6, 12, -1, -1, 44, -1, -1, 12288, WESTMERE, XEON_ , 0, "Gulftown (Xeon)" }, - { 6, 12, -1, -1, 44, 4, -1, 12288, NC, CORE_|_I_|_7 , 0, "Gulftown (Core i7)" }, - { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_5 , 0, "Clarkdale (Core i5)" }, - { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_3 , 0, "Clarkdale (Core i3)" }, - { 6, 5, -1, -1, 37, 2, -1, -1, NC, PENTIUM_ , 0, "Arrandale" }, - { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_7 , 0, "Arrandale (Core i7)" }, - { 6, 5, -1, -1, 37, 2, -1, 3072, NC, CORE_|_I_|_5 , 0, "Arrandale (Core i5)" }, - { 6, 5, -1, -1, 37, 2, -1, 3072, NC, CORE_|_I_|_3 , 0, "Arrandale (Core i3)" }, - - /* Sandy Bridge CPUs (32nm): */ - { 6, 10, -1, -1, 42, -1, -1, -1, NC, 0 , 0, "Unknown Sandy Bridge" }, - { 6, 10, -1, -1, 42, -1, -1, -1, NC, XEON_ , 0, "Sandy Bridge (Xeon)" }, - { 6, 10, -1, -1, 42, -1, -1, -1, NC, CORE_|_I_|_7 , 0, "Sandy Bridge (Core i7)" }, - { 6, 10, -1, -1, 42, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Sandy Bridge (Core i7)" }, - { 6, 10, -1, -1, 42, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Sandy Bridge (Core i5)" }, - { 6, 10, -1, -1, 42, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Sandy Bridge (Core i3)" }, - { 6, 10, -1, -1, 42, 2, -1, -1, NC, PENTIUM_ , 0, "Sandy Bridge (Pentium)" }, - { 6, 10, -1, -1, 42, 1, -1, -1, NC, CELERON_ , 0, "Sandy Bridge (Celeron)" }, - { 6, 10, -1, -1, 42, 2, -1, -1, NC, CELERON_ , 0, "Sandy Bridge (Celeron)" }, - { 6, 13, -1, -1, 45, -1, -1, -1, NC, CORE_|_I_|_3 , 0, "Sandy Bridge-E" }, - { 6, 13, -1, -1, 45, -1, -1, -1, NC, XEON_ , 0, "Sandy Bridge-E (Xeon)" }, - - /* Ivy Bridge CPUs (22nm): */ - { 6, 10, -1, -1, 58, -1, -1, -1, NC, XEON_ , 0, "Ivy Bridge (Xeon)" }, - { 6, 10, -1, -1, 58, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Ivy Bridge (Core i7)" }, - { 6, 10, -1, -1, 58, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Ivy Bridge (Core i5)" }, - { 6, 10, -1, -1, 58, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Ivy Bridge (Core i3)" }, - { 6, 10, -1, -1, 58, 2, -1, -1, NC, PENTIUM_ , 0, "Ivy Bridge (Pentium)" }, - { 6, 10, -1, -1, 58, 1, -1, -1, NC, CELERON_ , 0, "Ivy Bridge (Celeron)" }, - { 6, 10, -1, -1, 58, 2, -1, -1, NC, CELERON_ , 0, "Ivy Bridge (Celeron)" }, - { 6, 14, -1, -1, 62, -1, -1, -1, NC, 0 , 0, "Ivy Bridge-E" }, - - /* Haswell CPUs (22nm): */ - { 6, 12, -1, -1, 60, -1, -1, -1, NC, XEON_ , 0, "Haswell (Xeon)" }, - { 6, 12, -1, -1, 60, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, - { 6, 5, -1, -1, 69, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, - { 6, 6, -1, -1, 70, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, - { 6, 12, -1, -1, 60, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, - { 6, 5, -1, -1, 69, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, - { 6, 12, -1, -1, 60, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, - { 6, 5, -1, -1, 69, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, - { 6, 12, -1, -1, 60, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Haswell (Core i3)" }, - { 6, 5, -1, -1, 69, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Haswell (Core i3)" }, - { 6, 12, -1, -1, 60, 2, -1, -1, NC, PENTIUM_ , 0, "Haswell (Pentium)" }, - { 6, 12, -1, -1, 60, 2, -1, -1, NC, CELERON_ , 0, "Haswell (Celeron)" }, - { 6, 12, -1, -1, 60, 1, -1, -1, NC, CELERON_ , 0, "Haswell (Celeron)" }, - { 6, 15, -1, -1, 63, -1, -1, -1, NC, 0 , 0, "Haswell-E" }, - - /* Broadwell CPUs (14nm): */ - { 6, 7, -1, -1, 71, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell (Core i7)" }, - { 6, 7, -1, -1, 71, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell (Core i5)" }, - { 6, 13, -1, -1, 61, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-U (Core i7)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-U (Core i7)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-U (Core i5)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Broadwell-U (Core i3)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NC, PENTIUM_ , 0, "Broadwell-U (Pentium)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NC, CELERON_ , 0, "Broadwell-U (Celeron)" }, - { 6, 13, -1, -1, 61, 2, -1, -1, NA, 0 , 0, "Broadwell-U (Core M)" }, - { 6, 15, -1, -1, 79, -1, -1, -1, NC, XEON_ , 0, "Broadwell-E (Xeon)" }, - { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Broadwell-E (Core i3)" }, - { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-E (Core i5)" }, - { 6, 15, -1, -1, 79, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-E (Core i5)" }, - { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-E (Core i7)" }, - { 6, 15, -1, -1, 79, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-E (Core i7)" }, - - /* Skylake CPUs (14nm): */ - { 6, 14, -1, -1, 94, -1, -1, -1, NC, XEON_ , 0, "Skylake (Xeon)" }, - { 6, 14, -1, -1, 94, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Skylake (Core i7)" }, - { 6, 14, -1, -1, 94, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Skylake (Core i5)" }, - { 6, 14, -1, -1, 94, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Skylake (Core i3)" }, - { 6, 14, -1, -1, 94, 2, -1, -1, NC, PENTIUM_ , 0, "Skylake (Pentium)" }, - { 6, 14, -1, -1, 78, 2, -1, -1, NC, PENTIUM_ , 0, "Skylake (Pentium)" }, - { 6, 14, -1, -1, 94, 2, -1, -1, NC, CELERON_ , 0, "Skylake (Celeron)" }, - { 6, 14, -1, -1, 78, 2, -1, -1, NC, CELERON_ , 0, "Skylake (Celeron)" }, - { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_7 , 0, "Skylake (Core m7)" }, - { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_5 , 0, "Skylake (Core m5)" }, - { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_3 , 0, "Skylake (Core m3)" }, - - /* Kaby Lake CPUs (14nm): */ - { 6, 14, -1, -1, 158, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Kaby Lake (Core i7)" }, - { 6, 14, -1, -1, 158, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Kaby Lake (Core i5)" }, - { 6, 14, -1, -1, 158, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Kaby Lake (Core i3)" }, - { 6, 14, -1, -1, 158, 2, -1, -1, NC, PENTIUM_ , 0, "Kaby Lake (Pentium)" }, - { 6, 14, -1, -1, 158, 2, -1, -1, NC, CELERON_ , 0, "Kaby Lake (Celeron)" }, - { 6, 14, -1, -1, 158, 2, -1, -1, NC, CORE_|_M_|_3 , 0, "Kaby Lake (Core m3)" }, - - /* Itaniums */ - { 7, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Itanium" }, - { 15, -1, -1, 16, -1, 1, -1, -1, NC, 0 , 0, "Itanium 2" }, -}; - - -static void load_intel_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - const struct feature_map_t matchtable_edx1[] = { - { 18, CPU_FEATURE_PN }, - { 21, CPU_FEATURE_DTS }, - { 22, CPU_FEATURE_ACPI }, - { 27, CPU_FEATURE_SS }, - { 29, CPU_FEATURE_TM }, - { 30, CPU_FEATURE_IA64 }, - { 31, CPU_FEATURE_PBE }, - }; - const struct feature_map_t matchtable_ecx1[] = { - { 2, CPU_FEATURE_DTS64 }, - { 4, CPU_FEATURE_DS_CPL }, - { 5, CPU_FEATURE_VMX }, - { 6, CPU_FEATURE_SMX }, - { 7, CPU_FEATURE_EST }, - { 8, CPU_FEATURE_TM2 }, - { 10, CPU_FEATURE_CID }, - { 14, CPU_FEATURE_XTPR }, - { 15, CPU_FEATURE_PDCM }, - { 18, CPU_FEATURE_DCA }, - { 21, CPU_FEATURE_X2APIC }, - }; - const struct feature_map_t matchtable_edx81[] = { - { 20, CPU_FEATURE_XD }, - }; - const struct feature_map_t matchtable_ebx7[] = { - { 2, CPU_FEATURE_SGX }, - { 4, CPU_FEATURE_HLE }, - { 11, CPU_FEATURE_RTM }, - { 16, CPU_FEATURE_AVX512F }, - { 17, CPU_FEATURE_AVX512DQ }, - { 18, CPU_FEATURE_RDSEED }, - { 19, CPU_FEATURE_ADX }, - { 26, CPU_FEATURE_AVX512PF }, - { 27, CPU_FEATURE_AVX512ER }, - { 28, CPU_FEATURE_AVX512CD }, - { 29, CPU_FEATURE_SHA_NI }, - { 30, CPU_FEATURE_AVX512BW }, - { 31, CPU_FEATURE_AVX512VL }, - }; - if (raw->basic_cpuid[0][0] >= 1) { - match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data); - match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data); - } - if (raw->ext_cpuid[0][0] >= 1) { - match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); - } - // detect TSX/AVX512: - if (raw->basic_cpuid[0][0] >= 7) { - match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data); - } -} - -enum _cache_type_t { - L1I, - L1D, - L2, - L3, - L4 -}; -typedef enum _cache_type_t cache_type_t; - -static void check_case(uint8_t on, cache_type_t cache, int size, int assoc, int linesize, struct cpu_id_t* data) -{ - if (!on) return; - switch (cache) { - case L1I: - data->l1_instruction_cache = size; - break; - case L1D: - data->l1_data_cache = size; - data->l1_assoc = assoc; - data->l1_cacheline = linesize; - break; - case L2: - data->l2_cache = size; - data->l2_assoc = assoc; - data->l2_cacheline = linesize; - break; - case L3: - data->l3_cache = size; - data->l3_assoc = assoc; - data->l3_cacheline = linesize; - break; - case L4: - data->l4_cache = size; - data->l4_assoc = assoc; - data->l4_cacheline = linesize; - break; - default: - break; - } -} - -static void decode_intel_oldstyle_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - uint8_t f[256] = {0}; - int reg, off; - uint32_t x; - for (reg = 0; reg < 4; reg++) { - x = raw->basic_cpuid[2][reg]; - if (x & 0x80000000) continue; - for (off = 0; off < 4; off++) { - f[x & 0xff] = 1; - x >>= 8; - } - } - - check_case(f[0x06], L1I, 8, 4, 32, data); - check_case(f[0x08], L1I, 16, 4, 32, data); - check_case(f[0x0A], L1D, 8, 2, 32, data); - check_case(f[0x0C], L1D, 16, 4, 32, data); - check_case(f[0x22], L3, 512, 4, 64, data); - check_case(f[0x23], L3, 1024, 8, 64, data); - check_case(f[0x25], L3, 2048, 8, 64, data); - check_case(f[0x29], L3, 4096, 8, 64, data); - check_case(f[0x2C], L1D, 32, 8, 64, data); - check_case(f[0x30], L1I, 32, 8, 64, data); - check_case(f[0x39], L2, 128, 4, 64, data); - check_case(f[0x3A], L2, 192, 6, 64, data); - check_case(f[0x3B], L2, 128, 2, 64, data); - check_case(f[0x3C], L2, 256, 4, 64, data); - check_case(f[0x3D], L2, 384, 6, 64, data); - check_case(f[0x3E], L2, 512, 4, 64, data); - check_case(f[0x41], L2, 128, 4, 32, data); - check_case(f[0x42], L2, 256, 4, 32, data); - check_case(f[0x43], L2, 512, 4, 32, data); - check_case(f[0x44], L2, 1024, 4, 32, data); - check_case(f[0x45], L2, 2048, 4, 32, data); - check_case(f[0x46], L3, 4096, 4, 64, data); - check_case(f[0x47], L3, 8192, 8, 64, data); - check_case(f[0x4A], L3, 6144, 12, 64, data); - check_case(f[0x4B], L3, 8192, 16, 64, data); - check_case(f[0x4C], L3, 12288, 12, 64, data); - check_case(f[0x4D], L3, 16384, 16, 64, data); - check_case(f[0x4E], L2, 6144, 24, 64, data); - check_case(f[0x60], L1D, 16, 8, 64, data); - check_case(f[0x66], L1D, 8, 4, 64, data); - check_case(f[0x67], L1D, 16, 4, 64, data); - check_case(f[0x68], L1D, 32, 4, 64, data); - /* The following four entries are trace cache. Intel does not - * specify a cache-line size, so we use -1 instead - */ - check_case(f[0x70], L1I, 12, 8, -1, data); - check_case(f[0x71], L1I, 16, 8, -1, data); - check_case(f[0x72], L1I, 32, 8, -1, data); - check_case(f[0x73], L1I, 64, 8, -1, data); - - check_case(f[0x78], L2, 1024, 4, 64, data); - check_case(f[0x79], L2, 128, 8, 64, data); - check_case(f[0x7A], L2, 256, 8, 64, data); - check_case(f[0x7B], L2, 512, 8, 64, data); - check_case(f[0x7C], L2, 1024, 8, 64, data); - check_case(f[0x7D], L2, 2048, 8, 64, data); - check_case(f[0x7F], L2, 512, 2, 64, data); - check_case(f[0x82], L2, 256, 8, 32, data); - check_case(f[0x83], L2, 512, 8, 32, data); - check_case(f[0x84], L2, 1024, 8, 32, data); - check_case(f[0x85], L2, 2048, 8, 32, data); - check_case(f[0x86], L2, 512, 4, 64, data); - check_case(f[0x87], L2, 1024, 8, 64, data); - - if (f[0x49]) { - /* This flag is overloaded with two meanings. On Xeon MP - * (family 0xf, model 0x6) this means L3 cache. On all other - * CPUs (notably Conroe et al), this is L2 cache. In both cases - * it means 4MB, 16-way associative, 64-byte line size. - */ - if (data->family == 0xf && data->model == 0x6) { - data->l3_cache = 4096; - data->l3_assoc = 16; - data->l3_cacheline = 64; - } else { - data->l2_cache = 4096; - data->l2_assoc = 16; - data->l2_cacheline = 64; - } - } - if (f[0x40]) { - /* Again, a special flag. It means: - * 1) If no L2 is specified, then CPU is w/o L2 (0 KB) - * 2) If L2 is specified by other flags, then, CPU is w/o L3. - */ - if (data->l2_cache == -1) { - data->l2_cache = 0; - } else { - data->l3_cache = 0; - } - } -} - -static void decode_intel_deterministic_cache_info(struct cpu_raw_data_t* raw, - struct cpu_id_t* data) -{ - int ecx; - int ways, partitions, linesize, sets, size, level, typenumber; - cache_type_t type; - for (ecx = 0; ecx < MAX_INTELFN4_LEVEL; ecx++) { - typenumber = raw->intel_fn4[ecx][0] & 0x1f; - if (typenumber == 0) break; - level = (raw->intel_fn4[ecx][0] >> 5) & 0x7; - if (level == 1 && typenumber == 1) - type = L1D; - else if (level == 1 && typenumber == 2) - type = L1I; - else if (level == 2 && typenumber == 3) - type = L2; - else if (level == 3 && typenumber == 3) - type = L3; - else if (level == 4 && typenumber == 3) - type = L4; - else { - warnf("deterministic_cache: unknown level/typenumber combo (%d/%d), cannot\n", level, typenumber); - warnf("deterministic_cache: recognize cache type\n"); - continue; - } - ways = ((raw->intel_fn4[ecx][1] >> 22) & 0x3ff) + 1; - partitions = ((raw->intel_fn4[ecx][1] >> 12) & 0x3ff) + 1; - linesize = (raw->intel_fn4[ecx][1] & 0xfff) + 1; - sets = raw->intel_fn4[ecx][2] + 1; - size = ways * partitions * linesize * sets / 1024; - check_case(1, type, size, ways, linesize, data); - } -} - -static int decode_intel_extended_topology(struct cpu_raw_data_t* raw, - struct cpu_id_t* data) -{ - int i, level_type, num_smt = -1, num_core = -1; - for (i = 0; i < MAX_INTELFN11_LEVEL; i++) { - level_type = (raw->intel_fn11[i][2] & 0xff00) >> 8; - switch (level_type) { - case 0x01: - num_smt = raw->intel_fn11[i][1] & 0xffff; - break; - case 0x02: - num_core = raw->intel_fn11[i][1] & 0xffff; - break; - default: - break; - } - } - if (num_smt == -1 || num_core == -1) return 0; - data->num_logical_cpus = num_core; - data->num_cores = num_core / num_smt; - // make sure num_cores is at least 1. In VMs, the CPUID instruction - // is rigged and may give nonsensical results, but we should at least - // avoid outputs like data->num_cores == 0. - if (data->num_cores <= 0) data->num_cores = 1; - return 1; -} - -static void decode_intel_number_of_cores(struct cpu_raw_data_t* raw, - struct cpu_id_t* data) -{ - int logical_cpus = -1, num_cores = -1; - - if (raw->basic_cpuid[0][0] >= 11) { - if (decode_intel_extended_topology(raw, data)) return; - } - - if (raw->basic_cpuid[0][0] >= 1) { - logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; - if (raw->basic_cpuid[0][0] >= 4) { - num_cores = 1 + ((raw->basic_cpuid[4][0] >> 26) & 0x3f); - } - } - if (data->flags[CPU_FEATURE_HT]) { - if (num_cores > 1) { - data->num_cores = num_cores; - data->num_logical_cpus = logical_cpus; - } else { - data->num_cores = 1; - data->num_logical_cpus = (logical_cpus >= 1 ? logical_cpus : 1); - if (data->num_logical_cpus == 1) - data->flags[CPU_FEATURE_HT] = 0; - } - } else { - data->num_cores = data->num_logical_cpus = 1; - } -} - -static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) -{ - intel_code_t code = (intel_code_t) NC; - intel_code_and_bits_t result; - uint64_t bits = 0; - int i = 0; - const char* bs = data->brand_str; - const char* s; - const struct { intel_code_t c; const char *search; } matchtable[] = { - { PENTIUM_M, "Pentium(R) M" }, - { CORE_SOLO, "Pentium(R) Dual CPU" }, - { CORE_SOLO, "Pentium(R) Dual-Core" }, - { PENTIUM_D, "Pentium(R) D" }, - { CORE_SOLO, "Genuine Intel(R) CPU" }, - { CORE_SOLO, "Intel(R) Core(TM)" }, - { DIAMONDVILLE, "CPU [N ][23]## " }, - { SILVERTHORNE, "CPU Z" }, - { PINEVIEW, "CPU [ND][45]## " }, - { CEDARVIEW, "CPU [ND]#### " }, - }; - - const struct { uint64_t bit; const char* search; } bit_matchtable[] = { - { XEON_, "Xeon" }, - { _MP, " MP" }, - { ATOM_, "Atom(TM) CPU" }, - { MOBILE_, "Mobile" }, - { CELERON_, "Celeron" }, - { PENTIUM_, "Pentium" }, - }; - - for (i = 0; i < COUNT_OF(bit_matchtable); i++) { - if (match_pattern(bs, bit_matchtable[i].search)) - bits |= bit_matchtable[i].bit; - } - - if ((i = match_pattern(bs, "Core(TM) [im][357]")) != 0) { - bits |= CORE_; - i--; - switch (bs[i + 9]) { - case 'i': bits |= _I_; break; - case 'm': bits |= _M_; break; - } - switch (bs[i + 10]) { - case '3': bits |= _3; break; - case '5': bits |= _5; break; - case '7': bits |= _7; break; - } - } - for (i = 0; i < COUNT_OF(matchtable); i++) - if (match_pattern(bs, matchtable[i].search)) { - code = matchtable[i].c; - break; - } - debugf(2, "intel matchtable result is %d\n", code); - if (bits & XEON_) { - if (match_pattern(bs, "W35##") || match_pattern(bs, "[ELXW]75##")) - bits |= _7; - else if (match_pattern(bs, "[ELXW]55##")) - code = GAINESTOWN; - else if (match_pattern(bs, "[ELXW]56##")) - code = WESTMERE; - else if (data->l3_cache > 0 && data->family == 16) - /* restrict by family, since later Xeons also have L3 ... */ - code = IRWIN; - } - if (match_all(bits, XEON_ + _MP) && data->l3_cache > 0) - code = POTOMAC; - if (code == CORE_SOLO) { - s = strstr(bs, "CPU"); - if (s) { - s += 3; - while (*s == ' ') s++; - if (*s == 'T') - bits |= MOBILE_; - } - } - if (code == CORE_SOLO) { - switch (data->num_cores) { - case 1: break; - case 2: - { - code = CORE_DUO; - if (data->num_logical_cpus > 2) - code = DUAL_CORE_HT; - break; - } - case 4: - { - code = QUAD_CORE; - if (data->num_logical_cpus > 4) - code = QUAD_CORE_HT; - break; - } - default: - code = MORE_THAN_QUADCORE; break; - } - } - - if (code == CORE_DUO && (bits & MOBILE_) && data->model != 14) { - if (data->ext_model < 23) { - code = MEROM; - } else { - code = PENRYN; - } - } - if (data->ext_model == 23 && - (code == CORE_DUO || code == PENTIUM_D || (bits & CELERON_))) { - code = WOLFDALE; - } - - result.code = code; - result.bits = bits; - return result; -} - -static intel_model_t get_model_code(struct cpu_id_t* data) -{ - int i = 0; - int l = (int) strlen(data->brand_str); - const char *bs = data->brand_str; - int mod_flags = 0, model_no = 0, ndigs = 0; - /* If the CPU is a Core ix, then just return the model number generation: */ - if ((i = match_pattern(bs, "Core(TM) i[357]")) != 0) { - i += 11; - if (i + 4 >= l) return UNKNOWN; - if (bs[i] == '2') return _2xxx; - if (bs[i] == '3') return _3xxx; - return UNKNOWN; - } - - /* For Core2-based Xeons: */ - while (i < l - 3) { - if (bs[i] == 'C' && bs[i+1] == 'P' && bs[i+2] == 'U') - break; - i++; - } - if (i >= l - 3) return UNKNOWN; - i += 3; - while (i < l - 4 && bs[i] == ' ') i++; - if (i >= l - 4) return UNKNOWN; - while (i < l - 4 && !isdigit(bs[i])) { - if (bs[i] >= 'A' && bs[i] <= 'Z') - mod_flags |= (1 << (bs[i] - 'A')); - i++; - } - if (i >= l - 4) return UNKNOWN; - while (isdigit(bs[i])) { - ndigs++; - model_no = model_no * 10 + (int) (bs[i] - '0'); - i++; - } - if (ndigs != 4) return UNKNOWN; -#define HAVE(ch, flags) ((flags & (1 << ((int)(ch-'A')))) != 0) - switch (model_no / 100) { - case 30: return _3000; - case 31: return _3100; - case 32: - { - return (HAVE('X', mod_flags)) ? X3200 : _3200; - } - case 33: - { - return (HAVE('X', mod_flags)) ? X3300 : _3300; - } - case 51: return _5100; - case 52: return _5200; - case 53: return _5300; - case 54: return _5400; - default: - return UNKNOWN; - } -#undef HAVE -} - -static void decode_intel_sgx_features(const struct cpu_raw_data_t* raw, struct cpu_id_t* data) -{ - struct cpu_epc_t epc; - int i; - - if (raw->basic_cpuid[0][0] < 0x12) return; // no 12h leaf - if (raw->basic_cpuid[0x12][0] == 0) return; // no sub-leafs available, probably it's disabled by BIOS - - // decode sub-leaf 0: - if (raw->basic_cpuid[0x12][0] & 1) data->sgx.flags[INTEL_SGX1] = 1; - if (raw->basic_cpuid[0x12][0] & 2) data->sgx.flags[INTEL_SGX2] = 1; - if (data->sgx.flags[INTEL_SGX1] || data->sgx.flags[INTEL_SGX2]) - data->sgx.present = 1; - data->sgx.misc_select = raw->basic_cpuid[0x12][1]; - data->sgx.max_enclave_32bit = (raw->basic_cpuid[0x12][3] ) & 0xff; - data->sgx.max_enclave_64bit = (raw->basic_cpuid[0x12][3] >> 8) & 0xff; - - // decode sub-leaf 1: - data->sgx.secs_attributes = raw->intel_fn12h[1][0] | (((uint64_t) raw->intel_fn12h[1][1]) << 32); - data->sgx.secs_xfrm = raw->intel_fn12h[1][2] | (((uint64_t) raw->intel_fn12h[1][3]) << 32); - - // decode higher-order subleafs, whenever present: - data->sgx.num_epc_sections = -1; - for (i = 0; i < 1000000; i++) { - epc = cpuid_get_epc(i, raw); - if (epc.length == 0) { - debugf(2, "SGX: epc section request for %d returned null, no more EPC sections.\n", i); - data->sgx.num_epc_sections = i; - break; - } - } - if (data->sgx.num_epc_sections == -1) { - debugf(1, "SGX: warning: seems to be infinitude of EPC sections.\n"); - data->sgx.num_epc_sections = 1000000; - } -} - -struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw) -{ - uint32_t regs[4]; - struct cpu_epc_t retval = {0, 0}; - if (raw && index < MAX_INTELFN12H_LEVEL - 2) { - // this was queried already, use the data: - memcpy(regs, raw->intel_fn12h[2 + index], sizeof(regs)); - } else { - // query this ourselves: - regs[0] = 0x12; - regs[2] = 2 + index; - regs[1] = regs[3] = 0; - cpu_exec_cpuid_ext(regs); - } - - // decode values: - if ((regs[0] & 0xf) == 0x1) { - retval.start_addr |= (regs[0] & 0xfffff000); // bits [12, 32) -> bits [12, 32) - retval.start_addr |= ((uint64_t) (regs[1] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) - retval.length |= (regs[2] & 0xfffff000); // bits [12, 32) -> bits [12, 32) - retval.length |= ((uint64_t) (regs[3] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) - } - return retval; -} - -int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) -{ - intel_code_and_bits_t brand; - intel_model_t model_code; - int i; - char* brand_code_str = NULL; - - load_intel_features(raw, data); - if (raw->basic_cpuid[0][0] >= 4) { - /* Deterministic way is preferred, being more generic */ - decode_intel_deterministic_cache_info(raw, data); - } else if (raw->basic_cpuid[0][0] >= 2) { - decode_intel_oldstyle_cache_info(raw, data); - } - decode_intel_number_of_cores(raw, data); - - brand = get_brand_code_and_bits(data); - model_code = get_model_code(data); - for (i = 0; i < COUNT_OF(intel_bcode_str); i++) { - if (brand.code == intel_bcode_str[i].code) { - brand_code_str = intel_bcode_str[i].str; - break; - } - } - if (brand_code_str) - debugf(2, "Detected Intel brand code: %d (%s)\n", brand.code, brand_code_str); - else - debugf(2, "Detected Intel brand code: %d\n", brand.code); - if (brand.bits) { - debugf(2, "Detected Intel bits: "); - debug_print_lbits(2, brand.bits); - } - debugf(2, "Detected Intel model code: %d\n", model_code); - - internal->code.intel = brand.code; - internal->bits = brand.bits; - - if (data->flags[CPU_FEATURE_SGX]) { - debugf(2, "SGX seems to be present, decoding...\n"); - // if SGX is indicated by the CPU, verify its presence: - decode_intel_sgx_features(raw, data); - } - - internal->score = match_cpu_codename(cpudb_intel, COUNT_OF(cpudb_intel), data, - brand.code, brand.bits, model_code); - return 0; -} - -void cpuid_get_list_intel(struct cpu_list_t* list) -{ - generic_get_cpu_list(cpudb_intel, COUNT_OF(cpudb_intel), list); -} diff --git a/compat/winansi.c b/compat/winansi.c deleted file mode 100644 index 41cecd6a..00000000 --- a/compat/winansi.c +++ /dev/null @@ -1,392 +0,0 @@ -/** - * Old Git implementation of windows terminal colors (2009) - * before use of a threaded wrapper. - */ - -#undef NOGDI -#include -#include -#include -#include -#include -#include - -#include "compat/winansi.h" -/* -* Copyright 2008 Peter Harris -*/ - -/* -Functions to be wrapped: -*/ -#undef printf -#undef fprintf -#undef fputs -#undef vfprintf -/* TODO: write */ - -/* -ANSI codes used by git: m, K - -This file is git-specific. Therefore, this file does not attempt -to implement any codes that are not used by git. -*/ - -static HANDLE console; -static WORD plain_attr; -static WORD attr; -static int negative; - -static void init(void) -{ - CONSOLE_SCREEN_BUFFER_INFO sbi; - - static int initialized = 0; - if (initialized) - return; - - console = GetStdHandle(STD_OUTPUT_HANDLE); - if (console == INVALID_HANDLE_VALUE) - console = NULL; - - if (!console) - return; - - GetConsoleScreenBufferInfo(console, &sbi); - attr = plain_attr = sbi.wAttributes; - negative = 0; - - initialized = 1; -} - -static int write_console(const char *str, int len) -{ - /* convert utf-8 to utf-16, write directly to console */ - int wlen = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); - wchar_t *wbuf = (wchar_t *)alloca(wlen * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, str, len, wbuf, wlen); - - WriteConsoleW(console, wbuf, wlen, NULL, NULL); - - /* return original (utf-8 encoded) length */ - return len; -} - -#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) -#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) - -static void set_console_attr(void) -{ - WORD attributes = attr; - if (negative) { - attributes &= ~FOREGROUND_ALL; - attributes &= ~BACKGROUND_ALL; - - /* This could probably use a bitmask - instead of a series of ifs */ - if (attr & FOREGROUND_RED) - attributes |= BACKGROUND_RED; - if (attr & FOREGROUND_GREEN) - attributes |= BACKGROUND_GREEN; - if (attr & FOREGROUND_BLUE) - attributes |= BACKGROUND_BLUE; - - if (attr & BACKGROUND_RED) - attributes |= FOREGROUND_RED; - if (attr & BACKGROUND_GREEN) - attributes |= FOREGROUND_GREEN; - if (attr & BACKGROUND_BLUE) - attributes |= FOREGROUND_BLUE; - } - SetConsoleTextAttribute(console, attributes); -} - -static void erase_in_line(void) -{ - CONSOLE_SCREEN_BUFFER_INFO sbi; - DWORD dummy; /* Needed for Windows 7 (or Vista) regression */ - - if (!console) - return; - - GetConsoleScreenBufferInfo(console, &sbi); - FillConsoleOutputCharacterA(console, ' ', - sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, - &dummy); -} - - -static const char *set_attr(const char *str) -{ - const char *func; - size_t len = strspn(str, "0123456789;"); - func = str + len; - - switch (*func) { - case 'm': - do { - long val = strtol(str, (char **)&str, 10); - switch (val) { - case 0: /* reset */ - attr = plain_attr; - negative = 0; - break; - case 1: /* bold */ - attr |= FOREGROUND_INTENSITY; - break; - case 2: /* faint */ - case 22: /* normal */ - attr &= ~FOREGROUND_INTENSITY; - break; - case 3: /* italic */ - /* Unsupported */ - break; - case 4: /* underline */ - case 21: /* double underline */ - /* Wikipedia says this flag does nothing */ - /* Furthermore, mingw doesn't define this flag - attr |= COMMON_LVB_UNDERSCORE; */ - break; - case 24: /* no underline */ - /* attr &= ~COMMON_LVB_UNDERSCORE; */ - break; - case 5: /* slow blink */ - case 6: /* fast blink */ - /* We don't have blink, but we do have - background intensity */ - attr |= BACKGROUND_INTENSITY; - break; - case 25: /* no blink */ - attr &= ~BACKGROUND_INTENSITY; - break; - case 7: /* negative */ - negative = 1; - break; - case 27: /* positive */ - negative = 0; - break; - case 8: /* conceal */ - case 28: /* reveal */ - /* Unsupported */ - break; - case 30: /* Black */ - attr &= ~FOREGROUND_ALL; - break; - case 31: /* Red */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_RED; - break; - case 32: /* Green */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_GREEN; - break; - case 33: /* Yellow */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_RED | FOREGROUND_GREEN; - break; - case 34: /* Blue */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_BLUE; - break; - case 35: /* Magenta */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_RED | FOREGROUND_BLUE; - break; - case 36: /* Cyan */ - attr &= ~FOREGROUND_ALL; - attr |= FOREGROUND_GREEN | FOREGROUND_BLUE; - break; - case 37: /* White */ - attr |= FOREGROUND_RED | - FOREGROUND_GREEN | - FOREGROUND_BLUE; - break; - case 38: /* Unknown */ - break; - case 39: /* reset */ - attr &= ~FOREGROUND_ALL; - attr |= (plain_attr & FOREGROUND_ALL); - break; - case 40: /* Black */ - attr &= ~BACKGROUND_ALL; - break; - case 41: /* Red */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_RED; - break; - case 42: /* Green */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_GREEN; - break; - case 43: /* Yellow */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_RED | BACKGROUND_GREEN; - break; - case 44: /* Blue */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_BLUE; - break; - case 45: /* Magenta */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_RED | BACKGROUND_BLUE; - break; - case 46: /* Cyan */ - attr &= ~BACKGROUND_ALL; - attr |= BACKGROUND_GREEN | BACKGROUND_BLUE; - break; - case 47: /* White */ - attr |= BACKGROUND_RED | - BACKGROUND_GREEN | - BACKGROUND_BLUE; - break; - case 48: /* Unknown */ - break; - case 49: /* reset */ - attr &= ~BACKGROUND_ALL; - attr |= (plain_attr & BACKGROUND_ALL); - break; - default: - /* Unsupported code */ - break; - } - str++; - } while (*(str - 1) == ';'); - - set_console_attr(); - break; - case 'K': - erase_in_line(); - break; - default: - /* Unsupported code */ - break; - } - - return func + 1; -} - -static int ansi_emulate(const char *str, FILE *stream) -{ - int rv = 0; - const char *pos = str; - - fflush(stream); - - while (*pos) { - pos = strstr(str, "\033["); - if (pos) { - int len = (int) (pos - str); - - if (len) { - int out_len = write_console(str, len); - rv += out_len; - if (out_len < len) - return rv; - } - - str = pos + 2; - rv += 2; - - pos = set_attr(str); - rv += (int) (pos - str); - str = pos; - } - else { - int len = (int) strlen(str); - rv += write_console(str, len); - return rv; - } - } - return rv; -} - -int winansi_fputs(const char *str, FILE *stream) -{ - int rv; - - if (!isatty(fileno(stream))) - return fputs(str, stream); - - init(); - - if (!console) - return fputs(str, stream); - - rv = ansi_emulate(str, stream); - - if (rv >= 0) - return 0; - else - return EOF; -} - -int winansi_vfprintf(FILE *stream, const char *format, va_list list) -{ - int len, rv; - char small_buf[256] = { 0 }; - char *buf = small_buf; - va_list cp; - - if (!isatty(fileno(stream))) - goto abort; - - init(); - - if (!console) - goto abort; - - va_copy(cp, list); - len = vsnprintf(small_buf, sizeof(small_buf), format, cp); -#ifdef WIN32 - /* bug on long strings without that */ - if (len == -1) - len = _vscprintf(format, cp); -#endif - va_end(cp); - - if (len > sizeof(small_buf) - 1) { - buf = malloc(len + 1); - if (!buf) - goto abort; - - len = vsnprintf(buf, len + 1, format, list); -#ifdef WIN32 - if (len == -1) - len = _vscprintf(format, list); -#endif - } - - rv = ansi_emulate(buf, stream); - - if (buf != small_buf) - free(buf); - return rv; - -abort: - rv = vfprintf(stream, format, list); - return rv; -} - -int winansi_fprintf(FILE *stream, const char *format, ...) -{ - va_list list; - int rv; - - va_start(list, format); - rv = winansi_vfprintf(stream, format, list); - va_end(list); - - return rv; -} - -int winansi_printf(const char *format, ...) -{ - va_list list; - int rv; - - va_start(list, format); - rv = winansi_vfprintf(stdout, format, list); - va_end(list); - - return rv; -} \ No newline at end of file diff --git a/compat/winansi.h b/compat/winansi.h deleted file mode 100644 index 70c13734..00000000 --- a/compat/winansi.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * ANSI emulation wrappers - */ -#ifdef WIN32 -#include -#include -#include - -#define isatty(fd) _isatty(fd) -#define fileno(fd) _fileno(fd) - -#ifdef __cplusplus -extern "C" { -#endif - int winansi_fputs(const char *str, FILE *stream); - int winansi_printf(const char *format, ...); - int winansi_fprintf(FILE *stream, const char *format, ...); - int winansi_vfprintf(FILE *stream, const char *format, va_list list); -#ifdef __cplusplus -} -#endif - -#undef fputs -#undef fprintf -#undef vfprintf - -#define fputs winansi_fputs -#define printf winansi_printf -#define fprintf winansi_fprintf -#define vfprintf winansi_vfprintf - -#endif \ No newline at end of file diff --git a/cpu.c b/cpu.c deleted file mode 100644 index 2f6ef8b6..00000000 --- a/cpu.c +++ /dev/null @@ -1,100 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#ifndef BUILD_TEST -# include -#endif - -#include "cpu.h" - - -#ifndef BUILD_TEST -void cpu_init_common() { - struct cpu_raw_data_t raw = { 0 }; - struct cpu_id_t data = { 0 }; - - cpuid_get_raw_data(&raw); - cpu_identify(&raw, &data); - - strncpy(cpu_info.brand, data.brand_str, sizeof(cpu_info.brand) - 1); - - cpu_info.total_logical_cpus = data.total_logical_cpus; - cpu_info.sockets = data.total_logical_cpus / data.num_logical_cpus; - cpu_info.total_cores = data.num_cores * cpu_info.sockets; - cpu_info.l3_cache = data.l3_cache > 0 ? data.l3_cache * cpu_info.sockets : 0; - - // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 - if (data.vendor == VENDOR_AMD && data.l3_cache <= 0 && data.l2_assoc == 16 && data.ext_family >= 21) { - cpu_info.l2_cache = data.l2_cache * (cpu_info.total_cores / 2) * cpu_info.sockets; - } - else { - cpu_info.l2_cache = data.l2_cache > 0 ? data.l2_cache * cpu_info.total_cores * cpu_info.sockets : 0; - } - - -# ifdef __x86_64__ - cpu_info.flags |= CPU_FLAG_X86_64; -# endif - - if (data.flags[CPU_FEATURE_AES]) { - cpu_info.flags |= CPU_FLAG_AES; - } - - if (data.flags[CPU_FEATURE_BMI2]) { - cpu_info.flags |= CPU_FLAG_BMI2; - } -} -#endif - - -int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage) { - if (cpu_info.total_logical_cpus == 1) { - return 1; - } - - int cache = cpu_info.l3_cache ? cpu_info.l3_cache : cpu_info.l2_cache; - int count = 0; - const int size = (algo ? 1024 : 2048) * (double_hash ? 2 : 1); - - if (cache) { - count = cache / size; - } - else { - count = cpu_info.total_logical_cpus / 2; - } - - if (count > cpu_info.total_logical_cpus) { - count = cpu_info.total_logical_cpus; - } - - if (((float) count / cpu_info.total_logical_cpus * 100) > max_cpu_usage) { - count = ceil((float) cpu_info.total_logical_cpus * (max_cpu_usage / 100.0)); - } - - return count < 1 ? 1 : count; -} diff --git a/crypto/c_keccak.c b/crypto/c_keccak.c deleted file mode 100644 index 2a0fd65b..00000000 --- a/crypto/c_keccak.c +++ /dev/null @@ -1,123 +0,0 @@ -// keccak.c -// 19-Nov-11 Markku-Juhani O. Saarinen -// A baseline Keccak (3rd round) implementation. - -#include -#include - -#define HASH_DATA_AREA 136 -#define KECCAK_ROUNDS 24 - -#ifndef ROTL64 -#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) -#endif - -const uint64_t keccakf_rndc[24] = -{ - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, - 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, - 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, - 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, - 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, - 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, - 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 -}; - -const int keccakf_rotc[24] = -{ - 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 -}; - -const int keccakf_piln[24] = -{ - 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 -}; - -// update the state with given number of rounds - -void keccakf(uint64_t st[25], int rounds) -{ - int i, j, round; - uint64_t t, bc[5]; - - for (round = 0; round < rounds; ++round) { - - // Theta - bc[0] = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20]; - bc[1] = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21]; - bc[2] = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22]; - bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23]; - bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24]; - - for (i = 0; i < 5; ++i) { - t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); - st[i ] ^= t; - st[i + 5] ^= t; - st[i + 10] ^= t; - st[i + 15] ^= t; - st[i + 20] ^= t; - } - - // Rho Pi - t = st[1]; - for (i = 0; i < 24; ++i) { - bc[0] = st[keccakf_piln[i]]; - st[keccakf_piln[i]] = ROTL64(t, keccakf_rotc[i]); - t = bc[0]; - } - - // Chi - for (j = 0; j < 25; j += 5) { - bc[0] = st[j ]; - bc[1] = st[j + 1]; - bc[2] = st[j + 2]; - bc[3] = st[j + 3]; - bc[4] = st[j + 4]; - st[j ] ^= (~bc[1]) & bc[2]; - st[j + 1] ^= (~bc[2]) & bc[3]; - st[j + 2] ^= (~bc[3]) & bc[4]; - st[j + 3] ^= (~bc[4]) & bc[0]; - st[j + 4] ^= (~bc[0]) & bc[1]; - } - - // Iota - st[0] ^= keccakf_rndc[round]; - } -} - -// compute a keccak hash (md) of given byte length from "in" -typedef uint64_t state_t[25]; - -void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) -{ - state_t st; - uint8_t temp[144]; - int i, rsiz, rsizw; - - rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; - rsizw = rsiz / 8; - - memset(st, 0, sizeof(st)); - - for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { - for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) in)[i]; - keccakf(st, KECCAK_ROUNDS); - } - - // last block and padding - memcpy(temp, in, inlen); - temp[inlen++] = 1; - memset(temp + inlen, 0, rsiz - inlen); - temp[rsiz - 1] |= 0x80; - - for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) temp)[i]; - - keccakf(st, KECCAK_ROUNDS); - - memcpy(md, st, mdlen); -} diff --git a/crypto/c_skein.c b/crypto/c_skein.c deleted file mode 100644 index 255d14a6..00000000 --- a/crypto/c_skein.c +++ /dev/null @@ -1,2036 +0,0 @@ -/*********************************************************************** -** -** Implementation of the Skein hash function. -** -** Source code author: Doug Whiting, 2008. -** -** This algorithm and source code is released to the public domain. -** -************************************************************************/ - -#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */ - -#include /* get size_t definition */ -#include /* get the memcpy/memset functions */ -#include "c_skein.h" /* get the Skein API definitions */ - -#define DISABLE_UNUSED 0 - -#ifndef SKEIN_256_NIST_MAX_HASHBITS -#define SKEIN_256_NIST_MAX_HASHBITS (0) -#endif - -#ifndef SKEIN_512_NIST_MAX_HASHBITS -#define SKEIN_512_NIST_MAX_HASHBITS (512) -#endif - -#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */ - -#define SKEIN_256_STATE_WORDS ( 4) -#define SKEIN_512_STATE_WORDS ( 8) -#define SKEIN1024_STATE_WORDS (16) -#define SKEIN_MAX_STATE_WORDS (16) - -#define SKEIN_256_STATE_BYTES ( 8*SKEIN_256_STATE_WORDS) -#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS) -#define SKEIN1024_STATE_BYTES ( 8*SKEIN1024_STATE_WORDS) - -#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS) -#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS) -#define SKEIN1024_STATE_BITS (64*SKEIN1024_STATE_WORDS) - -#define SKEIN_256_BLOCK_BYTES ( 8*SKEIN_256_STATE_WORDS) -#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS) -#define SKEIN1024_BLOCK_BYTES ( 8*SKEIN1024_STATE_WORDS) - -#define SKEIN_RND_SPECIAL (1000u) -#define SKEIN_RND_KEY_INITIAL (SKEIN_RND_SPECIAL+0u) -#define SKEIN_RND_KEY_INJECT (SKEIN_RND_SPECIAL+1u) -#define SKEIN_RND_FEED_FWD (SKEIN_RND_SPECIAL+2u) - -typedef struct -{ - size_t hashBitLen; /* size of hash result, in bits */ - size_t bCnt; /* current byte count in buffer b[] */ - u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */ -} Skein_Ctxt_Hdr_t; - -typedef struct /* 256-bit Skein hash context structure */ -{ - Skein_Ctxt_Hdr_t h; /* common header context variables */ - u64b_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */ - u08b_t b[SKEIN_256_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ -} Skein_256_Ctxt_t; - -typedef struct /* 512-bit Skein hash context structure */ -{ - Skein_Ctxt_Hdr_t h; /* common header context variables */ - u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */ - u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ -} Skein_512_Ctxt_t; - -typedef struct /* 1024-bit Skein hash context structure */ -{ - Skein_Ctxt_Hdr_t h; /* common header context variables */ - u64b_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */ - u08b_t b[SKEIN1024_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ -} Skein1024_Ctxt_t; - -/* Skein APIs for (incremental) "straight hashing" */ -#if SKEIN_256_NIST_MAX_HASH_BITS -static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); -#endif -static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); -static int Skein1024_Init (Skein1024_Ctxt_t *ctx, size_t hashBitLen); - -static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); -static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); -static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); - -static int Skein_256_Final (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein1024_Final (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); - -/* -** Skein APIs for "extended" initialization: MAC keys, tree hashing. -** After an InitExt() call, just use Update/Final calls as with Init(). -** -** Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes. -** When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL, -** the results of InitExt() are identical to calling Init(). -** The function Init() may be called once to "precompute" the IV for -** a given hashBitLen value, then by saving a copy of the context -** the IV computation may be avoided in later calls. -** Similarly, the function InitExt() may be called once per MAC key -** to precompute the MAC IV, then a copy of the context saved and -** reused for each new MAC computation. -**/ -#if 0 -static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); -static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); -static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); -#endif - -/* -** Skein APIs for MAC and tree hash: -** Final_Pad: pad, do final block, but no OUTPUT type -** Output: do just the output stage -*/ -#if 0 -static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t * hashVal); -#endif - -#ifndef SKEIN_TREE_HASH -#define SKEIN_TREE_HASH (1) -#endif -#if 0 -#if SKEIN_TREE_HASH -static int Skein_256_Output (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein_512_Output (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); -static int Skein1024_Output (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); -#endif -#endif - -/***************************************************************** -** "Internal" Skein definitions -** -- not needed for sequential hashing API, but will be -** helpful for other uses of Skein (e.g., tree hash mode). -** -- included here so that they can be shared between -** reference and optimized code. -******************************************************************/ - -/* tweak word T[1]: bit field starting positions */ -#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */ - -#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */ -#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */ -#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */ -#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */ -#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */ - -/* tweak word T[1]: flag bit definition(s) */ -#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST) -#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL) -#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD) - -/* tweak word T[1]: tree level bit field mask */ -#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL) -#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL) - -/* tweak word T[1]: block type field */ -#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */ -#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */ -#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */ -#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */ -#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */ -#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */ -#define SKEIN_BLK_TYPE_MSG (48) /* message processing */ -#define SKEIN_BLK_TYPE_OUT (63) /* output stage */ -#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */ - -#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE) -#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */ -#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */ -#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */ -#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */ -#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */ -#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */ -#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */ -#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */ -#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */ - -#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL) -#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL) - -#define SKEIN_VERSION (1) - -#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */ -#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/ -#endif - -#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32)) -#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE) -#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22) - -#define SKEIN_CFG_STR_LEN (4*8) - -/* bit field definitions in config block treeInfo word */ -#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0) -#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8) -#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16) - -#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS) -#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS) -#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS) - -#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \ - ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \ - (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \ - (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) ) - -#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */ - -/* -** Skein macros for getting/setting tweak words, etc. -** These are useful for partial input bytes, hash tree init/update, etc. -**/ -#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM]) -#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);} - -#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0) -#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1) -#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0) -#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1) - -/* set both tweak words at once */ -#define Skein_Set_T0_T1(ctxPtr,T0,T1) \ -{ \ - Skein_Set_T0(ctxPtr,(T0)); \ - Skein_Set_T1(ctxPtr,(T1)); \ -} - -#define Skein_Set_Type(ctxPtr,BLK_TYPE) \ - Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE) - -/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */ -#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \ -{ Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; } - -#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; } -#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; } - -#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);} - -/***************************************************************** -** "Internal" Skein definitions for debugging and error checking -******************************************************************/ -#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr) -#define Skein_Show_Round(bits,ctx,r,X) -#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr) -#define Skein_Show_Final(bits,ctx,cnt,outPtr) -#define Skein_Show_Key(bits,ctx,key,keyBytes) - - -#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */ -#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */ -#define Skein_assert(x) -#elif defined(SKEIN_ASSERT) -#include -#define Skein_Assert(x,retCode) assert(x) -#define Skein_assert(x) assert(x) -#else -#include -#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */ -#define Skein_assert(x) assert(x) /* internal error */ -#endif - -/***************************************************************** -** Skein block function constants (shared across Ref and Opt code) -******************************************************************/ -enum -{ - /* Skein_256 round rotation constants */ - R_256_0_0=14, R_256_0_1=16, - R_256_1_0=52, R_256_1_1=57, - R_256_2_0=23, R_256_2_1=40, - R_256_3_0= 5, R_256_3_1=37, - R_256_4_0=25, R_256_4_1=33, - R_256_5_0=46, R_256_5_1=12, - R_256_6_0=58, R_256_6_1=22, - R_256_7_0=32, R_256_7_1=32, - - /* Skein_512 round rotation constants */ - R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37, - R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42, - R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39, - R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56, - R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24, - R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17, - R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43, - R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22, - - /* Skein1024 round rotation constants */ - R1024_0_0=24, R1024_0_1=13, R1024_0_2= 8, R1024_0_3=47, R1024_0_4= 8, R1024_0_5=17, R1024_0_6=22, R1024_0_7=37, - R1024_1_0=38, R1024_1_1=19, R1024_1_2=10, R1024_1_3=55, R1024_1_4=49, R1024_1_5=18, R1024_1_6=23, R1024_1_7=52, - R1024_2_0=33, R1024_2_1= 4, R1024_2_2=51, R1024_2_3=13, R1024_2_4=34, R1024_2_5=41, R1024_2_6=59, R1024_2_7=17, - R1024_3_0= 5, R1024_3_1=20, R1024_3_2=48, R1024_3_3=41, R1024_3_4=47, R1024_3_5=28, R1024_3_6=16, R1024_3_7=25, - R1024_4_0=41, R1024_4_1= 9, R1024_4_2=37, R1024_4_3=31, R1024_4_4=12, R1024_4_5=47, R1024_4_6=44, R1024_4_7=30, - R1024_5_0=16, R1024_5_1=34, R1024_5_2=56, R1024_5_3=51, R1024_5_4= 4, R1024_5_5=53, R1024_5_6=42, R1024_5_7=41, - R1024_6_0=31, R1024_6_1=44, R1024_6_2=47, R1024_6_3=46, R1024_6_4=19, R1024_6_5=42, R1024_6_6=44, R1024_6_7=25, - R1024_7_0= 9, R1024_7_1=48, R1024_7_2=35, R1024_7_3=52, R1024_7_4=23, R1024_7_5=31, R1024_7_6=37, R1024_7_7=20 -}; - -#ifndef SKEIN_ROUNDS -#define SKEIN_256_ROUNDS_TOTAL (72) /* number of rounds for the different block sizes */ -#define SKEIN_512_ROUNDS_TOTAL (72) -#define SKEIN1024_ROUNDS_TOTAL (80) -#else /* allow command-line define in range 8*(5..14) */ -#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5)) -#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5)) -#define SKEIN1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS ) + 5) % 10) + 5)) -#endif - - -/* -***************** Pre-computed Skein IVs ******************* -** -** NOTE: these values are not "magic" constants, but -** are generated using the Threefish block function. -** They are pre-computed here only for speed; i.e., to -** avoid the need for a Threefish call during Init(). -** -** The IV for any fixed hash length may be pre-computed. -** Only the most common values are included here. -** -************************************************************ -**/ - -#define MK_64 SKEIN_MK_64 - -/* blkSize = 256 bits. hashSize = 128 bits */ -const u64b_t SKEIN_256_IV_128[] = - { - MK_64(0xE1111906,0x964D7260), - MK_64(0x883DAAA7,0x7C8D811C), - MK_64(0x10080DF4,0x91960F7A), - MK_64(0xCCF7DDE5,0xB45BC1C2) - }; - -/* blkSize = 256 bits. hashSize = 160 bits */ -const u64b_t SKEIN_256_IV_160[] = - { - MK_64(0x14202314,0x72825E98), - MK_64(0x2AC4E9A2,0x5A77E590), - MK_64(0xD47A5856,0x8838D63E), - MK_64(0x2DD2E496,0x8586AB7D) - }; - -/* blkSize = 256 bits. hashSize = 224 bits */ -const u64b_t SKEIN_256_IV_224[] = - { - MK_64(0xC6098A8C,0x9AE5EA0B), - MK_64(0x876D5686,0x08C5191C), - MK_64(0x99CB88D7,0xD7F53884), - MK_64(0x384BDDB1,0xAEDDB5DE) - }; - -/* blkSize = 256 bits. hashSize = 256 bits */ -const u64b_t SKEIN_256_IV_256[] = - { - MK_64(0xFC9DA860,0xD048B449), - MK_64(0x2FCA6647,0x9FA7D833), - MK_64(0xB33BC389,0x6656840F), - MK_64(0x6A54E920,0xFDE8DA69) - }; - -/* blkSize = 512 bits. hashSize = 128 bits */ -const u64b_t SKEIN_512_IV_128[] = - { - MK_64(0xA8BC7BF3,0x6FBF9F52), - MK_64(0x1E9872CE,0xBD1AF0AA), - MK_64(0x309B1790,0xB32190D3), - MK_64(0xBCFBB854,0x3F94805C), - MK_64(0x0DA61BCD,0x6E31B11B), - MK_64(0x1A18EBEA,0xD46A32E3), - MK_64(0xA2CC5B18,0xCE84AA82), - MK_64(0x6982AB28,0x9D46982D) - }; - -/* blkSize = 512 bits. hashSize = 160 bits */ -const u64b_t SKEIN_512_IV_160[] = - { - MK_64(0x28B81A2A,0xE013BD91), - MK_64(0xC2F11668,0xB5BDF78F), - MK_64(0x1760D8F3,0xF6A56F12), - MK_64(0x4FB74758,0x8239904F), - MK_64(0x21EDE07F,0x7EAF5056), - MK_64(0xD908922E,0x63ED70B8), - MK_64(0xB8EC76FF,0xECCB52FA), - MK_64(0x01A47BB8,0xA3F27A6E) - }; - -/* blkSize = 512 bits. hashSize = 224 bits */ -const u64b_t SKEIN_512_IV_224[] = - { - MK_64(0xCCD06162,0x48677224), - MK_64(0xCBA65CF3,0xA92339EF), - MK_64(0x8CCD69D6,0x52FF4B64), - MK_64(0x398AED7B,0x3AB890B4), - MK_64(0x0F59D1B1,0x457D2BD0), - MK_64(0x6776FE65,0x75D4EB3D), - MK_64(0x99FBC70E,0x997413E9), - MK_64(0x9E2CFCCF,0xE1C41EF7) - }; - -/* blkSize = 512 bits. hashSize = 256 bits */ -const u64b_t SKEIN_512_IV_256[] = - { - MK_64(0xCCD044A1,0x2FDB3E13), - MK_64(0xE8359030,0x1A79A9EB), - MK_64(0x55AEA061,0x4F816E6F), - MK_64(0x2A2767A4,0xAE9B94DB), - MK_64(0xEC06025E,0x74DD7683), - MK_64(0xE7A436CD,0xC4746251), - MK_64(0xC36FBAF9,0x393AD185), - MK_64(0x3EEDBA18,0x33EDFC13) - }; - -/* blkSize = 512 bits. hashSize = 384 bits */ -const u64b_t SKEIN_512_IV_384[] = - { - MK_64(0xA3F6C6BF,0x3A75EF5F), - MK_64(0xB0FEF9CC,0xFD84FAA4), - MK_64(0x9D77DD66,0x3D770CFE), - MK_64(0xD798CBF3,0xB468FDDA), - MK_64(0x1BC4A666,0x8A0E4465), - MK_64(0x7ED7D434,0xE5807407), - MK_64(0x548FC1AC,0xD4EC44D6), - MK_64(0x266E1754,0x6AA18FF8) - }; - -/* blkSize = 512 bits. hashSize = 512 bits */ -const u64b_t SKEIN_512_IV_512[] = - { - MK_64(0x4903ADFF,0x749C51CE), - MK_64(0x0D95DE39,0x9746DF03), - MK_64(0x8FD19341,0x27C79BCE), - MK_64(0x9A255629,0xFF352CB1), - MK_64(0x5DB62599,0xDF6CA7B0), - MK_64(0xEABE394C,0xA9D5C3F4), - MK_64(0x991112C7,0x1A75B523), - MK_64(0xAE18A40B,0x660FCC33) - }; - -/* blkSize = 1024 bits. hashSize = 384 bits */ -const u64b_t SKEIN1024_IV_384[] = - { - MK_64(0x5102B6B8,0xC1894A35), - MK_64(0xFEEBC9E3,0xFE8AF11A), - MK_64(0x0C807F06,0xE32BED71), - MK_64(0x60C13A52,0xB41A91F6), - MK_64(0x9716D35D,0xD4917C38), - MK_64(0xE780DF12,0x6FD31D3A), - MK_64(0x797846B6,0xC898303A), - MK_64(0xB172C2A8,0xB3572A3B), - MK_64(0xC9BC8203,0xA6104A6C), - MK_64(0x65909338,0xD75624F4), - MK_64(0x94BCC568,0x4B3F81A0), - MK_64(0x3EBBF51E,0x10ECFD46), - MK_64(0x2DF50F0B,0xEEB08542), - MK_64(0x3B5A6530,0x0DBC6516), - MK_64(0x484B9CD2,0x167BBCE1), - MK_64(0x2D136947,0xD4CBAFEA) - }; - -/* blkSize = 1024 bits. hashSize = 512 bits */ -const u64b_t SKEIN1024_IV_512[] = - { - MK_64(0xCAEC0E5D,0x7C1B1B18), - MK_64(0xA01B0E04,0x5F03E802), - MK_64(0x33840451,0xED912885), - MK_64(0x374AFB04,0xEAEC2E1C), - MK_64(0xDF25A0E2,0x813581F7), - MK_64(0xE4004093,0x8B12F9D2), - MK_64(0xA662D539,0xC2ED39B6), - MK_64(0xFA8B85CF,0x45D8C75A), - MK_64(0x8316ED8E,0x29EDE796), - MK_64(0x053289C0,0x2E9F91B8), - MK_64(0xC3F8EF1D,0x6D518B73), - MK_64(0xBDCEC3C4,0xD5EF332E), - MK_64(0x549A7E52,0x22974487), - MK_64(0x67070872,0x5B749816), - MK_64(0xB9CD28FB,0xF0581BD1), - MK_64(0x0E2940B8,0x15804974) - }; - -/* blkSize = 1024 bits. hashSize = 1024 bits */ -const u64b_t SKEIN1024_IV_1024[] = - { - MK_64(0xD593DA07,0x41E72355), - MK_64(0x15B5E511,0xAC73E00C), - MK_64(0x5180E5AE,0xBAF2C4F0), - MK_64(0x03BD41D3,0xFCBCAFAF), - MK_64(0x1CAEC6FD,0x1983A898), - MK_64(0x6E510B8B,0xCDD0589F), - MK_64(0x77E2BDFD,0xC6394ADA), - MK_64(0xC11E1DB5,0x24DCB0A3), - MK_64(0xD6D14AF9,0xC6329AB5), - MK_64(0x6A9B0BFC,0x6EB67E0D), - MK_64(0x9243C60D,0xCCFF1332), - MK_64(0x1A1F1DDE,0x743F02D4), - MK_64(0x0996753C,0x10ED0BB8), - MK_64(0x6572DD22,0xF2B4969A), - MK_64(0x61FD3062,0xD00A579A), - MK_64(0x1DE0536E,0x8682E539) - }; - - -#ifndef SKEIN_USE_ASM -#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */ -#endif - -#ifndef SKEIN_LOOP -#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */ -#endif - -#define BLK_BITS (WCNT*64) /* some useful definitions for code here */ -#define KW_TWK_BASE (0) -#define KW_KEY_BASE (3) -#define ks (kw + KW_KEY_BASE) -#define ts (kw + KW_TWK_BASE) - -#ifdef SKEIN_DEBUG -#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; } -#else -#define DebugSaveTweak(ctx) -#endif - -/***************************** Skein_256 ******************************/ -#if !(SKEIN_USE_ASM & 256) -static void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) - { /* do it in C */ - enum - { - WCNT = SKEIN_256_STATE_WORDS - }; -#undef RCNT -#define RCNT (SKEIN_256_ROUNDS_TOTAL/8) - -#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ -#define SKEIN_UNROLL_256 (((SKEIN_LOOP)/100)%10) -#else -#define SKEIN_UNROLL_256 (0) -#endif - -#if SKEIN_UNROLL_256 -#if (RCNT % SKEIN_UNROLL_256) -#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */ -#endif - size_t r; - u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ -#else - u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ -#endif - u64b_t X0,X1,X2,X3; /* local copy of context vars, for speed */ - u64b_t w [WCNT]; /* local copy of input block */ -#ifdef SKEIN_DEBUG - const u64b_t *Xptr[4]; /* use for debugging (help compiler put Xn in registers) */ - Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; -#endif - Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ - ts[0] = ctx->h.T[0]; - ts[1] = ctx->h.T[1]; - do { - /* this implementation only supports 2**64 input bytes (no carry out here) */ - ts[0] += byteCntAdd; /* update processed length */ - - /* precompute the key schedule for this block */ - ks[0] = ctx->X[0]; - ks[1] = ctx->X[1]; - ks[2] = ctx->X[2]; - ks[3] = ctx->X[3]; - ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY; - - ts[2] = ts[0] ^ ts[1]; - - Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ - DebugSaveTweak(ctx); - Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); - - X0 = w[0] + ks[0]; /* do the first full key injection */ - X1 = w[1] + ks[1] + ts[0]; - X2 = w[2] + ks[2] + ts[1]; - X3 = w[3] + ks[3]; - - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); /* show starting state values */ - - blkPtr += SKEIN_256_BLOCK_BYTES; - - /* run the rounds */ - -#define Round256(p0,p1,p2,p3,ROT,rNum) \ - X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ - X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ - -#if SKEIN_UNROLL_256 == 0 -#define R256(p0,p1,p2,p3,ROT,rNum) /* fully unrolled */ \ - Round256(p0,p1,p2,p3,ROT,rNum) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); - -#define I256(R) \ - X0 += ks[((R)+1) % 5]; /* inject the key schedule value */ \ - X1 += ks[((R)+2) % 5] + ts[((R)+1) % 3]; \ - X2 += ks[((R)+3) % 5] + ts[((R)+2) % 3]; \ - X3 += ks[((R)+4) % 5] + (R)+1; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); -#else /* looping version */ -#define R256(p0,p1,p2,p3,ROT,rNum) \ - Round256(p0,p1,p2,p3,ROT,rNum) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); - -#define I256(R) \ - X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ - X1 += ks[r+(R)+1] + ts[r+(R)+0]; \ - X2 += ks[r+(R)+2] + ts[r+(R)+1]; \ - X3 += ks[r+(R)+3] + r+(R) ; \ - ks[r + (R)+4 ] = ks[r+(R)-1]; /* rotate key schedule */\ - ts[r + (R)+2 ] = ts[r+(R)-1]; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); - - for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_256) /* loop thru it */ -#endif - { -#define R256_8_rounds(R) \ - R256(0,1,2,3,R_256_0,8*(R) + 1); \ - R256(0,3,2,1,R_256_1,8*(R) + 2); \ - R256(0,1,2,3,R_256_2,8*(R) + 3); \ - R256(0,3,2,1,R_256_3,8*(R) + 4); \ - I256(2*(R)); \ - R256(0,1,2,3,R_256_4,8*(R) + 5); \ - R256(0,3,2,1,R_256_5,8*(R) + 6); \ - R256(0,1,2,3,R_256_6,8*(R) + 7); \ - R256(0,3,2,1,R_256_7,8*(R) + 8); \ - I256(2*(R)+1); - - R256_8_rounds( 0); - -#define R256_Unroll_R(NN) ((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_256 > (NN))) - - #if R256_Unroll_R( 1) - R256_8_rounds( 1); - #endif - #if R256_Unroll_R( 2) - R256_8_rounds( 2); - #endif - #if R256_Unroll_R( 3) - R256_8_rounds( 3); - #endif - #if R256_Unroll_R( 4) - R256_8_rounds( 4); - #endif - #if R256_Unroll_R( 5) - R256_8_rounds( 5); - #endif - #if R256_Unroll_R( 6) - R256_8_rounds( 6); - #endif - #if R256_Unroll_R( 7) - R256_8_rounds( 7); - #endif - #if R256_Unroll_R( 8) - R256_8_rounds( 8); - #endif - #if R256_Unroll_R( 9) - R256_8_rounds( 9); - #endif - #if R256_Unroll_R(10) - R256_8_rounds(10); - #endif - #if R256_Unroll_R(11) - R256_8_rounds(11); - #endif - #if R256_Unroll_R(12) - R256_8_rounds(12); - #endif - #if R256_Unroll_R(13) - R256_8_rounds(13); - #endif - #if R256_Unroll_R(14) - R256_8_rounds(14); - #endif - #if (SKEIN_UNROLL_256 > 14) -#error "need more unrolling in Skein_256_Process_Block" - #endif - } - /* do the final "feedforward" xor, update context chaining vars */ - ctx->X[0] = X0 ^ w[0]; - ctx->X[1] = X1 ^ w[1]; - ctx->X[2] = X2 ^ w[2]; - ctx->X[3] = X3 ^ w[3]; - - Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); - - ts[1] &= ~SKEIN_T1_FLAG_FIRST; - } - while (--blkCnt); - ctx->h.T[0] = ts[0]; - ctx->h.T[1] = ts[1]; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein_256_Process_Block_CodeSize(void) - { - return ((u08b_t *) Skein_256_Process_Block_CodeSize) - - ((u08b_t *) Skein_256_Process_Block); - } -static uint_t Skein_256_Unroll_Cnt(void) - { - return SKEIN_UNROLL_256; - } -#endif -#endif - -/***************************** Skein_512 ******************************/ -#if !(SKEIN_USE_ASM & 512) -static void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) - { /* do it in C */ - enum - { - WCNT = SKEIN_512_STATE_WORDS - }; -#undef RCNT -#define RCNT (SKEIN_512_ROUNDS_TOTAL/8) - -#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ -#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10) -#else -#define SKEIN_UNROLL_512 (0) -#endif - -#if SKEIN_UNROLL_512 -#if (RCNT % SKEIN_UNROLL_512) -#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */ -#endif - size_t r; - u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ -#else - u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ -#endif - u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */ - u64b_t w [WCNT]; /* local copy of input block */ -#ifdef SKEIN_DEBUG - const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */ - Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; - Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7; -#endif - - Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ - ts[0] = ctx->h.T[0]; - ts[1] = ctx->h.T[1]; - do { - /* this implementation only supports 2**64 input bytes (no carry out here) */ - ts[0] += byteCntAdd; /* update processed length */ - - /* precompute the key schedule for this block */ - ks[0] = ctx->X[0]; - ks[1] = ctx->X[1]; - ks[2] = ctx->X[2]; - ks[3] = ctx->X[3]; - ks[4] = ctx->X[4]; - ks[5] = ctx->X[5]; - ks[6] = ctx->X[6]; - ks[7] = ctx->X[7]; - ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ - ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY; - - ts[2] = ts[0] ^ ts[1]; - - Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ - DebugSaveTweak(ctx); - Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); - - X0 = w[0] + ks[0]; /* do the first full key injection */ - X1 = w[1] + ks[1]; - X2 = w[2] + ks[2]; - X3 = w[3] + ks[3]; - X4 = w[4] + ks[4]; - X5 = w[5] + ks[5] + ts[0]; - X6 = w[6] + ks[6] + ts[1]; - X7 = w[7] + ks[7]; - - blkPtr += SKEIN_512_BLOCK_BYTES; - - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); - /* run the rounds */ -#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ - X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ - X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ - X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ - X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ - -#if SKEIN_UNROLL_512 == 0 -#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \ - Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); - -#define I512(R) \ - X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \ - X1 += ks[((R)+2) % 9]; \ - X2 += ks[((R)+3) % 9]; \ - X3 += ks[((R)+4) % 9]; \ - X4 += ks[((R)+5) % 9]; \ - X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \ - X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \ - X7 += ks[((R)+8) % 9] + (R)+1; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); -#else /* looping version */ -#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ - Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); - -#define I512(R) \ - X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ - X1 += ks[r+(R)+1]; \ - X2 += ks[r+(R)+2]; \ - X3 += ks[r+(R)+3]; \ - X4 += ks[r+(R)+4]; \ - X5 += ks[r+(R)+5] + ts[r+(R)+0]; \ - X6 += ks[r+(R)+6] + ts[r+(R)+1]; \ - X7 += ks[r+(R)+7] + r+(R) ; \ - ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \ - ts[r + (R)+2] = ts[r+(R)-1]; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); - - for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */ -#endif /* end of looped code definitions */ - { -#define R512_8_rounds(R) /* do 8 full rounds */ \ - R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \ - R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \ - R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \ - R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \ - I512(2*(R)); \ - R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \ - R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \ - R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \ - R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \ - I512(2*(R)+1); /* and key injection */ - - R512_8_rounds( 0); - -#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN))) - - #if R512_Unroll_R( 1) - R512_8_rounds( 1); - #endif - #if R512_Unroll_R( 2) - R512_8_rounds( 2); - #endif - #if R512_Unroll_R( 3) - R512_8_rounds( 3); - #endif - #if R512_Unroll_R( 4) - R512_8_rounds( 4); - #endif - #if R512_Unroll_R( 5) - R512_8_rounds( 5); - #endif - #if R512_Unroll_R( 6) - R512_8_rounds( 6); - #endif - #if R512_Unroll_R( 7) - R512_8_rounds( 7); - #endif - #if R512_Unroll_R( 8) - R512_8_rounds( 8); - #endif - #if R512_Unroll_R( 9) - R512_8_rounds( 9); - #endif - #if R512_Unroll_R(10) - R512_8_rounds(10); - #endif - #if R512_Unroll_R(11) - R512_8_rounds(11); - #endif - #if R512_Unroll_R(12) - R512_8_rounds(12); - #endif - #if R512_Unroll_R(13) - R512_8_rounds(13); - #endif - #if R512_Unroll_R(14) - R512_8_rounds(14); - #endif - #if (SKEIN_UNROLL_512 > 14) -#error "need more unrolling in Skein_512_Process_Block" - #endif - } - - /* do the final "feedforward" xor, update context chaining vars */ - ctx->X[0] = X0 ^ w[0]; - ctx->X[1] = X1 ^ w[1]; - ctx->X[2] = X2 ^ w[2]; - ctx->X[3] = X3 ^ w[3]; - ctx->X[4] = X4 ^ w[4]; - ctx->X[5] = X5 ^ w[5]; - ctx->X[6] = X6 ^ w[6]; - ctx->X[7] = X7 ^ w[7]; - Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); - - ts[1] &= ~SKEIN_T1_FLAG_FIRST; - } - while (--blkCnt); - ctx->h.T[0] = ts[0]; - ctx->h.T[1] = ts[1]; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein_512_Process_Block_CodeSize(void) - { - return ((u08b_t *) Skein_512_Process_Block_CodeSize) - - ((u08b_t *) Skein_512_Process_Block); - } -static uint_t Skein_512_Unroll_Cnt(void) - { - return SKEIN_UNROLL_512; - } -#endif -#endif - -/***************************** Skein1024 ******************************/ -#if !(SKEIN_USE_ASM & 1024) -static void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) - { /* do it in C, always looping (unrolled is bigger AND slower!) */ - enum - { - WCNT = SKEIN1024_STATE_WORDS - }; -#undef RCNT -#define RCNT (SKEIN1024_ROUNDS_TOTAL/8) - -#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ -#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10) -#else -#define SKEIN_UNROLL_1024 (0) -#endif - -#if (SKEIN_UNROLL_1024 != 0) -#if (RCNT % SKEIN_UNROLL_1024) -#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */ -#endif - size_t r; - u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ -#else - u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ -#endif - - u64b_t X00,X01,X02,X03,X04,X05,X06,X07, /* local copy of vars, for speed */ - X08,X09,X10,X11,X12,X13,X14,X15; - u64b_t w [WCNT]; /* local copy of input block */ -#ifdef SKEIN_DEBUG - const u64b_t *Xptr[16]; /* use for debugging (help compiler put Xn in registers) */ - Xptr[ 0] = &X00; Xptr[ 1] = &X01; Xptr[ 2] = &X02; Xptr[ 3] = &X03; - Xptr[ 4] = &X04; Xptr[ 5] = &X05; Xptr[ 6] = &X06; Xptr[ 7] = &X07; - Xptr[ 8] = &X08; Xptr[ 9] = &X09; Xptr[10] = &X10; Xptr[11] = &X11; - Xptr[12] = &X12; Xptr[13] = &X13; Xptr[14] = &X14; Xptr[15] = &X15; -#endif - - Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ - ts[0] = ctx->h.T[0]; - ts[1] = ctx->h.T[1]; - do { - /* this implementation only supports 2**64 input bytes (no carry out here) */ - ts[0] += byteCntAdd; /* update processed length */ - - /* precompute the key schedule for this block */ - ks[ 0] = ctx->X[ 0]; - ks[ 1] = ctx->X[ 1]; - ks[ 2] = ctx->X[ 2]; - ks[ 3] = ctx->X[ 3]; - ks[ 4] = ctx->X[ 4]; - ks[ 5] = ctx->X[ 5]; - ks[ 6] = ctx->X[ 6]; - ks[ 7] = ctx->X[ 7]; - ks[ 8] = ctx->X[ 8]; - ks[ 9] = ctx->X[ 9]; - ks[10] = ctx->X[10]; - ks[11] = ctx->X[11]; - ks[12] = ctx->X[12]; - ks[13] = ctx->X[13]; - ks[14] = ctx->X[14]; - ks[15] = ctx->X[15]; - ks[16] = ks[ 0] ^ ks[ 1] ^ ks[ 2] ^ ks[ 3] ^ - ks[ 4] ^ ks[ 5] ^ ks[ 6] ^ ks[ 7] ^ - ks[ 8] ^ ks[ 9] ^ ks[10] ^ ks[11] ^ - ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY; - - ts[2] = ts[0] ^ ts[1]; - - Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ - DebugSaveTweak(ctx); - Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); - - X00 = w[ 0] + ks[ 0]; /* do the first full key injection */ - X01 = w[ 1] + ks[ 1]; - X02 = w[ 2] + ks[ 2]; - X03 = w[ 3] + ks[ 3]; - X04 = w[ 4] + ks[ 4]; - X05 = w[ 5] + ks[ 5]; - X06 = w[ 6] + ks[ 6]; - X07 = w[ 7] + ks[ 7]; - X08 = w[ 8] + ks[ 8]; - X09 = w[ 9] + ks[ 9]; - X10 = w[10] + ks[10]; - X11 = w[11] + ks[11]; - X12 = w[12] + ks[12]; - X13 = w[13] + ks[13] + ts[0]; - X14 = w[14] + ks[14] + ts[1]; - X15 = w[15] + ks[15]; - - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); - -#define Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rNum) \ - X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ - X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ - X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ - X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ - X##p8 += X##p9; X##p9 = RotL_64(X##p9,ROT##_4); X##p9 ^= X##p8; \ - X##pA += X##pB; X##pB = RotL_64(X##pB,ROT##_5); X##pB ^= X##pA; \ - X##pC += X##pD; X##pD = RotL_64(X##pD,ROT##_6); X##pD ^= X##pC; \ - X##pE += X##pF; X##pF = RotL_64(X##pF,ROT##_7); X##pF ^= X##pE; \ - -#if SKEIN_UNROLL_1024 == 0 -#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ - Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rn,Xptr); - -#define I1024(R) \ - X00 += ks[((R)+ 1) % 17]; /* inject the key schedule value */ \ - X01 += ks[((R)+ 2) % 17]; \ - X02 += ks[((R)+ 3) % 17]; \ - X03 += ks[((R)+ 4) % 17]; \ - X04 += ks[((R)+ 5) % 17]; \ - X05 += ks[((R)+ 6) % 17]; \ - X06 += ks[((R)+ 7) % 17]; \ - X07 += ks[((R)+ 8) % 17]; \ - X08 += ks[((R)+ 9) % 17]; \ - X09 += ks[((R)+10) % 17]; \ - X10 += ks[((R)+11) % 17]; \ - X11 += ks[((R)+12) % 17]; \ - X12 += ks[((R)+13) % 17]; \ - X13 += ks[((R)+14) % 17] + ts[((R)+1) % 3]; \ - X14 += ks[((R)+15) % 17] + ts[((R)+2) % 3]; \ - X15 += ks[((R)+16) % 17] + (R)+1; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); -#else /* looping version */ -#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ - Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rn,Xptr); - -#define I1024(R) \ - X00 += ks[r+(R)+ 0]; /* inject the key schedule value */ \ - X01 += ks[r+(R)+ 1]; \ - X02 += ks[r+(R)+ 2]; \ - X03 += ks[r+(R)+ 3]; \ - X04 += ks[r+(R)+ 4]; \ - X05 += ks[r+(R)+ 5]; \ - X06 += ks[r+(R)+ 6]; \ - X07 += ks[r+(R)+ 7]; \ - X08 += ks[r+(R)+ 8]; \ - X09 += ks[r+(R)+ 9]; \ - X10 += ks[r+(R)+10]; \ - X11 += ks[r+(R)+11]; \ - X12 += ks[r+(R)+12]; \ - X13 += ks[r+(R)+13] + ts[r+(R)+0]; \ - X14 += ks[r+(R)+14] + ts[r+(R)+1]; \ - X15 += ks[r+(R)+15] + r+(R) ; \ - ks[r + (R)+16] = ks[r+(R)-1]; /* rotate key schedule */ \ - ts[r + (R)+ 2] = ts[r+(R)-1]; \ - Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); - - for (r=1;r <= 2*RCNT;r+=2*SKEIN_UNROLL_1024) /* loop thru it */ -#endif - { -#define R1024_8_rounds(R) /* do 8 full rounds */ \ - R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_0,8*(R) + 1); \ - R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_1,8*(R) + 2); \ - R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_2,8*(R) + 3); \ - R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_3,8*(R) + 4); \ - I1024(2*(R)); \ - R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_4,8*(R) + 5); \ - R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_5,8*(R) + 6); \ - R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_6,8*(R) + 7); \ - R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_7,8*(R) + 8); \ - I1024(2*(R)+1); - - R1024_8_rounds( 0); - -#define R1024_Unroll_R(NN) ((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_1024 > (NN))) - - #if R1024_Unroll_R( 1) - R1024_8_rounds( 1); - #endif - #if R1024_Unroll_R( 2) - R1024_8_rounds( 2); - #endif - #if R1024_Unroll_R( 3) - R1024_8_rounds( 3); - #endif - #if R1024_Unroll_R( 4) - R1024_8_rounds( 4); - #endif - #if R1024_Unroll_R( 5) - R1024_8_rounds( 5); - #endif - #if R1024_Unroll_R( 6) - R1024_8_rounds( 6); - #endif - #if R1024_Unroll_R( 7) - R1024_8_rounds( 7); - #endif - #if R1024_Unroll_R( 8) - R1024_8_rounds( 8); - #endif - #if R1024_Unroll_R( 9) - R1024_8_rounds( 9); - #endif - #if R1024_Unroll_R(10) - R1024_8_rounds(10); - #endif - #if R1024_Unroll_R(11) - R1024_8_rounds(11); - #endif - #if R1024_Unroll_R(12) - R1024_8_rounds(12); - #endif - #if R1024_Unroll_R(13) - R1024_8_rounds(13); - #endif - #if R1024_Unroll_R(14) - R1024_8_rounds(14); - #endif - #if (SKEIN_UNROLL_1024 > 14) -#error "need more unrolling in Skein_1024_Process_Block" - #endif - } - /* do the final "feedforward" xor, update context chaining vars */ - - ctx->X[ 0] = X00 ^ w[ 0]; - ctx->X[ 1] = X01 ^ w[ 1]; - ctx->X[ 2] = X02 ^ w[ 2]; - ctx->X[ 3] = X03 ^ w[ 3]; - ctx->X[ 4] = X04 ^ w[ 4]; - ctx->X[ 5] = X05 ^ w[ 5]; - ctx->X[ 6] = X06 ^ w[ 6]; - ctx->X[ 7] = X07 ^ w[ 7]; - ctx->X[ 8] = X08 ^ w[ 8]; - ctx->X[ 9] = X09 ^ w[ 9]; - ctx->X[10] = X10 ^ w[10]; - ctx->X[11] = X11 ^ w[11]; - ctx->X[12] = X12 ^ w[12]; - ctx->X[13] = X13 ^ w[13]; - ctx->X[14] = X14 ^ w[14]; - ctx->X[15] = X15 ^ w[15]; - - Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); - - ts[1] &= ~SKEIN_T1_FLAG_FIRST; - blkPtr += SKEIN1024_BLOCK_BYTES; - } - while (--blkCnt); - ctx->h.T[0] = ts[0]; - ctx->h.T[1] = ts[1]; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein1024_Process_Block_CodeSize(void) - { - return ((u08b_t *) Skein1024_Process_Block_CodeSize) - - ((u08b_t *) Skein1024_Process_Block); - } -static uint_t Skein1024_Unroll_Cnt(void) - { - return SKEIN_UNROLL_1024; - } -#endif -#endif - - -#if 0 -/*****************************************************************/ -/* 256-bit Skein */ -/*****************************************************************/ - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a straight hashing operation */ -static int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen) - { - union - { - u08b_t b[SKEIN_256_STATE_BYTES]; - u64b_t w[SKEIN_256_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - - switch (hashBitLen) - { /* use pre-computed values, where available */ -#ifndef SKEIN_NO_PRECOMP - case 256: memcpy(ctx->X,SKEIN_256_IV_256,sizeof(ctx->X)); break; - case 224: memcpy(ctx->X,SKEIN_256_IV_224,sizeof(ctx->X)); break; - case 160: memcpy(ctx->X,SKEIN_256_IV_160,sizeof(ctx->X)); break; - case 128: memcpy(ctx->X,SKEIN_256_IV_128,sizeof(ctx->X)); break; -#endif - default: - /* here if there is no precomputed IV value available */ - /* build/process the config block, type == CONFIG (could be precomputed) */ - Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ - - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); - memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ - - /* compute the initial chaining values from config block */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ - Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - break; - } - /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ - /* Set up to process the data message portion of the hash (default) */ - Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a MAC and/or tree hash operation */ -/* [identical to Skein_256_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ -static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) - { - union - { - u08b_t b[SKEIN_256_STATE_BYTES]; - u64b_t w[SKEIN_256_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); - - /* compute the initial chaining values ctx->X[], based on key */ - if (keyBytes == 0) /* is there a key? */ - { - memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ - } - else /* here to pre-process a key */ - { - Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); - /* do a mini-Init right here */ - ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ - Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ - Skein_256_Update(ctx,key,keyBytes); /* hash the key */ - Skein_256_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ - memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ -#if SKEIN_NEED_SWAP - { - uint_t i; - for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); - } -#endif - } - /* build/process the config block, type == CONFIG (could be precomputed for each key) */ - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - Skein_Start_New_Type(ctx,CFG_FINAL); - - memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ - - Skein_Show_Key(256,&ctx->h,key,keyBytes); - - /* compute the initial chaining values from config block */ - Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - - /* The chaining vars ctx->X are now initialized */ - /* Set up to process the data message portion of the hash (default) */ - ctx->h.bCnt = 0; /* buffer b[] starts out empty */ - Skein_Start_New_Type(ctx,MSG); - - return SKEIN_SUCCESS; - } -#endif - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* process the input bytes */ -static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) - { - size_t n; - - Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* process full blocks, if any */ - if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES) - { - if (ctx->h.bCnt) /* finish up any buffered message data */ - { - n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ - if (n) - { - Skein_assert(n < msgByteCnt); /* check on our logic here */ - memcpy(&ctx->b[ctx->h.bCnt],msg,n); - msgByteCnt -= n; - msg += n; - ctx->h.bCnt += n; - } - Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES); - Skein_256_Process_Block(ctx,ctx->b,1,SKEIN_256_BLOCK_BYTES); - ctx->h.bCnt = 0; - } - /* now process any remaining full blocks, directly from input message data */ - if (msgByteCnt > SKEIN_256_BLOCK_BYTES) - { - n = (msgByteCnt-1) / SKEIN_256_BLOCK_BYTES; /* number of full blocks to process */ - Skein_256_Process_Block(ctx,msg,n,SKEIN_256_BLOCK_BYTES); - msgByteCnt -= n * SKEIN_256_BLOCK_BYTES; - msg += n * SKEIN_256_BLOCK_BYTES; - } - Skein_assert(ctx->h.bCnt == 0); - } - - /* copy any remaining source message data bytes into b[] */ - if (msgByteCnt) - { - Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES); - memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); - ctx->h.bCnt += msgByteCnt; - } - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the result */ -static int Skein_256_Final(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN_256_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); - - Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i < byteCnt;i += SKEIN_256_BLOCK_BYTES) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i; /* number of output bytes left to go */ - if (n >= SKEIN_256_BLOCK_BYTES) - n = SKEIN_256_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein_256_API_CodeSize(void) - { - return ((u08b_t *) Skein_256_API_CodeSize) - - ((u08b_t *) Skein_256_Init); - } -#endif - -/*****************************************************************/ -/* 512-bit Skein */ -/*****************************************************************/ - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a straight hashing operation */ -static int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen) - { - union - { - u08b_t b[SKEIN_512_STATE_BYTES]; - u64b_t w[SKEIN_512_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - - switch (hashBitLen) - { /* use pre-computed values, where available */ -#ifndef SKEIN_NO_PRECOMP - case 512: memcpy(ctx->X,SKEIN_512_IV_512,sizeof(ctx->X)); break; - case 384: memcpy(ctx->X,SKEIN_512_IV_384,sizeof(ctx->X)); break; - case 256: memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X)); break; - case 224: memcpy(ctx->X,SKEIN_512_IV_224,sizeof(ctx->X)); break; -#endif - default: - /* here if there is no precomputed IV value available */ - /* build/process the config block, type == CONFIG (could be precomputed) */ - Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ - - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); - memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ - - /* compute the initial chaining values from config block */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ - Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - break; - } - - /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ - /* Set up to process the data message portion of the hash (default) */ - Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ - - return SKEIN_SUCCESS; - } - -#if 0 -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a MAC and/or tree hash operation */ -/* [identical to Skein_512_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ -static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) - { - union - { - u08b_t b[SKEIN_512_STATE_BYTES]; - u64b_t w[SKEIN_512_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); - - /* compute the initial chaining values ctx->X[], based on key */ - if (keyBytes == 0) /* is there a key? */ - { - memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ - } - else /* here to pre-process a key */ - { - Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); - /* do a mini-Init right here */ - ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ - Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ - Skein_512_Update(ctx,key,keyBytes); /* hash the key */ - Skein_512_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ - memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ -#if SKEIN_NEED_SWAP - { - uint_t i; - for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); - } -#endif - } - /* build/process the config block, type == CONFIG (could be precomputed for each key) */ - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - Skein_Start_New_Type(ctx,CFG_FINAL); - - memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ - - Skein_Show_Key(512,&ctx->h,key,keyBytes); - - /* compute the initial chaining values from config block */ - Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - - /* The chaining vars ctx->X are now initialized */ - /* Set up to process the data message portion of the hash (default) */ - ctx->h.bCnt = 0; /* buffer b[] starts out empty */ - Skein_Start_New_Type(ctx,MSG); - - return SKEIN_SUCCESS; - } -#endif - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* process the input bytes */ -static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) - { - size_t n; - - Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* process full blocks, if any */ - if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) - { - if (ctx->h.bCnt) /* finish up any buffered message data */ - { - n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ - if (n) - { - Skein_assert(n < msgByteCnt); /* check on our logic here */ - memcpy(&ctx->b[ctx->h.bCnt],msg,n); - msgByteCnt -= n; - msg += n; - ctx->h.bCnt += n; - } - Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES); - Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES); - ctx->h.bCnt = 0; - } - /* now process any remaining full blocks, directly from input message data */ - if (msgByteCnt > SKEIN_512_BLOCK_BYTES) - { - n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */ - Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES); - msgByteCnt -= n * SKEIN_512_BLOCK_BYTES; - msg += n * SKEIN_512_BLOCK_BYTES; - } - Skein_assert(ctx->h.bCnt == 0); - } - - /* copy any remaining source message data bytes into b[] */ - if (msgByteCnt) - { - Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES); - memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); - ctx->h.bCnt += msgByteCnt; - } - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the result */ -static int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN_512_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); - - Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ - if (n >= SKEIN_512_BLOCK_BYTES) - n = SKEIN_512_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein_512_API_CodeSize(void) - { - return ((u08b_t *) Skein_512_API_CodeSize) - - ((u08b_t *) Skein_512_Init); - } -#endif - -/*****************************************************************/ -/* 1024-bit Skein */ -/*****************************************************************/ -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a straight hashing operation */ -static int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen) - { - union - { - u08b_t b[SKEIN1024_STATE_BYTES]; - u64b_t w[SKEIN1024_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - - switch (hashBitLen) - { /* use pre-computed values, where available */ -#ifndef SKEIN_NO_PRECOMP - case 512: memcpy(ctx->X,SKEIN1024_IV_512 ,sizeof(ctx->X)); break; - case 384: memcpy(ctx->X,SKEIN1024_IV_384 ,sizeof(ctx->X)); break; - case 1024: memcpy(ctx->X,SKEIN1024_IV_1024,sizeof(ctx->X)); break; -#endif - default: - /* here if there is no precomputed IV value available */ - /* build/process the config block, type == CONFIG (could be precomputed) */ - Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ - - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); - memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ - - /* compute the initial chaining values from config block */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ - Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - break; - } - - /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ - /* Set up to process the data message portion of the hash (default) */ - Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ - - return SKEIN_SUCCESS; - } - -#if 0 -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* init the context for a MAC and/or tree hash operation */ -/* [identical to Skein1024_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ -static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) - { - union - { - u08b_t b[SKEIN1024_STATE_BYTES]; - u64b_t w[SKEIN1024_STATE_WORDS]; - } cfg; /* config block */ - - Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); - Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); - - /* compute the initial chaining values ctx->X[], based on key */ - if (keyBytes == 0) /* is there a key? */ - { - memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ - } - else /* here to pre-process a key */ - { - Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); - /* do a mini-Init right here */ - ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ - Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ - memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ - Skein1024_Update(ctx,key,keyBytes); /* hash the key */ - Skein1024_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ - memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ -#if SKEIN_NEED_SWAP - { - uint_t i; - for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); - } -#endif - } - /* build/process the config block, type == CONFIG (could be precomputed for each key) */ - ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ - Skein_Start_New_Type(ctx,CFG_FINAL); - - memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ - cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); - cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ - cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ - - Skein_Show_Key(1024,&ctx->h,key,keyBytes); - - /* compute the initial chaining values from config block */ - Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); - - /* The chaining vars ctx->X are now initialized */ - /* Set up to process the data message portion of the hash (default) */ - ctx->h.bCnt = 0; /* buffer b[] starts out empty */ - Skein_Start_New_Type(ctx,MSG); - - return SKEIN_SUCCESS; - } -#endif - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* process the input bytes */ -static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) - { - size_t n; - - Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* process full blocks, if any */ - if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES) - { - if (ctx->h.bCnt) /* finish up any buffered message data */ - { - n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ - if (n) - { - Skein_assert(n < msgByteCnt); /* check on our logic here */ - memcpy(&ctx->b[ctx->h.bCnt],msg,n); - msgByteCnt -= n; - msg += n; - ctx->h.bCnt += n; - } - Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES); - Skein1024_Process_Block(ctx,ctx->b,1,SKEIN1024_BLOCK_BYTES); - ctx->h.bCnt = 0; - } - /* now process any remaining full blocks, directly from input message data */ - if (msgByteCnt > SKEIN1024_BLOCK_BYTES) - { - n = (msgByteCnt-1) / SKEIN1024_BLOCK_BYTES; /* number of full blocks to process */ - Skein1024_Process_Block(ctx,msg,n,SKEIN1024_BLOCK_BYTES); - msgByteCnt -= n * SKEIN1024_BLOCK_BYTES; - msg += n * SKEIN1024_BLOCK_BYTES; - } - Skein_assert(ctx->h.bCnt == 0); - } - - /* copy any remaining source message data bytes into b[] */ - if (msgByteCnt) - { - Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES); - memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); - ctx->h.bCnt += msgByteCnt; - } - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the result */ -static int Skein1024_Final(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN1024_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); - - Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ - if (n >= SKEIN1024_BLOCK_BYTES) - n = SKEIN1024_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(1024,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } - -#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) -static size_t Skein1024_API_CodeSize(void) - { - return ((u08b_t *) Skein1024_API_CodeSize) - - ((u08b_t *) Skein1024_Init); - } -#endif - -/**************** Functions to support MAC/tree hashing ***************/ -/* (this code is identical for Optimized and Reference versions) */ - -#if 0 -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the block, no OUTPUT stage */ -static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) - { - Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); - Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_256_BLOCK_BYTES); /* "output" the state bytes */ - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the block, no OUTPUT stage */ -static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) - { - Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); - Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_512_BLOCK_BYTES); /* "output" the state bytes */ - - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize the hash computation and output the block, no OUTPUT stage */ -static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) - { - Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ - if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ - memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); - Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ - - Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN1024_BLOCK_BYTES); /* "output" the state bytes */ - - return SKEIN_SUCCESS; - } - - -#if SKEIN_TREE_HASH -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* just do the OUTPUT stage */ -static int Skein_256_Output(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN_256_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ - if (n >= SKEIN_256_BLOCK_BYTES) - n = SKEIN_256_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* just do the OUTPUT stage */ -static int Skein_512_Output(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN_512_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ - if (n >= SKEIN_512_BLOCK_BYTES) - n = SKEIN_512_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* just do the OUTPUT stage */ -static int Skein1024_Output(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) - { - size_t i,n,byteCnt; - u64b_t X[SKEIN1024_STATE_WORDS]; - Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ - - /* now output the result */ - byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ - - /* run Threefish in "counter mode" to generate output */ - memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ - memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ - for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) - { - ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ - Skein_Start_New_Type(ctx,OUT_FINAL); - Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ - n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ - if (n >= SKEIN1024_BLOCK_BYTES) - n = SKEIN1024_BLOCK_BYTES; - Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ - Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); - memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ - } - return SKEIN_SUCCESS; - } -#endif -#endif - -typedef struct -{ - uint_t statebits; /* 256, 512, or 1024 */ - union - { - Skein_Ctxt_Hdr_t h; /* common header "overlay" */ - Skein_256_Ctxt_t ctx_256; - Skein_512_Ctxt_t ctx_512; - Skein1024_Ctxt_t ctx1024; - } u; -} -hashState; - -/* "incremental" hashing API */ -static SkeinHashReturn Init (hashState *state, int hashbitlen); -static SkeinHashReturn Update(hashState *state, const SkeinBitSequence *data, SkeinDataLength databitlen); -static SkeinHashReturn Final (hashState *state, SkeinBitSequence *hashval); - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* select the context size and init the context */ -static SkeinHashReturn Init(hashState *state, int hashbitlen) -{ -#if SKEIN_256_NIST_MAX_HASH_BITS - if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) - { - Skein_Assert(hashbitlen > 0,BAD_HASHLEN); - state->statebits = 64*SKEIN_256_STATE_WORDS; - return Skein_256_Init(&state->u.ctx_256,(size_t) hashbitlen); - } -#endif - if (hashbitlen <= SKEIN_512_NIST_MAX_HASHBITS) - { - state->statebits = 64*SKEIN_512_STATE_WORDS; - return Skein_512_Init(&state->u.ctx_512,(size_t) hashbitlen); - } - else - { - state->statebits = 64*SKEIN1024_STATE_WORDS; - return Skein1024_Init(&state->u.ctx1024,(size_t) hashbitlen); - } -} - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* process data to be hashed */ -static SkeinHashReturn Update(hashState *state, const SkeinBitSequence *data, SkeinDataLength databitlen) -{ - /* only the final Update() call is allowed do partial bytes, else assert an error */ - Skein_Assert((state->u.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || databitlen == 0, SKEIN_FAIL); - - Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,SKEIN_FAIL); - if ((databitlen & 7) == 0) /* partial bytes? */ - { - switch ((state->statebits >> 8) & 3) - { - case 2: return Skein_512_Update(&state->u.ctx_512,data,databitlen >> 3); - case 1: return Skein_256_Update(&state->u.ctx_256,data,databitlen >> 3); - case 0: return Skein1024_Update(&state->u.ctx1024,data,databitlen >> 3); - default: return SKEIN_FAIL; - } - } - else - { /* handle partial final byte */ - size_t bCnt = (databitlen >> 3) + 1; /* number of bytes to handle (nonzero here!) */ - u08b_t b,mask; - - mask = (u08b_t) (1u << (7 - (databitlen & 7))); /* partial byte bit mask */ - b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ - - switch ((state->statebits >> 8) & 3) - { - case 2: Skein_512_Update(&state->u.ctx_512,data,bCnt-1); /* process all but the final byte */ - Skein_512_Update(&state->u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ - break; - case 1: Skein_256_Update(&state->u.ctx_256,data,bCnt-1); /* process all but the final byte */ - Skein_256_Update(&state->u.ctx_256,&b , 1 ); /* process the (masked) partial byte */ - break; - case 0: Skein1024_Update(&state->u.ctx1024,data,bCnt-1); /* process all but the final byte */ - Skein1024_Update(&state->u.ctx1024,&b , 1 ); /* process the (masked) partial byte */ - break; - default: return SKEIN_FAIL; - } - Skein_Set_Bit_Pad_Flag(state->u.h); /* set tweak flag for the final call */ - - return SKEIN_SUCCESS; - } -} - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* finalize hash computation and output the result (hashbitlen bits) */ -static SkeinHashReturn Final(hashState *state, SkeinBitSequence *hashval) -{ - Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,FAIL); - switch ((state->statebits >> 8) & 3) - { - case 2: return Skein_512_Final(&state->u.ctx_512,hashval); - case 1: return Skein_256_Final(&state->u.ctx_256,hashval); - case 0: return Skein1024_Final(&state->u.ctx1024,hashval); - default: return SKEIN_FAIL; - } -} - -/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ -/* all-in-one hash function */ -SkeinHashReturn skein_hash(int hashbitlen, const SkeinBitSequence *data, /* all-in-one call */ - SkeinDataLength databitlen,SkeinBitSequence *hashval) -{ - hashState state; - SkeinHashReturn r = Init(&state,hashbitlen); - if (r == SKEIN_SUCCESS) - { /* these calls do not fail when called properly */ - r = Update(&state,data,databitlen); - Final(&state,hashval); - } - return r; -} diff --git a/crypto/hash.c b/crypto/hash.c deleted file mode 100644 index f3a16f0c..00000000 --- a/crypto/hash.c +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include - -#include "hash-ops.h" -#include "c_keccak.h" - -void hash_permutation(union hash_state *state) { - keccakf((uint64_t*)state, 24); -} - -void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { - keccak1600(buf, count, (uint8_t*)state); -} - -void cn_fast_hash(const void *data, size_t length, char *hash) { - union hash_state state; - hash_process(&state, data, length); - memcpy(hash, &state, HASH_SIZE); -} diff --git a/crypto/soft_aes.c b/crypto/soft_aes.c deleted file mode 100644 index 6904c526..00000000 --- a/crypto/soft_aes.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GNU GPL version 3 section 7 - * - * If you modify this Program, or any covered work, by linking or combining - * it with OpenSSL (or a modified version of that library), containing parts - * covered by the terms of OpenSSL License and SSLeay License, the licensors - * of this Program grant you additional permission to convey the resulting work. - * - */ - -/* - * The orginal author of this AES implementation is Karl Malbrain. - */ - -#ifdef __GNUC__ -#include -#else -#include -#endif // __GNUC__ - -#include - -#define TABLE_ALIGN 32 -#define WPOLY 0x011b -#define N_COLS 4 -#define AES_BLOCK_SIZE 16 -#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) - -#if defined(_MSC_VER) -#define ALIGN __declspec(align(TABLE_ALIGN)) -#elif defined(__GNUC__) -#define ALIGN __attribute__ ((aligned(16))) -#else -#define ALIGN -#endif - -#define rf1(r,c) (r) -#define word_in(x,c) (*((uint32_t*)(x)+(c))) -#define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v)) - -#define s(x,c) x[c] -#define si(y,x,c) (s(y,c) = word_in(x, c)) -#define so(y,x,c) word_out(y, c, s(x,c)) -#define state_in(y,x) si(y,x,0); si(y,x,1); si(y,x,2); si(y,x,3) -#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) -#define round(y,x,k) \ -y[0] = (k)[0] ^ (t_fn[0][x[0] & 0xff] ^ t_fn[1][(x[1] >> 8) & 0xff] ^ t_fn[2][(x[2] >> 16) & 0xff] ^ t_fn[3][x[3] >> 24]); \ -y[1] = (k)[1] ^ (t_fn[0][x[1] & 0xff] ^ t_fn[1][(x[2] >> 8) & 0xff] ^ t_fn[2][(x[3] >> 16) & 0xff] ^ t_fn[3][x[0] >> 24]); \ -y[2] = (k)[2] ^ (t_fn[0][x[2] & 0xff] ^ t_fn[1][(x[3] >> 8) & 0xff] ^ t_fn[2][(x[0] >> 16) & 0xff] ^ t_fn[3][x[1] >> 24]); \ -y[3] = (k)[3] ^ (t_fn[0][x[3] & 0xff] ^ t_fn[1][(x[0] >> 8) & 0xff] ^ t_fn[2][(x[1] >> 16) & 0xff] ^ t_fn[3][x[2] >> 24]); -#define to_byte(x) ((x) & 0xff) -#define bval(x,n) to_byte((x) >> (8 * (n))) - -#define fwd_var(x,r,c)\ - ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ - : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\ - : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ - : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) - -#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) - -#define sb_data(w) {\ - w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ - w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ - w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ - w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ - w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ - w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ - w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ - w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ - w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ - w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ - w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ - w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ - w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ - w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ - w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ - w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ - w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ - w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ - w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ - w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ - w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ - w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ - w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ - w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ - w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ - w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ - w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ - w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ - w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ - w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ - w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ - w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } - -#define rc_data(w) {\ - w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ - w(0x1b), w(0x36) } - -#define bytes2word(b0, b1, b2, b3) (((uint32_t)(b3) << 24) | \ - ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) - -#define h0(x) (x) -#define w0(p) bytes2word(p, 0, 0, 0) -#define w1(p) bytes2word(0, p, 0, 0) -#define w2(p) bytes2word(0, 0, p, 0) -#define w3(p) bytes2word(0, 0, 0, p) - -#define u0(p) bytes2word(f2(p), p, p, f3(p)) -#define u1(p) bytes2word(f3(p), f2(p), p, p) -#define u2(p) bytes2word(p, f3(p), f2(p), p) -#define u3(p) bytes2word(p, p, f3(p), f2(p)) - -#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) -#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) -#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) -#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) - -#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) -#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) -#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) ^ (((x>>5) & 4) * WPOLY)) -#define f3(x) (f2(x) ^ x) -#define f9(x) (f8(x) ^ x) -#define fb(x) (f8(x) ^ f2(x) ^ x) -#define fd(x) (f8(x) ^ f4(x) ^ x) -#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) - -#define t_dec(m,n) t_##m##n -#define t_set(m,n) t_##m##n -#define t_use(m,n) t_##m##n - -#define d_4(t,n,b,e,f,g,h) ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) } - -#define four_tables(x,tab,vf,rf,c) \ - (tab[0][bval(vf(x,0,c),rf(0,c))] \ - ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ - ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ - ^ tab[3][bval(vf(x,3,c),rf(3,c))]) - -d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3); - -__m128i soft_aesenc(__m128i in, __m128i key) -{ - uint32_t x0, x1, x2, x3; - x0 = _mm_cvtsi128_si32(in); - x1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0x55)); - x2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0xAA)); - x3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0xFF)); - - __m128i out = _mm_set_epi32( - (t_fn[0][x3 & 0xff] ^ t_fn[1][(x0 >> 8) & 0xff] ^ t_fn[2][(x1 >> 16) & 0xff] ^ t_fn[3][x2 >> 24]), - (t_fn[0][x2 & 0xff] ^ t_fn[1][(x3 >> 8) & 0xff] ^ t_fn[2][(x0 >> 16) & 0xff] ^ t_fn[3][x1 >> 24]), - (t_fn[0][x1 & 0xff] ^ t_fn[1][(x2 >> 8) & 0xff] ^ t_fn[2][(x3 >> 16) & 0xff] ^ t_fn[3][x0 >> 24]), - (t_fn[0][x0 & 0xff] ^ t_fn[1][(x1 >> 8) & 0xff] ^ t_fn[2][(x2 >> 16) & 0xff] ^ t_fn[3][x3 >> 24])); - - return _mm_xor_si128(out, key); -} - -uint8_t Sbox[256] = { // forward s-box -0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, -0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, -0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, -0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, -0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, -0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, -0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, -0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, -0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, -0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, -0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, -0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, -0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, -0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, -0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, -0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16}; - -static inline void sub_word(uint8_t* key) -{ - key[0] = Sbox[key[0]]; - key[1] = Sbox[key[1]]; - key[2] = Sbox[key[2]]; - key[3] = Sbox[key[3]]; -} - -#ifdef __clang__ -uint32_t _rotr(uint32_t value, uint32_t amount) -{ - return (value >> amount) | (value << ((32 - amount) & 31)); -} -#endif - -__m128i soft_aeskeygenassist(__m128i key, uint8_t rcon) -{ - uint32_t X1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0x55)); - uint32_t X3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0xFF)); - sub_word((uint8_t*)&X1); - sub_word((uint8_t*)&X3); - return _mm_set_epi32(_rotr(X3, 8) ^ rcon, X3,_rotr(X1, 8) ^ rcon, X1); -} diff --git a/elist.h b/elist.h deleted file mode 100644 index a1356184..00000000 --- a/elist.h +++ /dev/null @@ -1,274 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef _LINUX_LIST_H -#define _LINUX_LIST_H - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head *prev, struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->next = (void *) 0; - entry->prev = (void *) 0; -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, - struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add_tail(list, head); -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(struct list_head *head) -{ - return head->next == head; -} - -static inline void __list_splice(struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(struct list_head *list, struct list_head *head) -{ - if (!list_empty(list)) - __list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); \ - pos = pos->next) -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); \ - pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop counter. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop counter. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop counter. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_continue - iterate over list of given type - * continuing after existing point - * @pos: the type * to use as a loop counter. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member), \ - prefetch(pos->member.next); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member), \ - prefetch(pos->member.next)) - -#endif diff --git a/mac/memory_mac.c b/mac/memory_mac.c deleted file mode 100644 index 3f5f714c..00000000 --- a/mac/memory_mac.c +++ /dev/null @@ -1,76 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "persistent_memory.h" -#include "options.h" -#include "utils/applog.h" - -char *persistent_memory; -int persistent_memory_flags = 0; - - -const char * persistent_memory_allocate() { - const int ratio = (opt_double_hash && opt_algo != ALGO_CRYPTONIGHT_LITE) ? 2 : 1; - const int size = MEMORY * (opt_n_threads * ratio + 1); - persistent_memory_flags |= MEMORY_HUGEPAGES_AVAILABLE; - - persistent_memory = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); - - if (persistent_memory == MAP_FAILED) { - persistent_memory = _mm_malloc(size, 16); - return persistent_memory; - } - - persistent_memory_flags |= MEMORY_HUGEPAGES_ENABLED; - - if (madvise(persistent_memory, size, MADV_RANDOM | MADV_WILLNEED) != 0) { - applog(LOG_ERR, "madvise failed"); - } - - if (mlock(persistent_memory, size) == 0) { - persistent_memory_flags |= MEMORY_LOCK; - } - - return persistent_memory; -} - - -void persistent_memory_free() { - const int size = MEMORY * (opt_n_threads + 1); - - if (persistent_memory_flags & MEMORY_HUGEPAGES_ENABLED) { - if (persistent_memory_flags & MEMORY_LOCK) { - munlock(persistent_memory, size); - } - - munmap(persistent_memory, size); - } - else { - _mm_free(persistent_memory); - } -} diff --git a/mac/xmrig_mac.c b/mac/xmrig_mac.c deleted file mode 100644 index f7278c6d..00000000 --- a/mac/xmrig_mac.c +++ /dev/null @@ -1,91 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "options.h" -#include "cpu.h" -#include "utils/applog.h" - - -static void signal_handler(int sig) -{ - switch (sig) { - case SIGHUP: - applog(LOG_WARNING, "SIGHUP received"); - break; - - case SIGINT: - applog(LOG_WARNING, "SIGINT received, exiting"); - proper_exit(0); - break; - - case SIGTERM: - applog(LOG_WARNING, "SIGTERM received, exiting"); - proper_exit(0); - break; - } -} - - -void proper_exit(int reason) { - exit(reason); -} - - -void os_specific_init() -{ - if (opt_affinity != -1) { - affine_to_cpu_mask(-1, opt_affinity); - } - - if (opt_background) { - int i = fork(); - if (i < 0) { - exit(1); - } - - if (i > 0) { - exit(0); - } - - i = setsid(); - - if (i < 0) { - applog(LOG_ERR, "setsid() failed (errno = %d)", errno); - } - - i = chdir("/"); - if (i < 0) { - applog(LOG_ERR, "chdir() failed (errno = %d)", errno); - } - - signal(SIGHUP, signal_handler); - signal(SIGTERM, signal_handler); - } - - signal(SIGINT, signal_handler); -} diff --git a/options.c b/options.c deleted file mode 100644 index 5dabefbd..00000000 --- a/options.c +++ /dev/null @@ -1,516 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "version.h" -#include "utils/applog.h" -#include "options.h" -#include "cpu.h" -#include "donate.h" -#include "algo/cryptonight/cryptonight.h" - - -int64_t opt_affinity = -1L; -int opt_n_threads = 0; -int opt_algo_variant = 0; -int opt_retries = 5; -int opt_retry_pause = 5; -int opt_donate_level = DONATE_LEVEL; -int opt_max_cpu_usage = 75; -bool opt_colors = true; -bool opt_keepalive = false; -bool opt_background = false; -bool opt_double_hash = false; -bool opt_safe = false; -bool opt_nicehash = false; -char *opt_url = NULL; -char *opt_backup_url = NULL; -char *opt_userpass = NULL; -char *opt_user = NULL; -char *opt_pass = NULL; - -enum mining_algo opt_algo = ALGO_CRYPTONIGHT; - - -static char const usage[] = "\ -Usage: " APP_ID " [OPTIONS]\n\ -Options:\n\ - -a, --algo=ALGO cryptonight (default) or cryptonight-lite\n\ - -o, --url=URL URL of mining server\n\ - -b, --backup-url=URL URL of backup mining server\n\ - -O, --userpass=U:P username:password pair for mining server\n\ - -u, --user=USERNAME username for mining server\n\ - -p, --pass=PASSWORD password for mining server\n\ - -t, --threads=N number of miner threads\n\ - -v, --av=N algorithm variation, 0 auto select\n\ - -k, --keepalive send keepalived for prevent timeout (need pool support)\n\ - -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ - -R, --retry-pause=N time to pause between retries (default: 5)\n\ - --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ - --no-color disable colored output\n\ - --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ - -B, --background run the miner in the background\n\ - -c, --config=FILE load a JSON-format configuration file\n\ - --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75)\n\ - --safe safe adjust threads and av settings for current CPU\n\ - --nicehash enable nicehash support\n\ - -h, --help display this help and exit\n\ - -V, --version output version information and exit\n\ -"; - - -static char const short_options[] = "a:c:khBp:Px:r:R:s:t:T:o:u:O:v:Vb:"; - - -static struct option const options[] = { - { "algo", 1, NULL, 'a' }, - { "av", 1, NULL, 'v' }, - { "background", 0, NULL, 'B' }, - { "backup-url", 1, NULL, 'b' }, - { "config", 1, NULL, 'c' }, - { "cpu-affinity", 1, NULL, 1020 }, - { "donate-level", 1, NULL, 1003 }, - { "help", 0, NULL, 'h' }, - { "keepalive", 0, NULL ,'k' }, - { "max-cpu-usage", 1, NULL, 1004 }, - { "nicehash", 0, NULL, 1006 }, - { "no-color", 0, NULL, 1002 }, - { "pass", 1, NULL, 'p' }, - { "retries", 1, NULL, 'r' }, - { "retry-pause", 1, NULL, 'R' }, - { "safe", 0, NULL, 1005 }, - { "threads", 1, NULL, 't' }, - { "url", 1, NULL, 'o' }, - { "user", 1, NULL, 'u' }, - { "userpass", 1, NULL, 'O' }, - { "version", 0, NULL, 'V' }, - { 0, 0, 0, 0 } -}; - - -static const char *algo_names[] = { - [ALGO_CRYPTONIGHT] = "cryptonight", -# ifndef XMRIG_NO_AEON - [ALGO_CRYPTONIGHT_LITE] = "cryptonight-lite" -# endif -}; - - -#ifndef XMRIG_NO_AEON -static int get_cryptonight_lite_variant(int variant) { - if (variant <= AEON_AV0_AUTO || variant >= AEON_AV_MAX) { - return (cpu_info.flags & CPU_FLAG_AES) ? AEON_AV2_AESNI_DOUBLE : AEON_AV4_SOFT_AES_DOUBLE; - } - - if (opt_safe && !(cpu_info.flags & CPU_FLAG_AES) && variant <= AEON_AV2_AESNI_DOUBLE) { - return variant + 2; - } - - return variant; -} -#endif - - -static int get_algo_variant(int algo, int variant) { -# ifndef XMRIG_NO_AEON - if (algo == ALGO_CRYPTONIGHT_LITE) { - return get_cryptonight_lite_variant(variant); - } -# endif - - if (variant <= XMR_AV0_AUTO || variant >= XMR_AV_MAX) { - return (cpu_info.flags & CPU_FLAG_AES) ? XMR_AV1_AESNI : XMR_AV3_SOFT_AES; - } - - if (opt_safe && !(cpu_info.flags & CPU_FLAG_AES) && variant <= XMR_AV2_AESNI_DOUBLE) { - return variant + 2; - } - - return variant; -} - - -static void parse_config(json_t *config, char *ref); -static char *parse_url(const char *arg); - - -static void parse_arg(int key, char *arg) { - char *p; - int v; - uint64_t ul; - - switch (key) - { - case 'a': - for (int i = 0; i < ARRAY_SIZE(algo_names); i++) { - if (algo_names[i] && !strcmp(arg, algo_names[i])) { - opt_algo = i; - break; - } - -# ifndef XMRIG_NO_AEON - if (i == ARRAY_SIZE(algo_names) - 1 && !strcmp(arg, "cryptonight-light")) { - opt_algo = i = ALGO_CRYPTONIGHT_LITE; - } -# endif - } - break; - - case 'O': /* --userpass */ - p = strchr(arg, ':'); - if (!p) { - show_usage_and_exit(1); - } - - free(opt_userpass); - opt_userpass = strdup(arg); - free(opt_user); - opt_user = calloc(p - arg + 1, 1); - strncpy(opt_user, arg, p - arg); - free(opt_pass); - opt_pass = strdup(p + 1); - break; - - case 'o': /* --url */ - p = parse_url(arg); - if (p) { - free(opt_url); - opt_url = p; - } - break; - - case 'b': /* --backup-url */ - p = parse_url(arg); - if (p) { - free(opt_backup_url); - opt_backup_url = p; - } - break; - - case 'u': /* --user */ - free(opt_user); - opt_user = strdup(arg); - break; - - case 'p': /* --pass */ - free(opt_pass); - opt_pass = strdup(arg); - break; - - case 'r': /* --retries */ - v = atoi(arg); - if (v < 1 || v > 1000) { - show_usage_and_exit(1); - } - - opt_retries = v; - break; - - case 'R': /* --retry-pause */ - v = atoi(arg); - if (v < 1 || v > 3600) { - show_usage_and_exit(1); - } - - opt_retry_pause = v; - break; - - case 't': /* --threads */ - v = atoi(arg); - if (v < 1 || v > 1024) { - show_usage_and_exit(1); - } - - opt_n_threads = v; - break; - - case 1004: /* --max-cpu-usage */ - v = atoi(arg); - if (v < 1 || v > 100) { - show_usage_and_exit(1); - } - - opt_max_cpu_usage = v; - break; - - case 1005: /* --safe */ - opt_safe = true; - break; - - case 'k': /* --keepalive */ - opt_keepalive = true; - break; - - case 'V': /* --version */ - show_version_and_exit(); - break; - - case 'h': /* --help */ - show_usage_and_exit(0); - break; - - case 'c': { /* --config */ - json_error_t err; - json_t *config = json_load_file(arg, 0, &err); - - if (!json_is_object(config)) { - if (err.line < 0) { - applog(LOG_ERR, "%s\n", err.text); - } - else { - applog(LOG_ERR, "%s:%d: %s\n", arg, err.line, err.text); - } - } else { - parse_config(config, arg); - json_decref(config); - } - break; - } - - case 'B': /* --background */ - opt_background = true; - opt_colors = false; - break; - - case 'v': /* --av */ - v = atoi(arg); - if (v < 0 || v > 1000) { - show_usage_and_exit(1); - } - - opt_algo_variant = v; - break; - - case 1020: /* --cpu-affinity */ - p = strstr(arg, "0x"); - ul = p ? strtoul(p, NULL, 16) : atol(arg); - if (ul > (1UL << cpu_info.total_logical_cpus) -1) { - ul = -1; - } - - opt_affinity = ul; - break; - - case 1002: /* --no-color */ - opt_colors = false; - break; - - case 1003: /* --donate-level */ - v = atoi(arg); - if (v < 1 || v > 99) { - show_usage_and_exit(1); - } - - opt_donate_level = v; - break; - - case 1006: /* --nicehash */ - opt_nicehash = true; - break; - - default: - show_usage_and_exit(1); - } -} - - -static void parse_config(json_t *config, char *ref) -{ - int i; - char buf[16]; - json_t *val; - - applog(LOG_ERR, ref); - - for (i = 0; i < ARRAY_SIZE(options); i++) { - if (!options[i].name) { - break; - } - - val = json_object_get(config, options[i].name); - if (!val) { - continue; - } - - if (options[i].has_arg && json_is_string(val)) { - char *s = strdup(json_string_value(val)); - if (!s) { - break; - } - - parse_arg(options[i].val, s); - free(s); - } - else if (options[i].has_arg && json_is_integer(val)) { - sprintf(buf, "%d", (int) json_integer_value(val)); - parse_arg(options[i].val, buf); - } - else if (options[i].has_arg && json_is_real(val)) { - sprintf(buf, "%f", json_real_value(val)); - parse_arg(options[i].val, buf); - } - else if (!options[i].has_arg) { - if (json_is_true(val)) { - parse_arg(options[i].val, ""); - } - } - else { - applog(LOG_ERR, "JSON option %s invalid", options[i].name); - } - } -} - - -static char *parse_url(const char *arg) -{ - char *p = strstr(arg, "://"); - if (p) { - if (strncasecmp(arg, "stratum+tcp://", 14)) { - show_usage_and_exit(1); - } - - return strdup(arg); - } - - - if (!strlen(arg) || *arg == '/') { - show_usage_and_exit(1); - } - - char *dest = malloc(strlen(arg) + 16); - sprintf(dest, "stratum+tcp://%s", arg); - - return dest; -} - - -/** - * Parse application command line via getopt. - */ -void parse_cmdline(int argc, char *argv[]) { - opt_user = strdup("x"); - opt_pass = strdup("x"); - - int key; - - while (1) { - key = getopt_long(argc, argv, short_options, options, NULL); - if (key < 0) { - break; - } - - parse_arg(key, optarg); - } - - if (optind < argc) { - fprintf(stderr, "%s: unsupported non-option argument '%s'\n", argv[0], argv[optind]); - show_usage_and_exit(1); - } - - if (!opt_url) { - applog_notime(LOG_ERR, "No pool URL supplied. Exiting.\n", argv[0]); - proper_exit(1); - } - - if (strstr(opt_url, ".nicehash.com:") != NULL) { - opt_nicehash = true; - } - - if (!opt_userpass) { - opt_userpass = malloc(strlen(opt_user) + strlen(opt_pass) + 2); - if (!opt_userpass) { - proper_exit(1); - } - - sprintf(opt_userpass, "%s:%s", opt_user, opt_pass); - } - - opt_algo_variant = get_algo_variant(opt_algo, opt_algo_variant); - - if (!cryptonight_init(opt_algo_variant)) { - applog(LOG_ERR, "Cryptonight hash self-test failed. This might be caused by bad compiler optimizations."); - proper_exit(1); - } - - if (!opt_n_threads) { - opt_n_threads = get_optimal_threads_count(opt_algo, opt_double_hash, opt_max_cpu_usage); - } - - if (opt_safe) { - const int count = get_optimal_threads_count(opt_algo, opt_double_hash, opt_max_cpu_usage); - if (opt_n_threads > count) { - opt_n_threads = count; - } - } -} - - -void show_usage_and_exit(int status) { - if (status) { - fprintf(stderr, "Try \"" APP_ID "\" --help' for more information.\n"); - } - else { - printf(usage); - } - - proper_exit(status); -} - - -void show_version_and_exit(void) { - printf(APP_NAME " " APP_VERSION "\n built on " __DATE__ - - #ifdef __GNUC__ - " with GCC"); - printf(" %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); - #endif - - printf("\n features:" - #ifdef __i386__ - " i386" - #endif - #ifdef __x86_64__ - " x86_64" - #endif - #ifdef __AES__ - " AES-NI" - #endif - "\n"); - - printf("\n%s\n", curl_version()); - #ifdef JANSSON_VERSION - printf("libjansson/%s\n", JANSSON_VERSION); - #endif - proper_exit(0); -} - - -const char* get_current_algo_name(void) { - return algo_names[opt_algo]; -} diff --git a/options.h b/options.h deleted file mode 100644 index a14aaeeb..00000000 --- a/options.h +++ /dev/null @@ -1,91 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __OPTIONS_H__ -#define __OPTIONS_H__ - -#include -#include - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#endif - - -enum mining_algo { - ALGO_CRYPTONIGHT, /* CryptoNight (Monero) */ - ALGO_CRYPTONIGHT_LITE, /* CryptoNight-Lite (AEON) */ -}; - - -enum xmr_algo_variant { - XMR_AV0_AUTO, - XMR_AV1_AESNI, - XMR_AV2_AESNI_DOUBLE, - XMR_AV3_SOFT_AES, - XMR_AV4_SOFT_AES_DOUBLE, - XMR_AV_MAX -}; - - -#ifndef XMRIG_NO_AEON -enum aeon_algo_variant { - AEON_AV0_AUTO, - AEON_AV1_AESNI, - AEON_AV2_AESNI_DOUBLE, - AEON_AV3_SOFT_AES, - AEON_AV4_SOFT_AES_DOUBLE, - AEON_AV_MAX -}; -#endif - - -extern bool opt_colors; -extern bool opt_keepalive; -extern bool opt_background; -extern bool opt_double_hash; -extern bool opt_safe; -extern bool opt_nicehash; -extern char *opt_url; -extern char *opt_backup_url; -extern char *opt_userpass; -extern char *opt_user; -extern char *opt_pass; -extern int opt_n_threads; -extern int opt_algo_variant; -extern int opt_retry_pause; -extern int opt_retries; -extern int opt_donate_level; -extern int opt_max_cpu_usage; -extern int64_t opt_affinity; -extern enum mining_algo opt_algo; - -void parse_cmdline(int argc, char *argv[]); -void show_usage_and_exit(int status); -void show_version_and_exit(void); -const char* get_current_algo_name(void); - -extern void proper_exit(int reason); - - -#endif /* __OPTIONS_H__ */ diff --git a/win/app.ico b/res/app.ico similarity index 100% rename from win/app.ico rename to res/app.ico diff --git a/win/app.rc b/res/app.rc similarity index 96% rename from win/app.rc rename to res/app.rc index cb47aa3c..800ce2dd 100644 --- a/win/app.rc +++ b/res/app.rc @@ -1,5 +1,5 @@ #include -#include "../version.h" +#include "../src/version.h" IDI_ICON1 ICON DISCARDABLE "app.ico" diff --git a/src/3rdparty/align.h b/src/3rdparty/align.h new file mode 100644 index 00000000..b61179b9 --- /dev/null +++ b/src/3rdparty/align.h @@ -0,0 +1,33 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ALIGN_H__ +#define __ALIGN_H__ + +#ifdef _MSC_VER +# define VAR_ALIGN(x, decl) __declspec(align(x)) decl +#else +# define VAR_ALIGN(x, decl) decl __attribute__ ((aligned(x))) +#endif + +#endif /* __ALIGN_H__ */ diff --git a/src/3rdparty/getopt/getopt.h b/src/3rdparty/getopt/getopt.h new file mode 100644 index 00000000..bcbff179 --- /dev/null +++ b/src/3rdparty/getopt/getopt.h @@ -0,0 +1,653 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma warning(disable:4996) + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ diff --git a/compat/libcpuid/CMakeLists.txt b/src/3rdparty/libcpuid/CMakeLists.txt similarity index 56% rename from compat/libcpuid/CMakeLists.txt rename to src/3rdparty/libcpuid/CMakeLists.txt index 5c37492a..51eab1d3 100644 --- a/compat/libcpuid/CMakeLists.txt +++ b/src/3rdparty/libcpuid/CMakeLists.txt @@ -3,17 +3,19 @@ project (cpuid C) add_definitions(/DVERSION="0.4.0") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Os") + set(HEADERS libcpuid.h libcpuid_types.h libcpuid_constants.h libcpuid_internal.h -amd_code_t.h -intel_code_t.h -recog_amd.h -recog_intel.h -asm-bits.h -libcpuid_util.h + amd_code_t.h + intel_code_t.h + recog_amd.h + recog_intel.h + asm-bits.h + libcpuid_util.h ) set(SOURCES @@ -24,7 +26,13 @@ set(SOURCES libcpuid_util.c ) +if (CMAKE_CL_64) + enable_language(ASM_MASM) + set(SOURCES_ASM masm-x64.asm) +endif() + add_library(cpuid STATIC ${HEADERS} ${SOURCES} + ${SOURCES_ASM} ) diff --git a/compat/libcpuid/amd_code_t.h b/src/3rdparty/libcpuid/amd_code_t.h similarity index 100% rename from compat/libcpuid/amd_code_t.h rename to src/3rdparty/libcpuid/amd_code_t.h diff --git a/compat/libcpuid/asm-bits.c b/src/3rdparty/libcpuid/asm-bits.c similarity index 100% rename from compat/libcpuid/asm-bits.c rename to src/3rdparty/libcpuid/asm-bits.c diff --git a/compat/libcpuid/asm-bits.h b/src/3rdparty/libcpuid/asm-bits.h similarity index 100% rename from compat/libcpuid/asm-bits.h rename to src/3rdparty/libcpuid/asm-bits.h diff --git a/compat/libcpuid/cpuid_main.c b/src/3rdparty/libcpuid/cpuid_main.c similarity index 82% rename from compat/libcpuid/cpuid_main.c rename to src/3rdparty/libcpuid/cpuid_main.c index 64c4198b..f22c7dd6 100644 --- a/compat/libcpuid/cpuid_main.c +++ b/src/3rdparty/libcpuid/cpuid_main.c @@ -46,11 +46,6 @@ int set_error(cpu_error_t err) return (int) err; } -static void raw_data_t_constructor(struct cpu_raw_data_t* raw) -{ - memset(raw, 0, sizeof(struct cpu_raw_data_t)); -} - static void cpu_id_t_constructor(struct cpu_id_t* id) { memset(id, 0, sizeof(struct cpu_id_t)); @@ -60,29 +55,6 @@ static void cpu_id_t_constructor(struct cpu_id_t* id) id->sse_size = -1; } -static int parse_token(const char* expected_token, const char *token, - const char *value, uint32_t array[][4], int limit, int *recognized) -{ - char format[32]; - int veax, vebx, vecx, vedx; - int index; - - if (*recognized) return 1; /* already recognized */ - if (strncmp(token, expected_token, strlen(expected_token))) return 1; /* not what we search for */ - sprintf(format, "%s[%%d]", expected_token); - *recognized = 1; - if (1 == sscanf(token, format, &index) && index >=0 && index < limit) { - if (4 == sscanf(value, "%x%x%x%x", &veax, &vebx, &vecx, &vedx)) { - array[index][0] = veax; - array[index][1] = vebx; - array[index][2] = vecx; - array[index][3] = vedx; - return 1; - } - } - return 0; -} - /* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */ #ifdef __APPLE__ #include @@ -117,7 +89,7 @@ static int get_total_cpus(void) #if defined linux || defined __linux__ || defined __sun #include #include - + static int get_total_cpus(void) { return sysconf(_SC_NPROCESSORS_ONLN); @@ -249,42 +221,42 @@ static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* da static cpu_vendor_t cpuid_vendor_identify(const uint32_t *raw_vendor, char *vendor_str) { - int i; - cpu_vendor_t vendor = VENDOR_UNKNOWN; - const struct { cpu_vendor_t vendor; char match[16]; } - matchtable[NUM_CPU_VENDORS] = { - /* source: http://www.sandpile.org/ia32/cpuid.htm */ - { VENDOR_INTEL , "GenuineIntel" }, - { VENDOR_AMD , "AuthenticAMD" }, - { VENDOR_CYRIX , "CyrixInstead" }, - { VENDOR_NEXGEN , "NexGenDriven" }, - { VENDOR_TRANSMETA , "GenuineTMx86" }, - { VENDOR_UMC , "UMC UMC UMC " }, - { VENDOR_CENTAUR , "CentaurHauls" }, - { VENDOR_RISE , "RiseRiseRise" }, - { VENDOR_SIS , "SiS SiS SiS " }, - { VENDOR_NSC , "Geode by NSC" }, - }; + int i; + cpu_vendor_t vendor = VENDOR_UNKNOWN; + const struct { cpu_vendor_t vendor; char match[16]; } + matchtable[NUM_CPU_VENDORS] = { + /* source: http://www.sandpile.org/ia32/cpuid.htm */ + { VENDOR_INTEL , "GenuineIntel" }, + { VENDOR_AMD , "AuthenticAMD" }, + { VENDOR_CYRIX , "CyrixInstead" }, + { VENDOR_NEXGEN , "NexGenDriven" }, + { VENDOR_TRANSMETA , "GenuineTMx86" }, + { VENDOR_UMC , "UMC UMC UMC " }, + { VENDOR_CENTAUR , "CentaurHauls" }, + { VENDOR_RISE , "RiseRiseRise" }, + { VENDOR_SIS , "SiS SiS SiS " }, + { VENDOR_NSC , "Geode by NSC" }, + }; - memcpy(vendor_str + 0, &raw_vendor[1], 4); - memcpy(vendor_str + 4, &raw_vendor[3], 4); - memcpy(vendor_str + 8, &raw_vendor[2], 4); - vendor_str[12] = 0; + memcpy(vendor_str + 0, &raw_vendor[1], 4); + memcpy(vendor_str + 4, &raw_vendor[3], 4); + memcpy(vendor_str + 8, &raw_vendor[2], 4); + vendor_str[12] = 0; - /* Determine vendor: */ - for (i = 0; i < NUM_CPU_VENDORS; i++) - if (!strcmp(vendor_str, matchtable[i].match)) { - vendor = matchtable[i].vendor; - break; - } - return vendor; + /* Determine vendor: */ + for (i = 0; i < NUM_CPU_VENDORS; i++) + if (!strcmp(vendor_str, matchtable[i].match)) { + vendor = matchtable[i].vendor; + break; + } + return vendor; } static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { int i, j, basic, xmodel, xfamily, ext; char brandstr[64] = {0}; - data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); + data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); if (data->vendor == VENDOR_UNKNOWN) return set_error(ERR_CPU_UNKN); @@ -302,7 +274,7 @@ static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* dat data->ext_model = data->model + (xmodel << 4); } ext = raw->ext_cpuid[0][0] - 0x8000000; - + /* obtain the brand string, if present: */ if (ext >= 4) { for (i = 0; i < 3; i++) @@ -320,27 +292,6 @@ static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* dat return set_error(ERR_OK); } -static void make_list_from_string(const char* csv, struct cpu_list_t* list) -{ - int i, n, l, last; - l = (int) strlen(csv); - n = 0; - for (i = 0; i < l; i++) if (csv[i] == ',') n++; - n++; - list->num_entries = n; - list->names = (char**) malloc(sizeof(char*) * n); - last = -1; - n = 0; - for (i = 0; i <= l; i++) if (i == l || csv[i] == ',') { - list->names[n] = (char*) malloc(i - last); - memcpy(list->names[n], &csv[last + 1], i - last - 1); - list->names[n][i - last - 1] = '\0'; - n++; - last = i; - } -} - - /* Interface: */ int cpuid_get_total_cpus(void) diff --git a/compat/libcpuid/intel_code_t.h b/src/3rdparty/libcpuid/intel_code_t.h similarity index 100% rename from compat/libcpuid/intel_code_t.h rename to src/3rdparty/libcpuid/intel_code_t.h diff --git a/compat/libcpuid/libcpuid.h b/src/3rdparty/libcpuid/libcpuid.h similarity index 56% rename from compat/libcpuid/libcpuid.h rename to src/3rdparty/libcpuid/libcpuid.h index f99c0fe5..c44990c3 100644 --- a/compat/libcpuid/libcpuid.h +++ b/src/3rdparty/libcpuid/libcpuid.h @@ -610,39 +610,6 @@ void cpu_exec_cpuid_ext(uint32_t* regs); */ int cpuid_get_raw_data(struct cpu_raw_data_t* data); -/** - * @brief Writes the raw CPUID data to a text file - * @param data - a pointer to cpu_raw_data_t structure - * @param filename - the path of the file, where the serialized data should be - * written. If empty, stdout will be used. - * @note This is intended primarily for debugging. On some processor, which is - * not currently supported or not completely recognized by cpu_identify, - * one can still successfully get the raw data and write it to a file. - * libcpuid developers can later import this file and debug the detection - * code as if running on the actual hardware. - * The file is simple text format of "something=value" pairs. Version info - * is also written, but the format is not intended to be neither backward- - * nor forward compatible. - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); - -/** - * @brief Reads raw CPUID data from file - * @param data - a pointer to cpu_raw_data_t structure. The deserialized data will - * be written here. - * @param filename - the path of the file, containing the serialized raw data. - * If empty, stdin will be used. - * @note This function may fail, if the file is created by different version of - * the library. Also, see the notes on cpuid_serialize_raw_data. - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t -*/ -int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename); - /** * @brief Identifies the CPU * @param raw - Input - a pointer to the raw CPUID data, which is obtained @@ -668,222 +635,6 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename */ int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data); -/** - * @brief Returns the short textual representation of a CPU flag - * @param feature - the feature, whose textual representation is wanted. - * @returns a constant string like "fpu", "tsc", "sse2", etc. - * @note the names of the returned flags are compatible with those from - * /proc/cpuinfo in Linux, with the exception of `tm_amd' - */ -const char* cpu_feature_str(cpu_feature_t feature); - -/** - * @brief Returns textual description of the last error - * - * libcpuid stores an `errno'-style error status, whose description - * can be obtained with this function. - * @note This function is not thread-safe - * @see cpu_error_t - */ -const char* cpuid_error(void); - -/** - * @brief Executes RDTSC - * - * The RDTSC (ReaD Time Stamp Counter) instruction gives access to an - * internal 64-bit counter, which usually increments at each clock cycle. - * This can be used for various timing routines, and as a very precise - * clock source. It is set to zero on system startup. Beware that may not - * increment at the same frequency as the CPU. Consecutive calls of RDTSC - * are, however, guaranteed to return monotonically-increasing values. - * - * @param result - a pointer to a 64-bit unsigned integer, where the TSC value - * will be stored - * - * @note If 100% compatibility is a concern, you must first check if the - * RDTSC instruction is present (if it is not, your program will crash - * with "invalid opcode" exception). Only some very old processors (i486, - * early AMD K5 and some Cyrix CPUs) lack that instruction - they should - * have become exceedingly rare these days. To verify RDTSC presence, - * run cpu_identify() and check flags[CPU_FEATURE_TSC]. - * - * @note The monotonically increasing nature of the TSC may be violated - * on SMP systems, if their TSC clocks run at different rate. If the OS - * doesn't account for that, the TSC drift may become arbitrary large. - */ -void cpu_rdtsc(uint64_t* result); - -/** - * @brief Store TSC and timing info - * - * This function stores the current TSC value and current - * time info from a precise OS-specific clock source in the cpu_mark_t - * structure. The sys_clock field contains time with microsecond resolution. - * The values can later be used to measure time intervals, number of clocks, - * FPU frequency, etc. - * @see cpu_rdtsc - * - * @param mark [out] - a pointer to a cpu_mark_t structure - */ -void cpu_tsc_mark(struct cpu_mark_t* mark); - -/** - * @brief Calculate TSC and timing difference - * - * @param mark - input/output: a pointer to a cpu_mark_t sturcture, which has - * already been initialized by cpu_tsc_mark. The difference in - * TSC and time will be written here. - * - * This function calculates the TSC and time difference, by obtaining the - * current TSC and timing values and subtracting the contents of the `mark' - * structure from them. Results are written in the same structure. - * - * Example: - * @code - * ... - * struct cpu_mark_t mark; - * cpu_tsc_mark(&mark); - * foo(); - * cpu_tsc_unmark(&mark); - * printf("Foo finished. Executed in %llu cycles and %llu usecs\n", - * mark.tsc, mark.sys_clock); - * ... - * @endcode - */ -void cpu_tsc_unmark(struct cpu_mark_t* mark); - -/** - * @brief Calculates the CPU clock - * - * @param mark - pointer to a cpu_mark_t structure, which has been initialized - * with cpu_tsc_mark and later `stopped' with cpu_tsc_unmark. - * - * @note For reliable results, the marked time interval should be at least about - * 10 ms. - * - * @returns the CPU clock frequency, in MHz. Due to measurement error, it will - * differ from the true value in a few least-significant bits. Accuracy depends - * on the timing interval - the more, the better. If the timing interval is - * insufficient, the result is -1. Also, see the comment on cpu_clock_measure - * for additional issues and pitfalls in using RDTSC for CPU frequency - * measurements. - */ -int cpu_clock_by_mark(struct cpu_mark_t* mark); - -/** - * @brief Returns the CPU clock, as reported by the OS - * - * This function uses OS-specific functions to obtain the CPU clock. It may - * differ from the true clock for several reasons:

- * - * i) The CPU might be in some power saving state, while the OS reports its - * full-power frequency, or vice-versa.
- * ii) In some cases you can raise or lower the CPU frequency with overclocking - * utilities and the OS will not notice. - * - * @returns the CPU clock frequency in MHz. If the OS is not (yet) supported - * or lacks the necessary reporting machinery, the return value is -1 - */ -int cpu_clock_by_os(void); - -/** - * @brief Measure the CPU clock frequency - * - * @param millis - How much time to waste in the busy-wait cycle. In millisecs. - * Useful values 10 - 1000 - * @param quad_check - Do a more thorough measurement if nonzero - * (see the explanation). - * - * The function performs a busy-wait cycle for the given time and calculates - * the CPU frequency by the difference of the TSC values. The accuracy of the - * calculation depends on the length of the busy-wait cycle: more is better, - * but 100ms should be enough for most purposes. - * - * While this will calculate the CPU frequency correctly in most cases, there are - * several reasons why it might be incorrect:
- * - * i) RDTSC doesn't guarantee it will run at the same clock as the CPU. - * Apparently there aren't CPUs at the moment, but still, there's no - * guarantee.
- * ii) The CPU might be in a low-frequency power saving mode, and the CPU - * might be switched to higher frequency at any time. If this happens - * during the measurement, the result can be anywhere between the - * low and high frequencies. Also, if you're interested in the - * high frequency value only, this function might return the low one - * instead.
- * iii) On SMP systems exhibiting TSC drift (see \ref cpu_rdtsc) - * - * the quad_check option will run four consecutive measurements and - * then return the average of the two most-consistent results. The total - * runtime of the function will still be `millis' - consider using - * a bit more time for the timing interval. - * - * Finally, for benchmarking / CPU intensive applications, the best strategy is - * to use the cpu_tsc_mark() / cpu_tsc_unmark() / cpu_clock_by_mark() method. - * Begin by mark()-ing about one second after application startup (allowing the - * power-saving manager to kick in and rise the frequency during that time), - * then unmark() just before application finishing. The result will most - * acurately represent at what frequency your app was running. - * - * @returns the CPU clock frequency in MHz (within some measurement error - * margin). If RDTSC is not supported, the result is -1. - */ -int cpu_clock_measure(int millis, int quad_check); - -/** - * @brief Measure the CPU clock frequency using instruction-counting - * - * @param millis - how much time to allocate for each run, in milliseconds - * @param runs - how many runs to perform - * - * The function performs a busy-wait cycle using a known number of "heavy" (SSE) - * instructions. These instructions run at (more or less guaranteed) 1 IPC rate, - * so by running a busy loop for a fixed amount of time, and measuring the - * amount of instructions done, the CPU clock is accurately measured. - * - * Of course, this function is still affected by the power-saving schemes, so - * the warnings as of cpu_clock_measure() still apply. However, this function is - * immune to problems with detection, related to the Intel Nehalem's "Turbo" - * mode, where the internal clock is raised, but the RDTSC rate is unaffected. - * - * The function will run for about (millis * runs) milliseconds. - * You can make only a single busy-wait run (runs == 1); however, this can - * be affected by task scheduling (which will break the counting), so allowing - * more than one run is recommended. As run length is not imperative for - * accurate readings (e.g., 50ms is sufficient), you can afford a lot of short - * runs, e.g. 10 runs of 50ms or 20 runs of 25ms. - * - * Recommended values - millis = 50, runs = 4. For more robustness, - * increase the number of runs. - * - * NOTE: on Bulldozer and later CPUs, the busy-wait cycle runs at 1.4 IPC, thus - * the results are skewed. This is corrected internally by dividing the resulting - * value by 1.4. - * However, this only occurs if the thread is executed on a single CMT - * module - if there are other threads competing for resources, the results are - * unpredictable. Make sure you run cpu_clock_by_ic() on a CPU that is free from - * competing threads, or if there are such threads, they shouldn't exceed the - * number of modules. On a Bulldozer X8, that means 4 threads. - * - * @returns the CPU clock frequency in MHz (within some measurement error - * margin). If SSE is not supported, the result is -1. If the input parameters - * are incorrect, or some other internal fault is detected, the result is -2. - */ -int cpu_clock_by_ic(int millis, int runs); - -/** - * @brief Get the CPU clock frequency (all-in-one method) - * - * This is an all-in-one method for getting the CPU clock frequency. - * It tries to use the OS for that. If the OS doesn't have this info, it - * uses cpu_clock_measure with 200ms time interval and quadruple checking. - * - * @returns the CPU clock frequency in MHz. If every possible method fails, - * the result is -1. - */ -int cpu_clock(void); - - /** * @brief The return value of cpuid_get_epc(). * @details @@ -916,230 +667,6 @@ struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw); */ const char* cpuid_lib_version(void); -typedef void (*libcpuid_warn_fn_t) (const char *msg); -/** - * @brief Sets the warning print function - * - * In some cases, the internal libcpuid machinery would like to emit useful - * debug warnings. By default, these warnings are written to stderr. However, - * you can set a custom function that will receive those warnings. - * - * @param warn_fun - the warning function you want to set. If NULL, warnings - * are disabled. The function takes const char* argument. - * - * @returns the current warning function. You can use the return value to - * keep the previous warning function and restore it at your discretion. - */ -libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t warn_fun); - -/** - * @brief Sets the verbosiness level - * - * When the verbosiness level is above zero, some functions might print - * diagnostic information about what are they doing. The higher the level is, - * the more detail is printed. Level zero is guaranteed to omit all such - * output. The output is written using the same machinery as the warnings, - * @see cpuid_set_warn_function() - * - * @param level the desired verbosiness level. Useful values 0..2 inclusive - */ -void cpuid_set_verbosiness_level(int level); - - -/** - * @brief Obtains the CPU vendor from CPUID from the current CPU - * @note The result is cached. - * @returns VENDOR_UNKNOWN if failed, otherwise the CPU vendor type. - * @see cpu_vendor_t - */ -cpu_vendor_t cpuid_get_vendor(void); - -/** - * @brief a structure that holds a list of processor names - */ -struct cpu_list_t { - /** Number of entries in the list */ - int num_entries; - /** Pointers to names. There will be num_entries of them */ - char **names; -}; - -/** - * @brief Gets a list of all known CPU names from a specific vendor. - * - * This function compiles a list of all known CPU (code)names - * (i.e. the possible values of cpu_id_t::cpu_codename) for the given vendor. - * - * There are about 100 entries for Intel and AMD, and a few for the other - * vendors. The list is written out in approximate chronological introduction - * order of the parts. - * - * @param vendor the vendor to be queried - * @param list [out] the resulting list will be written here. - * NOTE: As the memory is dynamically allocated, be sure to call - * cpuid_free_cpu_list() after you're done with the data - * @see cpu_list_t - */ -void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list); - -/** - * @brief Frees a CPU list - * - * This function deletes all the memory associated with a CPU list, as obtained - * by cpuid_get_cpu_list() - * - * @param list - the list to be free()'d. - */ -void cpuid_free_cpu_list(struct cpu_list_t* list); - -struct msr_driver_t; -/** - * @brief Starts/opens a driver, needed to read MSRs (Model Specific Registers) - * - * On systems that support it, this function will create a temporary - * system driver, that has privileges to execute the RDMSR instruction. - * After the driver is created, you can read MSRs by calling \ref cpu_rdmsr - * - * @returns a handle to the driver on success, and NULL on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -struct msr_driver_t* cpu_msr_driver_open(void); - -/** - * @brief Similar to \ref cpu_msr_driver_open, but accept one parameter - * - * This function works on certain operating systems (GNU/Linux, FreeBSD) - * - * @param core_num specify the core number for MSR. - * The first core number is 0. - * The last core number is \ref cpuid_get_total_cpus - 1. - * - * @returns a handle to the driver on success, and NULL on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -struct msr_driver_t* cpu_msr_driver_open_core(unsigned core_num); - -/** - * @brief Reads a Model-Specific Register (MSR) - * - * If the CPU has MSRs (as indicated by the CPU_FEATURE_MSR flag), you can - * read a MSR with the given index by calling this function. - * - * There are several prerequisites you must do before reading MSRs: - * 1) You must ensure the CPU has RDMSR. Check the CPU_FEATURE_MSR flag - * in cpu_id_t::flags - * 2) You must ensure that the CPU implements the specific MSR you intend to - * read. - * 3) You must open a MSR-reader driver. RDMSR is a privileged instruction and - * needs ring-0 access in order to work. This temporary driver is created - * by calling \ref cpu_msr_driver_open - * - * @param handle - a handle to the MSR reader driver, as created by - * cpu_msr_driver_open - * @param msr_index - the numeric ID of the MSR you want to read - * @param result - a pointer to a 64-bit integer, where the MSR value is stored - * - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -int cpu_rdmsr(struct msr_driver_t* handle, uint32_t msr_index, uint64_t* result); - - -typedef enum { - INFO_MPERF, /*!< Maximum performance frequency clock. This - is a counter, which increments as a - proportion of the actual processor speed. */ - INFO_APERF, /*!< Actual performance frequency clock. This - accumulates the core clock counts when the - core is active. */ - INFO_MIN_MULTIPLIER, /*!< Minimum CPU:FSB ratio for this CPU, - multiplied by 100. */ - INFO_CUR_MULTIPLIER, /*!< Current CPU:FSB ratio, multiplied by 100. - e.g., a CPU:FSB value of 18.5 reads as - "1850". */ - INFO_MAX_MULTIPLIER, /*!< Maximum CPU:FSB ratio for this CPU, - multiplied by 100. */ - INFO_TEMPERATURE, /*!< The current core temperature in Celsius. */ - INFO_THROTTLING, /*!< 1 if the current logical processor is - throttling. 0 if it is running normally. */ - INFO_VOLTAGE, /*!< The current core voltage in Volt, - multiplied by 100. */ - INFO_BCLK, /*!< See \ref INFO_BUS_CLOCK. */ - INFO_BUS_CLOCK, /*!< The main bus clock in MHz, - e.g., FSB/QPI/DMI/HT base clock, - multiplied by 100. */ -} cpu_msrinfo_request_t; - -/** - * @brief Similar to \ref cpu_rdmsr, but extract a range of bits - * - * @param handle - a handle to the MSR reader driver, as created by - * cpu_msr_driver_open - * @param msr_index - the numeric ID of the MSR you want to read - * @param highbit - the high bit in range, must be inferior to 64 - * @param lowbit - the low bit in range, must be equal or superior to 0 - * @param result - a pointer to a 64-bit integer, where the MSR value is stored - * - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -int cpu_rdmsr_range(struct msr_driver_t* handle, uint32_t msr_index, uint8_t highbit, - uint8_t lowbit, uint64_t* result); - -/** - * @brief Reads extended CPU information from Model-Specific Registers. - * @param handle - a handle to an open MSR driver, @see cpu_msr_driver_open - * @param which - which info field should be returned. A list of - * available information entities is listed in the - * cpu_msrinfo_request_t enum. - * @retval - if the requested information is available for the current - * processor model, the respective value is returned. - * if no information is available, or the CPU doesn't support - * the query, the special value CPU_INVALID_VALUE is returned - * @note This function is not MT-safe. If you intend to call it from multiple - * threads, guard it through a mutex or a similar primitive. - */ -int cpu_msrinfo(struct msr_driver_t* handle, cpu_msrinfo_request_t which); -#define CPU_INVALID_VALUE 0x3fffffff - -/** - * @brief Writes the raw MSR data to a text file - * @param data - a pointer to msr_driver_t structure - * @param filename - the path of the file, where the serialized data should be - * written. If empty, stdout will be used. - * @note This is intended primarily for debugging. On some processor, which is - * not currently supported or not completely recognized by cpu_identify, - * one can still successfully get the raw data and write it to a file. - * libcpuid developers can later import this file and debug the detection - * code as if running on the actual hardware. - * The file is simple text format of "something=value" pairs. Version info - * is also written, but the format is not intended to be neither backward- - * nor forward compatible. - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -int msr_serialize_raw_data(struct msr_driver_t* handle, const char* filename); - -/** - * @brief Closes an open MSR driver - * - * This function unloads the MSR driver opened by cpu_msr_driver_open and - * frees any resources associated with it. - * - * @param handle - a handle to the MSR reader driver, as created by - * cpu_msr_driver_open - * - * @returns zero if successful, and some negative number on error. - * The error message can be obtained by calling \ref cpuid_error. - * @see cpu_error_t - */ -int cpu_msr_driver_close(struct msr_driver_t* handle); - #ifdef __cplusplus }; /* extern "C" */ #endif diff --git a/compat/libcpuid/libcpuid_constants.h b/src/3rdparty/libcpuid/libcpuid_constants.h similarity index 100% rename from compat/libcpuid/libcpuid_constants.h rename to src/3rdparty/libcpuid/libcpuid_constants.h diff --git a/compat/libcpuid/libcpuid_internal.h b/src/3rdparty/libcpuid/libcpuid_internal.h similarity index 66% rename from compat/libcpuid/libcpuid_internal.h rename to src/3rdparty/libcpuid/libcpuid_internal.h index 7f3671da..038aa209 100644 --- a/compat/libcpuid/libcpuid_internal.h +++ b/src/3rdparty/libcpuid/libcpuid_internal.h @@ -58,6 +58,48 @@ struct internal_id_info_t { int score; // detection (matchtable) score }; +#define LBIT(x) (((long long) 1) << x) + +enum _common_bits_t { + _M_ = LBIT( 0 ), + MOBILE_ = LBIT( 1 ), + _MP_ = LBIT( 2 ), +}; + +// additional detection bits for Intel CPUs: +enum _intel_bits_t { + PENTIUM_ = LBIT( 10 ), + CELERON_ = LBIT( 11 ), + CORE_ = LBIT( 12 ), + _I_ = LBIT( 13 ), + _3 = LBIT( 14 ), + _5 = LBIT( 15 ), + _7 = LBIT( 16 ), + XEON_ = LBIT( 17 ), + ATOM_ = LBIT( 18 ), +}; +typedef enum _intel_bits_t intel_bits_t; + +enum _amd_bits_t { + ATHLON_ = LBIT( 10 ), + _XP_ = LBIT( 11 ), + DURON_ = LBIT( 12 ), + SEMPRON_ = LBIT( 13 ), + OPTERON_ = LBIT( 14 ), + TURION_ = LBIT( 15 ), + _LV_ = LBIT( 16 ), + _64_ = LBIT( 17 ), + _X2 = LBIT( 18 ), + _X3 = LBIT( 19 ), + _X4 = LBIT( 20 ), + _X6 = LBIT( 21 ), + _FX = LBIT( 22 ), + _APU_ = LBIT( 23 ), +}; +typedef enum _amd_bits_t amd_bits_t; + + + int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal); diff --git a/compat/libcpuid/libcpuid_types.h b/src/3rdparty/libcpuid/libcpuid_types.h similarity index 100% rename from compat/libcpuid/libcpuid_types.h rename to src/3rdparty/libcpuid/libcpuid_types.h diff --git a/src/3rdparty/libcpuid/libcpuid_util.c b/src/3rdparty/libcpuid/libcpuid_util.c new file mode 100644 index 00000000..440b3724 --- /dev/null +++ b/src/3rdparty/libcpuid/libcpuid_util.c @@ -0,0 +1,93 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" + +void match_features(const struct feature_map_t* matchtable, int count, uint32_t reg, struct cpu_id_t* data) +{ + int i; + for (i = 0; i < count; i++) + if (reg & (1u << matchtable[i].bit)) + data->flags[matchtable[i].feature] = 1; +} + +static int xmatch_entry(char c, const char* p) +{ + int i, j; + if (c == 0) return -1; + if (c == p[0]) return 1; + if (p[0] == '.') return 1; + if (p[0] == '#' && isdigit(c)) return 1; + if (p[0] == '[') { + j = 1; + while (p[j] && p[j] != ']') j++; + if (!p[j]) return -1; + for (i = 1; i < j; i++) + if (p[i] == c) return j + 1; + } + return -1; +} + +int match_pattern(const char* s, const char* p) +{ + int i, j, dj, k, n, m; + n = (int) strlen(s); + m = (int) strlen(p); + for (i = 0; i < n; i++) { + if (xmatch_entry(s[i], p) != -1) { + j = 0; + k = 0; + while (j < m && ((dj = xmatch_entry(s[i + k], p + j)) != -1)) { + k++; + j += dj; + } + if (j == m) return i + 1; + } + } + return 0; +} + +struct cpu_id_t* get_cached_cpuid(void) +{ + static int initialized = 0; + static struct cpu_id_t id; + if (initialized) return &id; + if (cpu_identify(NULL, &id)) + memset(&id, 0, sizeof(id)); + initialized = 1; + return &id; +} + +int match_all(uint64_t bits, uint64_t mask) +{ + return (bits & mask) == mask; +} diff --git a/compat/libcpuid/libcpuid_util.h b/src/3rdparty/libcpuid/libcpuid_util.h similarity index 84% rename from compat/libcpuid/libcpuid_util.h rename to src/3rdparty/libcpuid/libcpuid_util.h index aba63f1a..0c8200e8 100644 --- a/compat/libcpuid/libcpuid_util.h +++ b/src/3rdparty/libcpuid/libcpuid_util.h @@ -28,8 +28,6 @@ #define COUNT_OF(array) (sizeof(array) / sizeof(array[0])) -#define LBIT(x) (((long long) 1) << x) - struct feature_map_t { unsigned bit; cpu_feature_t feature; @@ -50,20 +48,6 @@ struct match_entry_t { int match_cpu_codename(const struct match_entry_t* matchtable, int count, struct cpu_id_t* data, int brand_code, uint64_t bits, int model_code); - -void warnf(const char* format, ...) -#ifdef __GNUC__ -__attribute__((format(printf, 1, 2))) -#endif -; -void debugf(int verboselevel, const char* format, ...) -#ifdef __GNUC__ -__attribute__((format(printf, 2, 3))) -#endif -; -void generic_get_cpu_list(const struct match_entry_t* matchtable, int count, - struct cpu_list_t* list); - /* * Seek for a pattern in `haystack'. * Pattern may be an fixed string, or contain the special metacharacters @@ -86,15 +70,9 @@ struct cpu_id_t* get_cached_cpuid(void); /* returns true if all bits of mask are present in `bits'. */ int match_all(uint64_t bits, uint64_t mask); -/* print what bits a mask consists of */ -void debug_print_lbits(int debuglevel, uint64_t mask); - /* * Sets the current errno */ int set_error(cpu_error_t err); -extern libcpuid_warn_fn_t _warn_fun; -extern int _current_verboselevel; - #endif /* __LIBCPUID_UTIL_H__ */ diff --git a/src/3rdparty/libcpuid/masm-x64.asm b/src/3rdparty/libcpuid/masm-x64.asm new file mode 100644 index 00000000..51e98651 --- /dev/null +++ b/src/3rdparty/libcpuid/masm-x64.asm @@ -0,0 +1,359 @@ + +.code +; procedure exec_cpuid +; Signature: void exec_cpiud(uint32_t *regs) +exec_cpuid Proc + push rbx + push rcx + push rdx + push rdi + + mov rdi, rcx + + mov eax, [rdi] + mov ebx, [rdi+4] + mov ecx, [rdi+8] + mov edx, [rdi+12] + + cpuid + + mov [rdi], eax + mov [rdi+4], ebx + mov [rdi+8], ecx + mov [rdi+12], edx + pop rdi + pop rdx + pop rcx + pop rbx + ret +exec_cpuid endp + +; procedure cpu_rdtsc +; Signature: void cpu_rdtsc(uint64_t *result) +cpu_rdtsc Proc + push rdx + rdtsc + mov [rcx], eax + mov [rcx+4], edx + pop rdx + ret +cpu_rdtsc endp + +; procedure busy_sse_loop +; Signature: void busy_sse_loop(int cycles) +busy_sse_loop Proc + ; save xmm6 & xmm7 into the shadow area, as Visual C++ 2008 + ; expects that we don't touch them: + movups [rsp + 8], xmm6 + movups [rsp + 24], xmm7 + + xorps xmm0, xmm0 + xorps xmm1, xmm1 + xorps xmm2, xmm2 + xorps xmm3, xmm3 + xorps xmm4, xmm4 + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + ; -- + align 16 +bsLoop: + ; 0: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 1: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 2: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 3: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 4: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 5: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 6: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 7: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 8: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 9: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 10: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 11: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 12: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 13: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 14: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 15: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 16: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 17: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 18: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 19: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 20: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 21: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 22: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 23: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 24: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 25: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 26: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 27: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 28: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 29: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 30: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; 31: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + ; ---------------------- + dec ecx + jnz bsLoop + + ; restore xmm6 & xmm7: + movups xmm6, [rsp + 8] + movups xmm7, [rsp + 24] + ret +busy_sse_loop endp + +END diff --git a/src/3rdparty/libcpuid/recog_amd.c b/src/3rdparty/libcpuid/recog_amd.c new file mode 100644 index 00000000..352d733b --- /dev/null +++ b/src/3rdparty/libcpuid/recog_amd.c @@ -0,0 +1,169 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" +#include "libcpuid_internal.h" +#include "recog_amd.h" + +const struct amd_code_str { amd_code_t code; char *str; } amd_code_str[] = { + #define CODE(x) { x, #x } + #define CODE2(x, y) CODE(x) + #include "amd_code_t.h" + #undef CODE +}; + +struct amd_code_and_bits_t { + int code; + uint64_t bits; +}; + +enum _amd_model_codes_t { + // Only for Ryzen CPUs: + _1400, + _1500, + _1600, +}; + +static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + const struct feature_map_t matchtable_edx81[] = { + { 20, CPU_FEATURE_NX }, + { 22, CPU_FEATURE_MMXEXT }, + { 25, CPU_FEATURE_FXSR_OPT }, + { 30, CPU_FEATURE_3DNOWEXT }, + { 31, CPU_FEATURE_3DNOW }, + }; + const struct feature_map_t matchtable_ecx81[] = { + { 1, CPU_FEATURE_CMP_LEGACY }, + { 2, CPU_FEATURE_SVM }, + { 5, CPU_FEATURE_ABM }, + { 6, CPU_FEATURE_SSE4A }, + { 7, CPU_FEATURE_MISALIGNSSE }, + { 8, CPU_FEATURE_3DNOWPREFETCH }, + { 9, CPU_FEATURE_OSVW }, + { 10, CPU_FEATURE_IBS }, + { 11, CPU_FEATURE_XOP }, + { 12, CPU_FEATURE_SKINIT }, + { 13, CPU_FEATURE_WDT }, + { 16, CPU_FEATURE_FMA4 }, + { 21, CPU_FEATURE_TBM }, + }; + const struct feature_map_t matchtable_edx87[] = { + { 0, CPU_FEATURE_TS }, + { 1, CPU_FEATURE_FID }, + { 2, CPU_FEATURE_VID }, + { 3, CPU_FEATURE_TTP }, + { 4, CPU_FEATURE_TM_AMD }, + { 5, CPU_FEATURE_STC }, + { 6, CPU_FEATURE_100MHZSTEPS }, + { 7, CPU_FEATURE_HWPSTATE }, + /* id 8 is handled in common */ + { 9, CPU_FEATURE_CPB }, + { 10, CPU_FEATURE_APERFMPERF }, + { 11, CPU_FEATURE_PFI }, + { 12, CPU_FEATURE_PA }, + }; + if (raw->ext_cpuid[0][0] >= 0x80000001) { + match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); + match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data); + } + if (raw->ext_cpuid[0][0] >= 0x80000007) + match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data); + if (raw->ext_cpuid[0][0] >= 0x8000001a) { + /* We have the extended info about SSE unit size */ + data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] = 1; + data->sse_size = (raw->ext_cpuid[0x1a][0] & 1) ? 128 : 64; + } +} + +static void decode_amd_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + int l3_result; + const int assoc_table[16] = { + 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255 + }; + unsigned n = raw->ext_cpuid[0][0]; + + if (n >= 0x80000005) { + data->l1_data_cache = (raw->ext_cpuid[5][2] >> 24) & 0xff; + data->l1_assoc = (raw->ext_cpuid[5][2] >> 16) & 0xff; + data->l1_cacheline = (raw->ext_cpuid[5][2]) & 0xff; + data->l1_instruction_cache = (raw->ext_cpuid[5][3] >> 24) & 0xff; + } + if (n >= 0x80000006) { + data->l2_cache = (raw->ext_cpuid[6][2] >> 16) & 0xffff; + data->l2_assoc = assoc_table[(raw->ext_cpuid[6][2] >> 12) & 0xf]; + data->l2_cacheline = (raw->ext_cpuid[6][2]) & 0xff; + + l3_result = (raw->ext_cpuid[6][3] >> 18); + if (l3_result > 0) { + l3_result = 512 * l3_result; /* AMD spec says it's a range, + but we take the lower bound */ + data->l3_cache = l3_result; + data->l3_assoc = assoc_table[(raw->ext_cpuid[6][3] >> 12) & 0xf]; + data->l3_cacheline = (raw->ext_cpuid[6][3]) & 0xff; + } else { + data->l3_cache = 0; + } + } +} + +static void decode_amd_number_of_cores(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + int logical_cpus = -1, num_cores = -1; + + if (raw->basic_cpuid[0][0] >= 1) { + logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; + if (raw->ext_cpuid[0][0] >= 8) { + num_cores = 1 + (raw->ext_cpuid[8][2] & 0xff); + } + } + if (data->flags[CPU_FEATURE_HT]) { + if (num_cores > 1) { + if (data->ext_family >= 23) + num_cores /= 2; // e.g., Ryzen 7 reports 16 "real" cores, but they are really just 8. + data->num_cores = num_cores; + data->num_logical_cpus = logical_cpus; + } else { + data->num_cores = 1; + data->num_logical_cpus = (logical_cpus >= 2 ? logical_cpus : 2); + } + } else { + data->num_cores = data->num_logical_cpus = 1; + } +} + +int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + load_amd_features(raw, data); + decode_amd_cache_info(raw, data); + decode_amd_number_of_cores(raw, data); + return 0; +} diff --git a/compat/libcpuid/recog_amd.h b/src/3rdparty/libcpuid/recog_amd.h similarity index 96% rename from compat/libcpuid/recog_amd.h rename to src/3rdparty/libcpuid/recog_amd.h index 34e89598..19f839ba 100644 --- a/compat/libcpuid/recog_amd.h +++ b/src/3rdparty/libcpuid/recog_amd.h @@ -27,6 +27,5 @@ #define __RECOG_AMD_H__ int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal); -void cpuid_get_list_amd(struct cpu_list_t* list); #endif /* __RECOG_AMD_H__ */ diff --git a/src/3rdparty/libcpuid/recog_intel.c b/src/3rdparty/libcpuid/recog_intel.c new file mode 100644 index 00000000..5467c19f --- /dev/null +++ b/src/3rdparty/libcpuid/recog_intel.c @@ -0,0 +1,542 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" +#include "libcpuid_internal.h" +#include "recog_intel.h" + +const struct intel_bcode_str { intel_code_t code; char *str; } intel_bcode_str[] = { + #define CODE(x) { x, #x } + #define CODE2(x, y) CODE(x) + #include "intel_code_t.h" + #undef CODE +}; + +typedef struct { + int code; + uint64_t bits; +} intel_code_and_bits_t; + +enum _intel_model_t { + UNKNOWN = -1, + _3000 = 100, + _3100, + _3200, + X3200, + _3300, + X3300, + _5100, + _5200, + _5300, + _5400, + _2xxx, /* Core i[357] 2xxx */ + _3xxx, /* Core i[357] 3xxx */ +}; +typedef enum _intel_model_t intel_model_t; + +static void load_intel_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + const struct feature_map_t matchtable_edx1[] = { + { 18, CPU_FEATURE_PN }, + { 21, CPU_FEATURE_DTS }, + { 22, CPU_FEATURE_ACPI }, + { 27, CPU_FEATURE_SS }, + { 29, CPU_FEATURE_TM }, + { 30, CPU_FEATURE_IA64 }, + { 31, CPU_FEATURE_PBE }, + }; + const struct feature_map_t matchtable_ecx1[] = { + { 2, CPU_FEATURE_DTS64 }, + { 4, CPU_FEATURE_DS_CPL }, + { 5, CPU_FEATURE_VMX }, + { 6, CPU_FEATURE_SMX }, + { 7, CPU_FEATURE_EST }, + { 8, CPU_FEATURE_TM2 }, + { 10, CPU_FEATURE_CID }, + { 14, CPU_FEATURE_XTPR }, + { 15, CPU_FEATURE_PDCM }, + { 18, CPU_FEATURE_DCA }, + { 21, CPU_FEATURE_X2APIC }, + }; + const struct feature_map_t matchtable_edx81[] = { + { 20, CPU_FEATURE_XD }, + }; + const struct feature_map_t matchtable_ebx7[] = { + { 2, CPU_FEATURE_SGX }, + { 4, CPU_FEATURE_HLE }, + { 11, CPU_FEATURE_RTM }, + { 16, CPU_FEATURE_AVX512F }, + { 17, CPU_FEATURE_AVX512DQ }, + { 18, CPU_FEATURE_RDSEED }, + { 19, CPU_FEATURE_ADX }, + { 26, CPU_FEATURE_AVX512PF }, + { 27, CPU_FEATURE_AVX512ER }, + { 28, CPU_FEATURE_AVX512CD }, + { 29, CPU_FEATURE_SHA_NI }, + { 30, CPU_FEATURE_AVX512BW }, + { 31, CPU_FEATURE_AVX512VL }, + }; + if (raw->basic_cpuid[0][0] >= 1) { + match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data); + match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data); + } + if (raw->ext_cpuid[0][0] >= 1) { + match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); + } + // detect TSX/AVX512: + if (raw->basic_cpuid[0][0] >= 7) { + match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data); + } +} + +enum _cache_type_t { + L1I, + L1D, + L2, + L3, + L4 +}; +typedef enum _cache_type_t cache_type_t; + +static void check_case(uint8_t on, cache_type_t cache, int size, int assoc, int linesize, struct cpu_id_t* data) +{ + if (!on) return; + switch (cache) { + case L1I: + data->l1_instruction_cache = size; + break; + case L1D: + data->l1_data_cache = size; + data->l1_assoc = assoc; + data->l1_cacheline = linesize; + break; + case L2: + data->l2_cache = size; + data->l2_assoc = assoc; + data->l2_cacheline = linesize; + break; + case L3: + data->l3_cache = size; + data->l3_assoc = assoc; + data->l3_cacheline = linesize; + break; + case L4: + data->l4_cache = size; + data->l4_assoc = assoc; + data->l4_cacheline = linesize; + break; + default: + break; + } +} + +static void decode_intel_oldstyle_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + uint8_t f[256] = {0}; + int reg, off; + uint32_t x; + for (reg = 0; reg < 4; reg++) { + x = raw->basic_cpuid[2][reg]; + if (x & 0x80000000) continue; + for (off = 0; off < 4; off++) { + f[x & 0xff] = 1; + x >>= 8; + } + } + + check_case(f[0x06], L1I, 8, 4, 32, data); + check_case(f[0x08], L1I, 16, 4, 32, data); + check_case(f[0x0A], L1D, 8, 2, 32, data); + check_case(f[0x0C], L1D, 16, 4, 32, data); + check_case(f[0x22], L3, 512, 4, 64, data); + check_case(f[0x23], L3, 1024, 8, 64, data); + check_case(f[0x25], L3, 2048, 8, 64, data); + check_case(f[0x29], L3, 4096, 8, 64, data); + check_case(f[0x2C], L1D, 32, 8, 64, data); + check_case(f[0x30], L1I, 32, 8, 64, data); + check_case(f[0x39], L2, 128, 4, 64, data); + check_case(f[0x3A], L2, 192, 6, 64, data); + check_case(f[0x3B], L2, 128, 2, 64, data); + check_case(f[0x3C], L2, 256, 4, 64, data); + check_case(f[0x3D], L2, 384, 6, 64, data); + check_case(f[0x3E], L2, 512, 4, 64, data); + check_case(f[0x41], L2, 128, 4, 32, data); + check_case(f[0x42], L2, 256, 4, 32, data); + check_case(f[0x43], L2, 512, 4, 32, data); + check_case(f[0x44], L2, 1024, 4, 32, data); + check_case(f[0x45], L2, 2048, 4, 32, data); + check_case(f[0x46], L3, 4096, 4, 64, data); + check_case(f[0x47], L3, 8192, 8, 64, data); + check_case(f[0x4A], L3, 6144, 12, 64, data); + check_case(f[0x4B], L3, 8192, 16, 64, data); + check_case(f[0x4C], L3, 12288, 12, 64, data); + check_case(f[0x4D], L3, 16384, 16, 64, data); + check_case(f[0x4E], L2, 6144, 24, 64, data); + check_case(f[0x60], L1D, 16, 8, 64, data); + check_case(f[0x66], L1D, 8, 4, 64, data); + check_case(f[0x67], L1D, 16, 4, 64, data); + check_case(f[0x68], L1D, 32, 4, 64, data); + /* The following four entries are trace cache. Intel does not + * specify a cache-line size, so we use -1 instead + */ + check_case(f[0x70], L1I, 12, 8, -1, data); + check_case(f[0x71], L1I, 16, 8, -1, data); + check_case(f[0x72], L1I, 32, 8, -1, data); + check_case(f[0x73], L1I, 64, 8, -1, data); + + check_case(f[0x78], L2, 1024, 4, 64, data); + check_case(f[0x79], L2, 128, 8, 64, data); + check_case(f[0x7A], L2, 256, 8, 64, data); + check_case(f[0x7B], L2, 512, 8, 64, data); + check_case(f[0x7C], L2, 1024, 8, 64, data); + check_case(f[0x7D], L2, 2048, 8, 64, data); + check_case(f[0x7F], L2, 512, 2, 64, data); + check_case(f[0x82], L2, 256, 8, 32, data); + check_case(f[0x83], L2, 512, 8, 32, data); + check_case(f[0x84], L2, 1024, 8, 32, data); + check_case(f[0x85], L2, 2048, 8, 32, data); + check_case(f[0x86], L2, 512, 4, 64, data); + check_case(f[0x87], L2, 1024, 8, 64, data); + + if (f[0x49]) { + /* This flag is overloaded with two meanings. On Xeon MP + * (family 0xf, model 0x6) this means L3 cache. On all other + * CPUs (notably Conroe et al), this is L2 cache. In both cases + * it means 4MB, 16-way associative, 64-byte line size. + */ + if (data->family == 0xf && data->model == 0x6) { + data->l3_cache = 4096; + data->l3_assoc = 16; + data->l3_cacheline = 64; + } else { + data->l2_cache = 4096; + data->l2_assoc = 16; + data->l2_cacheline = 64; + } + } + if (f[0x40]) { + /* Again, a special flag. It means: + * 1) If no L2 is specified, then CPU is w/o L2 (0 KB) + * 2) If L2 is specified by other flags, then, CPU is w/o L3. + */ + if (data->l2_cache == -1) { + data->l2_cache = 0; + } else { + data->l3_cache = 0; + } + } +} + +static void decode_intel_deterministic_cache_info(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int ecx; + int ways, partitions, linesize, sets, size, level, typenumber; + cache_type_t type; + for (ecx = 0; ecx < MAX_INTELFN4_LEVEL; ecx++) { + typenumber = raw->intel_fn4[ecx][0] & 0x1f; + if (typenumber == 0) break; + level = (raw->intel_fn4[ecx][0] >> 5) & 0x7; + if (level == 1 && typenumber == 1) + type = L1D; + else if (level == 1 && typenumber == 2) + type = L1I; + else if (level == 2 && typenumber == 3) + type = L2; + else if (level == 3 && typenumber == 3) + type = L3; + else if (level == 4 && typenumber == 3) + type = L4; + else { + continue; + } + ways = ((raw->intel_fn4[ecx][1] >> 22) & 0x3ff) + 1; + partitions = ((raw->intel_fn4[ecx][1] >> 12) & 0x3ff) + 1; + linesize = (raw->intel_fn4[ecx][1] & 0xfff) + 1; + sets = raw->intel_fn4[ecx][2] + 1; + size = ways * partitions * linesize * sets / 1024; + check_case(1, type, size, ways, linesize, data); + } +} + +static int decode_intel_extended_topology(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int i, level_type, num_smt = -1, num_core = -1; + for (i = 0; i < MAX_INTELFN11_LEVEL; i++) { + level_type = (raw->intel_fn11[i][2] & 0xff00) >> 8; + switch (level_type) { + case 0x01: + num_smt = raw->intel_fn11[i][1] & 0xffff; + break; + case 0x02: + num_core = raw->intel_fn11[i][1] & 0xffff; + break; + default: + break; + } + } + if (num_smt == -1 || num_core == -1) return 0; + data->num_logical_cpus = num_core; + data->num_cores = num_core / num_smt; + // make sure num_cores is at least 1. In VMs, the CPUID instruction + // is rigged and may give nonsensical results, but we should at least + // avoid outputs like data->num_cores == 0. + if (data->num_cores <= 0) data->num_cores = 1; + return 1; +} + +static void decode_intel_number_of_cores(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int logical_cpus = -1, num_cores = -1; + + if (raw->basic_cpuid[0][0] >= 11) { + if (decode_intel_extended_topology(raw, data)) return; + } + + if (raw->basic_cpuid[0][0] >= 1) { + logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; + if (raw->basic_cpuid[0][0] >= 4) { + num_cores = 1 + ((raw->basic_cpuid[4][0] >> 26) & 0x3f); + } + } + if (data->flags[CPU_FEATURE_HT]) { + if (num_cores > 1) { + data->num_cores = num_cores; + data->num_logical_cpus = logical_cpus; + } else { + data->num_cores = 1; + data->num_logical_cpus = (logical_cpus >= 1 ? logical_cpus : 1); + if (data->num_logical_cpus == 1) + data->flags[CPU_FEATURE_HT] = 0; + } + } else { + data->num_cores = data->num_logical_cpus = 1; + } +} + +static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) +{ + intel_code_t code = (intel_code_t) NC; + intel_code_and_bits_t result; + uint64_t bits = 0; + int i = 0; + const char* bs = data->brand_str; + const char* s; + const struct { intel_code_t c; const char *search; } matchtable[] = { + { PENTIUM_M, "Pentium(R) M" }, + { CORE_SOLO, "Pentium(R) Dual CPU" }, + { CORE_SOLO, "Pentium(R) Dual-Core" }, + { PENTIUM_D, "Pentium(R) D" }, + { CORE_SOLO, "Genuine Intel(R) CPU" }, + { CORE_SOLO, "Intel(R) Core(TM)" }, + { DIAMONDVILLE, "CPU [N ][23]## " }, + { SILVERTHORNE, "CPU Z" }, + { PINEVIEW, "CPU [ND][45]## " }, + { CEDARVIEW, "CPU [ND]#### " }, + }; + + const struct { uint64_t bit; const char* search; } bit_matchtable[] = { + { XEON_, "Xeon" }, + { _MP_, " MP" }, + { ATOM_, "Atom(TM) CPU" }, + { MOBILE_, "Mobile" }, + { CELERON_, "Celeron" }, + { PENTIUM_, "Pentium" }, + }; + + for (i = 0; i < COUNT_OF(bit_matchtable); i++) { + if (match_pattern(bs, bit_matchtable[i].search)) + bits |= bit_matchtable[i].bit; + } + + if ((i = match_pattern(bs, "Core(TM) [im][357]")) != 0) { + bits |= CORE_; + i--; + switch (bs[i + 9]) { + case 'i': bits |= _I_; break; + case 'm': bits |= _M_; break; + } + switch (bs[i + 10]) { + case '3': bits |= _3; break; + case '5': bits |= _5; break; + case '7': bits |= _7; break; + } + } + for (i = 0; i < COUNT_OF(matchtable); i++) + if (match_pattern(bs, matchtable[i].search)) { + code = matchtable[i].c; + break; + } + if (bits & XEON_) { + if (match_pattern(bs, "W35##") || match_pattern(bs, "[ELXW]75##")) + bits |= _7; + else if (match_pattern(bs, "[ELXW]55##")) + code = GAINESTOWN; + else if (match_pattern(bs, "[ELXW]56##")) + code = WESTMERE; + else if (data->l3_cache > 0 && data->family == 16) + /* restrict by family, since later Xeons also have L3 ... */ + code = IRWIN; + } + if (match_all(bits, XEON_ + _MP_) && data->l3_cache > 0) + code = POTOMAC; + if (code == CORE_SOLO) { + s = strstr(bs, "CPU"); + if (s) { + s += 3; + while (*s == ' ') s++; + if (*s == 'T') + bits |= MOBILE_; + } + } + if (code == CORE_SOLO) { + switch (data->num_cores) { + case 1: break; + case 2: + { + code = CORE_DUO; + if (data->num_logical_cpus > 2) + code = DUAL_CORE_HT; + break; + } + case 4: + { + code = QUAD_CORE; + if (data->num_logical_cpus > 4) + code = QUAD_CORE_HT; + break; + } + default: + code = MORE_THAN_QUADCORE; break; + } + } + + if (code == CORE_DUO && (bits & MOBILE_) && data->model != 14) { + if (data->ext_model < 23) { + code = MEROM; + } else { + code = PENRYN; + } + } + if (data->ext_model == 23 && + (code == CORE_DUO || code == PENTIUM_D || (bits & CELERON_))) { + code = WOLFDALE; + } + + result.code = code; + result.bits = bits; + return result; +} + +static void decode_intel_sgx_features(const struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + struct cpu_epc_t epc; + int i; + + if (raw->basic_cpuid[0][0] < 0x12) return; // no 12h leaf + if (raw->basic_cpuid[0x12][0] == 0) return; // no sub-leafs available, probably it's disabled by BIOS + + // decode sub-leaf 0: + if (raw->basic_cpuid[0x12][0] & 1) data->sgx.flags[INTEL_SGX1] = 1; + if (raw->basic_cpuid[0x12][0] & 2) data->sgx.flags[INTEL_SGX2] = 1; + if (data->sgx.flags[INTEL_SGX1] || data->sgx.flags[INTEL_SGX2]) + data->sgx.present = 1; + data->sgx.misc_select = raw->basic_cpuid[0x12][1]; + data->sgx.max_enclave_32bit = (raw->basic_cpuid[0x12][3] ) & 0xff; + data->sgx.max_enclave_64bit = (raw->basic_cpuid[0x12][3] >> 8) & 0xff; + + // decode sub-leaf 1: + data->sgx.secs_attributes = raw->intel_fn12h[1][0] | (((uint64_t) raw->intel_fn12h[1][1]) << 32); + data->sgx.secs_xfrm = raw->intel_fn12h[1][2] | (((uint64_t) raw->intel_fn12h[1][3]) << 32); + + // decode higher-order subleafs, whenever present: + data->sgx.num_epc_sections = -1; + for (i = 0; i < 1000000; i++) { + epc = cpuid_get_epc(i, raw); + if (epc.length == 0) { + data->sgx.num_epc_sections = i; + break; + } + } + if (data->sgx.num_epc_sections == -1) { + data->sgx.num_epc_sections = 1000000; + } +} + +struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw) +{ + uint32_t regs[4]; + struct cpu_epc_t retval = {0, 0}; + if (raw && index < MAX_INTELFN12H_LEVEL - 2) { + // this was queried already, use the data: + memcpy(regs, raw->intel_fn12h[2 + index], sizeof(regs)); + } else { + // query this ourselves: + regs[0] = 0x12; + regs[2] = 2 + index; + regs[1] = regs[3] = 0; + cpu_exec_cpuid_ext(regs); + } + + // decode values: + if ((regs[0] & 0xf) == 0x1) { + retval.start_addr |= (regs[0] & 0xfffff000); // bits [12, 32) -> bits [12, 32) + retval.start_addr |= ((uint64_t) (regs[1] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) + retval.length |= (regs[2] & 0xfffff000); // bits [12, 32) -> bits [12, 32) + retval.length |= ((uint64_t) (regs[3] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) + } + return retval; +} + +int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + intel_code_and_bits_t brand; + + load_intel_features(raw, data); + if (raw->basic_cpuid[0][0] >= 4) { + /* Deterministic way is preferred, being more generic */ + decode_intel_deterministic_cache_info(raw, data); + } else if (raw->basic_cpuid[0][0] >= 2) { + decode_intel_oldstyle_cache_info(raw, data); + } + decode_intel_number_of_cores(raw, data); + + brand = get_brand_code_and_bits(data); + + internal->code.intel = brand.code; + internal->bits = brand.bits; + + if (data->flags[CPU_FEATURE_SGX]) { + // if SGX is indicated by the CPU, verify its presence: + decode_intel_sgx_features(raw, data); + } + + return 0; +} diff --git a/compat/libcpuid/recog_intel.h b/src/3rdparty/libcpuid/recog_intel.h similarity index 96% rename from compat/libcpuid/recog_intel.h rename to src/3rdparty/libcpuid/recog_intel.h index b99c783b..96676f3b 100644 --- a/compat/libcpuid/recog_intel.h +++ b/src/3rdparty/libcpuid/recog_intel.h @@ -27,6 +27,5 @@ #define __RECOG_INTEL_H__ int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal); -void cpuid_get_list_intel(struct cpu_list_t* list); #endif /*__RECOG_INTEL_H__*/ diff --git a/src/3rdparty/rapidjson/allocators.h b/src/3rdparty/rapidjson/allocators.h new file mode 100644 index 00000000..98affe03 --- /dev/null +++ b/src/3rdparty/rapidjson/allocators.h @@ -0,0 +1,271 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/src/3rdparty/rapidjson/document.h b/src/3rdparty/rapidjson/document.h new file mode 100644 index 00000000..e3e20dfb --- /dev/null +++ b/src/3rdparty/rapidjson/document.h @@ -0,0 +1,2575 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include + +RAPIDJSON_DIAG_PUSH +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions +#endif +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast(std::numeric_limits::max())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast(std::numeric_limits::min())) + && (d < static_cast(std::numeric_limits::max())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-std::numeric_limits::max()) + || a > static_cast(std::numeric_limits::max())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); + } + } + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } +} + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { return value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/src/3rdparty/rapidjson/encodedstream.h b/src/3rdparty/rapidjson/encodedstream.h new file mode 100644 index 00000000..14506838 --- /dev/null +++ b/src/3rdparty/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/3rdparty/rapidjson/encodings.h b/src/3rdparty/rapidjson/encodings.h new file mode 100644 index 00000000..baa7c2b1 --- /dev/null +++ b/src/3rdparty/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFF >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, (v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/src/3rdparty/rapidjson/error/en.h b/src/3rdparty/rapidjson/error/en.h new file mode 100644 index 00000000..2db838bf --- /dev/null +++ b/src/3rdparty/rapidjson/error/en.h @@ -0,0 +1,74 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/src/3rdparty/rapidjson/error/error.h b/src/3rdparty/rapidjson/error/error.h new file mode 100644 index 00000000..95cb31a7 --- /dev/null +++ b/src/3rdparty/rapidjson/error/error.h @@ -0,0 +1,155 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/src/3rdparty/rapidjson/filereadstream.h b/src/3rdparty/rapidjson/filereadstream.h new file mode 100644 index 00000000..b56ea13b --- /dev/null +++ b/src/3rdparty/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/3rdparty/rapidjson/filewritestream.h b/src/3rdparty/rapidjson/filewritestream.h new file mode 100644 index 00000000..6378dd60 --- /dev/null +++ b/src/3rdparty/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/3rdparty/rapidjson/fwd.h b/src/3rdparty/rapidjson/fwd.h new file mode 100644 index 00000000..e8104e84 --- /dev/null +++ b/src/3rdparty/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/src/3rdparty/rapidjson/internal/biginteger.h b/src/3rdparty/rapidjson/internal/biginteger.h new file mode 100644 index 00000000..9d3e88c9 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/src/3rdparty/rapidjson/internal/diyfp.h b/src/3rdparty/rapidjson/internal/diyfp.h new file mode 100644 index 00000000..c9fefdc6 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/diyfp.h @@ -0,0 +1,258 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/src/3rdparty/rapidjson/internal/dtoa.h b/src/3rdparty/rapidjson/internal/dtoa.h new file mode 100644 index 00000000..8d6350e6 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -static_cast(kappa); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/src/3rdparty/rapidjson/internal/ieee754.h b/src/3rdparty/rapidjson/internal/ieee754.h new file mode 100644 index 00000000..82bb0b99 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return static_cast(order) + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/src/3rdparty/rapidjson/internal/itoa.h b/src/3rdparty/rapidjson/internal/itoa.h new file mode 100644 index 00000000..01a4e7e7 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/itoa.h @@ -0,0 +1,304 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/src/3rdparty/rapidjson/internal/meta.h b/src/3rdparty/rapidjson/internal/meta.h new file mode 100644 index 00000000..5a9aaa42 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/meta.h @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/src/3rdparty/rapidjson/internal/pow10.h b/src/3rdparty/rapidjson/internal/pow10.h new file mode 100644 index 00000000..02f475d7 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/src/3rdparty/rapidjson/internal/regex.h b/src/3rdparty/rapidjson/internal/regex.h new file mode 100644 index 00000000..422a5240 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/regex.h @@ -0,0 +1,701 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef typename Encoding::Ch Ch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream > ds(ss); + Parse(ds); + } + + ~GenericRegex() { + Allocator::Free(stateSet_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + + template + bool Match(InputStream& is) const { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) const { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) const { + return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); + } + + bool Search(const Ch* s) const { + GenericStringStream is(s); + return Search(is); + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + template + class DecodedStream { + public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + + private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + + // Preallocate buffer for SearchWithAnchoring() + RAPIDJSON_ASSERT(stateSet_ == 0); + if (stateCount_ > 0) { + stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); + state0_.template Reserve(stateCount_); + state1_.template Reserve(stateCount_); + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { + RAPIDJSON_ASSERT(IsValid()); + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == kAnyCharacterClass || + (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) const { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { + stateSet_[index >> 5] |= (1 << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = GetRange(rangeIndex); + if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + uint32_t* stateSet_; // allocated by states_.GetAllocator() + mutable Stack state0_; + mutable Stack state1_; + bool anchorBegin_; + bool anchorEnd_; +}; + +typedef GenericRegex > Regex; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/src/3rdparty/rapidjson/internal/stack.h b/src/3rdparty/rapidjson/internal/stack.h new file mode 100644 index 00000000..022c9aab --- /dev/null +++ b/src/3rdparty/rapidjson/internal/stack.h @@ -0,0 +1,230 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/src/3rdparty/rapidjson/internal/strfunc.h b/src/3rdparty/rapidjson/internal/strfunc.h new file mode 100644 index 00000000..2edfae52 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/strfunc.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/src/3rdparty/rapidjson/internal/strtod.h b/src/3rdparty/rapidjson/internal/strtod.h new file mode 100644 index 00000000..289c413b --- /dev/null +++ b/src/3rdparty/rapidjson/internal/strtod.h @@ -0,0 +1,269 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + static_cast(kUlp); + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= static_cast(delta); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/src/3rdparty/rapidjson/internal/swap.h b/src/3rdparty/rapidjson/internal/swap.h new file mode 100644 index 00000000..666e49f9 --- /dev/null +++ b/src/3rdparty/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/src/3rdparty/rapidjson/istreamwrapper.h b/src/3rdparty/rapidjson/istreamwrapper.h new file mode 100644 index 00000000..f5fe2897 --- /dev/null +++ b/src/3rdparty/rapidjson/istreamwrapper.h @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/src/3rdparty/rapidjson/memorybuffer.h b/src/3rdparty/rapidjson/memorybuffer.h new file mode 100644 index 00000000..39bee1de --- /dev/null +++ b/src/3rdparty/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/src/3rdparty/rapidjson/memorystream.h b/src/3rdparty/rapidjson/memorystream.h new file mode 100644 index 00000000..1d71d8a4 --- /dev/null +++ b/src/3rdparty/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/src/3rdparty/rapidjson/msinttypes/inttypes.h b/src/3rdparty/rapidjson/msinttypes/inttypes.h new file mode 100644 index 00000000..18111286 --- /dev/null +++ b/src/3rdparty/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/src/3rdparty/rapidjson/msinttypes/stdint.h b/src/3rdparty/rapidjson/msinttypes/stdint.h new file mode 100644 index 00000000..3d4477b9 --- /dev/null +++ b/src/3rdparty/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/src/3rdparty/rapidjson/ostreamwrapper.h b/src/3rdparty/rapidjson/ostreamwrapper.h new file mode 100644 index 00000000..6f4667c0 --- /dev/null +++ b/src/3rdparty/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/src/3rdparty/rapidjson/pointer.h b/src/3rdparty/rapidjson/pointer.h new file mode 100644 index 00000000..0206ac1c --- /dev/null +++ b/src/3rdparty/rapidjson/pointer.h @@ -0,0 +1,1358 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = buffer[i]; + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/src/3rdparty/rapidjson/prettywriter.h b/src/3rdparty/rapidjson/prettywriter.h new file mode 100644 index 00000000..0dcb0fee --- /dev/null +++ b/src/3rdparty/rapidjson/prettywriter.h @@ -0,0 +1,255 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/3rdparty/rapidjson/rapidjson.h b/src/3rdparty/rapidjson/rapidjson.h new file mode 100644 index 00000000..2ef9bc56 --- /dev/null +++ b/src/3rdparty/rapidjson/rapidjson.h @@ -0,0 +1,614 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && defined(_M_ARM) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#else +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#define RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/3rdparty/rapidjson/reader.h b/src/3rdparty/rapidjson/reader.h new file mode 100644 index 00000000..303aac2e --- /dev/null +++ b/src/3rdparty/rapidjson/reader.h @@ -0,0 +1,1879 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseCommentsFlag | kParseTrailingCommasFlag +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n'); + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + useNanOrInf = true; + if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { + d = std::numeric_limits::quiet_NaN(); + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState + }; + + enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/src/3rdparty/rapidjson/schema.h b/src/3rdparty/rapidjson/schema.h new file mode 100644 index 00000000..b182aa27 --- /dev/null +++ b/src/3rdparty/rapidjson/schema.h @@ -0,0 +1,2006 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = GetTypeless(); + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + if (allocator_) { + allocator_->Free(enum_); + } + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + allocator_->Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = GetTypeless(); + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = GetTypeless(); + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = GetTypeless(); + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + static const SchemaType* GetTypeless() { + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + return &typeless; + } + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + return pattern->Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) + /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = buffer[i]; + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = SchemaType::GetTypeless(); + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(outputHandler), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && outputHandler_.method arg2 + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = outputHandler_.StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = outputHandler_.Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = outputHandler_.StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + return StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + if (CurrentContext().valueSchema) + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static OutputHandler& GetNullHandler() { + static OutputHandler nullHandler; + return nullHandler; + } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/src/3rdparty/rapidjson/stream.h b/src/3rdparty/rapidjson/stream.h new file mode 100644 index 00000000..fef82c25 --- /dev/null +++ b/src/3rdparty/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/src/3rdparty/rapidjson/stringbuffer.h b/src/3rdparty/rapidjson/stringbuffer.h new file mode 100644 index 00000000..78f34d20 --- /dev/null +++ b/src/3rdparty/rapidjson/stringbuffer.h @@ -0,0 +1,117 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/src/3rdparty/rapidjson/writer.h b/src/3rdparty/rapidjson/writer.h new file mode 100644 index 00000000..94f22dd5 --- /dev/null +++ b/src/3rdparty/rapidjson/writer.h @@ -0,0 +1,610 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return EndValue(WriteRawValue(json, length)); } + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/App.cpp b/src/App.cpp new file mode 100644 index 00000000..1aae7ae0 --- /dev/null +++ b/src/App.cpp @@ -0,0 +1,221 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include + + +#include "api/Api.h" +#include "App.h" +#include "Console.h" +#include "Cpu.h" +#include "crypto/CryptoNight.h" +#include "log/ConsoleLog.h" +#include "log/FileLog.h" +#include "log/Log.h" +#include "Mem.h" +#include "net/Network.h" +#include "Options.h" +#include "Platform.h" +#include "Summary.h" +#include "version.h" +#include "workers/Workers.h" + + +#ifdef HAVE_SYSLOG_H +# include "log/SysLog.h" +#endif + +#ifndef XMRIG_NO_HTTPD +# include "api/Httpd.h" +#endif + + +App *App::m_self = nullptr; + + + +App::App(int argc, char **argv) : + m_console(nullptr), + m_httpd(nullptr), + m_network(nullptr), + m_options(nullptr) +{ + m_self = this; + + Cpu::init(); + m_options = Options::parse(argc, argv); + if (!m_options) { + return; + } + + Log::init(); + + 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 + + Platform::init(m_options->userAgent()); + Platform::setProcessPriority(m_options->priority()); + + m_network = new Network(m_options); + + uv_signal_init(uv_default_loop(), &m_signal); +} + + +App::~App() +{ + uv_tty_reset_mode(); + +# ifndef XMRIG_NO_HTTPD + delete m_httpd; +# endif + + delete m_console; +} + + +int App::exec() +{ + if (!m_options) { + return 0; + } + + uv_signal_start(&m_signal, App::onSignal, SIGHUP); + uv_signal_start(&m_signal, App::onSignal, SIGTERM); + uv_signal_start(&m_signal, App::onSignal, SIGINT); + + background(); + + if (!CryptoNight::init(m_options->algo(), m_options->algoVariant())) { + LOG_ERR("\"%s\" hash self-test failed.", m_options->algoName()); + return 1; + } + + Mem::allocate(m_options->algo(), m_options->threads(), m_options->doubleHash(), m_options->hugePages()); + Summary::print(); + +# ifndef XMRIG_NO_API + Api::start(); +# endif + +# ifndef XMRIG_NO_HTTPD + m_httpd = new Httpd(m_options->apiPort(), m_options->apiToken()); + m_httpd->start(); +# endif + + Workers::start(m_options->affinity(), m_options->priority()); + + m_network->connect(); + + const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + uv_loop_close(uv_default_loop()); + + delete m_network; + + Options::release(); + Mem::release(); + Platform::release(); + + return r; +} + + +void App::onConsoleCommand(char command) +{ + switch (command) { + case 'h': + case 'H': + Workers::printHashrate(true); + break; + + case 'p': + case 'P': + LOG_INFO(m_options->colors() ? "\x1B[01;33mpaused\x1B[0m, press \x1B[01;35mr\x1B[0m to resume" : "paused, press 'r' to resume"); + Workers::setEnabled(false); + break; + + case 'r': + case 'R': + if (!Workers::isEnabled()) { + LOG_INFO(m_options->colors() ? "\x1B[01;32mresumed" : "resumed"); + Workers::setEnabled(true); + } + break; + + case 3: + LOG_WARN("Ctrl+C received, exiting"); + close(); + break; + + default: + break; + } +} + + +void App::close() +{ + m_network->stop(); + Workers::stop(); + + uv_stop(uv_default_loop()); +} + + +void App::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->close(); +} diff --git a/src/App.h b/src/App.h new file mode 100644 index 00000000..781f78f2 --- /dev/null +++ b/src/App.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __APP_H__ +#define __APP_H__ + + +#include + + +#include "interfaces/IConsoleListener.h" + + +class Console; +class Httpd; +class Network; +class Options; + + +class App : public IConsoleListener +{ +public: + App(int argc, char **argv); + ~App(); + + int exec(); + +protected: + void onConsoleCommand(char command) override; + +private: + void background(); + void close(); + + static void onSignal(uv_signal_t *handle, int signum); + + static App *m_self; + + Console *m_console; + Httpd *m_httpd; + Network *m_network; + Options *m_options; + uv_signal_t m_signal; +}; + + +#endif /* __APP_H__ */ diff --git a/src/App_unix.cpp b/src/App_unix.cpp new file mode 100644 index 00000000..66957208 --- /dev/null +++ b/src/App_unix.cpp @@ -0,0 +1,66 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include + + +#include "App.h" +#include "Cpu.h" +#include "log/Log.h" +#include "Options.h" + + +void App::background() +{ + if (m_options->affinity() != -1L) { + Cpu::setAffinity(-1, m_options->affinity()); + } + + if (!m_options->background()) { + return; + } + + int i = fork(); + if (i < 0) { + exit(1); + } + + if (i > 0) { + exit(0); + } + + i = setsid(); + + if (i < 0) { + LOG_ERR("setsid() failed (errno = %d)", errno); + } + + i = chdir("/"); + if (i < 0) { + LOG_ERR("chdir() failed (errno = %d)", errno); + } +} diff --git a/util.h b/src/App_win.cpp similarity index 68% rename from util.h rename to src/App_win.cpp index 47ab8904..895f3bdf 100644 --- a/util.h +++ b/src/App_win.cpp @@ -21,23 +21,32 @@ * along with this program. If not, see . */ -#ifndef __UTIL_H__ -#define __UTIL_H__ -#include +#include +#include -json_t *json_decode(const char *s); - -char *bin2hex(const unsigned char *p, size_t len); -bool hex2bin(unsigned char *p, const char *hexstr, size_t len); - -struct thread_q *tq_new(void); -void tq_free(struct thread_q *tq); -bool tq_push(struct thread_q *tq, void *data); -void *tq_pop(struct thread_q *tq, const struct timespec *abstime); -void tq_freeze(struct thread_q *tq); -void tq_thaw(struct thread_q *tq); +#include "App.h" +#include "Options.h" +#include "Cpu.h" -#endif /* __UTIL_H__ */ +void App::background() +{ + if (m_options->affinity() != -1L) { + Cpu::setAffinity(-1, m_options->affinity()); + } + + if (!m_options->background()) { + return; + } + + HWND hcon = GetConsoleWindow(); + if (hcon) { + ShowWindow(hcon, SW_HIDE); + } else { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + CloseHandle(h); + FreeConsole(); + } +} diff --git a/src/Console.cpp b/src/Console.cpp new file mode 100644 index 00000000..3d95ada4 --- /dev/null +++ b/src/Console.cpp @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "Console.h" +#include "interfaces/IConsoleListener.h" + + +Console::Console(IConsoleListener *listener) + : m_listener(listener) +{ + m_tty.data = this; + uv_tty_init(uv_default_loop(), &m_tty, 0, 1); + + if (!uv_is_readable(reinterpret_cast(&m_tty))) { + return; + } + + uv_tty_set_mode(&m_tty, UV_TTY_MODE_RAW); + uv_read_start(reinterpret_cast(&m_tty), Console::onAllocBuffer, Console::onRead); +} + + +void Console::onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) +{ + auto console = static_cast(handle->data); + buf->len = 1; + buf->base = console->m_buf; +} + + +void Console::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) +{ + if (nread < 0) { + return uv_close(reinterpret_cast(stream), nullptr); + } + + if (nread == 1) { + static_cast(stream->data)->m_listener->onConsoleCommand(buf->base[0]); + } +} diff --git a/compat.h b/src/Console.h similarity index 71% rename from compat.h rename to src/Console.h index bf488226..bde95d7d 100644 --- a/compat.h +++ b/src/Console.h @@ -21,27 +21,29 @@ * along with this program. If not, see . */ -#ifndef __COMPAT_H__ -#define __COMPAT_H__ +#ifndef __CONSOLE_H__ +#define __CONSOLE_H__ -#define unlikely(expr) (__builtin_expect(!!(expr), 0)) -#define likely(expr) (__builtin_expect(!!(expr), 1)) -#ifdef WIN32 +#include -#include -#define sleep(secs) Sleep((secs) * 1000) +class IConsoleListener; -enum { - PRIO_PROCESS = 0, + +class Console +{ +public: + Console(IConsoleListener *listener); + +private: + static void onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); + static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); + + char m_buf[1]; + IConsoleListener *m_listener; + uv_tty_t m_tty; }; -static inline int setpriority(int which, int who, int prio) -{ - return -!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); -} -#endif /* WIN32 */ - -#endif /* __COMPAT_H__ */ +#endif /* __CONSOLE_H__ */ diff --git a/src/Cpu.cpp b/src/Cpu.cpp new file mode 100644 index 00000000..ff6f49e9 --- /dev/null +++ b/src/Cpu.cpp @@ -0,0 +1,118 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + +#include "Cpu.h" + + +bool Cpu::m_l2_exclusive = false; +char Cpu::m_brand[64] = { 0 }; +int Cpu::m_flags = 0; +int Cpu::m_l2_cache = 0; +int Cpu::m_l3_cache = 0; +int Cpu::m_sockets = 1; +int Cpu::m_totalCores = 0; +int Cpu::m_totalThreads = 0; + + +int Cpu::optimalThreadsCount(int algo, bool doubleHash, int maxCpuUsage) +{ + if (m_totalThreads == 1) { + return 1; + } + + int cache = 0; + if (m_l3_cache) { + cache = m_l2_exclusive ? (m_l2_cache + m_l3_cache) : m_l3_cache; + } + else { + cache = m_l2_cache; + } + + int count = 0; + const int size = (algo ? 1024 : 2048) * (doubleHash ? 2 : 1); + + if (cache) { + count = cache / size; + } + else { + count = m_totalThreads / 2; + } + + if (count > m_totalThreads) { + count = m_totalThreads; + } + + if (((float) count / m_totalThreads * 100) > maxCpuUsage) { + count = (int) ceil((float) m_totalThreads * (maxCpuUsage / 100.0)); + } + + return count < 1 ? 1 : count; +} + + +void Cpu::initCommon() +{ + struct cpu_raw_data_t raw = { 0 }; + struct cpu_id_t data = { 0 }; + + cpuid_get_raw_data(&raw); + cpu_identify(&raw, &data); + + strncpy(m_brand, data.brand_str, sizeof(m_brand) - 1); + + m_totalThreads = data.total_logical_cpus; + m_sockets = m_totalThreads / data.num_logical_cpus; + + if (m_sockets == 0) { + m_sockets = 1; + } + + m_totalCores = data.num_cores * m_sockets; + m_l3_cache = data.l3_cache > 0 ? data.l3_cache * m_sockets : 0; + + // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 + if (data.vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { + m_l2_cache = data.l2_cache * (m_totalCores / 2) * m_sockets; + m_l2_exclusive = true; + } + else { + m_l2_cache = data.l2_cache > 0 ? data.l2_cache * m_totalCores * m_sockets : 0; + } + +# if defined(__x86_64__) || defined(_M_AMD64) + m_flags |= X86_64; +# endif + + if (data.flags[CPU_FEATURE_AES]) { + m_flags |= AES; + } + + if (data.flags[CPU_FEATURE_BMI2]) { + m_flags |= BMI2; + } +} diff --git a/src/Cpu.h b/src/Cpu.h new file mode 100644 index 00000000..9444274d --- /dev/null +++ b/src/Cpu.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CPU_H__ +#define __CPU_H__ + + +#include + + +class Cpu +{ +public: + enum Flags { + X86_64 = 1, + AES = 2, + BMI2 = 4 + }; + + static int optimalThreadsCount(int algo, bool doubleHash, int maxCpuUsage); + static void init(); + static void setAffinity(int id, uint64_t mask); + + static inline bool hasAES() { return (m_flags & AES) != 0; } + static inline bool isX64() { return (m_flags & X86_64) != 0; } + static inline const char *brand() { return m_brand; } + static inline int cores() { return m_totalCores; } + static inline int l2() { return m_l2_cache; } + static inline int l3() { return m_l3_cache; } + static inline int sockets() { return m_sockets; } + static inline int threads() { return m_totalThreads; } + +private: + static void initCommon(); + + static bool m_l2_exclusive; + static char m_brand[64]; + static int m_flags; + static int m_l2_cache; + static int m_l3_cache; + static int m_sockets; + static int m_totalCores; + static int m_totalThreads; +}; + + +#endif /* __CPU_H__ */ diff --git a/mac/cpu_mac.c b/src/Cpu_mac.cpp similarity index 82% rename from mac/cpu_mac.c rename to src/Cpu_mac.cpp index 88bda541..357e15ef 100644 --- a/mac/cpu_mac.c +++ b/src/Cpu_mac.cpp @@ -21,27 +21,25 @@ * along with this program. If not, see . */ -#include -#include + #include - -#include "cpu.h" +#include +#include -struct cpu_info cpu_info = { 0 }; -void cpu_init_common(); +#include "Cpu.h" -void cpu_init() { -# ifdef XMRIG_NO_LIBCPUID - cpu_info.total_logical_cpus = sysconf(_SC_NPROCESSORS_CONF); -# endif - - cpu_init_common(); -} - - -int affine_to_cpu_mask(int id, unsigned long mask) +void Cpu::init() +{ +# ifdef XMRIG_NO_LIBCPUID + m_totalThreads = sysconf(_SC_NPROCESSORS_CONF); +# endif + + initCommon(); +} + + +void Cpu::setAffinity(int id, uint64_t mask) { - return 0; } diff --git a/cpu_stub.c b/src/Cpu_stub.cpp similarity index 72% rename from cpu_stub.c rename to src/Cpu_stub.cpp index 83d5efc3..0b9196ee 100644 --- a/cpu_stub.c +++ b/src/Cpu_stub.cpp @@ -21,10 +21,20 @@ * along with this program. If not, see . */ -#include + +#ifdef _MSC_VER +# include + +# define bit_AES (1 << 25) +# define bit_BMI2 (1 << 8) +#else +# include +#endif + #include -#include -#include "cpu.h" + + +#include "Cpu.h" #define VENDOR_ID (0) @@ -41,6 +51,11 @@ #define EDX_Reg (3) +#ifdef _MSC_VER +static inline void cpuid(int level, int output[4]) { + __cpuid(output, level); +} +#else static inline void cpuid(int level, int output[4]) { int a, b, c, d; __cpuid_count(level, 0, a, b, c, d); @@ -50,9 +65,10 @@ static inline void cpuid(int level, int output[4]) { output[2] = c; output[3] = d; } +#endif -static void cpu_brand_string(char* s) { +static inline void cpu_brand_string(char* s) { int cpu_info[4] = { 0 }; cpuid(VENDOR_ID, cpu_info); @@ -66,7 +82,7 @@ static void cpu_brand_string(char* s) { } -static bool has_aes_ni() +static inline bool has_aes_ni() { int cpu_info[4] = { 0 }; cpuid(PROCESSOR_INFO, cpu_info); @@ -75,7 +91,7 @@ static bool has_aes_ni() } -static bool has_bmi2() { +static inline bool has_bmi2() { int cpu_info[4] = { 0 }; cpuid(EXTENDED_FEATURES, cpu_info); @@ -83,25 +99,35 @@ static bool has_bmi2() { } -void cpu_init_common() { - cpu_info.sockets = 1; - cpu_brand_string(cpu_info.brand); +char Cpu::m_brand[64] = { 0 }; +int Cpu::m_flags = 0; +int Cpu::m_l2_cache = 0; +int Cpu::m_l3_cache = 0; +int Cpu::m_sockets = 1; +int Cpu::m_totalCores = 0; +int Cpu::m_totalThreads = 0; -# ifdef __x86_64__ - cpu_info.flags |= CPU_FLAG_X86_64; + +int Cpu::optimalThreadsCount(int algo, bool doubleHash, int maxCpuUsage) +{ + int count = m_totalThreads / 2; + return count < 1 ? 1 : count; +} + + +void Cpu::initCommon() +{ + cpu_brand_string(m_brand); + +# if defined(__x86_64__) || defined(_M_AMD64) + m_flags |= X86_64; # endif if (has_aes_ni()) { - cpu_info.flags |= CPU_FLAG_AES; + m_flags |= AES; } if (has_bmi2()) { - cpu_info.flags |= CPU_FLAG_BMI2; + m_flags |= BMI2; } } - - -int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage) { - int count = cpu_info.total_logical_cpus / 2; - return count < 1 ? 1 : count; -} diff --git a/unix/cpu_unix.c b/src/Cpu_unix.cpp similarity index 76% rename from unix/cpu_unix.c rename to src/Cpu_unix.cpp index 05b859b2..8de98c8c 100644 --- a/unix/cpu_unix.c +++ b/src/Cpu_unix.cpp @@ -21,39 +21,54 @@ * along with this program. If not, see . */ -#include -#include + +#ifdef __FreeBSD__ +# include +# include +# include +# include +#endif + + #include - -#include "cpu.h" +#include +#include +#include -struct cpu_info cpu_info = { 0 }; -void cpu_init_common(); +#include "Cpu.h" -void cpu_init() { +#ifdef __FreeBSD__ +typedef cpuset_t cpu_set_t; +#endif + + +void Cpu::init() +{ # ifdef XMRIG_NO_LIBCPUID - cpu_info.total_logical_cpus = sysconf(_SC_NPROCESSORS_CONF); + m_totalThreads = sysconf(_SC_NPROCESSORS_CONF); # endif - cpu_init_common(); + initCommon(); } -int affine_to_cpu_mask(int id, unsigned long mask) +void Cpu::setAffinity(int id, uint64_t mask) { cpu_set_t set; CPU_ZERO(&set); - for (unsigned i = 0; i < cpu_info.total_logical_cpus; i++) { + for (int i = 0; i < m_totalThreads; i++) { if (mask & (1UL << i)) { CPU_SET(i, &set); } } if (id == -1) { +# ifndef __FreeBSD__ sched_setaffinity(0, sizeof(&set), &set); +# endif } else { pthread_setaffinity_np(pthread_self(), sizeof(&set), &set); } diff --git a/win/cpu_win.c b/src/Cpu_win.cpp similarity index 84% rename from win/cpu_win.c rename to src/Cpu_win.cpp index 6b5cc7fa..13113a17 100644 --- a/win/cpu_win.c +++ b/src/Cpu_win.cpp @@ -21,29 +21,27 @@ * along with this program. If not, see . */ + #include -#include - -#include "cpu.h" -struct cpu_info cpu_info = { 0 }; -void cpu_init_common(); +#include "Cpu.h" -void cpu_init() { +void Cpu::init() +{ # ifdef XMRIG_NO_LIBCPUID SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); - cpu_info.total_logical_cpus = sysinfo.dwNumberOfProcessors; + m_totalThreads = sysinfo.dwNumberOfProcessors; # endif - cpu_init_common(); + initCommon(); } -int affine_to_cpu_mask(int id, unsigned long mask) +void Cpu::setAffinity(int id, uint64_t mask) { if (id == -1) { SetProcessAffinityMask(GetCurrentProcess(), mask); diff --git a/memory.c b/src/Mem.cpp similarity index 51% rename from memory.c rename to src/Mem.cpp index 112f1115..4437c673 100644 --- a/memory.c +++ b/src/Mem.cpp @@ -21,38 +21,45 @@ * along with this program. If not, see . */ -#include -#include "persistent_memory.h" -#include "algo/cryptonight/cryptonight.h" -#include "options.h" - -static size_t offset = 0; +#include -#ifndef XMRIG_NO_AEON -static void * create_persistent_ctx_lite(int thr_id) { - struct cryptonight_ctx *ctx = NULL; +#include "crypto/CryptoNight.h" +#include "Mem.h" +#include "Options.h" - if (!opt_double_hash) { - const size_t offset = MEMORY * (thr_id + 1); - ctx = (struct cryptonight_ctx *) &persistent_memory[offset + MEMORY_LITE]; - ctx->memory = (uint8_t*) &persistent_memory[offset]; - return ctx; +bool Mem::m_doubleHash = false; +int Mem::m_algo = 0; +int Mem::m_flags = 0; +int Mem::m_threads = 0; +size_t Mem::m_offset = 0; +uint8_t *Mem::m_memory = nullptr; + + +cryptonight_ctx *Mem::create(int threadId) +{ +# ifndef XMRIG_NO_AEON + if (m_algo == Options::ALGO_CRYPTONIGHT_LITE) { + return createLite(threadId); } +# endif - ctx = (struct cryptonight_ctx *) &persistent_memory[MEMORY - sizeof(struct cryptonight_ctx) * (thr_id + 1)]; - ctx->memory = (uint8_t*) &persistent_memory[MEMORY * (thr_id + 1)]; + cryptonight_ctx *ctx = reinterpret_cast(&m_memory[MEMORY - sizeof(cryptonight_ctx) * (threadId + 1)]); + + const int ratio = m_doubleHash ? 2 : 1; + ctx->memory = &m_memory[MEMORY * (threadId * ratio + 1)]; return ctx; } -#endif -void * persistent_calloc(size_t num, size_t size) { - void *mem = &persistent_memory[offset]; - offset += (num * size); + +void *Mem::calloc(size_t num, size_t size) +{ + void *mem = &m_memory[m_offset]; + m_offset += (num * size); memset(mem, 0, num * size); @@ -60,17 +67,21 @@ void * persistent_calloc(size_t num, size_t size) { } -void * create_persistent_ctx(int thr_id) { -# ifndef XMRIG_NO_AEON - if (opt_algo == ALGO_CRYPTONIGHT_LITE) { - return create_persistent_ctx_lite(thr_id); +#ifndef XMRIG_NO_AEON +cryptonight_ctx *Mem::createLite(int threadId) { + cryptonight_ctx *ctx; + + if (!m_doubleHash) { + const size_t offset = MEMORY * (threadId + 1); + + ctx = reinterpret_cast(&m_memory[offset + MEMORY_LITE]); + ctx->memory = &m_memory[offset]; + return ctx; } -# endif - struct cryptonight_ctx *ctx = (struct cryptonight_ctx *) &persistent_memory[MEMORY - sizeof(struct cryptonight_ctx) * (thr_id + 1)]; - - const int ratio = opt_double_hash ? 2 : 1; - ctx->memory = (uint8_t*) &persistent_memory[MEMORY * (thr_id * ratio + 1)]; + ctx = reinterpret_cast(&m_memory[MEMORY - sizeof(cryptonight_ctx) * (threadId + 1)]); + ctx->memory = &m_memory[MEMORY * (threadId + 1)]; return ctx; } +#endif diff --git a/src/Mem.h b/src/Mem.h new file mode 100644 index 00000000..58dba848 --- /dev/null +++ b/src/Mem.h @@ -0,0 +1,72 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MEM_H__ +#define __MEM_H__ + + +#include +#include + + +#include "align.h" + + +struct cryptonight_ctx; + + +class Mem +{ +public: + enum Flags { + HugepagesAvailable = 1, + HugepagesEnabled = 2, + Lock = 4 + }; + + static bool allocate(int algo, int threads, bool doubleHash, bool enabled); + static cryptonight_ctx *create(int threadId); + static void *calloc(size_t num, size_t size); + static void release(); + + static inline bool isDoubleHash() { return m_doubleHash; } + static inline bool isHugepagesAvailable() { return (m_flags & HugepagesAvailable) != 0; } + static inline bool isHugepagesEnabled() { return (m_flags & HugepagesEnabled) != 0; } + static inline int flags() { return m_flags; } + static inline int threads() { return m_threads; } + +private: + static bool m_doubleHash; + static int m_algo; + static int m_flags; + static int m_threads; + static size_t m_offset; + VAR_ALIGN(16, static uint8_t *m_memory); + +# ifndef XMRIG_NO_AEON + static cryptonight_ctx *createLite(int threadId); +# endif +}; + + +#endif /* __MEM_H__ */ diff --git a/src/Mem_unix.cpp b/src/Mem_unix.cpp new file mode 100644 index 00000000..2cb14bc9 --- /dev/null +++ b/src/Mem_unix.cpp @@ -0,0 +1,92 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "crypto/CryptoNight.h" +#include "log/Log.h" +#include "Mem.h" +#include "Options.h" + + +bool Mem::allocate(int algo, int threads, bool doubleHash, bool enabled) +{ + m_algo = algo; + m_threads = threads; + m_doubleHash = doubleHash; + + const int ratio = (doubleHash && algo != Options::ALGO_CRYPTONIGHT_LITE) ? 2 : 1; + const size_t size = MEMORY * (threads * ratio + 1); + + if (!enabled) { + m_memory = static_cast(_mm_malloc(size, 16)); + return true; + } + + m_flags |= HugepagesAvailable; + +# if defined(__APPLE__) + m_memory = static_cast(mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0)); +# elif defined(__FreeBSD__) + m_memory = static_cast(mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER | MAP_PREFAULT_READ, -1, 0)); +# else + m_memory = static_cast(mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0)); +# endif + if (m_memory == MAP_FAILED) { + m_memory = static_cast(_mm_malloc(size, 16)); + return true; + } + + m_flags |= HugepagesEnabled; + + if (madvise(m_memory, size, MADV_RANDOM | MADV_WILLNEED) != 0) { + LOG_ERR("madvise failed"); + } + + if (mlock(m_memory, size) == 0) { + m_flags |= Lock; + } + + return true; +} + + +void Mem::release() +{ + const int size = MEMORY * (m_threads + 1); + + if (m_flags & HugepagesEnabled) { + if (m_flags & Lock) { + munlock(m_memory, size); + } + + munmap(m_memory, size); + } + else { + _mm_free(m_memory); + } +} diff --git a/win/memory_win.c b/src/Mem_win.cpp similarity index 77% rename from win/memory_win.c rename to src/Mem_win.cpp index 9ff63dad..a4f92cfe 100644 --- a/win/memory_win.c +++ b/src/Mem_win.cpp @@ -21,20 +21,22 @@ * along with this program. If not, see . */ -#ifndef __MEMORY_H__ -#define __MEMORY_H__ +#include #include #include #include -#include "options.h" -#include "persistent_memory.h" -#include "utils/applog.h" +#ifdef __GNUC__ +# include +#else +# include +#endif - -char *persistent_memory; -int persistent_memory_flags = 0; +#include "log/Log.h" +#include "crypto/CryptoNight.h" +#include "Mem.h" +#include "Options.h" /***************************************************************** @@ -83,9 +85,8 @@ static BOOL SetLockPagesPrivilege() { static LSA_UNICODE_STRING StringToLsaUnicodeString(LPCTSTR string) { LSA_UNICODE_STRING lsaString; - DWORD dwLen = 0; - dwLen = wcslen(string); + DWORD dwLen = (DWORD) wcslen(string); lsaString.Buffer = (LPWSTR) string; lsaString.Length = (USHORT)((dwLen) * sizeof(WCHAR)); lsaString.MaximumLength = (USHORT)((dwLen + 1) * sizeof(WCHAR)); @@ -122,7 +123,7 @@ static BOOL ObtainLockPagesPrivilege() { LSA_UNICODE_STRING str = StringToLsaUnicodeString(_T(SE_LOCK_MEMORY_NAME)); if (LsaAddAccountRights(handle, user->User.Sid, &str, 1) == 0) { - applog_notime(LOG_WARNING, "Huge pages support was successfully enabled, but reboot required to use it"); + LOG_NOTICE("Huge pages support was successfully enabled, but reboot required to use it"); result = TRUE; } @@ -143,33 +144,42 @@ static BOOL TrySetLockPagesPrivilege() { } -const char * persistent_memory_allocate() { - const int ratio = (opt_double_hash && opt_algo != ALGO_CRYPTONIGHT_LITE) ? 2 : 1; - const int size = MEMORY * (opt_n_threads * ratio + 1); +bool Mem::allocate(int algo, int threads, bool doubleHash, bool enabled) +{ + m_algo = algo; + m_threads = threads; + m_doubleHash = doubleHash; + + const int ratio = (doubleHash && algo != Options::ALGO_CRYPTONIGHT_LITE) ? 2 : 1; + const size_t size = MEMORY * (threads * ratio + 1); + + if (!enabled) { + m_memory = static_cast(_mm_malloc(size, 16)); + return true; + } if (TrySetLockPagesPrivilege()) { - persistent_memory_flags |= MEMORY_HUGEPAGES_AVAILABLE; + m_flags |= HugepagesAvailable; } - persistent_memory = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); - if (!persistent_memory) { - persistent_memory = _mm_malloc(size, 16); + m_memory = static_cast(VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE)); + if (!m_memory) { + m_memory = static_cast(_mm_malloc(size, 16)); } else { - persistent_memory_flags |= MEMORY_HUGEPAGES_ENABLED; + m_flags |= HugepagesEnabled; } - return persistent_memory; + return true; } -void persistent_memory_free() { - if (persistent_memory_flags & MEMORY_HUGEPAGES_ENABLED) { - VirtualFree(persistent_memory, 0, MEM_RELEASE); +void Mem::release() +{ + if (m_flags & HugepagesEnabled) { + VirtualFree(m_memory, 0, MEM_RELEASE); } else { - _mm_free(persistent_memory); + _mm_free(m_memory); } } - -#endif /* __MEMORY_H__ */ diff --git a/src/Options.cpp b/src/Options.cpp new file mode 100644 index 00000000..b1197223 --- /dev/null +++ b/src/Options.cpp @@ -0,0 +1,729 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include + + +#ifdef _MSC_VER +# include "getopt/getopt.h" +#else +# include +#endif + + +#include "Cpu.h" +#include "donate.h" +#include "net/Url.h" +#include "Options.h" +#include "Platform.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/filereadstream.h" +#include "version.h" + + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + + +Options *Options::m_self = nullptr; + + +static char const usage[] = "\ +Usage: " APP_ID " [OPTIONS]\n\ +Options:\n\ + -a, --algo=ALGO cryptonight (default) or cryptonight-lite\n\ + -o, --url=URL URL of mining server\n\ + -O, --userpass=U:P username:password pair for mining server\n\ + -u, --user=USERNAME username for mining server\n\ + -p, --pass=PASSWORD password for mining server\n\ + -t, --threads=N number of miner threads\n\ + -v, --av=N algorithm variation, 0 auto select\n\ + -k, --keepalive send keepalived for prevent timeout (need pool support)\n\ + -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ + -R, --retry-pause=N time to pause between retries (default: 5)\n\ + --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ + --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n\ + --no-huge-pages disable huge pages support\n\ + --no-color disable colored output\n\ + --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ + --user-agent set custom user-agent string for pool\n\ + -B, --background run the miner in the background\n\ + -c, --config=FILE load a JSON-format configuration file\n\ + -l, --log-file=FILE log all output to a file\n" +# ifdef HAVE_SYSLOG_H +"\ + -S, --syslog use system log for output messages\n" +# endif +"\ + --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75)\n\ + --safe safe adjust threads and av settings for current CPU\n\ + --nicehash enable nicehash/xmrig-proxy support\n\ + --print-time=N print hashrate report every N seconds\n\ + --api-port=N port for the miner API\n\ + --api-access-token=T access token for API\n\ + --api-worker-id=ID custom worker-id for API\n\ + -h, --help display this help and exit\n\ + -V, --version output version information and exit\n\ +"; + + +static char const short_options[] = "a:c:khBp:Px:r:R:s:t:T:o:u:O:v:Vl:S"; + + +static struct option const options[] = { + { "algo", 1, nullptr, 'a' }, + { "av", 1, nullptr, 'v' }, + { "background", 0, nullptr, 'B' }, + { "config", 1, nullptr, 'c' }, + { "cpu-affinity", 1, nullptr, 1020 }, + { "cpu-priority", 1, nullptr, 1021 }, + { "donate-level", 1, nullptr, 1003 }, + { "help", 0, nullptr, 'h' }, + { "keepalive", 0, nullptr ,'k' }, + { "log-file", 1, nullptr, 'l' }, + { "max-cpu-usage", 1, nullptr, 1004 }, + { "nicehash", 0, nullptr, 1006 }, + { "no-color", 0, nullptr, 1002 }, + { "no-huge-pages", 0, nullptr, 1009 }, + { "pass", 1, nullptr, 'p' }, + { "print-time", 1, nullptr, 1007 }, + { "retries", 1, nullptr, 'r' }, + { "retry-pause", 1, nullptr, 'R' }, + { "safe", 0, nullptr, 1005 }, + { "syslog", 0, nullptr, 'S' }, + { "threads", 1, nullptr, 't' }, + { "url", 1, nullptr, 'o' }, + { "user", 1, nullptr, 'u' }, + { "user-agent", 1, nullptr, 1008 }, + { "userpass", 1, nullptr, 'O' }, + { "version", 0, nullptr, 'V' }, + { "api-port", 1, nullptr, 4000 }, + { "api-access-token", 1, nullptr, 4001 }, + { "api-worker-id", 1, nullptr, 4002 }, + { 0, 0, 0, 0 } +}; + + +static struct option const config_options[] = { + { "algo", 1, nullptr, 'a' }, + { "av", 1, nullptr, 'v' }, + { "background", 0, nullptr, 'B' }, + { "colors", 0, nullptr, 2000 }, + { "cpu-affinity", 1, nullptr, 1020 }, + { "cpu-priority", 1, nullptr, 1021 }, + { "donate-level", 1, nullptr, 1003 }, + { "huge-pages", 0, nullptr, 1009 }, + { "log-file", 1, nullptr, 'l' }, + { "max-cpu-usage", 1, nullptr, 1004 }, + { "print-time", 1, nullptr, 1007 }, + { "retries", 1, nullptr, 'r' }, + { "retry-pause", 1, nullptr, 'R' }, + { "safe", 0, nullptr, 1005 }, + { "syslog", 0, nullptr, 'S' }, + { "threads", 1, nullptr, 't' }, + { "user-agent", 1, nullptr, 1008 }, + { 0, 0, 0, 0 } +}; + + +static struct option const pool_options[] = { + { "url", 1, nullptr, 'o' }, + { "pass", 1, nullptr, 'p' }, + { "user", 1, nullptr, 'u' }, + { "userpass", 1, nullptr, 'O' }, + { "keepalive", 0, nullptr ,'k' }, + { "nicehash", 0, nullptr, 1006 }, + { 0, 0, 0, 0 } +}; + + +static struct option const api_options[] = { + { "port", 1, nullptr, 4000 }, + { "access-token", 1, nullptr, 4001 }, + { "worker-id", 1, nullptr, 4002 }, + { 0, 0, 0, 0 } +}; + + +static const char *algo_names[] = { + "cryptonight", +# ifndef XMRIG_NO_AEON + "cryptonight-lite" +# endif +}; + + +Options *Options::parse(int argc, char **argv) +{ + Options *options = new Options(argc, argv); + if (options->isReady()) { + m_self = options; + return m_self; + } + + delete options; + return nullptr; +} + + +const char *Options::algoName() const +{ + return algo_names[m_algo]; +} + + +Options::Options(int argc, char **argv) : + m_background(false), + m_colors(true), + m_doubleHash(false), + m_hugePages(true), + m_ready(false), + m_safe(false), + m_syslog(false), + m_apiToken(nullptr), + m_apiWorkerId(nullptr), + m_logFile(nullptr), + m_userAgent(nullptr), + m_algo(0), + m_algoVariant(0), + m_apiPort(0), + m_donateLevel(kDonateLevel), + m_maxCpuUsage(75), + m_printTime(60), + m_priority(-1), + m_retries(5), + m_retryPause(5), + m_threads(0), + m_affinity(-1L) +{ + m_pools.push_back(new Url()); + + int key; + + while (1) { + key = getopt_long(argc, argv, short_options, options, NULL); + if (key < 0) { + break; + } + + if (!parseArg(key, optarg)) { + return; + } + } + + if (optind < argc) { + fprintf(stderr, "%s: unsupported non-option argument '%s'\n", argv[0], argv[optind]); + return; + } + + if (!m_pools[0]->isValid()) { + parseConfig(Platform::defaultConfigName()); + } + + if (!m_pools[0]->isValid()) { + fprintf(stderr, "No pool URL supplied. Exiting.\n"); + return; + } + + m_algoVariant = getAlgoVariant(); + if (m_algoVariant == AV2_AESNI_DOUBLE || m_algoVariant == AV4_SOFT_AES_DOUBLE) { + m_doubleHash = true; + } + + if (!m_threads) { + m_threads = Cpu::optimalThreadsCount(m_algo, m_doubleHash, m_maxCpuUsage); + } + else if (m_safe) { + const int count = Cpu::optimalThreadsCount(m_algo, m_doubleHash, m_maxCpuUsage); + if (m_threads > count) { + m_threads = count; + } + } + + for (Url *url : m_pools) { + url->applyExceptions(); + } + + m_ready = true; +} + + +Options::~Options() +{ +} + + +bool Options::getJSON(const char *fileName, rapidjson::Document &doc) +{ + uv_fs_t req; + const int fd = uv_fs_open(uv_default_loop(), &req, fileName, O_RDONLY, 0644, nullptr); + if (fd < 0) { + fprintf(stderr, "unable to open %s: %s\n", fileName, uv_strerror(fd)); + return false; + } + + uv_fs_req_cleanup(&req); + + FILE *fp = fdopen(fd, "rb"); + char buf[8192]; + rapidjson::FileReadStream is(fp, buf, sizeof(buf)); + + doc.ParseStream(is); + + uv_fs_close(uv_default_loop(), &req, fd, nullptr); + uv_fs_req_cleanup(&req); + + if (doc.HasParseError()) { + printf("%s:%d: %s\n", fileName, (int) doc.GetErrorOffset(), rapidjson::GetParseError_En(doc.GetParseError())); + return false; + } + + return doc.IsObject(); +} + + +bool Options::parseArg(int key, const char *arg) +{ + switch (key) { + case 'a': /* --algo */ + if (!setAlgo(arg)) { + return false; + } + break; + + case 'o': /* --url */ + if (m_pools.size() > 1 || m_pools[0]->isValid()) { + Url *url = new Url(arg); + if (url->isValid()) { + m_pools.push_back(url); + } + else { + delete url; + } + } + else { + m_pools[0]->parse(arg); + } + + if (!m_pools.back()->isValid()) { + return false; + } + break; + + case 'O': /* --userpass */ + if (!m_pools.back()->setUserpass(arg)) { + return false; + } + break; + + case 'u': /* --user */ + m_pools.back()->setUser(arg); + break; + + case 'p': /* --pass */ + m_pools.back()->setPassword(arg); + break; + + case 'l': /* --log-file */ + free(m_logFile); + m_logFile = strdup(arg); + m_colors = false; + break; + + case 4001: /* --access-token */ + free(m_apiToken); + m_apiToken = strdup(arg); + break; + + case 4002: /* --worker-id */ + free(m_apiWorkerId); + m_apiWorkerId = strdup(arg); + break; + + case 'r': /* --retries */ + case 'R': /* --retry-pause */ + case 'v': /* --av */ + case 1003: /* --donate-level */ + case 1004: /* --max-cpu-usage */ + case 1007: /* --print-time */ + case 1021: /* --cpu-priority */ + case 4000: /* --api-port */ + return parseArg(key, strtol(arg, nullptr, 10)); + + case 'B': /* --background */ + case 'k': /* --keepalive */ + case 'S': /* --syslog */ + case 1005: /* --safe */ + case 1006: /* --nicehash */ + return parseBoolean(key, true); + + case 1002: /* --no-color */ + case 1009: /* --no-huge-pages */ + return parseBoolean(key, false); + + case 't': /* --threads */ + if (strncmp(arg, "all", 3) == 0) { + m_threads = Cpu::threads(); + return true; + } + + return parseArg(key, strtol(arg, nullptr, 10)); + + case 'V': /* --version */ + showVersion(); + return false; + + case 'h': /* --help */ + showUsage(0); + return false; + + case 'c': /* --config */ + parseConfig(arg); + break; + + case 1020: { /* --cpu-affinity */ + const char *p = strstr(arg, "0x"); + return parseArg(key, p ? strtoull(p, nullptr, 16) : strtoull(arg, nullptr, 10)); + } + + case 1008: /* --user-agent */ + free(m_userAgent); + m_userAgent = strdup(arg); + break; + + default: + showUsage(1); + return false; + } + + return true; +} + + +bool Options::parseArg(int key, uint64_t arg) +{ + switch (key) { + case 'r': /* --retries */ + if (arg < 1 || arg > 1000) { + showUsage(1); + return false; + } + + m_retries = (int) arg; + break; + + case 'R': /* --retry-pause */ + if (arg < 1 || arg > 3600) { + showUsage(1); + return false; + } + + m_retryPause = (int) arg; + break; + + case 't': /* --threads */ + if (arg < 1 || arg > 1024) { + showUsage(1); + return false; + } + + m_threads = (int) arg; + break; + + case 'v': /* --av */ + if (arg > 1000) { + showUsage(1); + return false; + } + + m_algoVariant = (int) arg; + break; + + case 1003: /* --donate-level */ + if (arg < 1 || arg > 99) { + return true; + } + + m_donateLevel = (int) arg; + break; + + case 1004: /* --max-cpu-usage */ + if (arg < 1 || arg > 100) { + showUsage(1); + return false; + } + + m_maxCpuUsage = (int) arg; + break; + + case 1007: /* --print-time */ + if (arg > 1000) { + showUsage(1); + return false; + } + + m_printTime = (int) arg; + break; + + case 1020: /* --cpu-affinity */ + if (arg) { + m_affinity = arg; + } + break; + + case 1021: /* --cpu-priority */ + if (arg <= 5) { + m_priority = (int) arg; + } + break; + + case 4000: /* --api-port */ + if (arg <= 65536) { + m_apiPort = (int) arg; + } + break; + + default: + break; + } + + return true; +} + + +bool Options::parseBoolean(int key, bool enable) +{ + switch (key) { + case 'k': /* --keepalive */ + m_pools.back()->setKeepAlive(enable); + break; + + case 'B': /* --background */ + m_background = enable; + m_colors = enable ? false : m_colors; + break; + + case 'S': /* --syslog */ + m_syslog = enable; + m_colors = enable ? false : m_colors; + break; + + case 1002: /* --no-color */ + m_colors = enable; + break; + + case 1005: /* --safe */ + m_safe = enable; + break; + + case 1006: /* --nicehash */ + m_pools.back()->setNicehash(enable); + break; + + case 1009: /* --no-huge-pages */ + m_hugePages = enable; + break; + + case 2000: /* colors */ + m_colors = enable; + break; + + default: + break; + } + + return true; +} + + +Url *Options::parseUrl(const char *arg) const +{ + auto url = new Url(arg); + if (!url->isValid()) { + delete url; + return nullptr; + } + + return url; +} + + +void Options::parseConfig(const char *fileName) +{ + rapidjson::Document doc; + if (!getJSON(fileName, doc)) { + return; + } + + for (size_t i = 0; i < ARRAY_SIZE(config_options); i++) { + parseJSON(&config_options[i], doc); + } + + const rapidjson::Value &pools = doc["pools"]; + if (pools.IsArray()) { + for (const rapidjson::Value &value : pools.GetArray()) { + if (!value.IsObject()) { + continue; + } + + for (size_t i = 0; i < ARRAY_SIZE(pool_options); i++) { + parseJSON(&pool_options[i], value); + } + } + } + + const rapidjson::Value &api = doc["api"]; + if (api.IsObject()) { + for (size_t i = 0; i < ARRAY_SIZE(api_options); i++) { + parseJSON(&api_options[i], api); + } + } +} + + +void Options::parseJSON(const struct option *option, const rapidjson::Value &object) +{ + if (!option->name || !object.HasMember(option->name)) { + return; + } + + const rapidjson::Value &value = object[option->name]; + + if (option->has_arg && value.IsString()) { + parseArg(option->val, value.GetString()); + } + else if (option->has_arg && value.IsUint64()) { + parseArg(option->val, value.GetUint64()); + } + else if (!option->has_arg && value.IsBool()) { + parseBoolean(option->val, value.IsTrue()); + } +} + + +void Options::showUsage(int status) const +{ + if (status) { + fprintf(stderr, "Try \"" APP_ID "\" --help' for more information.\n"); + } + else { + printf(usage); + } +} + + +void Options::showVersion() +{ + printf(APP_NAME " " APP_VERSION "\n built on " __DATE__ + +# if defined(__clang__) + " with clang " __clang_version__); +# elif defined(__GNUC__) + " with GCC"); + printf(" %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif defined(_MSC_VER) + " with MSVC"); + printf(" %d", MSVC_VERSION); +# else + ); +# endif + + printf("\n features:" +# if defined(__i386__) || defined(_M_IX86) + " i386" +# elif defined(__x86_64__) || defined(_M_AMD64) + " x86_64" +# endif + +# if defined(__AES__) || defined(_MSC_VER) + " AES-NI" +# endif + "\n"); + + printf("\nlibuv/%s\n", uv_version_string()); +} + + +bool Options::setAlgo(const char *algo) +{ + for (size_t i = 0; i < ARRAY_SIZE(algo_names); i++) { + if (algo_names[i] && !strcmp(algo, algo_names[i])) { + m_algo = (int) i; + break; + } + +# ifndef XMRIG_NO_AEON + if (i == ARRAY_SIZE(algo_names) - 1 && !strcmp(algo, "cryptonight-light")) { + m_algo = ALGO_CRYPTONIGHT_LITE; + break; + } +# endif + + if (i == ARRAY_SIZE(algo_names) - 1) { + showUsage(1); + return false; + } + } + + return true; +} + + +int Options::getAlgoVariant() const +{ +# ifndef XMRIG_NO_AEON + if (m_algo == ALGO_CRYPTONIGHT_LITE) { + return getAlgoVariantLite(); + } +# endif + + if (m_algoVariant <= AV0_AUTO || m_algoVariant >= AV_MAX) { + return Cpu::hasAES() ? AV1_AESNI : AV3_SOFT_AES; + } + + if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV2_AESNI_DOUBLE) { + return m_algoVariant + 2; + } + + return m_algoVariant; +} + + +#ifndef XMRIG_NO_AEON +int Options::getAlgoVariantLite() const +{ + if (m_algoVariant <= AV0_AUTO || m_algoVariant >= AV_MAX) { + return Cpu::hasAES() ? AV2_AESNI_DOUBLE : AV4_SOFT_AES_DOUBLE; + } + + if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV2_AESNI_DOUBLE) { + return m_algoVariant + 2; + } + + return m_algoVariant; +} +#endif diff --git a/src/Options.h b/src/Options.h new file mode 100644 index 00000000..06b86f38 --- /dev/null +++ b/src/Options.h @@ -0,0 +1,134 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + + +#include +#include + + +#include "rapidjson/fwd.h" + + +class Url; +struct option; + + +class Options +{ +public: + enum Algo { + ALGO_CRYPTONIGHT, /* CryptoNight (Monero) */ + ALGO_CRYPTONIGHT_LITE, /* CryptoNight-Lite (AEON) */ + }; + + enum AlgoVariant { + AV0_AUTO, + AV1_AESNI, + AV2_AESNI_DOUBLE, + AV3_SOFT_AES, + AV4_SOFT_AES_DOUBLE, + AV_MAX + }; + + static inline Options* i() { return m_self; } + static Options *parse(int argc, char **argv); + + inline bool background() const { return m_background; } + inline bool colors() const { return m_colors; } + inline bool doubleHash() const { return m_doubleHash; } + inline bool hugePages() const { return m_hugePages; } + inline bool syslog() const { return m_syslog; } + inline const char *apiToken() const { return m_apiToken; } + inline const char *apiWorkerId() const { return m_apiWorkerId; } + inline const char *logFile() const { return m_logFile; } + inline const char *userAgent() const { return m_userAgent; } + inline const std::vector &pools() const { return m_pools; } + inline int algo() const { return m_algo; } + inline int algoVariant() const { return m_algoVariant; } + inline int apiPort() const { return m_apiPort; } + inline int donateLevel() const { return m_donateLevel; } + inline int printTime() const { return m_printTime; } + inline int priority() const { return m_priority; } + inline int retries() const { return m_retries; } + inline int retryPause() const { return m_retryPause; } + inline int threads() const { return m_threads; } + inline int64_t affinity() const { return m_affinity; } + + inline static void release() { delete m_self; } + + const char *algoName() const; + +private: + Options(int argc, char **argv); + ~Options(); + + inline bool isReady() const { return m_ready; } + + static Options *m_self; + + bool getJSON(const char *fileName, rapidjson::Document &doc); + bool parseArg(int key, const char *arg); + bool parseArg(int key, uint64_t arg); + bool parseBoolean(int key, bool enable); + Url *parseUrl(const char *arg) const; + void parseConfig(const char *fileName); + void parseJSON(const struct option *option, const rapidjson::Value &object); + void showUsage(int status) const; + void showVersion(void); + + bool setAlgo(const char *algo); + + int getAlgoVariant() const; +# ifndef XMRIG_NO_AEON + int getAlgoVariantLite() const; +# endif + + bool m_background; + bool m_colors; + bool m_doubleHash; + bool m_hugePages; + bool m_ready; + bool m_safe; + bool m_syslog; + char *m_apiToken; + char *m_apiWorkerId; + char *m_logFile; + char *m_userAgent; + int m_algo; + int m_algoVariant; + int m_apiPort; + int m_donateLevel; + int m_maxCpuUsage; + int m_printTime; + int m_priority; + int m_retries; + int m_retryPause; + int m_threads; + int64_t m_affinity; + std::vector m_pools; +}; + +#endif /* __OPTIONS_H__ */ diff --git a/src/Platform.cpp b/src/Platform.cpp new file mode 100644 index 00000000..4ddb1429 --- /dev/null +++ b/src/Platform.cpp @@ -0,0 +1,62 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include + + +#include "Platform.h" + + +char *Platform::m_defaultConfigName = nullptr; +char *Platform::m_userAgent = nullptr; + + +const char *Platform::defaultConfigName() +{ + size_t size = 520; + + if (m_defaultConfigName == nullptr) { + m_defaultConfigName = new char[size]; + } + + if (uv_exepath(m_defaultConfigName, &size) < 0) { + return nullptr; + } + + if (size < 500) { +# ifdef WIN32 + char *p = strrchr(m_defaultConfigName, '\\'); +# else + char *p = strrchr(m_defaultConfigName, '/'); +# endif + + if (p) { + strcpy(p + 1, "config.json"); + return m_defaultConfigName; + } + } + + return nullptr; +} diff --git a/src/Platform.h b/src/Platform.h new file mode 100644 index 00000000..87c8cc4d --- /dev/null +++ b/src/Platform.h @@ -0,0 +1,45 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + + +class Platform +{ +public: + static const char *defaultConfigName(); + static void init(const char *userAgent); + static void release(); + static void setProcessPriority(int priority); + static void setThreadPriority(int priority); + + static inline const char *userAgent() { return m_userAgent; } + +private: + static char *m_defaultConfigName; + static char *m_userAgent; +}; + + +#endif /* __PLATFORM_H__ */ diff --git a/src/Platform_mac.cpp b/src/Platform_mac.cpp new file mode 100644 index 00000000..5e53aacb --- /dev/null +++ b/src/Platform_mac.cpp @@ -0,0 +1,108 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "Platform.h" +#include "version.h" + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + +static inline char *createUserAgent() +{ + const size_t max = 160; + + char *buf = new char[max]; + +# ifdef XMRIG_NVIDIA_PROJECT + const int cudaVersion = cuda_get_runtime_version(); + snprintf(buf, max, "%s/%s (Macintosh; Intel Mac OS X) libuv/%s CUDA/%d.%d clang/%d.%d.%d", APP_NAME, APP_VERSION, uv_version_string(), cudaVersion / 1000, cudaVersion % 100, __clang_major__, __clang_minor__, __clang_patchlevel__); +# else + snprintf(buf, max, "%s/%s (Macintosh; Intel Mac OS X) libuv/%s clang/%d.%d.%d", APP_NAME, APP_VERSION, uv_version_string(), __clang_major__, __clang_minor__, __clang_patchlevel__); +# endif + + return buf; +} + + +void Platform::init(const char *userAgent) +{ + m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); +} + + +void Platform::release() +{ + delete [] m_userAgent; +} + + +void Platform::setProcessPriority(int priority) +{ + +} + + +void Platform::setThreadPriority(int priority) +{ + if (priority == -1) { + return; + } + + int prio = 19; + switch (priority) + { + case 1: + prio = 5; + break; + + case 2: + prio = 0; + break; + + case 3: + prio = -5; + break; + + case 4: + prio = -10; + break; + + case 5: + prio = -15; + break; + + default: + break; + } + + setpriority(PRIO_PROCESS, 0, prio); +} + diff --git a/src/Platform_unix.cpp b/src/Platform_unix.cpp new file mode 100644 index 00000000..ecccc49e --- /dev/null +++ b/src/Platform_unix.cpp @@ -0,0 +1,128 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + + +#include "Platform.h" +#include "version.h" + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + +static inline char *createUserAgent() +{ + const size_t max = 160; + + char *buf = new char[max]; + int length = snprintf(buf, max, "%s/%s (Linux ", APP_NAME, APP_VERSION); + +# if defined(__x86_64__) + length += snprintf(buf + length, max - length, "x86_64) libuv/%s", uv_version_string()); +# else + length += snprintf(buf + length, max - length, "i686) libuv/%s", uv_version_string()); +# endif + +# ifdef XMRIG_NVIDIA_PROJECT + const int cudaVersion = cuda_get_runtime_version(); + length += snprintf(buf + length, max - length, " CUDA/%d.%d", cudaVersion / 1000, cudaVersion % 100); +# endif + +# ifdef __GNUC__ + length += snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# endif + + return buf; +} + + +void Platform::init(const char *userAgent) +{ + m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); +} + + +void Platform::release() +{ + delete [] m_userAgent; +} + + +void Platform::setProcessPriority(int priority) +{ +} + + + +void Platform::setThreadPriority(int priority) +{ + if (priority == -1) { + return; + } + + int prio = 19; + switch (priority) + { + case 1: + prio = 5; + break; + + case 2: + prio = 0; + break; + + case 3: + prio = -5; + break; + + case 4: + prio = -10; + break; + + case 5: + prio = -15; + break; + + default: + break; + } + + setpriority(PRIO_PROCESS, 0, prio); + +# ifdef SCHED_IDLE + if (priority == 0) { + sched_param param; + param.sched_priority = 0; + + if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) { + sched_setscheduler(0, SCHED_BATCH, ¶m); + } + } +# endif +} diff --git a/src/Platform_win.cpp b/src/Platform_win.cpp new file mode 100644 index 00000000..880bdd98 --- /dev/null +++ b/src/Platform_win.cpp @@ -0,0 +1,170 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "Platform.h" +#include "version.h" + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + +static inline OSVERSIONINFOEX winOsVersion() +{ + typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO); + OSVERSIONINFOEX result = { sizeof(OSVERSIONINFOEX), 0, 0, 0, 0, {'\0'}, 0, 0, 0, 0, 0}; + + HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll ) { + RtlGetVersionFunction pRtlGetVersion = reinterpret_cast(GetProcAddress(ntdll, "RtlGetVersion")); + + if (pRtlGetVersion) { + pRtlGetVersion((LPOSVERSIONINFO) &result); + } + } + + return result; +} + + +static inline char *createUserAgent() +{ + const auto osver = winOsVersion(); + const size_t max = 160; + + char *buf = new char[max]; + int length = snprintf(buf, max, "%s/%s (Windows NT %lu.%lu", APP_NAME, APP_VERSION, osver.dwMajorVersion, osver.dwMinorVersion); + +# if defined(__x86_64__) || defined(_M_AMD64) + length += snprintf(buf + length, max - length, "; Win64; x64) libuv/%s", uv_version_string()); +# else + length += snprintf(buf + length, max - length, ") libuv/%s", uv_version_string()); +# endif + +# ifdef XMRIG_NVIDIA_PROJECT + const int cudaVersion = cuda_get_runtime_version(); + length += snprintf(buf + length, max - length, " CUDA/%d.%d", cudaVersion / 1000, cudaVersion % 100); +# endif + +# ifdef __GNUC__ + length += snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif _MSC_VER + length += snprintf(buf + length, max - length, " msvc/%d", MSVC_VERSION); +# endif + + return buf; +} + + +void Platform::init(const char *userAgent) +{ + m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); +} + + +void Platform::release() +{ + delete [] m_defaultConfigName; + delete [] m_userAgent; +} + + +void Platform::setProcessPriority(int priority) +{ + if (priority == -1) { + return; + } + + DWORD prio = IDLE_PRIORITY_CLASS; + switch (priority) + { + case 1: + prio = BELOW_NORMAL_PRIORITY_CLASS; + break; + + case 2: + prio = NORMAL_PRIORITY_CLASS; + break; + + case 3: + prio = ABOVE_NORMAL_PRIORITY_CLASS; + break; + + case 4: + prio = HIGH_PRIORITY_CLASS; + break; + + case 5: + prio = REALTIME_PRIORITY_CLASS; + + default: + break; + } + + SetPriorityClass(GetCurrentProcess(), prio); +} + + + +void Platform::setThreadPriority(int priority) +{ + if (priority == -1) { + return; + } + + int prio = THREAD_PRIORITY_IDLE; + switch (priority) + { + case 1: + prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + + case 2: + prio = THREAD_PRIORITY_NORMAL; + break; + + case 3: + prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + + case 4: + prio = THREAD_PRIORITY_HIGHEST; + break; + + case 5: + prio = THREAD_PRIORITY_TIME_CRITICAL; + break; + + default: + break; + } + + SetThreadPriority(GetCurrentThread(), prio); +} + diff --git a/src/Summary.cpp b/src/Summary.cpp new file mode 100644 index 00000000..c6c53341 --- /dev/null +++ b/src/Summary.cpp @@ -0,0 +1,169 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include + + +#include "Cpu.h" +#include "log/Log.h" +#include "Mem.h" +#include "net/Url.h" +#include "Options.h" +#include "Summary.h" +#include "version.h" + + +static void print_versions() +{ + 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(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mVERSIONS: \x1B[01;36mXMRig/%s\x1B[01;37m libuv/%s%s" : " * VERSIONS: XMRig/%s libuv/%s%s", + APP_VERSION, uv_version_string(), buf); +} + + +static void print_memory() { + if (Options::i()->colors()) { + Log::i()->text("\x1B[01;32m * \x1B[01;37mHUGE PAGES: %s, %s", + Mem::isHugepagesAvailable() ? "\x1B[01;32mavailable" : "\x1B[01;31munavailable", + Mem::isHugepagesEnabled() ? "\x1B[01;32menabled" : "\x1B[01;31mdisabled"); + } + else { + Log::i()->text(" * HUGE PAGES: %s, %s", Mem::isHugepagesAvailable() ? "available" : "unavailable", Mem::isHugepagesEnabled() ? "enabled" : "disabled"); + } +} + + +static void print_cpu() +{ + if (Options::i()->colors()) { + Log::i()->text("\x1B[01;32m * \x1B[01;37mCPU: %s (%d) %sx64 %sAES-NI", + Cpu::brand(), + Cpu::sockets(), + Cpu::isX64() ? "\x1B[01;32m" : "\x1B[01;31m-", + Cpu::hasAES() ? "\x1B[01;32m" : "\x1B[01;31m-"); +# ifndef XMRIG_NO_LIBCPUID + Log::i()->text("\x1B[01;32m * \x1B[01;37mCPU L2/L3: %.1f MB/%.1f MB", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); +# endif + } + else { + Log::i()->text(" * CPU: %s (%d) %sx64 %sAES-NI", Cpu::brand(), Cpu::sockets(), Cpu::isX64() ? "" : "-", Cpu::hasAES() ? "" : "-"); +# ifndef XMRIG_NO_LIBCPUID + Log::i()->text(" * CPU L2/L3: %.1f MB/%.1f MB", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); +# endif + } +} + + +static void print_threads() +{ + char buf[32]; + if (Options::i()->affinity() != -1L) { + snprintf(buf, 32, ", affinity=0x%" PRIX64, Options::i()->affinity()); + } + else { + buf[0] = '\0'; + } + + Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mTHREADS: \x1B[01;36m%d\x1B[01;37m, %s, av=%d, %sdonate=%d%%%s" : " * THREADS: %d, %s, av=%d, %sdonate=%d%%%s", + Options::i()->threads(), + Options::i()->algoName(), + Options::i()->algoVariant(), + Options::i()->colors() && Options::i()->donateLevel() == 0 ? "\x1B[01;31m" : "", + Options::i()->donateLevel(), + buf); +} + + +static void print_pools() +{ + const std::vector &pools = Options::i()->pools(); + + for (size_t i = 0; i < pools.size(); ++i) { + Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mPOOL #%d: \x1B[01;36m%s:%d" : " * POOL #%d: %s:%d", + i + 1, + pools[i]->host(), + pools[i]->port()); + } + +# ifdef APP_DEBUG + for (size_t i = 0; i < pools.size(); ++i) { + Log::i()->text("%s:%d, user: %s, pass: %s, ka: %d, nicehash: %d", pools[i]->host(), pools[i]->port(), pools[i]->user(), pools[i]->password(), pools[i]->isKeepAlive(), pools[i]->isNicehash()); + } +# endif +} + + +#ifndef XMRIG_NO_API +static void print_api() +{ + if (Options::i()->apiPort() == 0) { + return; + } + + Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mAPI PORT: \x1B[01;36m%d" : " * API PORT: %d", Options::i()->apiPort()); +} +#endif + + +static void print_commands() +{ + if (Options::i()->colors()) { + Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mh\x1B[01;37mashrate, \x1B[01;35mp\x1B[01;37mause, \x1B[01;35mr\x1B[01;37mesume"); + } + else { + Log::i()->text(" * COMMANDS: 'h' hashrate, 'p' pause, 'r' resume"); + } +} + + +void Summary::print() +{ + print_versions(); + print_memory(); + print_cpu(); + print_threads(); + print_pools(); + +# ifndef XMRIG_NO_API + print_api(); +# endif + + print_commands(); +} + + + diff --git a/utils/summary.h b/src/Summary.h similarity index 95% rename from utils/summary.h rename to src/Summary.h index 628c5304..3f64fd60 100644 --- a/utils/summary.h +++ b/src/Summary.h @@ -24,6 +24,12 @@ #ifndef __SUMMARY_H__ #define __SUMMARY_H__ -void print_summary(); + +class Summary +{ +public: + static void print(); +}; + #endif /* __SUMMARY_H__ */ diff --git a/src/api/Api.cpp b/src/api/Api.cpp new file mode 100644 index 00000000..729ebccd --- /dev/null +++ b/src/api/Api.cpp @@ -0,0 +1,85 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + + +#include "api/Api.h" +#include "api/ApiState.h" + + +ApiState *Api::m_state = nullptr; +uv_mutex_t Api::m_mutex; + + +bool Api::start() +{ + uv_mutex_init(&m_mutex); + m_state = new ApiState(); + + return true; +} + + +void Api::release() +{ + delete m_state; +} + + +char *Api::get(const char *url, int *status) +{ + if (!m_state) { + return nullptr; + } + + uv_mutex_lock(&m_mutex); + char *buf = m_state->get(url, status); + uv_mutex_unlock(&m_mutex); + + return buf; +} + + +void Api::tick(const Hashrate *hashrate) +{ + if (!m_state) { + return; + } + + uv_mutex_lock(&m_mutex); + m_state->tick(hashrate); + uv_mutex_unlock(&m_mutex); +} + + +void Api::tick(const NetworkState &network) +{ + if (!m_state) { + return; + } + + uv_mutex_lock(&m_mutex); + m_state->tick(network); + uv_mutex_unlock(&m_mutex); +} diff --git a/src/api/Api.h b/src/api/Api.h new file mode 100644 index 00000000..72c65c3c --- /dev/null +++ b/src/api/Api.h @@ -0,0 +1,51 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __API_H__ +#define __API_H__ + + +#include + + +class ApiState; +class Hashrate; +class NetworkState; + + +class Api +{ +public: + static bool start(); + static void release(); + + static char *get(const char *url, int *status); + static void tick(const Hashrate *hashrate); + static void tick(const NetworkState &results); + +private: + static ApiState *m_state; + static uv_mutex_t m_mutex; +}; + +#endif /* __API_H__ */ diff --git a/src/api/ApiState.cpp b/src/api/ApiState.cpp new file mode 100644 index 00000000..bade355a --- /dev/null +++ b/src/api/ApiState.cpp @@ -0,0 +1,260 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#if _WIN32 +# include "winsock2.h" +#else +# include "unistd.h" +#endif + + +#include "api/ApiState.h" +#include "Cpu.h" +#include "Mem.h" +#include "net/Job.h" +#include "Options.h" +#include "Platform.h" +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/prettywriter.h" +#include "version.h" +#include "workers/Hashrate.h" + + +extern "C" +{ +#include "crypto/c_keccak.h" +} + + +static inline double normalize(double d) +{ + if (!std::isnormal(d)) { + return 0.0; + } + + return std::floor(d * 100.0) / 100.0; +} + + +ApiState::ApiState() +{ + m_threads = Options::i()->threads(); + m_hashrate = new double[m_threads * 3](); + + memset(m_totalHashrate, 0, sizeof(m_totalHashrate)); + memset(m_workerId, 0, sizeof(m_workerId)); + + if (Options::i()->apiWorkerId()) { + strncpy(m_workerId, Options::i()->apiWorkerId(), sizeof(m_workerId) - 1); + } + else { + gethostname(m_workerId, sizeof(m_workerId) - 1); + } + + genId(); +} + + +ApiState::~ApiState() +{ + delete [] m_hashrate; +} + + +char *ApiState::get(const char *url, int *status) const +{ + rapidjson::Document doc; + doc.SetObject(); + + getIdentify(doc); + getMiner(doc); + getHashrate(doc); + getResults(doc); + getConnection(doc); + + return finalize(doc); +} + + +void ApiState::tick(const Hashrate *hashrate) +{ + for (int i = 0; i < m_threads; ++i) { + m_hashrate[i * 3] = hashrate->calc((size_t) i, Hashrate::ShortInterval); + m_hashrate[i * 3 + 1] = hashrate->calc((size_t) i, Hashrate::MediumInterval); + m_hashrate[i * 3 + 2] = hashrate->calc((size_t) i, Hashrate::LargeInterval); + } + + m_totalHashrate[0] = hashrate->calc(Hashrate::ShortInterval); + m_totalHashrate[1] = hashrate->calc(Hashrate::MediumInterval); + m_totalHashrate[2] = hashrate->calc(Hashrate::LargeInterval); + m_highestHashrate = hashrate->highest(); +} + + +void ApiState::tick(const NetworkState &network) +{ + m_network = network; +} + + +char *ApiState::finalize(rapidjson::Document &doc) const +{ + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + doc.Accept(writer); + + return strdup(buffer.GetString()); +} + + +void ApiState::genId() +{ + memset(m_id, 0, sizeof(m_id)); + + uv_interface_address_t *interfaces; + int count = 0; + + if (uv_interface_addresses(&interfaces, &count) < 0) { + return; + } + + for (int i = 0; i < count; i++) { + if (!interfaces[i].is_internal && interfaces[i].address.address4.sin_family == AF_INET) { + uint8_t hash[200]; + const size_t addrSize = sizeof(interfaces[i].phys_addr); + const size_t inSize = strlen(APP_KIND) + addrSize; + + uint8_t *input = new uint8_t[inSize](); + memcpy(input, interfaces[i].phys_addr, addrSize); + memcpy(input + addrSize, APP_KIND, strlen(APP_KIND)); + + keccak(input, static_cast(inSize), hash, sizeof(hash)); + Job::toHex(hash, 8, m_id); + + delete [] input; + break; + } + } + + uv_free_interface_addresses(interfaces, count); +} + + +void ApiState::getConnection(rapidjson::Document &doc) const +{ + auto &allocator = doc.GetAllocator(); + + rapidjson::Value connection(rapidjson::kObjectType); + connection.AddMember("pool", rapidjson::StringRef(m_network.pool), allocator); + connection.AddMember("uptime", m_network.connectionTime(), allocator); + connection.AddMember("ping", m_network.latency(), allocator); + connection.AddMember("failures", m_network.failures, allocator); + connection.AddMember("error_log", rapidjson::Value(rapidjson::kArrayType), allocator); + + doc.AddMember("connection", connection, allocator); +} + + +void ApiState::getHashrate(rapidjson::Document &doc) const +{ + auto &allocator = doc.GetAllocator(); + + rapidjson::Value hashrate(rapidjson::kObjectType); + rapidjson::Value total(rapidjson::kArrayType); + rapidjson::Value threads(rapidjson::kArrayType); + + for (int i = 0; i < 3; ++i) { + total.PushBack(normalize(m_totalHashrate[i]), allocator); + } + + for (int i = 0; i < m_threads * 3; i += 3) { + rapidjson::Value thread(rapidjson::kArrayType); + thread.PushBack(normalize(m_hashrate[i]), allocator); + thread.PushBack(normalize(m_hashrate[i + 1]), allocator); + thread.PushBack(normalize(m_hashrate[i + 2]), allocator); + + threads.PushBack(thread, allocator); + } + + hashrate.AddMember("total", total, allocator); + hashrate.AddMember("highest", normalize(m_highestHashrate), allocator); + hashrate.AddMember("threads", threads, allocator); + doc.AddMember("hashrate", hashrate, allocator); +} + + +void ApiState::getIdentify(rapidjson::Document &doc) const +{ + doc.AddMember("id", rapidjson::StringRef(m_id), doc.GetAllocator()); + doc.AddMember("worker_id", rapidjson::StringRef(m_workerId), doc.GetAllocator()); +} + + +void ApiState::getMiner(rapidjson::Document &doc) const +{ + auto &allocator = doc.GetAllocator(); + + rapidjson::Value cpu(rapidjson::kObjectType); + cpu.AddMember("brand", rapidjson::StringRef(Cpu::brand()), allocator); + cpu.AddMember("aes", Cpu::hasAES(), allocator); + cpu.AddMember("x64", Cpu::isX64(), allocator); + cpu.AddMember("sockets", Cpu::sockets(), allocator); + + doc.AddMember("version", APP_VERSION, allocator); + doc.AddMember("kind", APP_KIND, allocator); + doc.AddMember("ua", rapidjson::StringRef(Platform::userAgent()), allocator); + doc.AddMember("cpu", cpu, allocator); + doc.AddMember("algo", rapidjson::StringRef(Options::i()->algoName()), allocator); + doc.AddMember("hugepages", Mem::isHugepagesEnabled(), allocator); + doc.AddMember("donate_level", Options::i()->donateLevel(), allocator); +} + + +void ApiState::getResults(rapidjson::Document &doc) const +{ + auto &allocator = doc.GetAllocator(); + + rapidjson::Value results(rapidjson::kObjectType); + + results.AddMember("diff_current", m_network.diff, allocator); + results.AddMember("shares_good", m_network.accepted, allocator); + results.AddMember("shares_total", m_network.accepted + m_network.rejected, allocator); + results.AddMember("avg_time", m_network.avgTime(), allocator); + results.AddMember("hashes_total", m_network.total, allocator); + + rapidjson::Value best(rapidjson::kArrayType); + for (size_t i = 0; i < m_network.topDiff.size(); ++i) { + best.PushBack(m_network.topDiff[i], allocator); + } + + results.AddMember("best", best, allocator); + results.AddMember("error_log", rapidjson::Value(rapidjson::kArrayType), allocator); + + doc.AddMember("results", results, allocator); +} diff --git a/src/api/ApiState.h b/src/api/ApiState.h new file mode 100644 index 00000000..7ecca36d --- /dev/null +++ b/src/api/ApiState.h @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __APISTATE_H__ +#define __APISTATE_H__ + + +#include "api/NetworkState.h" +#include "rapidjson/fwd.h" + + +class Hashrate; + + +class ApiState +{ +public: + ApiState(); + ~ApiState(); + + char *get(const char *url, int *status) const; + void tick(const Hashrate *hashrate); + void tick(const NetworkState &results); + +private: + char *finalize(rapidjson::Document &doc) const; + void genId(); + void getConnection(rapidjson::Document &doc) const; + void getHashrate(rapidjson::Document &doc) const; + void getIdentify(rapidjson::Document &doc) const; + void getMiner(rapidjson::Document &doc) const; + void getResults(rapidjson::Document &doc) const; + + char m_id[17]; + char m_workerId[128]; + double *m_hashrate; + double m_highestHashrate; + double m_totalHashrate[3]; + int m_threads; + NetworkState m_network; +}; + +#endif /* __APISTATE_H__ */ diff --git a/src/api/Httpd.cpp b/src/api/Httpd.cpp new file mode 100644 index 00000000..996bc007 --- /dev/null +++ b/src/api/Httpd.cpp @@ -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 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include + + +#include "api/Api.h" +#include "api/Httpd.h" +#include "log/Log.h" + + +Httpd::Httpd(int port, const char *accessToken) : + m_accessToken(accessToken), + m_port(port), + m_daemon(nullptr) +{ +} + + +bool Httpd::start() +{ + if (!m_port) { + return false; + } + + m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, m_port, nullptr, nullptr, &Httpd::handler, this, MHD_OPTION_END); + if (!m_daemon) { + LOG_ERR("HTTP Daemon failed to start."); + return false; + } + + return true; +} + + +int Httpd::auth(const char *header) +{ + if (!m_accessToken) { + return MHD_HTTP_OK; + } + + if (m_accessToken && !header) { + return MHD_HTTP_UNAUTHORIZED; + } + + const size_t size = strlen(header); + if (size < 8 || strlen(m_accessToken) != size - 7 || memcmp("Bearer ", header, 7) != 0) { + return MHD_HTTP_FORBIDDEN; + } + + return strncmp(m_accessToken, header + 7, strlen(m_accessToken)) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; +} + + +int Httpd::done(MHD_Connection *connection, int status, MHD_Response *rsp) +{ + if (!rsp) { + rsp = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT); + } + + MHD_add_response_header(rsp, "Content-Type", "application/json"); + MHD_add_response_header(rsp, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(rsp, "Access-Control-Allow-Methods", "GET"); + MHD_add_response_header(rsp, "Access-Control-Allow-Headers", "Authorization"); + + const int ret = MHD_queue_response(connection, status, rsp); + MHD_destroy_response(rsp); + return ret; +} + + +int Httpd::handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) +{ + if (strcmp(method, "OPTIONS") == 0) { + return done(connection, MHD_HTTP_OK, nullptr); + } + + if (strcmp(method, "GET") != 0) { + return MHD_NO; + } + + int status = static_cast(cls)->auth(MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Authorization")); + if (status != MHD_HTTP_OK) { + return done(connection, status, nullptr); + } + + char *buf = Api::get(url, &status); + if (buf == nullptr) { + return MHD_NO; + } + + MHD_Response *rsp = MHD_create_response_from_buffer(strlen(buf), (void*) buf, MHD_RESPMEM_MUST_FREE); + return done(connection, status, rsp); +} diff --git a/xmrig.h b/src/api/Httpd.h similarity index 63% rename from xmrig.h rename to src/api/Httpd.h index 08091367..7a4dd932 100644 --- a/xmrig.h +++ b/src/api/Httpd.h @@ -21,37 +21,33 @@ * along with this program. If not, see . */ -#ifndef __XMRIG_H__ -#define __XMRIG_H__ - -#include -#include -#include -#include -#include - -#define unlikely(expr) (__builtin_expect(!!(expr), 0)) -#define likely(expr) (__builtin_expect(!!(expr), 1)) +#ifndef __HTTPD_H__ +#define __HTTPD_H__ -struct thr_info { - int id; - pthread_t pth; - struct thread_q *q; +#include + + +struct MHD_Connection; +struct MHD_Daemon; +struct MHD_Response; + + +class Httpd +{ +public: + Httpd(int port, const char *accessToken); + bool start(); + +private: + int auth(const char *header); + + static int done(MHD_Connection *connection, int status, MHD_Response *rsp); + static int handler(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls); + + const char *m_accessToken; + const int m_port; + MHD_Daemon *m_daemon; }; - -struct work_restart { - volatile unsigned long restart; - char padding[128 - sizeof(unsigned long)]; -}; - - -struct work; - - -extern struct thr_info *thr_info; -extern struct work_restart *work_restart; -extern void os_specific_init(); - -#endif /* __XMRIG_H__ */ +#endif /* __HTTPD_H__ */ diff --git a/src/api/NetworkState.cpp b/src/api/NetworkState.cpp new file mode 100644 index 00000000..997f52df --- /dev/null +++ b/src/api/NetworkState.cpp @@ -0,0 +1,112 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "api/NetworkState.h" +#include "net/SubmitResult.h" + + +NetworkState::NetworkState() : + diff(0), + accepted(0), + failures(0), + rejected(0), + total(0), + m_active(false) +{ + memset(pool, 0, sizeof(pool)); +} + + +int NetworkState::connectionTime() const +{ + return m_active ? ((uv_now(uv_default_loop()) - m_connectionTime) / 1000) : 0; +} + + +uint32_t NetworkState::avgTime() const +{ + if (m_latency.empty()) { + return 0; + } + + return (uint32_t) connectionTime() / m_latency.size(); +} + + +uint32_t NetworkState::latency() const +{ + const size_t calls = m_latency.size(); + if (calls == 0) { + return 0; + } + + auto v = m_latency; + std::nth_element(v.begin(), v.begin() + calls / 2, v.end()); + + return v[calls / 2]; +} + + +void NetworkState::add(const SubmitResult &result, const char *error) +{ + if (error) { + rejected++; + return; + } + + accepted++; + total += result.diff; + + const size_t ln = topDiff.size() - 1; + if (result.actualDiff > topDiff[ln]) { + topDiff[ln] = result.actualDiff; + std::sort(topDiff.rbegin(), topDiff.rend()); + } + + m_latency.push_back(result.elapsed > 0xFFFF ? 0xFFFF : (uint16_t) result.elapsed); +} + + +void NetworkState::setPool(const char *host, int port, const char *ip) +{ + snprintf(pool, sizeof(pool) - 1, "%s:%d", host, port); + + m_active = true; + m_connectionTime = uv_now(uv_default_loop()); +} + + +void NetworkState::stop() +{ + m_active = false; + diff = 0; + + failures++; + m_latency.clear(); +} diff --git a/src/api/NetworkState.h b/src/api/NetworkState.h new file mode 100644 index 00000000..d0998074 --- /dev/null +++ b/src/api/NetworkState.h @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __NETWORKSTATE_H__ +#define __NETWORKSTATE_H__ + + +#include +#include + + +class SubmitResult; + + +class NetworkState +{ +public: + NetworkState(); + + int connectionTime() const; + uint32_t avgTime() const; + uint32_t latency() const; + void add(const SubmitResult &result, const char *error); + void setPool(const char *host, int port, const char *ip); + void stop(); + + char pool[256]; + std::array topDiff { { } }; + uint32_t diff; + uint64_t accepted; + uint64_t failures; + uint64_t rejected; + uint64_t total; + +private: + bool m_active; + std::vector m_latency; + uint64_t m_connectionTime; +}; + +#endif /* __NETWORKSTATE_H__ */ diff --git a/src/config.json b/src/config.json new file mode 100644 index 00000000..7813d4b9 --- /dev/null +++ b/src/config.json @@ -0,0 +1,31 @@ +{ + "algo": "cryptonight", // cryptonight (default) or cryptonight-lite + "av": 0, // algorithm variation, 0 auto select + "background": false, // true to run the miner in the background + "colors": true, // false to disable colored output + "cpu-affinity": null, // set process affinity to CPU core(s), mask "0x3" for cores 0 and 1 + "cpu-priority": null, // set process priority (0 idle, 2 normal to 5 highest) + "donate-level": 5, // donate level, mininum 1% + "log-file": null, // log all output to a file, example: "c:/some/path/xmrig.log" + "max-cpu-usage": 75, // maximum CPU usage for automatic mode, usually limiting factor is CPU cache not this option. + "print-time": 60, // print hashrate report every N seconds + "retries": 5, // number of times to retry before switch to backup server + "retry-pause": 5, // time to pause between retries + "safe": false, // true to safe adjust threads and av settings for current CPU + "syslog": false, // use system log for output messages + "threads": null, // number of miner threads + "pools": [ + { + "url": "pool.minemonero.pro:5555", // URL of mining server + "user": "", // username for mining server + "pass": "x", // password for mining server + "keepalive": true, // send keepalived for prevent timeout (need pool support) + "nicehash": false // enable nicehash/xmrig-proxy support + } + ], + "api": { + "port": 0, // port for the miner API https://github.com/xmrig/xmrig/wiki/API + "access-token": null, // access token for API + "worker-id": null // custom worker-id for API + } +} \ No newline at end of file diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp new file mode 100644 index 00000000..c4e56678 --- /dev/null +++ b/src/crypto/CryptoNight.cpp @@ -0,0 +1,148 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "crypto/CryptoNight.h" +#include "crypto/CryptoNight_p.h" +#include "crypto/CryptoNight_test.h" +#include "net/Job.h" +#include "net/JobResult.h" +#include "Options.h" + + +void (*cryptonight_hash_ctx)(const void *input, size_t size, void *output, cryptonight_ctx *ctx) = nullptr; + + +static void cryptonight_av1_aesni(const void *input, size_t size, void *output, struct cryptonight_ctx *ctx) { + cryptonight_hash<0x80000, MEMORY, 0x1FFFF0, false>(input, size, output, ctx); +} + + +static void cryptonight_av2_aesni_double(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_double_hash<0x80000, MEMORY, 0x1FFFF0, false>(input, size, output, ctx); +} + + +static void cryptonight_av3_softaes(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_hash<0x80000, MEMORY, 0x1FFFF0, true>(input, size, output, ctx); +} + + +static void cryptonight_av4_softaes_double(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_double_hash<0x80000, MEMORY, 0x1FFFF0, true>(input, size, output, ctx); +} + + +#ifndef XMRIG_NO_AEON +static void cryptonight_lite_av1_aesni(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_hash<0x40000, MEMORY_LITE, 0xFFFF0, false>(input, size, output, ctx); +} + + +static void cryptonight_lite_av2_aesni_double(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_double_hash<0x40000, MEMORY_LITE, 0xFFFF0, false>(input, size, output, ctx); +} + + +static void cryptonight_lite_av3_softaes(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_hash<0x40000, MEMORY_LITE, 0xFFFF0, true>(input, size, output, ctx); +} + + +static void cryptonight_lite_av4_softaes_double(const void *input, size_t size, void *output, cryptonight_ctx *ctx) { + cryptonight_double_hash<0x40000, MEMORY_LITE, 0xFFFF0, true>(input, size, output, ctx); +} + +void (*cryptonight_variations[8])(const void *input, size_t size, void *output, cryptonight_ctx *ctx) = { + cryptonight_av1_aesni, + cryptonight_av2_aesni_double, + cryptonight_av3_softaes, + cryptonight_av4_softaes_double, + cryptonight_lite_av1_aesni, + cryptonight_lite_av2_aesni_double, + cryptonight_lite_av3_softaes, + cryptonight_lite_av4_softaes_double + }; +#else +void (*cryptonight_variations[4])(const void *input, size_t size, void *output, cryptonight_ctx *ctx) = { + cryptonight_av1_aesni, + cryptonight_av2_aesni_double, + cryptonight_av3_softaes, + cryptonight_av4_softaes_double + }; +#endif + + +bool CryptoNight::hash(const Job &job, JobResult &result, cryptonight_ctx *ctx) +{ + cryptonight_hash_ctx(job.blob(), job.size(), result.result, ctx); + + return *reinterpret_cast(result.result + 24) < job.target(); +} + + +bool CryptoNight::init(int algo, int variant) +{ + if (variant < 1 || variant > 4) { + return false; + } + +# ifndef XMRIG_NO_AEON + const int index = algo == Options::ALGO_CRYPTONIGHT_LITE ? (variant + 3) : (variant - 1); +# else + const int index = variant - 1; +# endif + + cryptonight_hash_ctx = cryptonight_variations[index]; + + return selfTest(algo); +} + + +void CryptoNight::hash(const uint8_t *input, size_t size, uint8_t *output, cryptonight_ctx *ctx) +{ + cryptonight_hash_ctx(input, size, output, ctx); +} + + +bool CryptoNight::selfTest(int algo) { + if (cryptonight_hash_ctx == nullptr) { + return false; + } + + char output[64]; + + struct cryptonight_ctx *ctx = (struct cryptonight_ctx*) _mm_malloc(sizeof(struct cryptonight_ctx), 16); + ctx->memory = (uint8_t *) _mm_malloc(MEMORY * 2, 16); + + cryptonight_hash_ctx(test_input, 76, output, ctx); + + _mm_free(ctx->memory); + _mm_free(ctx); + +# ifndef XMRIG_NO_AEON + return memcmp(output, algo == Options::ALGO_CRYPTONIGHT_LITE ? test_output1 : test_output0, (Options::i()->doubleHash() ? 64 : 32)) == 0; +# else + return memcmp(output, test_output0, (Options::i()->doubleHash() ? 64 : 32)) == 0; +# endif +} diff --git a/algo/cryptonight/cryptonight.h b/src/crypto/CryptoNight.h similarity index 64% rename from algo/cryptonight/cryptonight.h rename to src/crypto/CryptoNight.h index 0b017073..64fc0fd1 100644 --- a/algo/cryptonight/cryptonight.h +++ b/src/crypto/CryptoNight.h @@ -24,24 +24,38 @@ #ifndef __CRYPTONIGHT_H__ #define __CRYPTONIGHT_H__ + #include #include -#include + + +#include "align.h" + #define MEMORY 2097152 /* 2 MiB */ #define MEMORY_LITE 1048576 /* 1 MiB */ + struct cryptonight_ctx { - uint8_t state0[200] __attribute__((aligned(16))); - uint8_t state1[200] __attribute__((aligned(16))); - uint8_t* memory __attribute__((aligned(16))); + VAR_ALIGN(16, uint8_t state0[200]); + VAR_ALIGN(16, uint8_t state1[200]); + VAR_ALIGN(16, uint8_t* memory); }; -extern void (* const extra_hashes[4])(const void *, size_t, char *); +class Job; +class JobResult; -bool cryptonight_init(int variant); -int scanhash_cryptonight(int thr_id, uint32_t *hash, uint32_t *restrict blob, size_t blob_size, uint32_t target, uint32_t max_nonce, unsigned long *restrict hashes_done, struct cryptonight_ctx *restrict ctx); -int scanhash_cryptonight_double(int thr_id, uint32_t *hash, uint8_t *restrict blob, size_t blob_size, uint32_t target, uint32_t max_nonce, unsigned long *restrict hashes_done, struct cryptonight_ctx *restrict ctx); + +class CryptoNight +{ +public: + static bool hash(const Job &job, JobResult &result, cryptonight_ctx *ctx); + static bool init(int algo, int variant); + static void hash(const uint8_t *input, size_t size, uint8_t *output, cryptonight_ctx *ctx); + +private: + static bool selfTest(int algo); +}; #endif /* __CRYPTONIGHT_H__ */ diff --git a/src/crypto/CryptoNight_p.h b/src/crypto/CryptoNight_p.h new file mode 100644 index 00000000..75809b81 --- /dev/null +++ b/src/crypto/CryptoNight_p.h @@ -0,0 +1,451 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CRYPTONIGHT_P_H__ +#define __CRYPTONIGHT_P_H__ + + +#ifdef __GNUC__ +# include +#else +# include +# define __restrict__ __restrict +#endif + + +#include "crypto/CryptoNight.h" +#include "crypto/soft_aes.h" + + +extern "C" +{ +#include "crypto/c_keccak.h" +#include "crypto/c_groestl.h" +#include "crypto/c_blake256.h" +#include "crypto/c_jh.h" +#include "crypto/c_skein.h" +} + + +static inline void do_blake_hash(const void* input, size_t len, char* output) { + blake256_hash(reinterpret_cast(output), static_cast(input), len); +} + + +static inline void do_groestl_hash(const void* input, size_t len, char* output) { + groestl(static_cast(input), len * 8, reinterpret_cast(output)); +} + + +static inline void do_jh_hash(const void* input, size_t len, char* output) { + jh_hash(32 * 8, static_cast(input), 8 * len, reinterpret_cast(output)); +} + + +static inline void do_skein_hash(const void* input, size_t len, char* output) { + xmr_skein(static_cast(input), reinterpret_cast(output)); +} + + +void (* const extra_hashes[4])(const void *, size_t, char *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; + + + +#if defined(__x86_64__) || defined(_M_AMD64) +# define EXTRACT64(X) _mm_cvtsi128_si64(X) + +# ifdef __GNUC__ +static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) +{ + unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; + *hi = r >> 64; + return (uint64_t) r; +} +# else + #define __umul128 _umul128 +# endif +#elif defined(__i386__) || defined(_M_IX86) +# define HI32(X) \ + _mm_srli_si128((X), 4) + + +# define EXTRACT64(X) \ + ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ + ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) + +static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = multiplier >> 32; + uint64_t b = multiplier & 0xFFFFFFFF; + uint64_t c = multiplicand >> 32; + uint64_t d = multiplicand & 0xFFFFFFFF; + + //uint64_t ac = a * c; + uint64_t ad = a * d; + //uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + (b * c); + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + + return product_lo; +} +#endif + + +// This will shift and xor tmp1 into itself as 4 32-bit vals such as +// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) +static inline __m128i sl_xor(__m128i tmp1) +{ + __m128i tmp4; + tmp4 = _mm_slli_si128(tmp1, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + return tmp1; +} + + +template +static inline void aes_genkey_sub(__m128i* xout0, __m128i* xout2) +{ + __m128i xout1 = _mm_aeskeygenassist_si128(*xout2, rcon); + xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem + *xout0 = sl_xor(*xout0); + *xout0 = _mm_xor_si128(*xout0, xout1); + xout1 = _mm_aeskeygenassist_si128(*xout0, 0x00); + xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem + *xout2 = sl_xor(*xout2); + *xout2 = _mm_xor_si128(*xout2, xout1); +} + + +template +static inline void soft_aes_genkey_sub(__m128i* xout0, __m128i* xout2) +{ + __m128i xout1 = soft_aeskeygenassist(*xout2); + xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem + *xout0 = sl_xor(*xout0); + *xout0 = _mm_xor_si128(*xout0, xout1); + xout1 = soft_aeskeygenassist<0x00>(*xout0); + xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem + *xout2 = sl_xor(*xout2); + *xout2 = _mm_xor_si128(*xout2, xout1); +} + + +template +static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) +{ + __m128i xout0 = _mm_load_si128(memory); + __m128i xout2 = _mm_load_si128(memory + 1); + *k0 = xout0; + *k1 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x01>(&xout0, &xout2) : aes_genkey_sub<0x01>(&xout0, &xout2); + *k2 = xout0; + *k3 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x02>(&xout0, &xout2) : aes_genkey_sub<0x02>(&xout0, &xout2); + *k4 = xout0; + *k5 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x04>(&xout0, &xout2) : aes_genkey_sub<0x04>(&xout0, &xout2); + *k6 = xout0; + *k7 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x08>(&xout0, &xout2) : aes_genkey_sub<0x08>(&xout0, &xout2); + *k8 = xout0; + *k9 = xout2; +} + + +template +static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) +{ + if (SOFT_AES) { + *x0 = soft_aesenc(*x0, key); + *x1 = soft_aesenc(*x1, key); + *x2 = soft_aesenc(*x2, key); + *x3 = soft_aesenc(*x3, key); + *x4 = soft_aesenc(*x4, key); + *x5 = soft_aesenc(*x5, key); + *x6 = soft_aesenc(*x6, key); + *x7 = soft_aesenc(*x7, key); + } + else { + *x0 = _mm_aesenc_si128(*x0, key); + *x1 = _mm_aesenc_si128(*x1, key); + *x2 = _mm_aesenc_si128(*x2, key); + *x3 = _mm_aesenc_si128(*x3, key); + *x4 = _mm_aesenc_si128(*x4, key); + *x5 = _mm_aesenc_si128(*x5, key); + *x6 = _mm_aesenc_si128(*x6, key); + *x7 = _mm_aesenc_si128(*x7, key); + } +} + + +template +static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) +{ + __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xin0 = _mm_load_si128(input + 4); + xin1 = _mm_load_si128(input + 5); + xin2 = _mm_load_si128(input + 6); + xin3 = _mm_load_si128(input + 7); + xin4 = _mm_load_si128(input + 8); + xin5 = _mm_load_si128(input + 9); + xin6 = _mm_load_si128(input + 10); + xin7 = _mm_load_si128(input + 11); + + for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { + aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + + _mm_store_si128(output + i + 0, xin0); + _mm_store_si128(output + i + 1, xin1); + _mm_store_si128(output + i + 2, xin2); + _mm_store_si128(output + i + 3, xin3); + _mm_store_si128(output + i + 4, xin4); + _mm_store_si128(output + i + 5, xin5); + _mm_store_si128(output + i + 6, xin6); + _mm_store_si128(output + i + 7, xin7); + } +} + + +template +static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) +{ + __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xout0 = _mm_load_si128(output + 4); + xout1 = _mm_load_si128(output + 5); + xout2 = _mm_load_si128(output + 6); + xout3 = _mm_load_si128(output + 7); + xout4 = _mm_load_si128(output + 8); + xout5 = _mm_load_si128(output + 9); + xout6 = _mm_load_si128(output + 10); + xout7 = _mm_load_si128(output + 11); + + for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) + { + xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); + xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); + xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); + xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); + xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); + xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); + xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); + xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + _mm_store_si128(output + 4, xout0); + _mm_store_si128(output + 5, xout1); + _mm_store_si128(output + 6, xout2); + _mm_store_si128(output + 7, xout3); + _mm_store_si128(output + 8, xout4); + _mm_store_si128(output + 9, xout5); + _mm_store_si128(output + 10, xout6); + _mm_store_si128(output + 11, xout7); +} + + +template +inline void cryptonight_hash(const void *__restrict__ input, size_t size, void *__restrict__ output, cryptonight_ctx *__restrict__ ctx) +{ + keccak(static_cast(input), (int) size, ctx->state0, 200); + + cn_explode_scratchpad((__m128i*) ctx->state0, (__m128i*) ctx->memory); + + const uint8_t* l0 = ctx->memory; + uint64_t* h0 = reinterpret_cast(ctx->state0); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); + + uint64_t idx0 = h0[0] ^ h0[4]; + + for (size_t i = 0; i < ITERATIONS; i++) { + __m128i cx; + cx = _mm_load_si128((__m128i *) &l0[idx0 & MASK]); + + if (SOFT_AES) { + cx = soft_aesenc(cx, _mm_set_epi64x(ah0, al0)); + } + else { + cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah0, al0)); + } + + _mm_store_si128((__m128i *) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx)); + idx0 = EXTRACT64(cx); + bx0 = cx; + + uint64_t hi, lo, cl, ch; + cl = ((uint64_t*) &l0[idx0 & MASK])[0]; + ch = ((uint64_t*) &l0[idx0 & MASK])[1]; + lo = __umul128(idx0, cl, &hi); + + al0 += hi; + ah0 += lo; + + ((uint64_t*)&l0[idx0 & MASK])[0] = al0; + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0; + + ah0 ^= ch; + al0 ^= cl; + idx0 = al0; + } + + cn_implode_scratchpad((__m128i*) ctx->memory, (__m128i*) ctx->state0); + + keccakf(h0, 24); + extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, static_cast(output)); +} + + +template +inline void cryptonight_double_hash(const void *__restrict__ input, size_t size, void *__restrict__ output, struct cryptonight_ctx *__restrict__ ctx) +{ + keccak((const uint8_t *) input, (int) size, ctx->state0, 200); + keccak((const uint8_t *) input + size, (int) size, ctx->state1, 200); + + const uint8_t* l0 = ctx->memory; + const uint8_t* l1 = ctx->memory + MEM; + uint64_t* h0 = reinterpret_cast(ctx->state0); + uint64_t* h1 = reinterpret_cast(ctx->state1); + + cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); + cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t al1 = h1[0] ^ h1[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + uint64_t ah1 = h1[1] ^ h1[5]; + + __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); + __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); + + uint64_t idx0 = h0[0] ^ h0[4]; + uint64_t idx1 = h1[0] ^ h1[4]; + + for (size_t i = 0; i < ITERATIONS; i++) { + __m128i cx0 = _mm_load_si128((__m128i *) &l0[idx0 & MASK]); + __m128i cx1 = _mm_load_si128((__m128i *) &l1[idx1 & MASK]); + + if (SOFT_AES) { + cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); + cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); + } + else { + cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); + cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); + } + + _mm_store_si128((__m128i *) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); + _mm_store_si128((__m128i *) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); + + idx0 = EXTRACT64(cx0); + idx1 = EXTRACT64(cx1); + + bx0 = cx0; + bx1 = cx1; + + uint64_t hi, lo, cl, ch; + cl = ((uint64_t*) &l0[idx0 & MASK])[0]; + ch = ((uint64_t*) &l0[idx0 & MASK])[1]; + lo = __umul128(idx0, cl, &hi); + + al0 += hi; + ah0 += lo; + + ((uint64_t*) &l0[idx0 & MASK])[0] = al0; + ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; + + ah0 ^= ch; + al0 ^= cl; + idx0 = al0; + + cl = ((uint64_t*) &l1[idx1 & MASK])[0]; + ch = ((uint64_t*) &l1[idx1 & MASK])[1]; + lo = __umul128(idx1, cl, &hi); + + al1 += hi; + ah1 += lo; + + ((uint64_t*) &l1[idx1 & MASK])[0] = al1; + ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; + + ah1 ^= ch; + al1 ^= cl; + idx1 = al1; + } + + cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); + cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); + + keccakf(h0, 24); + keccakf(h1, 24); + + extra_hashes[ctx->state0[0] & 3](ctx->state0, 200, static_cast(output)); + extra_hashes[ctx->state1[0] & 3](ctx->state1, 200, static_cast(output) + 32); +} + +#endif /* __CRYPTONIGHT_P_H__ */ diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h new file mode 100644 index 00000000..b2985379 --- /dev/null +++ b/src/crypto/CryptoNight_test.h @@ -0,0 +1,60 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CRYPTONIGHT_TEST_H__ +#define __CRYPTONIGHT_TEST_H__ + + +const static uint8_t test_input[152] = { + 0x01, 0x00, 0xFB, 0x8E, 0x8A, 0xC8, 0x05, 0x89, 0x93, 0x23, 0x37, 0x1B, 0xB7, 0x90, 0xDB, 0x19, + 0x21, 0x8A, 0xFD, 0x8D, 0xB8, 0xE3, 0x75, 0x5D, 0x8B, 0x90, 0xF3, 0x9B, 0x3D, 0x55, 0x06, 0xA9, + 0xAB, 0xCE, 0x4F, 0xA9, 0x12, 0x24, 0x45, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x81, 0x46, 0xD4, 0x9F, + 0xA9, 0x3E, 0xE7, 0x24, 0xDE, 0xB5, 0x7D, 0x12, 0xCB, 0xC6, 0xC6, 0xF3, 0xB9, 0x24, 0xD9, 0x46, + 0x12, 0x7C, 0x7A, 0x97, 0x41, 0x8F, 0x93, 0x48, 0x82, 0x8F, 0x0F, 0x02, + 0x03, 0x05, 0xA0, 0xDB, 0xD6, 0xBF, 0x05, 0xCF, 0x16, 0xE5, 0x03, 0xF3, 0xA6, 0x6F, 0x78, 0x00, + 0x7C, 0xBF, 0x34, 0x14, 0x43, 0x32, 0xEC, 0xBF, 0xC2, 0x2E, 0xD9, 0x5C, 0x87, 0x00, 0x38, 0x3B, + 0x30, 0x9A, 0xCE, 0x19, 0x23, 0xA0, 0x96, 0x4B, 0x00, 0x00, 0x00, 0x08, 0xBA, 0x93, 0x9A, 0x62, + 0x72, 0x4C, 0x0D, 0x75, 0x81, 0xFC, 0xE5, 0x76, 0x1E, 0x9D, 0x8A, 0x0E, 0x6A, 0x1C, 0x3F, 0x92, + 0x4F, 0xDD, 0x84, 0x93, 0xD1, 0x11, 0x56, 0x49, 0xC0, 0x5E, 0xB6, 0x01 +}; + + +const static uint8_t test_output0[64] = { + 0x1B, 0x60, 0x6A, 0x3F, 0x4A, 0x07, 0xD6, 0x48, 0x9A, 0x1B, 0xCD, 0x07, 0x69, 0x7B, 0xD1, 0x66, + 0x96, 0xB6, 0x1C, 0x8A, 0xE9, 0x82, 0xF6, 0x1A, 0x90, 0x16, 0x0F, 0x4E, 0x52, 0x82, 0x8A, 0x7F, + 0x1A, 0x3F, 0xFB, 0xEE, 0x90, 0x9B, 0x42, 0x0D, 0x91, 0xF7, 0xBE, 0x6E, 0x5F, 0xB5, 0x6D, 0xB7, + 0x1B, 0x31, 0x10, 0xD8, 0x86, 0x01, 0x1E, 0x87, 0x7E, 0xE5, 0x78, 0x6A, 0xFD, 0x08, 0x01, 0x00 +}; + + +#ifndef XMRIG_NO_AEON +const static uint8_t test_output1[64] = { + 0x28, 0xA2, 0x2B, 0xAD, 0x3F, 0x93, 0xD1, 0x40, 0x8F, 0xCA, 0x47, 0x2E, 0xB5, 0xAD, 0x1C, 0xBE, + 0x75, 0xF2, 0x1D, 0x05, 0x3C, 0x8C, 0xE5, 0xB3, 0xAF, 0x10, 0x5A, 0x57, 0x71, 0x3E, 0x21, 0xDD, + 0x36, 0x95, 0xB4, 0xB5, 0x3B, 0xB0, 0x03, 0x58, 0xB0, 0xAD, 0x38, 0xDC, 0x16, 0x0F, 0xEB, 0x9E, + 0x00, 0x4E, 0xEC, 0xE0, 0x9B, 0x83, 0xA7, 0x2E, 0xF6, 0xBA, 0x98, 0x64, 0xD3, 0x51, 0x0C, 0x88, +}; +#endif + + +#endif /* __CRYPTONIGHT_TEST_H__ */ diff --git a/crypto/c_blake256.c b/src/crypto/c_blake256.c similarity index 99% rename from crypto/c_blake256.c rename to src/crypto/c_blake256.c index 0b484b94..00a84c22 100644 --- a/crypto/c_blake256.c +++ b/src/crypto/c_blake256.c @@ -148,7 +148,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { if (datalen > 0) { memcpy((void *) (S->buf + left), (void *) data, datalen >> 3); - S->buflen = (left << 3) + datalen; + S->buflen = (left << 3) + (int) datalen; } else { S->buflen = 0; } diff --git a/crypto/c_blake256.h b/src/crypto/c_blake256.h similarity index 100% rename from crypto/c_blake256.h rename to src/crypto/c_blake256.h diff --git a/crypto/c_groestl.c b/src/crypto/c_groestl.c similarity index 100% rename from crypto/c_groestl.c rename to src/crypto/c_groestl.c diff --git a/crypto/c_groestl.h b/src/crypto/c_groestl.h similarity index 100% rename from crypto/c_groestl.h rename to src/crypto/c_groestl.h diff --git a/crypto/c_jh.c b/src/crypto/c_jh.c similarity index 100% rename from crypto/c_jh.c rename to src/crypto/c_jh.c diff --git a/crypto/c_jh.h b/src/crypto/c_jh.h similarity index 100% rename from crypto/c_jh.h rename to src/crypto/c_jh.h diff --git a/src/crypto/c_keccak.c b/src/crypto/c_keccak.c new file mode 100644 index 00000000..997db241 --- /dev/null +++ b/src/crypto/c_keccak.c @@ -0,0 +1,176 @@ +// keccak.c +// 19-Nov-11 Markku-Juhani O. Saarinen +// A baseline Keccak (3rd round) implementation. + +#include +#include + +#define HASH_DATA_AREA 136 +#define KECCAK_ROUNDS 24 + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +const uint64_t keccakf_rndc[24] = +{ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 +}; + +// update the state with given number of rounds + +void keccakf(uint64_t st[25], int rounds) +{ + int i, j, round; + uint64_t t, bc[5]; + + for (round = 0; round < rounds; ++round) { + + // Theta + bc[0] = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20]; + bc[1] = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21]; + bc[2] = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22]; + bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23]; + bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24]; + + for (i = 0; i < 5; ++i) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + st[i ] ^= t; + st[i + 5] ^= t; + st[i + 10] ^= t; + st[i + 15] ^= t; + st[i + 20] ^= t; + } + + // Rho Pi + t = st[1]; + st[ 1] = ROTL64(st[ 6], 44); + st[ 6] = ROTL64(st[ 9], 20); + st[ 9] = ROTL64(st[22], 61); + st[22] = ROTL64(st[14], 39); + st[14] = ROTL64(st[20], 18); + st[20] = ROTL64(st[ 2], 62); + st[ 2] = ROTL64(st[12], 43); + st[12] = ROTL64(st[13], 25); + st[13] = ROTL64(st[19], 8); + st[19] = ROTL64(st[23], 56); + st[23] = ROTL64(st[15], 41); + st[15] = ROTL64(st[ 4], 27); + st[ 4] = ROTL64(st[24], 14); + st[24] = ROTL64(st[21], 2); + st[21] = ROTL64(st[ 8], 55); + st[ 8] = ROTL64(st[16], 45); + st[16] = ROTL64(st[ 5], 36); + st[ 5] = ROTL64(st[ 3], 28); + st[ 3] = ROTL64(st[18], 21); + st[18] = ROTL64(st[17], 15); + st[17] = ROTL64(st[11], 10); + st[11] = ROTL64(st[ 7], 6); + st[ 7] = ROTL64(st[10], 3); + st[10] = ROTL64(t, 1); + + // Chi + // unrolled loop, where only last iteration is different + j = 0; + bc[0] = st[j ]; + bc[1] = st[j + 1]; + + st[j ] ^= (~st[j + 1]) & st[j + 2]; + st[j + 1] ^= (~st[j + 2]) & st[j + 3]; + st[j + 2] ^= (~st[j + 3]) & st[j + 4]; + st[j + 3] ^= (~st[j + 4]) & bc[0]; + st[j + 4] ^= (~bc[0]) & bc[1]; + + j = 5; + bc[0] = st[j ]; + bc[1] = st[j + 1]; + + st[j ] ^= (~st[j + 1]) & st[j + 2]; + st[j + 1] ^= (~st[j + 2]) & st[j + 3]; + st[j + 2] ^= (~st[j + 3]) & st[j + 4]; + st[j + 3] ^= (~st[j + 4]) & bc[0]; + st[j + 4] ^= (~bc[0]) & bc[1]; + + j = 10; + bc[0] = st[j ]; + bc[1] = st[j + 1]; + + st[j ] ^= (~st[j + 1]) & st[j + 2]; + st[j + 1] ^= (~st[j + 2]) & st[j + 3]; + st[j + 2] ^= (~st[j + 3]) & st[j + 4]; + st[j + 3] ^= (~st[j + 4]) & bc[0]; + st[j + 4] ^= (~bc[0]) & bc[1]; + + j = 15; + bc[0] = st[j ]; + bc[1] = st[j + 1]; + + st[j ] ^= (~st[j + 1]) & st[j + 2]; + st[j + 1] ^= (~st[j + 2]) & st[j + 3]; + st[j + 2] ^= (~st[j + 3]) & st[j + 4]; + st[j + 3] ^= (~st[j + 4]) & bc[0]; + st[j + 4] ^= (~bc[0]) & bc[1]; + + j = 20; + bc[0] = st[j ]; + bc[1] = st[j + 1]; + bc[2] = st[j + 2]; + bc[3] = st[j + 3]; + bc[4] = st[j + 4]; + + st[j ] ^= (~bc[1]) & bc[2]; + st[j + 1] ^= (~bc[2]) & bc[3]; + st[j + 2] ^= (~bc[3]) & bc[4]; + st[j + 3] ^= (~bc[4]) & bc[0]; + st[j + 4] ^= (~bc[0]) & bc[1]; + + // Iota + st[0] ^= keccakf_rndc[round]; + } +} + +// compute a keccak hash (md) of given byte length from "in" +typedef uint64_t state_t[25]; + +void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) +{ + state_t st; + uint8_t temp[144]; + int i, rsiz, rsizw; + + rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; + rsizw = rsiz / 8; + + memset(st, 0, sizeof(st)); + + for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) in)[i]; + keccakf(st, KECCAK_ROUNDS); + } + + // last block and padding + memcpy(temp, in, inlen); + temp[inlen++] = 1; + memset(temp + inlen, 0, rsiz - inlen); + temp[rsiz - 1] |= 0x80; + + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) temp)[i]; + + keccakf(st, KECCAK_ROUNDS); + + memcpy(md, st, mdlen); +} + +void keccak1600(const uint8_t *in, int inlen, uint8_t *md) +{ + keccak(in, inlen, md, sizeof(state_t)); +} diff --git a/crypto/c_keccak.h b/src/crypto/c_keccak.h similarity index 100% rename from crypto/c_keccak.h rename to src/crypto/c_keccak.h diff --git a/src/crypto/c_skein.c b/src/crypto/c_skein.c new file mode 100644 index 00000000..994e4d46 --- /dev/null +++ b/src/crypto/c_skein.c @@ -0,0 +1,701 @@ +/*********************************************************************** +** +** Implementation of the Skein hash function. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +************************************************************************/ + +#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */ + +#include /* get size_t definition */ +#include /* get the memcpy/memset functions */ +#include "c_skein.h" /* get the Skein API definitions */ + +#ifndef SKEIN_512_NIST_MAX_HASHBITS +#define SKEIN_512_NIST_MAX_HASHBITS (512) +#endif + +#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */ + +#define SKEIN_512_STATE_WORDS ( 8) +#define SKEIN_MAX_STATE_WORDS (16) + +#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS) +#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS) + +#define SKEIN_RND_SPECIAL (1000u) +#define SKEIN_RND_KEY_INITIAL (SKEIN_RND_SPECIAL+0u) +#define SKEIN_RND_KEY_INJECT (SKEIN_RND_SPECIAL+1u) +#define SKEIN_RND_FEED_FWD (SKEIN_RND_SPECIAL+2u) + +typedef struct +{ + size_t hashBitLen; /* size of hash result, in bits */ + size_t bCnt; /* current byte count in buffer b[] */ + u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */ +} Skein_Ctxt_Hdr_t; + +typedef struct /* 512-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein_512_Ctxt_t; + +/* Skein APIs for (incremental) "straight hashing" */ +static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); + +#ifndef SKEIN_TREE_HASH +#define SKEIN_TREE_HASH (1) +#endif + +/***************************************************************** +** "Internal" Skein definitions +** -- not needed for sequential hashing API, but will be +** helpful for other uses of Skein (e.g., tree hash mode). +** -- included here so that they can be shared between +** reference and optimized code. +******************************************************************/ + +/* tweak word T[1]: bit field starting positions */ +#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */ + +#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */ +#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */ +#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */ +#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */ +#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */ + +/* tweak word T[1]: flag bit definition(s) */ +#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST) +#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL) +#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD) + +/* tweak word T[1]: tree level bit field mask */ +#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL) +#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL) + +/* tweak word T[1]: block type field */ +#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */ +#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */ +#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */ +#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */ +#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */ +#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */ +#define SKEIN_BLK_TYPE_MSG (48) /* message processing */ +#define SKEIN_BLK_TYPE_OUT (63) /* output stage */ +#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */ + +#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE) +#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */ +#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */ +#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */ +#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */ +#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */ +#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */ +#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */ +#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */ +#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */ + +#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL) +#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL) + +#define SKEIN_VERSION (1) + +#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */ +#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/ +#endif + +#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32)) +#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE) +#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22) + +#define SKEIN_CFG_STR_LEN (4*8) + +/* bit field definitions in config block treeInfo word */ +#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0) +#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8) +#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16) + +#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS) +#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS) +#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS) + +#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \ + ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \ + (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \ + (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) ) + +#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */ + +/* +** Skein macros for getting/setting tweak words, etc. +** These are useful for partial input bytes, hash tree init/update, etc. +**/ +#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM]) +#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);} + +#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0) +#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1) +#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0) +#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1) + +/* set both tweak words at once */ +#define Skein_Set_T0_T1(ctxPtr,T0,T1) \ +{ \ + Skein_Set_T0(ctxPtr,(T0)); \ + Skein_Set_T1(ctxPtr,(T1)); \ +} + +#define Skein_Set_Type(ctxPtr,BLK_TYPE) \ + Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE) + +/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */ +#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \ +{ Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; } + +#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; } +#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; } + +#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);} + +/***************************************************************** +** "Internal" Skein definitions for debugging and error checking +******************************************************************/ +#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr) +#define Skein_Show_Round(bits,ctx,r,X) +#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr) +#define Skein_Show_Final(bits,ctx,cnt,outPtr) +#define Skein_Show_Key(bits,ctx,key,keyBytes) + + +#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */ +#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */ +#define Skein_assert(x) +#elif defined(SKEIN_ASSERT) +#include +#define Skein_Assert(x,retCode) assert(x) +#define Skein_assert(x) assert(x) +#else +#include +#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */ +#define Skein_assert(x) assert(x) /* internal error */ +#endif + +/***************************************************************** +** Skein block function constants (shared across Ref and Opt code) +******************************************************************/ +enum +{ + /* Skein_512 round rotation constants */ + R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37, + R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42, + R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39, + R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56, + R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24, + R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17, + R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43, + R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22, +}; + +#ifndef SKEIN_ROUNDS +#define SKEIN_512_ROUNDS_TOTAL (72) +#else /* allow command-line define in range 8*(5..14) */ +#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5)) +#endif + + +/* +***************** Pre-computed Skein IVs ******************* +** +** NOTE: these values are not "magic" constants, but +** are generated using the Threefish block function. +** They are pre-computed here only for speed; i.e., to +** avoid the need for a Threefish call during Init(). +** +** The IV for any fixed hash length may be pre-computed. +** Only the most common values are included here. +** +************************************************************ +**/ + +#define MK_64 SKEIN_MK_64 + +/* blkSize = 512 bits. hashSize = 256 bits */ +const u64b_t SKEIN_512_IV_256[] = + { + MK_64(0xCCD044A1,0x2FDB3E13), + MK_64(0xE8359030,0x1A79A9EB), + MK_64(0x55AEA061,0x4F816E6F), + MK_64(0x2A2767A4,0xAE9B94DB), + MK_64(0xEC06025E,0x74DD7683), + MK_64(0xE7A436CD,0xC4746251), + MK_64(0xC36FBAF9,0x393AD185), + MK_64(0x3EEDBA18,0x33EDFC13) + }; + +#ifndef SKEIN_USE_ASM +#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */ +#endif + +#ifndef SKEIN_LOOP +#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */ +#endif + +#define BLK_BITS (WCNT*64) /* some useful definitions for code here */ +#define KW_TWK_BASE (0) +#define KW_KEY_BASE (3) +#define ks (kw + KW_KEY_BASE) +#define ts (kw + KW_TWK_BASE) + +#ifdef SKEIN_DEBUG +#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; } +#else +#define DebugSaveTweak(ctx) +#endif + +/***************************** Skein_512 ******************************/ +#if !(SKEIN_USE_ASM & 512) +static void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_512_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_512_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10) +#else +#define SKEIN_UNROLL_512 (0) +#endif + +#if SKEIN_UNROLL_512 +#if (RCNT % SKEIN_UNROLL_512) +#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; + Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ctx->X[4]; + ks[5] = ctx->X[5]; + ks[6] = ctx->X[6]; + ks[7] = ctx->X[7]; + ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ + ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1]; + X2 = w[2] + ks[2]; + X3 = w[3] + ks[3]; + X4 = w[4] + ks[4]; + X5 = w[5] + ks[5] + ts[0]; + X6 = w[6] + ks[6] + ts[1]; + X7 = w[7] + ks[7]; + + blkPtr += SKEIN_512_BLOCK_BYTES; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + /* run the rounds */ +#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + +#if SKEIN_UNROLL_512 == 0 +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I512(R) \ + X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 9]; \ + X2 += ks[((R)+3) % 9]; \ + X3 += ks[((R)+4) % 9]; \ + X4 += ks[((R)+5) % 9]; \ + X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \ + X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \ + X7 += ks[((R)+8) % 9] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I512(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1]; \ + X2 += ks[r+(R)+2]; \ + X3 += ks[r+(R)+3]; \ + X4 += ks[r+(R)+4]; \ + X5 += ks[r+(R)+5] + ts[r+(R)+0]; \ + X6 += ks[r+(R)+6] + ts[r+(R)+1]; \ + X7 += ks[r+(R)+7] + r+(R) ; \ + ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */ +#endif /* end of looped code definitions */ + { +#define R512_8_rounds(R) /* do 8 full rounds */ \ + R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \ + R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \ + R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \ + R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \ + I512(2*(R)); \ + R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \ + R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \ + R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \ + R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \ + I512(2*(R)+1); /* and key injection */ + + R512_8_rounds( 0); + +#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN))) + + #if R512_Unroll_R( 1) + R512_8_rounds( 1); + #endif + #if R512_Unroll_R( 2) + R512_8_rounds( 2); + #endif + #if R512_Unroll_R( 3) + R512_8_rounds( 3); + #endif + #if R512_Unroll_R( 4) + R512_8_rounds( 4); + #endif + #if R512_Unroll_R( 5) + R512_8_rounds( 5); + #endif + #if R512_Unroll_R( 6) + R512_8_rounds( 6); + #endif + #if R512_Unroll_R( 7) + R512_8_rounds( 7); + #endif + #if R512_Unroll_R( 8) + R512_8_rounds( 8); + #endif + #if R512_Unroll_R( 9) + R512_8_rounds( 9); + #endif + #if R512_Unroll_R(10) + R512_8_rounds(10); + #endif + #if R512_Unroll_R(11) + R512_8_rounds(11); + #endif + #if R512_Unroll_R(12) + R512_8_rounds(12); + #endif + #if R512_Unroll_R(13) + R512_8_rounds(13); + #endif + #if R512_Unroll_R(14) + R512_8_rounds(14); + #endif + #if (SKEIN_UNROLL_512 > 14) +#error "need more unrolling in Skein_512_Process_Block" + #endif + } + + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + ctx->X[4] = X4 ^ w[4]; + ctx->X[5] = X5 ^ w[5]; + ctx->X[6] = X6 ^ w[6]; + ctx->X[7] = X7 ^ w[7]; + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } +#endif + +/*****************************************************************/ +/* 512-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 256: memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES); + Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_512_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */ + Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_512_BLOCK_BYTES; + msg += n * SKEIN_512_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_API_CodeSize(void) + { + return ((u08b_t *) Skein_512_API_CodeSize) - + ((u08b_t *) Skein_512_Init); + } +#endif + +typedef struct +{ + uint_t statebits; /* 256, 512, or 1024 */ + union + { + Skein_Ctxt_Hdr_t h; /* common header "overlay" */ + Skein_512_Ctxt_t ctx_512; + } u; +} +hashState; + +/* "incremental" hashing API */ +static SkeinHashReturn Init (hashState *state, int hashbitlen); +static SkeinHashReturn Update(hashState *state, const SkeinBitSequence *data, SkeinDataLength databitlen); +static SkeinHashReturn Final (hashState *state, SkeinBitSequence *hashval); + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* select the context size and init the context */ +static SkeinHashReturn Init(hashState *state, int hashbitlen) +{ + state->statebits = 64*SKEIN_512_STATE_WORDS; + return Skein_512_Init(&state->u.ctx_512,(size_t) hashbitlen); +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process data to be hashed */ +static SkeinHashReturn Update(hashState *state, const SkeinBitSequence *data, SkeinDataLength databitlen) +{ + /* only the final Update() call is allowed do partial bytes, else assert an error */ + Skein_Assert((state->u.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || databitlen == 0, SKEIN_FAIL); + + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,SKEIN_FAIL); + if ((databitlen & 7) == 0) /* partial bytes? */ + { + return Skein_512_Update(&state->u.ctx_512,data,databitlen >> 3); + } + else + { /* handle partial final byte */ + size_t bCnt = (databitlen >> 3) + 1; /* number of bytes to handle (nonzero here!) */ + u08b_t b,mask; + + mask = (u08b_t) (1u << (7 - (databitlen & 7))); /* partial byte bit mask */ + b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ + + Skein_512_Update(&state->u.ctx_512,data,bCnt-1); /* process all but the final byte */ + Skein_512_Update(&state->u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ + Skein_Set_Bit_Pad_Flag(state->u.h); /* set tweak flag for the final call */ + + return SKEIN_SUCCESS; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize hash computation and output the result (hashbitlen bits) */ +static SkeinHashReturn Final(hashState *state, SkeinBitSequence *hashval) +{ + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,FAIL); + return Skein_512_Final(&state->u.ctx_512,hashval); +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* all-in-one hash function */ +SkeinHashReturn skein_hash(int hashbitlen, const SkeinBitSequence *data, /* all-in-one call */ + SkeinDataLength databitlen,SkeinBitSequence *hashval) +{ + hashState state; + SkeinHashReturn r = Init(&state,hashbitlen); + if (r == SKEIN_SUCCESS) + { /* these calls do not fail when called properly */ + r = Update(&state,data,databitlen); + Final(&state,hashval); + } + return r; +} + +void xmr_skein(const SkeinBitSequence *data, SkeinBitSequence *hashval){ + #define XMR_HASHBITLEN 256 + #define XMR_DATABITLEN 1600 + + // Init + hashState state; + state.statebits = 64*SKEIN_512_STATE_WORDS; + + // Skein_512_Init(&state.u.ctx_512, (size_t)XMR_HASHBITLEN); + state.u.ctx_512.h.hashBitLen = XMR_HASHBITLEN; + memcpy(state.u.ctx_512.X,SKEIN_512_IV_256,sizeof(state.u.ctx_512.X)); + Skein_512_Ctxt_t* ctx = &(state.u.ctx_512); + Skein_Start_New_Type(ctx,MSG); + + // Update + if ((XMR_DATABITLEN & 7) == 0){ /* partial bytes? */ + Skein_512_Update(&state.u.ctx_512,data,XMR_DATABITLEN >> 3); + }else{ /* handle partial final byte */ + size_t bCnt = (XMR_DATABITLEN >> 3) + 1; /* number of bytes to handle (nonzero here!) */ + u08b_t b,mask; + + mask = (u08b_t) (1u << (7 - (XMR_DATABITLEN & 7))); /* partial byte bit mask */ + b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ + + Skein_512_Update(&state.u.ctx_512,data,bCnt-1); /* process all but the final byte */ + Skein_512_Update(&state.u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ + Skein_Set_Bit_Pad_Flag(state.u.h); /* set tweak flag for the final call */ + } + + // Finalize + Skein_512_Final(&state.u.ctx_512, hashval); +} diff --git a/crypto/c_skein.h b/src/crypto/c_skein.h similarity index 96% rename from crypto/c_skein.h rename to src/crypto/c_skein.h index 0aaae760..c642e265 100644 --- a/crypto/c_skein.h +++ b/src/crypto/c_skein.h @@ -44,4 +44,6 @@ typedef u08b_t SkeinBitSequence; /* bit stream type */ SkeinHashReturn skein_hash(int hashbitlen, const SkeinBitSequence *data, SkeinDataLength databitlen, SkeinBitSequence *hashval); +void xmr_skein(const SkeinBitSequence *data, SkeinBitSequence *hashval); + #endif /* ifndef _SKEIN_H_ */ diff --git a/crypto/groestl_tables.h b/src/crypto/groestl_tables.h similarity index 100% rename from crypto/groestl_tables.h rename to src/crypto/groestl_tables.h diff --git a/crypto/hash.h b/src/crypto/hash.h similarity index 100% rename from crypto/hash.h rename to src/crypto/hash.h diff --git a/crypto/skein_port.h b/src/crypto/skein_port.h similarity index 100% rename from crypto/skein_port.h rename to src/crypto/skein_port.h diff --git a/src/crypto/soft_aes.h b/src/crypto/soft_aes.h new file mode 100644 index 00000000..e28be3fa --- /dev/null +++ b/src/crypto/soft_aes.h @@ -0,0 +1,125 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * + */ + +/* + * Parts of this file are originally copyright (c) 2014-2017, The Monero Project + */ +#pragma once + +#ifdef __GNUC__ +#include +#else +#include +#endif // __GNUC__ + +#include + +#define saes_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define SAES_WPOLY 0x011b + +#define saes_b2w(b0, b1, b2, b3) (((uint32_t)(b3) << 24) | \ + ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) + +#define saes_f2(x) ((x<<1) ^ (((x>>7) & 1) * SAES_WPOLY)) +#define saes_f3(x) (saes_f2(x) ^ x) +#define saes_h0(x) (x) + +#define saes_u0(p) saes_b2w(saes_f2(p), p, p, saes_f3(p)) +#define saes_u1(p) saes_b2w(saes_f3(p), saes_f2(p), p, p) +#define saes_u2(p) saes_b2w( p, saes_f3(p), saes_f2(p), p) +#define saes_u3(p) saes_b2w( p, p, saes_f3(p), saes_f2(p)) + +alignas(16) const uint32_t saes_table[4][256] = { saes_data(saes_u0), saes_data(saes_u1), saes_data(saes_u2), saes_data(saes_u3) }; +alignas(16) const uint8_t saes_sbox[256] = saes_data(saes_h0); + +static inline __m128i soft_aesenc(__m128i in, __m128i key) +{ + const uint32_t x0 = _mm_cvtsi128_si32(in); + const uint32_t x1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0x55)); + const uint32_t x2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0xAA)); + const uint32_t x3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0xFF)); + + __m128i out = _mm_set_epi32( + (saes_table[0][x3 & 0xff] ^ saes_table[1][(x0 >> 8) & 0xff] ^ saes_table[2][(x1 >> 16) & 0xff] ^ saes_table[3][x2 >> 24]), + (saes_table[0][x2 & 0xff] ^ saes_table[1][(x3 >> 8) & 0xff] ^ saes_table[2][(x0 >> 16) & 0xff] ^ saes_table[3][x1 >> 24]), + (saes_table[0][x1 & 0xff] ^ saes_table[1][(x2 >> 8) & 0xff] ^ saes_table[2][(x3 >> 16) & 0xff] ^ saes_table[3][x0 >> 24]), + (saes_table[0][x0 & 0xff] ^ saes_table[1][(x1 >> 8) & 0xff] ^ saes_table[2][(x2 >> 16) & 0xff] ^ saes_table[3][x3 >> 24])); + + return _mm_xor_si128(out, key); +} + +static inline uint32_t sub_word(uint32_t key) +{ + return (saes_sbox[key >> 24 ] << 24) | + (saes_sbox[(key >> 16) & 0xff] << 16 ) | + (saes_sbox[(key >> 8) & 0xff] << 8 ) | + saes_sbox[key & 0xff]; +} + +#ifdef __clang__ +static inline uint32_t _rotr(uint32_t value, uint32_t amount) +{ + return (value >> amount) | (value << ((32 - amount) & 31)); +} +#endif + +template +static inline __m128i soft_aeskeygenassist(__m128i key) +{ + const uint32_t X1 = sub_word(_mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0x55))); + const uint32_t X3 = sub_word(_mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0xFF))); + return _mm_set_epi32(_rotr(X3, 8) ^ rcon, X3, _rotr(X1, 8) ^ rcon, X1); +} diff --git a/src/donate.h b/src/donate.h new file mode 100644 index 00000000..3a000948 --- /dev/null +++ b/src/donate.h @@ -0,0 +1,43 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DONATE_H__ +#define __DONATE_H__ + + +/* + * Dev donation. + * + * Percentage of your hashing power that you want to donate to the developer, can be 0 if you don't want to do that. + * Example of how it works for the default setting of 1: + * You miner will mine into your usual pool for 99 minutes, then switch to the developer's pool for 1 minute. + * Switching is instant, and only happens after a successful connection, so you never loose any hashes. + * + * If you plan on changing this setting to 0 please consider making a one off donation to my wallet: + * XMR: 48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD + * BTC: 1P7ujsXeX7GxQwHNnJsRMgAdNkFZmNVqJT + */ +constexpr const int kDonateLevel = 5; + + +#endif /* __DONATE_H__ */ diff --git a/src/interfaces/IClientListener.h b/src/interfaces/IClientListener.h new file mode 100644 index 00000000..f6e7fd3c --- /dev/null +++ b/src/interfaces/IClientListener.h @@ -0,0 +1,48 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ICLIENTLISTENER_H__ +#define __ICLIENTLISTENER_H__ + + +#include + + +class Client; +class Job; +class SubmitResult; + + +class IClientListener +{ +public: + virtual ~IClientListener() {} + + virtual void onClose(Client *client, int failures) = 0; + virtual void onJobReceived(Client *client, const Job &job) = 0; + virtual void onLoginSuccess(Client *client) = 0; + virtual void onResultAccepted(Client *client, const SubmitResult &result, const char *error) = 0; +}; + + +#endif // __ICLIENTLISTENER_H__ diff --git a/stats.h b/src/interfaces/IConsoleListener.h similarity index 77% rename from stats.h rename to src/interfaces/IConsoleListener.h index 13ec117e..723f87df 100644 --- a/stats.h +++ b/src/interfaces/IConsoleListener.h @@ -21,17 +21,17 @@ * along with this program. If not, see . */ -#ifndef __STATS_H__ -#define __STATS_H__ - -#include -#include +#ifndef __ICONSOLELISTENER_H__ +#define __ICONSOLELISTENER_H__ -void stats_init(); -void stats_set_target(uint32_t new_target); -void stats_share_result(bool success); -void stats_add_hashes(int thr_id, struct timeval *tv_start, unsigned long hashes_done); +class IConsoleListener +{ +public: + virtual ~IConsoleListener() {} + + virtual void onConsoleCommand(char command) = 0; +}; -#endif /* __STATS_H__ */ +#endif // __ICONSOLELISTENER_H__ diff --git a/src/interfaces/IJobResultListener.h b/src/interfaces/IJobResultListener.h new file mode 100644 index 00000000..483a2062 --- /dev/null +++ b/src/interfaces/IJobResultListener.h @@ -0,0 +1,41 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __IJOBRESULTLISTENER_H__ +#define __IJOBRESULTLISTENER_H__ + + +class Client; +class JobResult; + + +class IJobResultListener +{ +public: + virtual ~IJobResultListener() {} + + virtual void onJobResult(const JobResult &result) = 0; +}; + + +#endif // __IJOBRESULTLISTENER_H__ diff --git a/src/interfaces/ILogBackend.h b/src/interfaces/ILogBackend.h new file mode 100644 index 00000000..458b504c --- /dev/null +++ b/src/interfaces/ILogBackend.h @@ -0,0 +1,41 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ILOGBACKEND_H__ +#define __ILOGBACKEND_H__ + + +#include + + +class ILogBackend +{ +public: + virtual ~ILogBackend() {} + + virtual void message(int level, const char* fmt, va_list args) = 0; + virtual void text(const char* fmt, va_list args) = 0; +}; + + +#endif // __ILOGBACKEND_H__ diff --git a/cpu.h b/src/interfaces/IStrategy.h similarity index 67% rename from cpu.h rename to src/interfaces/IStrategy.h index 419192bf..660529ea 100644 --- a/cpu.h +++ b/src/interfaces/IStrategy.h @@ -21,33 +21,28 @@ * along with this program. If not, see . */ -#ifndef __CPU_H__ -#define __CPU_H__ - -#include - -struct cpu_info { - int total_cores; - int total_logical_cpus; - int flags; - int sockets; - int l2_cache; - int l3_cache; - char brand[64]; -}; - -extern struct cpu_info cpu_info; +#ifndef __ISTRATEGY_H__ +#define __ISTRATEGY_H__ -enum cpu_flags { - CPU_FLAG_X86_64 = 1, - CPU_FLAG_AES = 2, - CPU_FLAG_BMI2 = 4 +#include + + +class JobResult; + + +class IStrategy +{ +public: + virtual ~IStrategy() {} + + virtual bool isActive() const = 0; + virtual int64_t submit(const JobResult &result) = 0; + virtual void connect() = 0; + virtual void resume() = 0; + virtual void stop() = 0; + virtual void tick(uint64_t now) = 0; }; -void cpu_init(); -int get_optimal_threads_count(int algo, bool double_hash, int max_cpu_usage); -int affine_to_cpu_mask(int id, unsigned long mask); - -#endif /* __CPU_H__ */ +#endif // __ISTRATEGY_H__ diff --git a/src/interfaces/IStrategyListener.h b/src/interfaces/IStrategyListener.h new file mode 100644 index 00000000..60f95734 --- /dev/null +++ b/src/interfaces/IStrategyListener.h @@ -0,0 +1,49 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ISTRATEGYLISTENER_H__ +#define __ISTRATEGYLISTENER_H__ + + +#include + + +class Client; +class IStrategy; +class Job; +class SubmitResult; + + +class IStrategyListener +{ +public: + virtual ~IStrategyListener() {} + + virtual void onActive(Client *client) = 0; + virtual void onJob(Client *client, const Job &job) = 0; + virtual void onPause(IStrategy *strategy) = 0; + virtual void onResultAccepted(Client *client, const SubmitResult &result, const char *error) = 0; +}; + + +#endif // __ISTRATEGYLISTENER_H__ diff --git a/src/interfaces/IWorker.h b/src/interfaces/IWorker.h new file mode 100644 index 00000000..b9b6eb0a --- /dev/null +++ b/src/interfaces/IWorker.h @@ -0,0 +1,42 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __IWORKER_H__ +#define __IWORKER_H__ + + +#include + + +class IWorker +{ +public: + virtual ~IWorker() {} + + virtual uint64_t hashCount() const = 0; + virtual uint64_t timestamp() const = 0; + virtual void start() = 0; +}; + + +#endif // __IWORKER_H__ diff --git a/src/log/ConsoleLog.cpp b/src/log/ConsoleLog.cpp new file mode 100644 index 00000000..ef8516eb --- /dev/null +++ b/src/log/ConsoleLog.cpp @@ -0,0 +1,153 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +# include +#endif + + +#include "log/ConsoleLog.h" +#include "log/Log.h" + + +ConsoleLog::ConsoleLog(bool colors) : + m_colors(colors), + m_stream(nullptr) +{ + if (uv_tty_init(uv_default_loop(), &m_tty, 1, 0) < 0) { + return; + } + + uv_tty_set_mode(&m_tty, UV_TTY_MODE_NORMAL); + m_uvBuf.base = m_buf; + m_stream = reinterpret_cast(&m_tty); + +# ifdef WIN32 + HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle != INVALID_HANDLE_VALUE) { + DWORD mode = 0; + if (GetConsoleMode(handle, &mode)) { + mode &= ~ENABLE_QUICK_EDIT_MODE; + SetConsoleMode(handle, mode | ENABLE_EXTENDED_FLAGS); + } + } +# endif +} + + +void ConsoleLog::message(int level, const char* fmt, va_list args) +{ + if (!isWritable()) { + return; + } + + time_t now = time(nullptr); + tm stime; + +# ifdef _WIN32 + localtime_s(&stime, &now); +# else + localtime_r(&now, &stime); +# endif + + const char* color = nullptr; + if (m_colors) { + switch (level) { + case Log::ERR: + color = Log::kCL_RED; + break; + + case Log::WARNING: + color = Log::kCL_YELLOW; + break; + + case Log::NOTICE: + color = Log::kCL_WHITE; + break; + + case Log::DEBUG: + color = Log::kCL_GRAY; + break; + + default: + color = ""; + break; + } + } + + snprintf(m_fmt, sizeof(m_fmt) - 1, "[%d-%02d-%02d %02d:%02d:%02d]%s %s%s\n", + stime.tm_year + 1900, + stime.tm_mon + 1, + stime.tm_mday, + stime.tm_hour, + stime.tm_min, + stime.tm_sec, + m_colors ? color : "", + fmt, + m_colors ? Log::kCL_N : "" + ); + + print(args); +} + + +void ConsoleLog::text(const char* fmt, va_list args) +{ + if (!isWritable()) { + return; + } + + snprintf(m_fmt, sizeof(m_fmt) - 1, "%s%s\n", fmt, m_colors ? Log::kCL_N : ""); + + print(args); +} + + +bool ConsoleLog::isWritable() const +{ + if (!m_stream || uv_is_writable(m_stream) != 1) { + return false; + } + + const uv_handle_type type = uv_guess_handle(1); + return type == UV_TTY || type == UV_NAMED_PIPE; +} + + +void ConsoleLog::print(va_list args) +{ + m_uvBuf.len = vsnprintf(m_buf, sizeof(m_buf) - 1, m_fmt, args); + if (m_uvBuf.len <= 0) { + return; + } + + uv_try_write(m_stream, &m_uvBuf, 1); +} diff --git a/src/log/ConsoleLog.h b/src/log/ConsoleLog.h new file mode 100644 index 00000000..a04a27c5 --- /dev/null +++ b/src/log/ConsoleLog.h @@ -0,0 +1,54 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CONSOLELOG_H__ +#define __CONSOLELOG_H__ + + +#include + + +#include "interfaces/ILogBackend.h" + + +class ConsoleLog : public ILogBackend +{ +public: + ConsoleLog(bool colors); + + void message(int level, const char *fmt, va_list args) override; + void text(const char *fmt, va_list args) override; + +private: + bool isWritable() const; + void print(va_list args); + + bool m_colors; + char m_buf[512]; + char m_fmt[256]; + uv_buf_t m_uvBuf; + uv_stream_t *m_stream; + uv_tty_t m_tty; +}; + +#endif /* __CONSOLELOG_H__ */ diff --git a/src/log/FileLog.cpp b/src/log/FileLog.cpp new file mode 100644 index 00000000..5eeb252c --- /dev/null +++ b/src/log/FileLog.cpp @@ -0,0 +1,97 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include + + +#include "log/FileLog.h" + + +FileLog::FileLog(const char *fileName) +{ + uv_fs_t req; + m_file = uv_fs_open(uv_default_loop(), &req, fileName, O_CREAT | O_APPEND | O_WRONLY, 0644, nullptr); + uv_fs_req_cleanup(&req); +} + + +void FileLog::message(int level, const char* fmt, va_list args) +{ + if (m_file < 0) { + return; + } + + time_t now = time(nullptr); + tm stime; + +# ifdef _WIN32 + localtime_s(&stime, &now); +# else + localtime_r(&now, &stime); +# endif + + char *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'; + + write(buf, size + 1); +} + + +void FileLog::text(const char* fmt, va_list args) +{ + message(0, fmt, args); +} + + + +void FileLog::onWrite(uv_fs_t *req) +{ + delete [] static_cast(req->data); + + uv_fs_req_cleanup(req); + delete req; +} + + +void FileLog::write(char *data, size_t size) +{ + uv_buf_t buf = uv_buf_init(data, (unsigned int) size); + uv_fs_t *req = new uv_fs_t; + req->data = buf.base; + + uv_fs_write(uv_default_loop(), req, m_file, &buf, 1, 0, FileLog::onWrite); +} diff --git a/src/log/FileLog.h b/src/log/FileLog.h new file mode 100644 index 00000000..2b3ca5d4 --- /dev/null +++ b/src/log/FileLog.h @@ -0,0 +1,50 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __FILELOG_H__ +#define __FILELOG_H__ + + +#include + + +#include "interfaces/ILogBackend.h" + + +class FileLog : public ILogBackend +{ +public: + FileLog(const char *fileName); + + void message(int level, const char* fmt, va_list args) override; + void text(const char* fmt, va_list args) override; + +private: + static void onWrite(uv_fs_t *req); + + void write(char *data, size_t size); + + int m_file; +}; + +#endif /* __FILELOG_H__ */ diff --git a/src/log/Log.cpp b/src/log/Log.cpp new file mode 100644 index 00000000..3e5d5671 --- /dev/null +++ b/src/log/Log.cpp @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include + + +#include "interfaces/ILogBackend.h" +#include "log/Log.h" + + +Log *Log::m_self = nullptr; + + +void Log::message(Log::Level level, const char* fmt, ...) +{ + va_list args; + va_list copy; + va_start(args, fmt); + + for (ILogBackend *backend : m_backends) { + va_copy(copy, args); + backend->message(level, fmt, copy); + va_end(copy); + } +} + + +void Log::text(const char* fmt, ...) +{ + va_list args; + va_list copy; + va_start(args, fmt); + + for (ILogBackend *backend : m_backends) { + va_copy(copy, args); + backend->text(fmt, copy); + va_end(copy); + } + + va_end(args); +} + + +Log::~Log() +{ + for (auto backend : m_backends) { + delete backend; + } +} diff --git a/src/log/Log.h b/src/log/Log.h new file mode 100644 index 00000000..fd944d80 --- /dev/null +++ b/src/log/Log.h @@ -0,0 +1,93 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + + +#include +#include + + +class ILogBackend; + + +class Log +{ +public: + enum Level { + ERR, + WARNING, + NOTICE, + INFO, + DEBUG + }; + + constexpr static const char* kCL_N = "\x1B[0m"; + constexpr static const char* kCL_RED = "\x1B[31m"; + constexpr static const char* kCL_YELLOW = "\x1B[33m"; + constexpr static const char* kCL_WHITE = "\x1B[01;37m"; + +# ifdef WIN32 + constexpr static const char* kCL_GRAY = "\x1B[01;30m"; +# else + constexpr static const char* kCL_GRAY = "\x1B[90m"; +# endif + + static inline Log* i() { return m_self; } + static inline void add(ILogBackend *backend) { i()->m_backends.push_back(backend); } + static inline void init() { if (!m_self) { m_self = new Log();} } + static inline void release() { delete m_self; } + + void message(Level level, const char* fmt, ...); + void text(const char* fmt, ...); + +private: + inline Log() {} + ~Log(); + + static Log *m_self; + std::vector m_backends; +}; + + +#define LOG_ERR(x, ...) Log::i()->message(Log::ERR, x, ##__VA_ARGS__) +#define LOG_WARN(x, ...) Log::i()->message(Log::WARNING, x, ##__VA_ARGS__) +#define LOG_NOTICE(x, ...) Log::i()->message(Log::NOTICE, x, ##__VA_ARGS__) +#define LOG_INFO(x, ...) Log::i()->message(Log::INFO, x, ##__VA_ARGS__) + +#ifdef APP_DEBUG +# define LOG_DEBUG(x, ...) Log::i()->message(Log::DEBUG, x, ##__VA_ARGS__) +#else +# define LOG_DEBUG(x, ...) +#endif + +#if defined(APP_DEBUG) || defined(APP_DEVEL) +# define LOG_DEBUG_ERR(x, ...) Log::i()->message(Log::ERR, x, ##__VA_ARGS__) +# define LOG_DEBUG_WARN(x, ...) Log::i()->message(Log::WARNING, x, ##__VA_ARGS__) +#else +# define LOG_DEBUG_ERR(x, ...) +# define LOG_DEBUG_WARN(x, ...) +#endif + +#endif /* __LOG_H__ */ diff --git a/src/log/SysLog.cpp b/src/log/SysLog.cpp new file mode 100644 index 00000000..f9b16cca --- /dev/null +++ b/src/log/SysLog.cpp @@ -0,0 +1,47 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "log/SysLog.h" +#include "version.h" + + +SysLog::SysLog() +{ + openlog(APP_ID, LOG_PID, LOG_USER); +} + + +void SysLog::message(int level, const char *fmt, va_list args) +{ + vsyslog(level, fmt, args); +} + + +void SysLog::text(const char *fmt, va_list args) +{ + message(LOG_INFO, fmt, args); +} diff --git a/src/log/SysLog.h b/src/log/SysLog.h new file mode 100644 index 00000000..38de1a6a --- /dev/null +++ b/src/log/SysLog.h @@ -0,0 +1,40 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SYSLOG_H__ +#define __SYSLOG_H__ + + +#include "interfaces/ILogBackend.h" + + +class SysLog : public ILogBackend +{ +public: + SysLog(); + + void message(int level, const char *fmt, va_list args) override; + void text(const char *fmt, va_list args) override; +}; + +#endif /* __SYSLOG_BACKEND_H__ */ diff --git a/src/net/Client.cpp b/src/net/Client.cpp new file mode 100644 index 00000000..88134ae7 --- /dev/null +++ b/src/net/Client.cpp @@ -0,0 +1,651 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + + +#include "interfaces/IClientListener.h" +#include "log/Log.h" +#include "net/Client.h" +#include "net/Url.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + + +#ifdef XMRIG_PROXY_PROJECT +# include "proxy/JobResult.h" +#else +# include "net/JobResult.h" +#endif + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +int64_t Client::m_sequence = 1; + + +Client::Client(int id, const char *agent, IClientListener *listener) : + m_quiet(false), + m_agent(agent), + m_listener(listener), + m_id(id), + m_retryPause(5000), + m_failures(0), + m_recvBufPos(0), + m_state(UnconnectedState), + m_expire(0), + m_stream(nullptr), + m_socket(nullptr) +{ + memset(m_ip, 0, sizeof(m_ip)); + memset(&m_hints, 0, sizeof(m_hints)); + + m_resolver.data = this; + + m_hints.ai_family = PF_INET; + m_hints.ai_socktype = SOCK_STREAM; + m_hints.ai_protocol = IPPROTO_TCP; + + m_recvBuf.base = m_buf; + m_recvBuf.len = sizeof(m_buf); + +# ifndef XMRIG_PROXY_PROJECT + m_keepAliveTimer.data = this; + uv_timer_init(uv_default_loop(), &m_keepAliveTimer); +# endif +} + + +Client::~Client() +{ + delete m_socket; +} + + +void Client::connect() +{ + resolve(m_url.host()); +} + + +/** + * @brief Connect to server. + * + * @param url + */ +void Client::connect(const Url *url) +{ + setUrl(url); + resolve(m_url.host()); +} + + +void Client::disconnect() +{ +# ifndef XMRIG_PROXY_PROJECT + uv_timer_stop(&m_keepAliveTimer); +# endif + + m_expire = 0; + m_failures = -1; + + close(); +} + + +void Client::setUrl(const Url *url) +{ + if (!url || !url->isValid()) { + return; + } + + m_url = url; +} + + +void Client::tick(uint64_t now) +{ + if (m_expire == 0 || now < m_expire) { + return; + } + + if (m_state == ConnectedState) { + LOG_DEBUG_ERR("[%s:%u] timeout", m_url.host(), m_url.port()); + close(); + } + + + if (m_state == ConnectingState) { + connect(); + } +} + + +int64_t Client::submit(const JobResult &result) +{ +# ifdef XMRIG_PROXY_PROJECT + const char *nonce = result.nonce; + const char *data = result.result; +# else + char nonce[9]; + char data[65]; + + Job::toHex(reinterpret_cast(&result.nonce), 4, nonce); + nonce[8] = '\0'; + + Job::toHex(result.result, 32, data); + data[64] = '\0'; +# endif + + const size_t size = snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRIu64 ",\"jsonrpc\":\"2.0\",\"method\":\"submit\",\"params\":{\"id\":\"%s\",\"job_id\":\"%s\",\"nonce\":\"%s\",\"result\":\"%s\"}}\n", + m_sequence, m_rpcId, result.jobId.data(), nonce, data); + + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); + return send(size); +} + + +bool Client::isCriticalError(const char *message) +{ + if (!message) { + return false; + } + + if (strncasecmp(message, "Unauthenticated", 15) == 0) { + return true; + } + + if (strncasecmp(message, "your IP is banned", 17) == 0) { + return true; + } + + if (strncasecmp(message, "IP Address currently banned", 27) == 0) { + return true; + } + + return false; +} + + +bool Client::parseJob(const rapidjson::Value ¶ms, int *code) +{ + if (!params.IsObject()) { + *code = 2; + return false; + } + + Job job(m_id, m_url.isNicehash()); + if (!job.setId(params["job_id"].GetString())) { + *code = 3; + return false; + } + + if (!job.setBlob(params["blob"].GetString())) { + *code = 4; + return false; + } + + if (!job.setTarget(params["target"].GetString())) { + *code = 5; + return false; + } + + if (m_job == job) { + if (!m_quiet) { + LOG_WARN("[%s:%u] duplicate job received, reconnect", m_url.host(), m_url.port()); + } + + close(); + return false; + } + + m_job = std::move(job); + return true; +} + + +bool Client::parseLogin(const rapidjson::Value &result, int *code) +{ + const char *id = result["id"].GetString(); + if (!id || strlen(id) >= sizeof(m_rpcId)) { + *code = 1; + return false; + } + + memset(m_rpcId, 0, sizeof(m_rpcId)); + memcpy(m_rpcId, id, strlen(id)); + + return parseJob(result["job"], code); +} + + +int Client::resolve(const char *host) +{ + setState(HostLookupState); + + m_expire = 0; + m_recvBufPos = 0; + + if (m_failures == -1) { + m_failures = 0; + } + + const int r = uv_getaddrinfo(uv_default_loop(), &m_resolver, Client::onResolved, host, NULL, &m_hints); + if (r) { + if (!m_quiet) { + LOG_ERR("[%s:%u] getaddrinfo error: \"%s\"", host, m_url.port(), uv_strerror(r)); + } + return 1; + } + + return 0; +} + + +int64_t Client::send(size_t size) +{ + LOG_DEBUG("[%s:%u] send (%d bytes): \"%s\"", m_url.host(), m_url.port(), size, m_sendBuf); + if (state() != ConnectedState || !uv_is_writable(m_stream)) { + LOG_DEBUG_ERR("[%s:%u] send failed, invalid state: %d", m_url.host(), m_url.port(), m_state); + return -1; + } + + uv_buf_t buf = uv_buf_init(m_sendBuf, (unsigned int) size); + + if (uv_try_write(m_stream, &buf, 1) < 0) { + close(); + return -1; + } + + m_expire = uv_now(uv_default_loop()) + kResponseTimeout; + return m_sequence++; +} + + +void Client::close() +{ + if (m_state == UnconnectedState || m_state == ClosingState || !m_socket) { + return; + } + + setState(ClosingState); + + if (uv_is_closing(reinterpret_cast(m_socket)) == 0) { + uv_close(reinterpret_cast(m_socket), Client::onClose); + } +} + + +void Client::connect(struct sockaddr *addr) +{ + setState(ConnectingState); + + reinterpret_cast(addr)->sin_port = htons(m_url.port()); + delete m_socket; + + uv_connect_t *req = new uv_connect_t; + req->data = this; + + m_socket = new uv_tcp_t; + m_socket->data = this; + + uv_tcp_init(uv_default_loop(), m_socket); + uv_tcp_nodelay(m_socket, 1); + +# ifndef WIN32 + uv_tcp_keepalive(m_socket, 1, 60); +# endif + + uv_tcp_connect(req, m_socket, reinterpret_cast(addr), Client::onConnect); +} + + +void Client::login() +{ + m_results.clear(); + + rapidjson::Document doc; + doc.SetObject(); + + auto &allocator = doc.GetAllocator(); + + doc.AddMember("id", 1, allocator); + doc.AddMember("jsonrpc", "2.0", allocator); + doc.AddMember("method", "login", allocator); + + rapidjson::Value params(rapidjson::kObjectType); + params.AddMember("login", rapidjson::StringRef(m_url.user()), allocator); + params.AddMember("pass", rapidjson::StringRef(m_url.password()), allocator); + params.AddMember("agent", rapidjson::StringRef(m_agent), allocator); + + doc.AddMember("params", params, allocator); + + rapidjson::StringBuffer buffer(0, 512); + rapidjson::Writer writer(buffer); + doc.Accept(writer); + + const size_t size = buffer.GetSize(); + if (size > (sizeof(m_buf) - 2)) { + return; + } + + memcpy(m_sendBuf, buffer.GetString(), size); + m_sendBuf[size] = '\n'; + m_sendBuf[size + 1] = '\0'; + + send(size + 1); +} + + +void Client::parse(char *line, size_t len) +{ + startTimeout(); + + line[len - 1] = '\0'; + + LOG_DEBUG("[%s:%u] received (%d bytes): \"%s\"", m_url.host(), m_url.port(), len, line); + + rapidjson::Document doc; + if (doc.ParseInsitu(line).HasParseError()) { + if (!m_quiet) { + LOG_ERR("[%s:%u] JSON decode failed: \"%s\"", m_url.host(), m_url.port(), rapidjson::GetParseError_En(doc.GetParseError())); + } + + return; + } + + if (!doc.IsObject()) { + return; + } + + const rapidjson::Value &id = doc["id"]; + if (id.IsInt64()) { + parseResponse(id.GetInt64(), doc["result"], doc["error"]); + } + else { + parseNotification(doc["method"].GetString(), doc["params"], doc["error"]); + } +} + + +void Client::parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error) +{ + if (error.IsObject()) { + if (!m_quiet) { + LOG_ERR("[%s:%u] error: \"%s\", code: %d", m_url.host(), m_url.port(), error["message"].GetString(), error["code"].GetInt()); + } + return; + } + + if (!method) { + return; + } + + if (strcmp(method, "job") == 0) { + int code = -1; + if (parseJob(params, &code)) { + m_listener->onJobReceived(this, m_job); + } + + return; + } + + LOG_WARN("[%s:%u] unsupported method: \"%s\"", m_url.host(), m_url.port(), method); +} + + +void Client::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error) +{ + if (error.IsObject()) { + const char *message = error["message"].GetString(); + + auto it = m_results.find(id); + if (it != m_results.end()) { + it->second.done(); + m_listener->onResultAccepted(this, it->second, message); + m_results.erase(it); + } + else if (!m_quiet) { + LOG_ERR("[%s:%u] error: \"%s\", code: %d", m_url.host(), m_url.port(), message, error["code"].GetInt()); + } + + if (id == 1 || isCriticalError(message)) { + close(); + } + + return; + } + + if (!result.IsObject()) { + return; + } + + if (id == 1) { + int code = -1; + if (!parseLogin(result, &code)) { + if (!m_quiet) { + LOG_ERR("[%s:%u] login error code: %d", m_url.host(), m_url.port(), code); + } + + return close(); + } + + m_failures = 0; + m_listener->onLoginSuccess(this); + m_listener->onJobReceived(this, m_job); + return; + } + + auto it = m_results.find(id); + if (it != m_results.end()) { + it->second.done(); + m_listener->onResultAccepted(this, it->second, nullptr); + m_results.erase(it); + } +} + + +void Client::ping() +{ + send(snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRId64 ",\"jsonrpc\":\"2.0\",\"method\":\"keepalived\",\"params\":{\"id\":\"%s\"}}\n", m_sequence, m_rpcId)); +} + + +void Client::reconnect() +{ + setState(ConnectingState); + +# ifndef XMRIG_PROXY_PROJECT + if (m_url.isKeepAlive()) { + uv_timer_stop(&m_keepAliveTimer); + } +# endif + + if (m_failures == -1) { + return m_listener->onClose(this, -1); + } + + m_failures++; + m_listener->onClose(this, (int) m_failures); + + m_expire = uv_now(uv_default_loop()) + m_retryPause; +} + + +void Client::setState(SocketState state) +{ + LOG_DEBUG("[%s:%u] state: %d", m_url.host(), m_url.port(), state); + + if (m_state == state) { + return; + } + + m_state = state; +} + + +void Client::startTimeout() +{ + m_expire = 0; + +# ifndef XMRIG_PROXY_PROJECT + if (!m_url.isKeepAlive()) { + return; + } + + uv_timer_start(&m_keepAliveTimer, [](uv_timer_t *handle) { getClient(handle->data)->ping(); }, kKeepAliveTimeout, 0); +# endif +} + + +void Client::onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) +{ + auto client = getClient(handle->data); + + buf->base = &client->m_recvBuf.base[client->m_recvBufPos]; + buf->len = client->m_recvBuf.len - client->m_recvBufPos; +} + + +void Client::onClose(uv_handle_t *handle) +{ + auto client = getClient(handle->data); + + delete client->m_socket; + + client->m_stream = nullptr; + client->m_socket = nullptr; + client->setState(UnconnectedState); + + client->reconnect(); +} + + +void Client::onConnect(uv_connect_t *req, int status) +{ + auto client = getClient(req->data); + if (status < 0) { + if (!client->m_quiet) { + LOG_ERR("[%s:%u] connect error: \"%s\"", client->m_url.host(), client->m_url.port(), uv_strerror(status)); + } + + delete req; + client->close(); + return; + } + + client->m_stream = static_cast(req->handle); + client->m_stream->data = req->data; + client->setState(ConnectedState); + + uv_read_start(client->m_stream, Client::onAllocBuffer, Client::onRead); + delete req; + + client->login(); +} + + +void Client::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) +{ + auto client = getClient(stream->data); + if (nread < 0) { + if (nread != UV_EOF && !client->m_quiet) { + LOG_ERR("[%s:%u] read error: \"%s\"", client->m_url.host(), client->m_url.port(), uv_strerror((int) nread)); + } + + return client->close();; + } + + if ((size_t) nread > (sizeof(m_buf) - 8 - client->m_recvBufPos)) { + return client->close();; + } + + client->m_recvBufPos += nread; + + char* end; + char* start = buf->base; + size_t remaining = client->m_recvBufPos; + + while ((end = static_cast(memchr(start, '\n', remaining))) != nullptr) { + end++; + size_t len = end - start; + client->parse(start, len); + + remaining -= len; + start = end; + } + + if (remaining == 0) { + client->m_recvBufPos = 0; + return; + } + + if (start == buf->base) { + return; + } + + memcpy(buf->base, start, remaining); + client->m_recvBufPos = remaining; +} + + +void Client::onResolved(uv_getaddrinfo_t *req, int status, struct addrinfo *res) +{ + auto client = getClient(req->data); + if (status < 0) { + LOG_ERR("[%s:%u] DNS error: \"%s\"", client->m_url.host(), client->m_url.port(), uv_strerror(status)); + return client->reconnect();; + } + + addrinfo *ptr = res; + std::vector ipv4; + + while (ptr != nullptr) { + if (ptr->ai_family == AF_INET) { + ipv4.push_back(ptr); + } + + ptr = ptr->ai_next; + } + + if (ipv4.empty()) { + LOG_ERR("[%s:%u] DNS error: \"No IPv4 records found\"", client->m_url.host(), client->m_url.port()); + return client->reconnect(); + } + + ptr = ipv4[rand() % ipv4.size()]; + + uv_ip4_name(reinterpret_cast(ptr->ai_addr), client->m_ip, 16); + + client->connect(ptr->ai_addr); + uv_freeaddrinfo(res); +} diff --git a/src/net/Client.h b/src/net/Client.h new file mode 100644 index 00000000..699f4903 --- /dev/null +++ b/src/net/Client.h @@ -0,0 +1,130 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + + +#include +#include + + +#include "net/Job.h" +#include "net/SubmitResult.h" +#include "net/Url.h" +#include "rapidjson/fwd.h" + + +class IClientListener; +class JobResult; + + +class Client +{ +public: + enum SocketState { + UnconnectedState, + HostLookupState, + ConnectingState, + ConnectedState, + ClosingState + }; + + constexpr static int kResponseTimeout = 20 * 1000; + constexpr static int kKeepAliveTimeout = 60 * 1000; + + Client(int id, const char *agent, IClientListener *listener); + ~Client(); + + int64_t submit(const JobResult &result); + void connect(); + void connect(const Url *url); + void disconnect(); + void setUrl(const Url *url); + void tick(uint64_t now); + + inline bool isReady() const { return m_state == ConnectedState && m_failures == 0; } + inline const char *host() const { return m_url.host(); } + inline const char *ip() const { return m_ip; } + inline const Job &job() const { return m_job; } + inline int id() const { return m_id; } + inline SocketState state() const { return m_state; } + inline uint16_t port() const { return m_url.port(); } + inline void setQuiet(bool quiet) { m_quiet = quiet; } + inline void setRetryPause(int ms) { m_retryPause = ms; } + +private: + bool isCriticalError(const char *message); + bool parseJob(const rapidjson::Value ¶ms, int *code); + bool parseLogin(const rapidjson::Value &result, int *code); + int resolve(const char *host); + int64_t send(size_t size); + void close(); + void connect(struct sockaddr *addr); + void login(); + void parse(char *line, size_t len); + void parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error); + void parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); + void ping(); + void reconnect(); + void setState(SocketState state); + void startTimeout(); + + static void onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); + static void onClose(uv_handle_t *handle); + static void onConnect(uv_connect_t *req, int status); + static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); + static void onResolved(uv_getaddrinfo_t *req, int status, struct addrinfo *res); + + static inline Client *getClient(void *data) { return static_cast(data); } + + addrinfo m_hints; + bool m_quiet; + char m_buf[2048]; + char m_ip[17]; + char m_rpcId[64]; + char m_sendBuf[768]; + const char *m_agent; + IClientListener *m_listener; + int m_id; + int m_retryPause; + int64_t m_failures; + Job m_job; + size_t m_recvBufPos; + SocketState m_state; + static int64_t m_sequence; + std::map m_results; + uint64_t m_expire; + Url m_url; + uv_buf_t m_recvBuf; + uv_getaddrinfo_t m_resolver; + uv_stream_t *m_stream; + uv_tcp_t *m_socket; + +# ifndef XMRIG_PROXY_PROJECT + uv_timer_t m_keepAliveTimer; +# endif +}; + + +#endif /* __CLIENT_H__ */ diff --git a/src/net/Job.cpp b/src/net/Job.cpp new file mode 100644 index 00000000..c3d33739 --- /dev/null +++ b/src/net/Job.cpp @@ -0,0 +1,175 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "net/Job.h" + + +static inline unsigned char hf_hex2bin(char c, bool &err) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xA; + } + else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xA; + } + + err = true; + return 0; +} + + +static inline char hf_bin2hex(unsigned char c) +{ + if (c <= 0x9) { + return '0' + c; + } + + return 'a' - 0xA + c; +} + + +Job::Job(int poolId, bool nicehash) : + m_nicehash(nicehash), + m_poolId(poolId), + m_threadId(-1), + m_size(0), + m_diff(0), + m_target(0) +{ +} + + +Job::~Job() +{ +} + + +bool Job::setBlob(const char *blob) +{ + if (!blob) { + return false; + } + + m_size = strlen(blob); + if (m_size % 2 != 0) { + return false; + } + + m_size /= 2; + if (m_size < 76 || m_size >= sizeof(m_blob)) { + return false; + } + + if (!fromHex(blob, (int) m_size * 2, m_blob)) { + return false; + } + + if (*nonce() != 0 && !m_nicehash) { + m_nicehash = true; + } + +# ifdef XMRIG_PROXY_PROJECT + memset(m_rawBlob, 0, sizeof(m_rawBlob)); + memcpy(m_rawBlob, blob, m_size * 2); +# endif + + return true; +} + + +bool Job::setTarget(const char *target) +{ + if (!target) { + return false; + } + + const size_t len = strlen(target); + + if (len <= 8) { + uint32_t tmp = 0; + char str[8]; + memcpy(str, target, len); + + if (!fromHex(str, 8, reinterpret_cast(&tmp)) || tmp == 0) { + return false; + } + + m_target = 0xFFFFFFFFFFFFFFFFULL / (0xFFFFFFFFULL / static_cast(tmp)); + } + else if (len <= 16) { + m_target = 0; + char str[16]; + memcpy(str, target, len); + + if (!fromHex(str, 16, reinterpret_cast(&m_target)) || m_target == 0) { + return false; + } + } + else { + return false; + } + +# ifdef XMRIG_PROXY_PROJECT + memset(m_rawTarget, 0, sizeof(m_rawTarget)); + memcpy(m_rawTarget, target, len); +# endif + + m_diff = toDiff(m_target); + return true; +} + + +bool Job::fromHex(const char* in, unsigned int len, unsigned char* out) +{ + bool error = false; + for (unsigned int i = 0; i < len; i += 2) { + out[i / 2] = (hf_hex2bin(in[i], error) << 4) | hf_hex2bin(in[i + 1], error); + + if (error) { + return false; + } + } + return true; +} + + +void Job::toHex(const unsigned char* in, unsigned int len, char* out) +{ + for (unsigned int i = 0; i < len; i++) { + out[i * 2] = hf_bin2hex((in[i] & 0xF0) >> 4); + out[i * 2 + 1] = hf_bin2hex(in[i] & 0x0F); + } +} + + +bool Job::operator==(const Job &other) const +{ + return m_id == other.m_id && memcmp(m_blob, other.m_blob, sizeof(m_blob)) == 0; +} diff --git a/src/net/Job.h b/src/net/Job.h new file mode 100644 index 00000000..bf8f8835 --- /dev/null +++ b/src/net/Job.h @@ -0,0 +1,89 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __JOB_H__ +#define __JOB_H__ + + +#include +#include + + +#include "align.h" +#include "net/JobId.h" + + +class Job +{ +public: + Job(int poolId = -2, bool nicehash = false); + ~Job(); + + bool setBlob(const char *blob); + bool setTarget(const char *target); + + inline bool isNicehash() const { return m_nicehash; } + inline bool isValid() const { return m_size > 0 && m_diff > 0; } + inline bool setId(const char *id) { return m_id.setId(id); } + inline const JobId &id() const { return m_id; } + inline const uint32_t *nonce() const { return reinterpret_cast(m_blob + 39); } + inline const uint8_t *blob() const { return m_blob; } + inline int poolId() const { return m_poolId; } + inline int threadId() const { return m_threadId; } + inline size_t size() const { return m_size; } + inline uint32_t *nonce() { return reinterpret_cast(m_blob + 39); } + inline uint32_t diff() const { return (uint32_t) m_diff; } + inline uint64_t target() const { return m_target; } + inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } + inline void setThreadId(int threadId) { m_threadId = threadId; } + +# ifdef XMRIG_PROXY_PROJECT + inline char *rawBlob() { return m_rawBlob; } + inline const char *rawTarget() const { return m_rawTarget; } +# endif + + static bool fromHex(const char* in, unsigned int len, unsigned char* out); + static inline uint32_t *nonce(uint8_t *blob) { return reinterpret_cast(blob + 39); } + static inline uint64_t toDiff(uint64_t target) { return 0xFFFFFFFFFFFFFFFFULL / target; } + static void toHex(const unsigned char* in, unsigned int len, char* out); + + bool operator==(const Job &other) const; + +private: + VAR_ALIGN(16, uint8_t m_blob[84]); // Max blob size is 84 (75 fixed + 9 variable), aligned to 96. https://github.com/xmrig/xmrig/issues/1 Thanks fireice-uk. + + bool m_nicehash; + int m_poolId; + int m_threadId; + JobId m_id; + size_t m_size; + uint64_t m_diff; + uint64_t m_target; + +# ifdef XMRIG_PROXY_PROJECT + VAR_ALIGN(16, char m_rawBlob[169]); + VAR_ALIGN(16, char m_rawTarget[17]); +# endif +}; + +#endif /* __JOB_H__ */ diff --git a/src/net/JobId.h b/src/net/JobId.h new file mode 100644 index 00000000..06189779 --- /dev/null +++ b/src/net/JobId.h @@ -0,0 +1,83 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __JOBID_H__ +#define __JOBID_H__ + + +#include + + +class JobId +{ +public: + inline JobId() + { + memset(m_data, 0, sizeof(m_data)); + } + + + inline JobId(const char *id, size_t sizeFix = 0) + { + setId(id, sizeFix); + } + + + inline bool operator==(const JobId &other) const + { + return memcmp(m_data, other.m_data, sizeof(m_data)) == 0; + } + + + inline bool operator!=(const JobId &other) const + { + return memcmp(m_data, other.m_data, sizeof(m_data)) != 0; + } + + + inline bool setId(const char *id, size_t sizeFix = 0) + { + memset(m_data, 0, sizeof(m_data)); + if (!id) { + return false; + } + + const size_t size = strlen(id); + if (size >= sizeof(m_data)) { + return false; + } + + memcpy(m_data, id, size - sizeFix); + return true; + } + + + inline const char *data() const { return m_data; } + inline bool isValid() const { return *m_data != '\0'; } + + +private: + char m_data[64]; +}; + +#endif /* __JOBID_H__ */ diff --git a/src/net/JobResult.h b/src/net/JobResult.h new file mode 100644 index 00000000..520022a7 --- /dev/null +++ b/src/net/JobResult.h @@ -0,0 +1,80 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __JOBRESULT_H__ +#define __JOBRESULT_H__ + + +#include +#include + + +#include "Job.h" + + +class JobResult +{ +public: + inline JobResult() : poolId(0), diff(0), nonce(0) {} + inline JobResult(int poolId, const JobId &jobId, uint32_t nonce, const uint8_t *result, uint32_t diff) : + poolId(poolId), + jobId(jobId), + diff(diff), + nonce(nonce) + { + memcpy(this->result, result, sizeof(this->result)); + } + + + inline JobResult(const Job &job) : poolId(0), diff(0), nonce(0) + { + jobId = job.id(); + poolId = job.poolId(); + diff = job.diff(); + nonce = *job.nonce(); + } + + + inline JobResult &operator=(const Job &job) { + jobId = job.id(); + poolId = job.poolId(); + diff = job.diff(); + + return *this; + } + + + inline uint64_t actualDiff() const + { + return Job::toDiff(reinterpret_cast(result)[3]); + } + + + int poolId; + JobId jobId; + uint32_t diff; + uint32_t nonce; + uint8_t result[32]; +}; + +#endif /* __JOBRESULT_H__ */ diff --git a/src/net/Network.cpp b/src/net/Network.cpp new file mode 100644 index 00000000..e0f65497 --- /dev/null +++ b/src/net/Network.cpp @@ -0,0 +1,195 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +#include +#include +#include + + +#include "api/Api.h" +#include "log/Log.h" +#include "net/Client.h" +#include "net/Network.h" +#include "net/strategies/DonateStrategy.h" +#include "net/strategies/FailoverStrategy.h" +#include "net/strategies/SinglePoolStrategy.h" +#include "net/SubmitResult.h" +#include "net/Url.h" +#include "Options.h" +#include "Platform.h" +#include "workers/Workers.h" + + +Network::Network(const Options *options) : + m_options(options), + m_donate(nullptr) +{ + srand(time(0) ^ (uintptr_t) this); + + Workers::setListener(this); + + const std::vector &pools = options->pools(); + + if (pools.size() > 1) { + m_strategy = new FailoverStrategy(pools, Platform::userAgent(), this); + } + else { + m_strategy = new SinglePoolStrategy(pools.front(), Platform::userAgent(), this); + } + + if (m_options->donateLevel() > 0) { + m_donate = new DonateStrategy(Platform::userAgent(), this); + } + + m_timer.data = this; + uv_timer_init(uv_default_loop(), &m_timer); + + uv_timer_start(&m_timer, Network::onTick, kTickInterval, kTickInterval); +} + + +Network::~Network() +{ +} + + +void Network::connect() +{ + m_strategy->connect(); +} + + +void Network::stop() +{ + if (m_donate) { + m_donate->stop(); + } + + m_strategy->stop(); +} + + +void Network::onActive(Client *client) +{ + if (client->id() == -1) { + LOG_NOTICE("dev donate started"); + return; + } + + m_state.setPool(client->host(), client->port(), client->ip()); + + LOG_INFO(m_options->colors() ? "\x1B[01;37muse pool \x1B[01;36m%s:%d \x1B[01;30m%s" : "use pool %s:%d %s", client->host(), client->port(), client->ip()); +} + + +void Network::onJob(Client *client, const Job &job) +{ + if (m_donate && m_donate->isActive() && client->id() != -1) { + return; + } + + setJob(client, job); +} + + +void Network::onJobResult(const JobResult &result) +{ + if (result.poolId == -1 && m_donate) { + m_donate->submit(result); + return; + } + + m_strategy->submit(result); +} + + +void Network::onPause(IStrategy *strategy) +{ + if (m_donate && m_donate == strategy) { + LOG_NOTICE("dev donate finished"); + m_strategy->resume(); + } + + if (!m_strategy->isActive()) { + LOG_ERR("no active pools, stop mining"); + m_state.stop(); + return Workers::pause(); + } +} + + +void Network::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +{ + m_state.add(result, error); + + if (error) { + LOG_INFO(m_options->colors() ? "\x1B[01;31mrejected\x1B[0m (%" PRId64 "/%" PRId64 ") diff \x1B[01;37m%u\x1B[0m \x1B[31m\"%s\"\x1B[0m \x1B[01;30m(%" PRIu64 " ms)" + : "rejected (%" PRId64 "/%" PRId64 ") diff %u \"%s\" (%" PRIu64 " ms)", + m_state.accepted, m_state.rejected, result.diff, error, result.elapsed); + } + else { + LOG_INFO(m_options->colors() ? "\x1B[01;32maccepted\x1B[0m (%" PRId64 "/%" PRId64 ") diff \x1B[01;37m%u\x1B[0m \x1B[01;30m(%" PRIu64 " ms)" + : "accepted (%" PRId64 "/%" PRId64 ") diff %u (%" PRIu64 " ms)", + m_state.accepted, m_state.rejected, result.diff, result.elapsed); + } +} + + +void Network::setJob(Client *client, const Job &job) +{ + if (m_options->colors()) { + LOG_INFO("\x1B[01;35mnew job\x1B[0m from \x1B[01;37m%s:%d\x1B[0m diff \x1B[01;37m%d", client->host(), client->port(), job.diff()); + } + else { + LOG_INFO("new job from %s:%d diff %d", client->host(), client->port(), job.diff()); + } + + m_state.diff = job.diff(); + Workers::setJob(job); +} + + +void Network::tick() +{ + const uint64_t now = uv_now(uv_default_loop()); + + m_strategy->tick(now); + + if (m_donate) { + m_donate->tick(now); + } + +# ifndef XMRIG_NO_API + Api::tick(m_state); +# endif +} + + +void Network::onTick(uv_timer_t *handle) +{ + static_cast(handle->data)->tick(); +} diff --git a/src/net/Network.h b/src/net/Network.h new file mode 100644 index 00000000..fe13d9b7 --- /dev/null +++ b/src/net/Network.h @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __NETWORK_H__ +#define __NETWORK_H__ + + +#include +#include + + +#include "api/NetworkState.h" +#include "interfaces/IJobResultListener.h" +#include "interfaces/IStrategyListener.h" + + +class IStrategy; +class Options; +class Url; + + +class Network : public IJobResultListener, public IStrategyListener +{ +public: + Network(const Options *options); + ~Network(); + + void connect(); + void stop(); + +protected: + void onActive(Client *client) override; + void onJob(Client *client, const Job &job) override; + void onJobResult(const JobResult &result) override; + void onPause(IStrategy *strategy) override; + void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + +private: + constexpr static int kTickInterval = 1 * 1000; + + void setJob(Client *client, const Job &job); + void tick(); + + static void onTick(uv_timer_t *handle); + + const Options *m_options; + IStrategy *m_donate; + IStrategy *m_strategy; + NetworkState m_state; + uv_timer_t m_timer; +}; + + +#endif /* __NETWORK_H__ */ diff --git a/src/net/SubmitResult.cpp b/src/net/SubmitResult.cpp new file mode 100644 index 00000000..2e81017c --- /dev/null +++ b/src/net/SubmitResult.cpp @@ -0,0 +1,44 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "net/SubmitResult.h" + + +SubmitResult::SubmitResult(int64_t seq, uint32_t diff, uint64_t actualDiff) : + seq(seq), + diff(diff), + actualDiff(actualDiff), + elapsed(0) +{ + start = uv_hrtime(); +} + + +void SubmitResult::done() +{ + elapsed = (uv_hrtime() - start) / 1000000; +} diff --git a/src/net/SubmitResult.h b/src/net/SubmitResult.h new file mode 100644 index 00000000..8eddef89 --- /dev/null +++ b/src/net/SubmitResult.h @@ -0,0 +1,48 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SUBMITRESULT_H__ +#define __SUBMITRESULT_H__ + + +#include + + +class SubmitResult +{ +public: + inline SubmitResult() : seq(0), diff(0), actualDiff(0), elapsed(0), start(0) {} + SubmitResult(int64_t seq, uint32_t diff, uint64_t actualDiff); + + void done(); + + int64_t seq; + uint32_t diff; + uint64_t actualDiff; + uint64_t elapsed; + +private: + uint64_t start; +}; + +#endif /* __SUBMITRESULT_H__ */ diff --git a/src/net/Url.cpp b/src/net/Url.cpp new file mode 100644 index 00000000..dcbe82af --- /dev/null +++ b/src/net/Url.cpp @@ -0,0 +1,194 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "net/Url.h" + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +Url::Url() : + m_keepAlive(false), + m_nicehash(false), + m_host(nullptr), + m_password(nullptr), + m_user(nullptr), + m_port(kDefaultPort) +{ +} + + +/** + * @brief Parse url. + * + * Valid urls: + * example.com + * example.com:3333 + * stratum+tcp://example.com + * stratum+tcp://example.com:3333 + * + * @param url + */ +Url::Url(const char *url) : + m_keepAlive(false), + m_nicehash(false), + m_host(nullptr), + m_password(nullptr), + m_user(nullptr), + m_port(kDefaultPort) +{ + parse(url); +} + + +Url::Url(const char *host, uint16_t port, const char *user, const char *password, bool keepAlive, bool nicehash) : + m_keepAlive(keepAlive), + m_nicehash(nicehash), + m_password(password ? strdup(password) : nullptr), + m_user(user ? strdup(user) : nullptr), + m_port(port) +{ + m_host = strdup(host); +} + + +Url::~Url() +{ + free(m_host); + free(m_password); + free(m_user); +} + + +bool Url::parse(const char *url) +{ + const char *p = strstr(url, "://"); + const char *base = url; + + if (p) { + if (strncasecmp(url, "stratum+tcp://", 14)) { + return false; + } + + base = url + 14; + } + + if (!strlen(base) || *base == '/') { + return false; + } + + const char *port = strchr(base, ':'); + if (!port) { + m_host = strdup(base); + return false; + } + + const size_t size = port++ - base + 1; + m_host = static_cast(malloc(size)); + memcpy(m_host, base, size - 1); + m_host[size - 1] = '\0'; + + m_port = (uint16_t) strtol(port, nullptr, 10); + return true; +} + + +bool Url::setUserpass(const char *userpass) +{ + const char *p = strchr(userpass, ':'); + if (!p) { + return false; + } + + free(m_user); + free(m_password); + + m_user = static_cast(calloc(p - userpass + 1, 1)); + strncpy(m_user, userpass, p - userpass); + m_password = strdup(p + 1); + + return true; +} + + +void Url::applyExceptions() +{ + if (!isValid()) { + return; + } + + if (strstr(m_host, ".nicehash.com")) { + m_keepAlive = false; + m_nicehash = true; + } + + if (strstr(m_host, ".minergate.com")) { + m_keepAlive = false; + } +} + + +void Url::setPassword(const char *password) +{ + if (!password) { + return; + } + + free(m_password); + m_password = strdup(password); +} + + +void Url::setUser(const char *user) +{ + if (!user) { + return; + } + + free(m_user); + m_user = strdup(user); +} + + +Url &Url::operator=(const Url *other) +{ + m_keepAlive = other->m_keepAlive; + m_nicehash = other->m_nicehash; + m_port = other->m_port; + + free(m_host); + m_host = strdup(other->m_host); + + setPassword(other->m_password); + setUser(other->m_user); + + return *this; +} diff --git a/src/net/Url.h b/src/net/Url.h new file mode 100644 index 00000000..a1982300 --- /dev/null +++ b/src/net/Url.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 + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __URL_H__ +#define __URL_H__ + + +#include + + +class Url +{ +public: + constexpr static const char *kDefaultPassword = "x"; + constexpr static const char *kDefaultUser = "x"; + constexpr static uint16_t kDefaultPort = 3333; + + Url(); + Url(const char *url); + Url(const char *host, uint16_t port, const char *user = nullptr, const char *password = nullptr, bool keepAlive = false, bool nicehash = false ); + ~Url(); + + inline bool isKeepAlive() const { return m_keepAlive; } + inline bool isNicehash() const { return m_nicehash; } + inline bool isValid() const { return m_host && m_port > 0; } + inline const char *host() const { return m_host; } + inline const char *password() const { return m_password ? m_password : kDefaultPassword; } + inline const char *user() const { return m_user ? m_user : kDefaultUser; } + inline uint16_t port() const { return m_port; } + inline void setKeepAlive(bool keepAlive) { m_keepAlive = keepAlive; } + inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } + + bool parse(const char *url); + bool setUserpass(const char *userpass); + void applyExceptions(); + void setPassword(const char *password); + void setUser(const char *user); + + Url &operator=(const Url *other); + +private: + bool m_keepAlive; + bool m_nicehash; + char *m_host; + char *m_password; + char *m_user; + uint16_t m_port; +}; + +#endif /* __URL_H__ */ diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp new file mode 100644 index 00000000..d7c721c6 --- /dev/null +++ b/src/net/strategies/DonateStrategy.cpp @@ -0,0 +1,146 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "interfaces/IStrategyListener.h" +#include "net/Client.h" +#include "net/Job.h" +#include "net/strategies/DonateStrategy.h" +#include "Options.h" + + +extern "C" +{ +#include "crypto/c_keccak.h" +} + + +DonateStrategy::DonateStrategy(const char *agent, IStrategyListener *listener) : + m_active(false), + m_donateTime(Options::i()->donateLevel() * 60 * 1000), + m_idleTime((100 - Options::i()->donateLevel()) * 60 * 1000), + m_listener(listener) +{ + uint8_t hash[200]; + char userId[65] = { 0 }; + const char *user = Options::i()->pools().front()->user(); + + keccak(reinterpret_cast(user), static_cast(strlen(user)), hash, sizeof(hash)); + Job::toHex(hash, 32, userId); + + Url *url = new Url("fee.xmrig.com", Options::i()->algo() == Options::ALGO_CRYPTONIGHT_LITE ? 3333 : 443, userId, nullptr, false, true); + + m_client = new Client(-1, agent, this); + m_client->setUrl(url); + m_client->setRetryPause(Options::i()->retryPause() * 1000); + m_client->setQuiet(true); + + delete url; + + m_timer.data = this; + uv_timer_init(uv_default_loop(), &m_timer); + + idle(); +} + + +int64_t DonateStrategy::submit(const JobResult &result) +{ + return m_client->submit(result); +} + + +void DonateStrategy::connect() +{ + m_client->connect(); +} + + +void DonateStrategy::stop() +{ + uv_timer_stop(&m_timer); + m_client->disconnect(); +} + + +void DonateStrategy::tick(uint64_t now) +{ + m_client->tick(now); +} + + +void DonateStrategy::onClose(Client *client, int failures) +{ +} + + +void DonateStrategy::onJobReceived(Client *client, const Job &job) +{ + m_listener->onJob(client, job); +} + + +void DonateStrategy::onLoginSuccess(Client *client) +{ + if (!isActive()) { + uv_timer_start(&m_timer, DonateStrategy::onTimer, m_donateTime, 0); + } + + m_active = true; + m_listener->onActive(client); +} + + +void DonateStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(client, result, error); +} + + +void DonateStrategy::idle() +{ + uv_timer_start(&m_timer, DonateStrategy::onTimer, m_idleTime, 0); +} + + +void DonateStrategy::suspend() +{ + m_client->disconnect(); + + m_active = false; + m_listener->onPause(this); + + idle(); +} + + +void DonateStrategy::onTimer(uv_timer_t *handle) +{ + auto strategy = static_cast(handle->data); + + if (!strategy->isActive()) { + return strategy->connect(); + } + + strategy->suspend(); +} diff --git a/src/net/strategies/DonateStrategy.h b/src/net/strategies/DonateStrategy.h new file mode 100644 index 00000000..302de292 --- /dev/null +++ b/src/net/strategies/DonateStrategy.h @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DONATESTRATEGY_H__ +#define __DONATESTRATEGY_H__ + + +#include + + +#include "interfaces/IClientListener.h" +#include "interfaces/IStrategy.h" + + +class Client; +class IStrategyListener; +class Url; + + +class DonateStrategy : public IStrategy, public IClientListener +{ +public: + DonateStrategy(const char *agent, IStrategyListener *listener); + +public: + inline bool isActive() const override { return m_active; } + inline void resume() override {} + + int64_t submit(const JobResult &result) override; + void connect() override; + void stop() override; + void tick(uint64_t now) override; + +protected: + void onClose(Client *client, int failures) override; + void onJobReceived(Client *client, const Job &job) override; + void onLoginSuccess(Client *client) override; + void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + +private: + void idle(); + void suspend(); + + static void onTimer(uv_timer_t *handle); + + bool m_active; + Client *m_client; + const int m_donateTime; + const int m_idleTime; + IStrategyListener *m_listener; + uv_timer_t m_timer; +}; + +#endif /* __DONATESTRATEGY_H__ */ diff --git a/src/net/strategies/FailoverStrategy.cpp b/src/net/strategies/FailoverStrategy.cpp new file mode 100644 index 00000000..47d390b0 --- /dev/null +++ b/src/net/strategies/FailoverStrategy.cpp @@ -0,0 +1,148 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "interfaces/IStrategyListener.h" +#include "net/Client.h" +#include "net/strategies/FailoverStrategy.h" +#include "Options.h" + + +FailoverStrategy::FailoverStrategy(const std::vector &urls, const char *agent, IStrategyListener *listener) : + m_active(-1), + m_index(0), + m_listener(listener) +{ + for (const Url *url : urls) { + add(url, agent); + } +} + + +int64_t FailoverStrategy::submit(const JobResult &result) +{ + return m_pools[m_active]->submit(result); +} + + +void FailoverStrategy::connect() +{ + m_pools[m_index]->connect(); +} + + +void FailoverStrategy::resume() +{ + if (!isActive()) { + return; + } + + m_listener->onJob( m_pools[m_active], m_pools[m_active]->job()); +} + + +void FailoverStrategy::stop() +{ + for (size_t i = 0; i < m_pools.size(); ++i) { + m_pools[i]->disconnect(); + } + + m_index = 0; + m_active = -1; + + m_listener->onPause(this); +} + + +void FailoverStrategy::tick(uint64_t now) +{ + for (Client *client : m_pools) { + client->tick(now); + } +} + + +void FailoverStrategy::onClose(Client *client, int failures) +{ + if (failures == -1) { + return; + } + + if (m_active == client->id()) { + m_active = -1; + m_listener->onPause(this); + } + + if (m_index == 0 && failures < Options::i()->retries()) { + return; + } + + if (m_index == client->id() && (m_pools.size() - m_index) > 1) { + m_pools[++m_index]->connect(); + } +} + + +void FailoverStrategy::onJobReceived(Client *client, const Job &job) +{ + if (m_active == client->id()) { + m_listener->onJob(client, job); + } +} + + +void FailoverStrategy::onLoginSuccess(Client *client) +{ + int active = m_active; + + if (client->id() == 0 || !isActive()) { + active = client->id(); + } + + for (size_t i = 1; i < m_pools.size(); ++i) { + if (active != static_cast(i)) { + m_pools[i]->disconnect(); + } + } + + if (active >= 0 && active != m_active) { + m_index = m_active = active; + m_listener->onActive(client); + } +} + + +void FailoverStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(client, result, error); +} + + +void FailoverStrategy::add(const Url *url, const char *agent) +{ + Client *client = new Client((int) m_pools.size(), agent, this); + client->setUrl(url); + client->setRetryPause(Options::i()->retryPause() * 1000); + + m_pools.push_back(client); +} diff --git a/src/net/strategies/FailoverStrategy.h b/src/net/strategies/FailoverStrategy.h new file mode 100644 index 00000000..963d3157 --- /dev/null +++ b/src/net/strategies/FailoverStrategy.h @@ -0,0 +1,69 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __FAILOVERSTRATEGY_H__ +#define __FAILOVERSTRATEGY_H__ + + +#include + + +#include "interfaces/IClientListener.h" +#include "interfaces/IStrategy.h" + + +class Client; +class IStrategyListener; +class Url; + + +class FailoverStrategy : public IStrategy, public IClientListener +{ +public: + FailoverStrategy(const std::vector &urls, const char *agent, IStrategyListener *listener); + +public: + inline bool isActive() const override { return m_active >= 0; } + + int64_t submit(const JobResult &result) override; + void connect() override; + void resume() override; + void stop() override; + void tick(uint64_t now) override; + +protected: + void onClose(Client *client, int failures) override; + void onJobReceived(Client *client, const Job &job) override; + void onLoginSuccess(Client *client) override; + void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + +private: + void add(const Url *url, const char *agent); + + int m_active; + int m_index; + IStrategyListener *m_listener; + std::vector m_pools; +}; + +#endif /* __FAILOVERSTRATEGY_H__ */ diff --git a/src/net/strategies/SinglePoolStrategy.cpp b/src/net/strategies/SinglePoolStrategy.cpp new file mode 100644 index 00000000..997dc00b --- /dev/null +++ b/src/net/strategies/SinglePoolStrategy.cpp @@ -0,0 +1,102 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "interfaces/IStrategyListener.h" +#include "net/Client.h" +#include "net/strategies/SinglePoolStrategy.h" +#include "Options.h" + + +SinglePoolStrategy::SinglePoolStrategy(const Url *url, const char *agent, IStrategyListener *listener) : + m_active(false), + m_listener(listener) +{ + m_client = new Client(0, agent, this); + m_client->setUrl(url); + m_client->setRetryPause(Options::i()->retryPause() * 1000); +} + + +int64_t SinglePoolStrategy::submit(const JobResult &result) +{ + return m_client->submit(result); +} + + +void SinglePoolStrategy::connect() +{ + m_client->connect(); +} + + +void SinglePoolStrategy::resume() +{ + if (!isActive()) { + return; + } + + m_listener->onJob(m_client, m_client->job()); +} + + +void SinglePoolStrategy::stop() +{ + m_client->disconnect(); +} + + +void SinglePoolStrategy::tick(uint64_t now) +{ + m_client->tick(now); +} + + +void SinglePoolStrategy::onClose(Client *client, int failures) +{ + if (!isActive()) { + return; + } + + m_active = false; + m_listener->onPause(this); +} + + +void SinglePoolStrategy::onJobReceived(Client *client, const Job &job) +{ + m_listener->onJob(client, job); +} + + +void SinglePoolStrategy::onLoginSuccess(Client *client) +{ + m_active = true; + m_listener->onActive(client); +} + + +void SinglePoolStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(client, result, error); +} diff --git a/utils/threads.h b/src/net/strategies/SinglePoolStrategy.h similarity index 51% rename from utils/threads.h rename to src/net/strategies/SinglePoolStrategy.h index a2ef09f3..95e21547 100644 --- a/utils/threads.h +++ b/src/net/strategies/SinglePoolStrategy.h @@ -21,21 +21,43 @@ * along with this program. If not, see . */ -#ifndef __THREADS_H__ -#define __THREADS_H__ +#ifndef __SINGLEPOOLSTRATEGY_H__ +#define __SINGLEPOOLSTRATEGY_H__ -#if defined(WIN32) && defined(USE_NATIVE_THREADS) -# include -# define MUTEX CRITICAL_SECTION -# define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex) -# define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex) -# define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex) -#else -# include -# define MUTEX pthread_mutex_t -# define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL) -# define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex) -# define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex) -#endif -#endif /* __THREADS_H__ */ +#include "interfaces/IClientListener.h" +#include "interfaces/IStrategy.h" + + +class Client; +class IStrategyListener; +class Url; + + +class SinglePoolStrategy : public IStrategy, public IClientListener +{ +public: + SinglePoolStrategy(const Url *url, const char *agent, IStrategyListener *listener); + +public: + inline bool isActive() const override { return m_active; } + + int64_t submit(const JobResult &result) override; + void connect() override; + void resume() override; + void stop() override; + void tick(uint64_t now) override; + +protected: + void onClose(Client *client, int failures) override; + void onJobReceived(Client *client, const Job &job) override; + void onLoginSuccess(Client *client) override; + void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + +private: + bool m_active; + Client *m_client; + IStrategyListener *m_listener; +}; + +#endif /* __SINGLEPOOLSTRATEGY_H__ */ diff --git a/version.h b/src/version.h similarity index 70% rename from version.h rename to src/version.h index a17b3301..8d2e992f 100644 --- a/version.h +++ b/src/version.h @@ -26,15 +26,32 @@ #define APP_ID "xmrig" #define APP_NAME "XMRig" -#define APP_DESC "Monero (XMR) CPU miner" -#define APP_VERSION "0.8.3-dev" +#define APP_DESC "XMRig CPU miner" +#define APP_VERSION "2.4.2" #define APP_DOMAIN "xmrig.com" #define APP_SITE "www.xmrig.com" #define APP_COPYRIGHT "Copyright (C) 2016-2017 xmrig.com" +#define APP_KIND "cpu" -#define APP_VER_MAJOR 0 -#define APP_VER_MINOR 8 -#define APP_VER_BUILD 3 +#define APP_VER_MAJOR 2 +#define APP_VER_MINOR 4 +#define APP_VER_BUILD 2 #define APP_VER_REV 0 +#ifdef _MSC_VER +# if (_MSC_VER == 1910 || _MSC_VER == 1911) +# define MSVC_VERSION 2017 +# elif _MSC_VER == 1900 +# define MSVC_VERSION 2015 +# elif _MSC_VER == 1800 +# define MSVC_VERSION 2013 +# elif _MSC_VER == 1700 +# define MSVC_VERSION 2012 +# elif _MSC_VER == 1600 +# define MSVC_VERSION 2010 +# else +# define MSVC_VERSION 0 +# endif +#endif + #endif /* __VERSION_H__ */ diff --git a/src/workers/DoubleWorker.cpp b/src/workers/DoubleWorker.cpp new file mode 100644 index 00000000..46d6f366 --- /dev/null +++ b/src/workers/DoubleWorker.cpp @@ -0,0 +1,151 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "crypto/CryptoNight.h" +#include "workers/DoubleWorker.h" +#include "workers/Workers.h" + + +class DoubleWorker::State +{ +public: + inline State() : + nonce1(0), + nonce2(0) + {} + + Job job; + uint32_t nonce1; + uint32_t nonce2; + uint8_t blob[84 * 2]; +}; + + +DoubleWorker::DoubleWorker(Handle *handle) + : Worker(handle) +{ + m_state = new State(); + m_pausedState = new State(); +} + + +DoubleWorker::~DoubleWorker() +{ + delete m_state; + delete m_pausedState; +} + + +void DoubleWorker::start() +{ + while (Workers::sequence() > 0) { + if (Workers::isPaused()) { + do { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + while (Workers::isPaused()); + + if (Workers::sequence() == 0) { + break; + } + + consumeJob(); + } + + while (!Workers::isOutdated(m_sequence)) { + if ((m_count & 0xF) == 0) { + storeStats(); + } + + m_count += 2; + *Job::nonce(m_state->blob) = ++m_state->nonce1; + *Job::nonce(m_state->blob + m_state->job.size()) = ++m_state->nonce2; + + CryptoNight::hash(m_state->blob, m_state->job.size(), m_hash, m_ctx); + + if (*reinterpret_cast(m_hash + 24) < m_state->job.target()) { + Workers::submit(JobResult(m_state->job.poolId(), m_state->job.id(), m_state->nonce1, m_hash, m_state->job.diff())); + } + + if (*reinterpret_cast(m_hash + 32 + 24) < m_state->job.target()) { + Workers::submit(JobResult(m_state->job.poolId(), m_state->job.id(), m_state->nonce2, m_hash + 32, m_state->job.diff())); + } + + std::this_thread::yield(); + } + + consumeJob(); + } +} + + +bool DoubleWorker::resume(const Job &job) +{ + if (m_state->job.poolId() == -1 && job.poolId() >= 0 && job.id() == m_pausedState->job.id()) { + *m_state = *m_pausedState; + return true; + } + + return false; +} + + +void DoubleWorker::consumeJob() +{ + Job job = Workers::job(); + m_sequence = Workers::sequence(); + if (m_state->job == job) { + return; + } + + save(job); + + if (resume(job)) { + return; + } + + m_state->job = std::move(job); + memcpy(m_state->blob, m_state->job.blob(), m_state->job.size()); + memcpy(m_state->blob + m_state->job.size(), m_state->job.blob(), m_state->job.size()); + + if (m_state->job.isNicehash()) { + m_state->nonce1 = (*Job::nonce(m_state->blob) & 0xff000000U) + (0xffffffU / (m_threads * 2) * m_id); + m_state->nonce2 = (*Job::nonce(m_state->blob + m_state->job.size()) & 0xff000000U) + (0xffffffU / (m_threads * 2) * (m_id + m_threads)); + } + else { + m_state->nonce1 = 0xffffffffU / (m_threads * 2) * m_id; + m_state->nonce2 = 0xffffffffU / (m_threads * 2) * (m_id + m_threads); + } +} + + +void DoubleWorker::save(const Job &job) +{ + if (job.poolId() == -1 && m_state->job.poolId() >= 0) { + *m_pausedState = *m_state; + } +} diff --git a/persistent_memory.h b/src/workers/DoubleWorker.h similarity index 67% rename from persistent_memory.h rename to src/workers/DoubleWorker.h index 5a6d6ca7..711f4bd1 100644 --- a/persistent_memory.h +++ b/src/workers/DoubleWorker.h @@ -21,30 +21,38 @@ * along with this program. If not, see . */ -#ifndef __PERSISTENT_MEMORY_H__ -#define __PERSISTENT_MEMORY_H__ - -#include +#ifndef __DOUBLEWORKER_H__ +#define __DOUBLEWORKER_H__ -enum memory_flags { - MEMORY_HUGEPAGES_AVAILABLE = 1, - MEMORY_HUGEPAGES_ENABLED = 2, - MEMORY_LOCK = 4 +#include "align.h" +#include "net/Job.h" +#include "net/JobResult.h" +#include "workers/Worker.h" + + +class Handle; + + +class DoubleWorker : public Worker +{ +public: + DoubleWorker(Handle *handle); + ~DoubleWorker(); + + void start() override; + +private: + bool resume(const Job &job); + void consumeJob(); + void save(const Job &job); + + class State; + + uint8_t m_hash[64]; + State *m_state; + State *m_pausedState; }; -#define MEMORY 2097152 - - -extern char *persistent_memory; -extern int persistent_memory_flags; - - -const char * persistent_memory_allocate(); -void persistent_memory_free(); -void * persistent_calloc(size_t num, size_t size); -void * create_persistent_ctx(int thr_id); - - -#endif /* __PERSISTENT_MEMORY_H__ */ +#endif /* __SINGLEWORKER_H__ */ diff --git a/src/workers/Handle.cpp b/src/workers/Handle.cpp new file mode 100644 index 00000000..c461cee7 --- /dev/null +++ b/src/workers/Handle.cpp @@ -0,0 +1,47 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "workers/Handle.h" + + +Handle::Handle(int threadId, int threads, int64_t affinity, int priority) : + m_priority(priority), + m_threadId(threadId), + m_threads(threads), + m_affinity(affinity), + m_worker(nullptr) +{ +} + + +void Handle::join() +{ + uv_thread_join(&m_thread); +} + + +void Handle::start(void (*callback) (void *)) +{ + uv_thread_create(&m_thread, callback, this); +} diff --git a/src/workers/Handle.h b/src/workers/Handle.h new file mode 100644 index 00000000..9faae0d0 --- /dev/null +++ b/src/workers/Handle.h @@ -0,0 +1,59 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __HANDLE_H__ +#define __HANDLE_H__ + + +#include +#include + + +class IWorker; + + +class Handle +{ +public: + Handle(int threadId, int threads, int64_t affinity, int priority); + void join(); + void start(void (*callback) (void *)); + + inline int priority() const { return m_priority; } + inline int threadId() const { return m_threadId; } + inline int threads() const { return m_threads; } + inline int64_t affinity() const { return m_affinity; } + inline IWorker *worker() const { return m_worker; } + inline void setWorker(IWorker *worker) { m_worker = worker; } + +private: + int m_priority; + int m_threadId; + int m_threads; + int64_t m_affinity; + IWorker *m_worker; + uv_thread_t m_thread; +}; + + +#endif /* __HANDLE_H__ */ diff --git a/src/workers/Hashrate.cpp b/src/workers/Hashrate.cpp new file mode 100644 index 00000000..5c20c247 --- /dev/null +++ b/src/workers/Hashrate.cpp @@ -0,0 +1,182 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + +#include "log/Log.h" +#include "Options.h" +#include "workers/Hashrate.h" + + +inline const char *format(double h, char* buf, size_t size) +{ + if (std::isnormal(h)) { + snprintf(buf, size, "%03.1f", h); + return buf; + } + + return "n/a"; +} + + +Hashrate::Hashrate(int threads) : + m_highest(0.0), + m_threads(threads) +{ + m_counts = new uint64_t*[threads]; + m_timestamps = new uint64_t*[threads]; + m_top = new uint32_t[threads]; + + for (int i = 0; i < threads; i++) { + m_counts[i] = new uint64_t[kBucketSize]; + m_timestamps[i] = new uint64_t[kBucketSize]; + m_top[i] = 0; + + memset(m_counts[0], 0, sizeof(uint64_t) * kBucketSize); + memset(m_timestamps[0], 0, sizeof(uint64_t) * kBucketSize); + } + + const int printTime = Options::i()->printTime(); + + if (printTime > 0) { + uv_timer_init(uv_default_loop(), &m_timer); + m_timer.data = this; + + uv_timer_start(&m_timer, Hashrate::onReport, (printTime + 4) * 1000, printTime * 1000); + } +} + + +double Hashrate::calc(size_t ms) const +{ + double result = 0.0; + double data; + + for (int i = 0; i < m_threads; ++i) { + data = calc(i, ms); + if (std::isnormal(data)) { + result += data; + } + } + + return result; +} + + +double Hashrate::calc(size_t threadId, size_t ms) const +{ + using namespace std::chrono; + const uint64_t now = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + + uint64_t earliestHashCount = 0; + uint64_t earliestStamp = 0; + uint64_t lastestStamp = 0; + uint64_t lastestHashCnt = 0; + bool haveFullSet = false; + + for (size_t i = 1; i < kBucketSize; i++) { + const size_t idx = (m_top[threadId] - i) & kBucketMask; + + if (m_timestamps[threadId][idx] == 0) { + break; + } + + if (lastestStamp == 0) { + lastestStamp = m_timestamps[threadId][idx]; + lastestHashCnt = m_counts[threadId][idx]; + } + + if (now - m_timestamps[threadId][idx] > ms) { + haveFullSet = true; + break; + } + + earliestStamp = m_timestamps[threadId][idx]; + earliestHashCount = m_counts[threadId][idx]; + } + + if (!haveFullSet || earliestStamp == 0 || lastestStamp == 0) { + return nan(""); + } + + if (lastestStamp - earliestStamp == 0) { + return nan(""); + } + + double hashes, time; + hashes = (double) lastestHashCnt - earliestHashCount; + time = (double) lastestStamp - earliestStamp; + time /= 1000.0; + + return hashes / time; +} + + +void Hashrate::add(size_t threadId, uint64_t count, uint64_t timestamp) +{ + const size_t top = m_top[threadId]; + m_counts[threadId][top] = count; + m_timestamps[threadId][top] = timestamp; + + m_top[threadId] = (top + 1) & kBucketMask; +} + + +void Hashrate::print() +{ + char num1[8]; + char num2[8]; + char num3[8]; + char num4[8]; + + LOG_INFO(Options::i()->colors() ? "\x1B[01;37mspeed\x1B[0m 2.5s/60s/15m \x1B[01;36m%s \x1B[22;36m%s %s \x1B[01;36mH/s\x1B[0m max: \x1B[01;36m%s H/s" : "speed 2.5s/60s/15m %s %s %s H/s max: %s H/s", + format(calc(ShortInterval), num1, sizeof(num1)), + format(calc(MediumInterval), num2, sizeof(num2)), + format(calc(LargeInterval), num3, sizeof(num3)), + format(m_highest, num4, sizeof(num4)) + ); +} + + +void Hashrate::stop() +{ + uv_timer_stop(&m_timer); +} + + +void Hashrate::updateHighest() +{ + double highest = calc(ShortInterval); + if (std::isnormal(highest) && highest > m_highest) { + m_highest = highest; + } +} + + +void Hashrate::onReport(uv_timer_t *handle) +{ + static_cast(handle->data)->print(); +} diff --git a/src/workers/Hashrate.h b/src/workers/Hashrate.h new file mode 100644 index 00000000..026c0cdf --- /dev/null +++ b/src/workers/Hashrate.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __HASHRATE_H__ +#define __HASHRATE_H__ + + +#include +#include + + +class Hashrate +{ +public: + enum Intervals { + ShortInterval = 2500, + MediumInterval = 60000, + LargeInterval = 900000 + }; + + Hashrate(int threads); + double calc(size_t ms) const; + double calc(size_t threadId, size_t ms) const; + void add(size_t threadId, uint64_t count, uint64_t timestamp); + void print(); + void stop(); + void updateHighest(); + + inline double highest() const { return m_highest; } + inline int threads() const { return m_threads; } + +private: + static void onReport(uv_timer_t *handle); + + constexpr static size_t kBucketSize = 2 << 11; + constexpr static size_t kBucketMask = kBucketSize - 1; + + double m_highest; + int m_threads; + uint32_t* m_top; + uint64_t** m_counts; + uint64_t** m_timestamps; + uv_timer_t m_timer; +}; + + +#endif /* __HASHRATE_H__ */ diff --git a/src/workers/SingleWorker.cpp b/src/workers/SingleWorker.cpp new file mode 100644 index 00000000..ecb566f8 --- /dev/null +++ b/src/workers/SingleWorker.cpp @@ -0,0 +1,119 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "crypto/CryptoNight.h" +#include "workers/SingleWorker.h" +#include "workers/Workers.h" + + +SingleWorker::SingleWorker(Handle *handle) + : Worker(handle) +{ +} + + +void SingleWorker::start() +{ + while (Workers::sequence() > 0) { + if (Workers::isPaused()) { + do { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + while (Workers::isPaused()); + + if (Workers::sequence() == 0) { + break; + } + + consumeJob(); + } + + while (!Workers::isOutdated(m_sequence)) { + if ((m_count & 0xF) == 0) { + storeStats(); + } + + m_count++; + *m_job.nonce() = ++m_result.nonce; + + if (CryptoNight::hash(m_job, m_result, m_ctx)) { + Workers::submit(m_result); + } + + std::this_thread::yield(); + } + + consumeJob(); + } +} + + +bool SingleWorker::resume(const Job &job) +{ + if (m_job.poolId() == -1 && job.poolId() >= 0 && job.id() == m_paused.id()) { + m_job = m_paused; + m_result = m_job; + m_result.nonce = *m_job.nonce(); + return true; + } + + return false; +} + + +void SingleWorker::consumeJob() +{ + Job job = Workers::job(); + m_sequence = Workers::sequence(); + if (m_job == job) { + return; + } + + save(job); + + if (resume(job)) { + return; + } + + m_job = std::move(job); + m_result = m_job; + + if (m_job.isNicehash()) { + m_result.nonce = (*m_job.nonce() & 0xff000000U) + (0xffffffU / m_threads * m_id); + } + else { + m_result.nonce = 0xffffffffU / m_threads * m_id; + } +} + + +void SingleWorker::save(const Job &job) +{ + if (job.poolId() == -1 && m_job.poolId() >= 0) { + m_paused = m_job; + } +} diff --git a/src/workers/SingleWorker.h b/src/workers/SingleWorker.h new file mode 100644 index 00000000..08ab1857 --- /dev/null +++ b/src/workers/SingleWorker.h @@ -0,0 +1,54 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SINGLEWORKER_H__ +#define __SINGLEWORKER_H__ + + +#include "net/Job.h" +#include "net/JobResult.h" +#include "workers/Worker.h" + + +class Handle; + + +class SingleWorker : public Worker +{ +public: + SingleWorker(Handle *handle); + + void start() override; + +private: + bool resume(const Job &job); + void consumeJob(); + void save(const Job &job); + + Job m_job; + Job m_paused; + JobResult m_result; +}; + + +#endif /* __SINGLEWORKER_H__ */ diff --git a/src/workers/Worker.cpp b/src/workers/Worker.cpp new file mode 100644 index 00000000..02646ced --- /dev/null +++ b/src/workers/Worker.cpp @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + + +#include "Cpu.h" +#include "Mem.h" +#include "Platform.h" +#include "workers/Handle.h" +#include "workers/Worker.h" + + +Worker::Worker(Handle *handle) : + m_id(handle->threadId()), + m_threads(handle->threads()), + m_hashCount(0), + m_timestamp(0), + m_count(0), + m_sequence(0) +{ + if (Cpu::threads() > 1 && handle->affinity() != -1L) { + Cpu::setAffinity(m_id, handle->affinity()); + } + + Platform::setThreadPriority(handle->priority()); + m_ctx = Mem::create(m_id); +} + + +Worker::~Worker() +{ +} + + +void Worker::storeStats() +{ + using namespace std::chrono; + + const uint64_t timestamp = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + m_hashCount.store(m_count, std::memory_order_relaxed); + m_timestamp.store(timestamp, std::memory_order_relaxed); +} diff --git a/src/workers/Worker.h b/src/workers/Worker.h new file mode 100644 index 00000000..11c4a198 --- /dev/null +++ b/src/workers/Worker.h @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __WORKER_H__ +#define __WORKER_H__ + + +#include +#include + + +#include "interfaces/IWorker.h" + + +struct cryptonight_ctx; +class Handle; + + +class Worker : public IWorker +{ +public: + Worker(Handle *handle); + ~Worker(); + + inline uint64_t hashCount() const override { return m_hashCount.load(std::memory_order_relaxed); } + inline uint64_t timestamp() const override { return m_timestamp.load(std::memory_order_relaxed); } + +protected: + void storeStats(); + + cryptonight_ctx *m_ctx; + int m_id; + int m_threads; + std::atomic m_hashCount; + std::atomic m_timestamp; + uint64_t m_count; + uint64_t m_sequence; +}; + + +#endif /* __WORKER_H__ */ diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp new file mode 100644 index 00000000..039a1793 --- /dev/null +++ b/src/workers/Workers.cpp @@ -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 + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "api/Api.h" +#include "interfaces/IJobResultListener.h" +#include "Mem.h" +#include "Options.h" +#include "workers/DoubleWorker.h" +#include "workers/Handle.h" +#include "workers/Hashrate.h" +#include "workers/SingleWorker.h" +#include "workers/Workers.h" + + +bool Workers::m_active = false; +bool Workers::m_enabled = true; +Hashrate *Workers::m_hashrate = nullptr; +IJobResultListener *Workers::m_listener = nullptr; +Job Workers::m_job; +std::atomic Workers::m_paused; +std::atomic Workers::m_sequence; +std::list Workers::m_queue; +std::vector Workers::m_workers; +uint64_t Workers::m_ticks = 0; +uv_async_t Workers::m_async; +uv_mutex_t Workers::m_mutex; +uv_rwlock_t Workers::m_rwlock; +uv_timer_t Workers::m_timer; + + +Job Workers::job() +{ + uv_rwlock_rdlock(&m_rwlock); + Job job = m_job; + uv_rwlock_rdunlock(&m_rwlock); + + return job; +} + + +void Workers::printHashrate(bool detail) +{ + m_hashrate->print(); +} + + +void Workers::setEnabled(bool enabled) +{ + if (m_enabled == enabled) { + return; + } + + m_enabled = enabled; + if (!m_active) { + return; + } + + m_paused = enabled ? 0 : 1; + m_sequence++; +} + + +void Workers::setJob(const Job &job) +{ + uv_rwlock_wrlock(&m_rwlock); + m_job = job; + uv_rwlock_wrunlock(&m_rwlock); + + m_active = true; + if (!m_enabled) { + return; + } + + m_sequence++; + m_paused = 0; +} + + +void Workers::start(int64_t affinity, int priority) +{ + const int threads = Mem::threads(); + m_hashrate = new Hashrate(threads); + + uv_mutex_init(&m_mutex); + uv_rwlock_init(&m_rwlock); + + m_sequence = 1; + m_paused = 1; + + uv_async_init(uv_default_loop(), &m_async, Workers::onResult); + uv_timer_init(uv_default_loop(), &m_timer); + uv_timer_start(&m_timer, Workers::onTick, 500, 500); + + for (int i = 0; i < threads; ++i) { + Handle *handle = new Handle(i, threads, affinity, priority); + m_workers.push_back(handle); + handle->start(Workers::onReady); + } +} + + +void Workers::stop() +{ + uv_timer_stop(&m_timer); + m_hashrate->stop(); + + uv_close(reinterpret_cast(&m_async), nullptr); + m_paused = 0; + m_sequence = 0; + + for (size_t i = 0; i < m_workers.size(); ++i) { + m_workers[i]->join(); + } +} + + +void Workers::submit(const JobResult &result) +{ + uv_mutex_lock(&m_mutex); + m_queue.push_back(result); + uv_mutex_unlock(&m_mutex); + + uv_async_send(&m_async); +} + + +void Workers::onReady(void *arg) +{ + auto handle = static_cast(arg); + if (Mem::isDoubleHash()) { + handle->setWorker(new DoubleWorker(handle)); + } + else { + handle->setWorker(new SingleWorker(handle)); + } + + handle->worker()->start(); +} + + +void Workers::onResult(uv_async_t *handle) +{ + std::list results; + + uv_mutex_lock(&m_mutex); + while (!m_queue.empty()) { + results.push_back(std::move(m_queue.front())); + m_queue.pop_front(); + } + uv_mutex_unlock(&m_mutex); + + for (auto result : results) { + m_listener->onJobResult(result); + } + + results.clear(); +} + + +void Workers::onTick(uv_timer_t *handle) +{ + for (Handle *handle : m_workers) { + if (!handle->worker()) { + return; + } + + m_hashrate->add(handle->threadId(), handle->worker()->hashCount(), handle->worker()->timestamp()); + } + + if ((m_ticks++ & 0xF) == 0) { + m_hashrate->updateHighest(); + } + +# ifndef XMRIG_NO_API + Api::tick(m_hashrate); +# endif +} diff --git a/src/workers/Workers.h b/src/workers/Workers.h new file mode 100644 index 00000000..e76d0a62 --- /dev/null +++ b/src/workers/Workers.h @@ -0,0 +1,82 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2017 XMRig + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __WORKERS_H__ +#define __WORKERS_H__ + + +#include +#include +#include +#include + +#include "net/Job.h" +#include "net/JobResult.h" + + +class Handle; +class Hashrate; +class IJobResultListener; + + +class Workers +{ +public: + static Job job(); + static void printHashrate(bool detail); + static void setEnabled(bool enabled); + static void setJob(const Job &job); + static void start(int64_t affinity, int priority); + static void stop(); + static void submit(const JobResult &result); + + static inline bool isEnabled() { return m_enabled; } + static inline bool isOutdated(uint64_t sequence) { return m_sequence.load(std::memory_order_relaxed) != sequence; } + static inline bool isPaused() { return m_paused.load(std::memory_order_relaxed) == 1; } + static inline uint64_t sequence() { return m_sequence.load(std::memory_order_relaxed); } + static inline void pause() { m_active = false; m_paused = 1; m_sequence++; } + static inline void setListener(IJobResultListener *listener) { m_listener = listener; } + +private: + static void onReady(void *arg); + static void onResult(uv_async_t *handle); + static void onTick(uv_timer_t *handle); + + static bool m_active; + static bool m_enabled; + static Hashrate *m_hashrate; + static IJobResultListener *m_listener; + static Job m_job; + static std::atomic m_paused; + static std::atomic m_sequence; + static std::list m_queue; + static std::vector m_workers; + static uint64_t m_ticks; + static uv_async_t m_async; + static uv_mutex_t m_mutex; + static uv_rwlock_t m_rwlock; + static uv_timer_t m_timer; +}; + + +#endif /* __WORKERS_H__ */ diff --git a/donate.h b/src/xmrig.cpp similarity index 91% rename from donate.h rename to src/xmrig.cpp index 4fc60787..48362ada 100644 --- a/donate.h +++ b/src/xmrig.cpp @@ -21,9 +21,11 @@ * along with this program. If not, see . */ -#ifndef __DONATE_H__ -#define __DONATE_H__ +#include "App.h" -#define DONATE_LEVEL 5 -#endif /* __DONATE_H__ */ +int main(int argc, char **argv) { + App app(argc, argv); + + return app.exec(); +} diff --git a/stats.c b/stats.c deleted file mode 100644 index ee131672..00000000 --- a/stats.c +++ /dev/null @@ -1,137 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "stats.h" -#include "options.h" -#include "utils/applog.h" -#include "persistent_memory.h" - - -static unsigned long accepted_count = 0L; -static unsigned long rejected_count = 0L; -static double *thr_hashrates; -static double *thr_times; -static uint32_t target = 0; - -pthread_mutex_t stats_lock; - - -static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); - - -/** - * @brief stats_init - */ -void stats_init() { - pthread_mutex_init(&stats_lock, NULL); - - thr_hashrates = (double *) persistent_calloc(opt_n_threads, sizeof(double)); - thr_times = (double *) persistent_calloc(opt_n_threads, sizeof(double)); -} - - - -/** - * @brief stats_set_target - * @param target - */ -void stats_set_target(uint32_t new_target) -{ - target = new_target; - - applog(LOG_DEBUG, "Pool set diff to %g", ((double) 0xffffffff) / target); -} - - -/** - * @brief stats_share_result - * @param result - */ -void stats_share_result(bool success) -{ - double hashrate = 0.0; - - pthread_mutex_lock(&stats_lock); - - for (int i = 0; i < opt_n_threads; i++) { - if (thr_times[i] > 0) { - hashrate += thr_hashrates[i] / thr_times[i]; - } - } - - success ? accepted_count++ : rejected_count++; - pthread_mutex_unlock(&stats_lock); - - applog(LOG_INFO, "accepted: %lu/%lu (%.2f%%), %.2f H/s at diff %g", - accepted_count, accepted_count + rejected_count, - 100. * accepted_count / (accepted_count + rejected_count), hashrate, - (((double) 0xffffffff) / target)); -} - - -void stats_add_hashes(int thr_id, struct timeval *tv_start, unsigned long hashes_done) -{ - struct timeval tv_end, diff; - - /* record scanhash elapsed time */ - gettimeofday(&tv_end, NULL); - timeval_subtract(&diff, &tv_end, tv_start); - - if (diff.tv_usec || diff.tv_sec) { - pthread_mutex_lock(&stats_lock); - thr_hashrates[thr_id] = hashes_done; - thr_times[thr_id] = (diff.tv_sec + 1e-6 * diff.tv_usec); - pthread_mutex_unlock(&stats_lock); - } -} - - -/* Subtract the `struct timeval' values X and Y, - storing the result in RESULT. - Return 1 if the difference is negative, otherwise 0. */ -static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) -{ - /* Perform the carry for the later subtraction by updating Y. */ - if (x->tv_usec < y->tv_usec) { - int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; - y->tv_usec -= 1000000 * nsec; - y->tv_sec += nsec; - } - if (x->tv_usec - y->tv_usec > 1000000) { - int nsec = (x->tv_usec - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - * `tv_usec' is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_usec = x->tv_usec - y->tv_usec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} diff --git a/stratum.c b/stratum.c deleted file mode 100644 index 4e937251..00000000 --- a/stratum.c +++ /dev/null @@ -1,718 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#if defined(WIN32) -# include -# include -#else -# include -# include -# include -#endif - -#ifdef __APPLE_CC__ -# include -#endif - -#include "stratum.h" -#include "version.h" -#include "stats.h" -#include "util.h" -#include "utils/applog.h" - - -#ifdef WIN32 -# define socket_blocks() (WSAGetLastError() == WSAEWOULDBLOCK) -# define poll(fdarray, nfds, timeout) WSAPoll(fdarray, nfds, timeout) -# define SHUT_RDWR SD_BOTH -#else -# define socket_blocks() (errno == EAGAIN || errno == EWOULDBLOCK) -# define closesocket(x) close((x)) -#endif - -#define RBUFSIZE 2048 -#define RECVSIZE (RBUFSIZE - 4) - -#define unlikely(expr) (__builtin_expect(!!(expr), 0)) - - -static struct work work; - - -static bool send_line(curl_socket_t sock, char *s); -static bool socket_full(curl_socket_t sock, int timeout); -static void buffer_append(struct stratum_ctx *sctx, const char *s); -static bool job(struct stratum_ctx *sctx, json_t *params); -static int sockopt_keepalive_cb(void *userdata, curl_socket_t fd, curlsocktype purpose); -static curl_socket_t opensocket_grab_cb(void *clientp, curlsocktype purpose, struct curl_sockaddr *addr); -static int closesocket_cb(void *clientp, curl_socket_t item); -static bool login_decode(struct stratum_ctx *sctx, const json_t *val); -static bool job_decode(const json_t *job); -static bool jobj_binary(const json_t *obj, const char *key, void *buf, size_t buflen); - - -/** - * @brief stratum_socket_full - * @param sctx - * @param timeout - * @return - */ -bool stratum_socket_full(struct stratum_ctx *sctx, int timeout) -{ - return strlen(sctx->sockbuf) || socket_full(sctx->sock, timeout); -} - - -/** - * @brief stratum_send_line - * @param sctx - * @param s - * @return - */ -bool stratum_send_line(struct stratum_ctx *sctx, char *s) -{ - bool ret = false; - - pthread_mutex_lock(&sctx->sock_lock); - ret = send_line(sctx->sock, s); - pthread_mutex_unlock(&sctx->sock_lock); - - return ret; -} - - -/** - * @brief stratum_recv_line - * @param sctx - * @return - */ -char *stratum_recv_line(struct stratum_ctx *sctx) -{ - if (!strstr(sctx->sockbuf, "\n")) { - bool ret = true; - time_t rstart; - - time(&rstart); - - if (!socket_full(sctx->sock, 60)) { - applog(LOG_ERR, "stratum_recv_line timed out"); - return NULL; - } - - do { - char s[RBUFSIZE]; - ssize_t n; - - memset(s, 0, RBUFSIZE); - n = recv(sctx->sock, s, RECVSIZE, 0); - if (!n) { - ret = false; - break; - } - - if (n < 0) { - if (!socket_blocks() || !socket_full(sctx->sock, 1)) { - ret = false; - break; - } - } else { - buffer_append(sctx, s); - } - } while (time(NULL) - rstart < 60 && !strstr(sctx->sockbuf, "\n")); - - if (!ret) { - applog(LOG_ERR, "stratum_recv_line failed"); - return NULL; - } - } - - ssize_t buflen = strlen(sctx->sockbuf); - char *tok = strtok(sctx->sockbuf, "\n"); - - if (!tok) { - applog(LOG_ERR, "stratum_recv_line failed to parse a newline-terminated string"); - return NULL; - } - - char *sret = strdup(tok); - ssize_t len = strlen(sret); - - if (buflen > len + 1) { - memmove(sctx->sockbuf, sctx->sockbuf + len + 1, buflen - len + 1); - } - else { - sctx->sockbuf[0] = '\0'; - } - - return sret; -} - - -/** - * @brief stratum_disconnect - * @param sctx - */ -void stratum_disconnect(struct stratum_ctx *sctx) -{ - pthread_mutex_lock(&sctx->sock_lock); - - sctx->ready = false; - - if (sctx->curl) { - curl_easy_cleanup(sctx->curl); - sctx->curl = NULL; - sctx->sockbuf[0] = '\0'; - } - - pthread_mutex_unlock(&sctx->sock_lock); -} - - -/** - * @brief stratum_handle_method - * @param sctx - * @param s - * @return - */ -bool stratum_handle_method(struct stratum_ctx *sctx, const char *s) -{ - bool ret = false; - const char *method; - json_t *val = json_decode(s); - - if (!val) { - return false; - } - - if (method = json_string_value(json_object_get(val, "method"))) { - if (!strcasecmp(method, "job")) { - ret = job(sctx, json_object_get(val, "params")); - } - else { - applog(LOG_WARNING, "Unknown method: \"%s\"", method); - } - } - - json_decref(val); - return ret; -} - - -/** - * @brief stratum_handle_response - * @param buf - * @return - */ -bool stratum_handle_response(char *buf) { - bool valid = false; - - json_t *val = json_decode(buf); - if (!val) { - return false; - } - - json_t *res_val = json_object_get(val, "result"); - json_t *err_val = json_object_get(val, "error"); - json_t *id_val = json_object_get(val, "id"); - - if (!id_val || json_is_null(id_val) || !res_val) { - const char* message; - - if (json_is_object(err_val) && (message = json_string_value(json_object_get(err_val, "message")))) { - applog(LOG_ERR, "error: \"%s\"", message); - } - - json_decref(val); - return false; - } - - json_t *status = json_object_get(res_val, "status"); - - if (status && !strcmp(json_string_value(status), "KEEPALIVED") ) { - applog(LOG_DEBUG, "Keepalived receveid"); - json_decref(val); - return true; - } - - if (status) { - valid = !strcmp(json_string_value(status), "OK") && json_is_null(err_val); - } else { - valid = json_is_null(err_val); - } - - stats_share_result(valid); - json_decref(val); - return true; -} - - -/** - * @brief stratum_keepalived - * @param sctx - * @return - */ -bool stratum_keepalived(struct stratum_ctx *sctx) -{ - char *s = malloc(128); - snprintf(s, 128, "{\"method\":\"keepalived\",\"params\":{\"id\":\"%s\"},\"id\":1}", sctx->id); - bool ret = stratum_send_line(sctx, s); - - free(s); - return ret; -} - - -/** - * @brief stratum_authorize - * @param sctx - * @param user - * @param pass - * @return - */ -bool stratum_authorize(struct stratum_ctx *sctx, const char *user, const char *pass) -{ - char *sret; - - char *req = malloc(128 + strlen(user) + strlen(pass)); - sprintf(req, "{\"method\":\"login\",\"params\":{\"login\":\"%s\",\"pass\":\"%s\",\"agent\":\"%s/%s\"},\"id\":1}", user, pass, APP_NAME, APP_VERSION); - - if (!stratum_send_line(sctx, req)) { - free(req); - return false; - } - - free(req); - - while (1) { - sret = stratum_recv_line(sctx); - if (!sret) { - return false; - } - - if (!stratum_handle_method(sctx, sret)) { - break; - } - - free(sret); - } - - json_t *val = json_decode(sret); - free(sret); - - if (!val) { - return false; - } - - json_t *result = json_object_get(val, "result"); - json_t *error = json_object_get(val, "error"); - - if (!result || json_is_false(result) || (error && !json_is_null(error))) { - const char* message; - - if (json_is_object(error) && (message = json_string_value(json_object_get(error, "message")))) { - applog(LOG_ERR, "Stratum authentication failed: \"%s\"", message); - } - else { - applog(LOG_ERR, "Stratum authentication failed"); - } - - json_decref(val); - return false; - } - - if (login_decode(sctx, val) && job(sctx, json_object_get(result, "job"))) { - pthread_mutex_lock(&sctx->sock_lock); - sctx->ready = true; - pthread_mutex_unlock(&sctx->sock_lock); - } - - json_decref(val); - return true; -} - - -/** - * @brief stratum_connect - * @param sctx - * @param url - * @return - */ -bool stratum_connect(struct stratum_ctx *sctx, const char *url) -{ - CURL *curl; - - pthread_mutex_lock(&sctx->sock_lock); - sctx->ready = false; - - if (sctx->curl) { - curl_easy_cleanup(sctx->curl); - } - - sctx->curl = curl_easy_init(); - if (!sctx->curl) { - applog(LOG_ERR, "CURL initialization failed"); - pthread_mutex_unlock(&sctx->sock_lock); - return false; - } - - curl = sctx->curl; - if (!sctx->sockbuf) { - sctx->sockbuf = calloc(RBUFSIZE, 1); - sctx->sockbuf_size = RBUFSIZE; - } - - sctx->sockbuf[0] = '\0'; - pthread_mutex_unlock(&sctx->sock_lock); - - if (url != sctx->url) { - free(sctx->url); - sctx->url = strdup(url); - } - - free(sctx->curl_url); - sctx->curl_url = malloc(strlen(url)); - sprintf(sctx->curl_url, "http%s/", strstr(url, "://")); - - curl_easy_setopt(curl, CURLOPT_VERBOSE, 0); - curl_easy_setopt(curl, CURLOPT_URL, sctx->curl_url); - curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, sctx->curl_err_str); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1); - curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb); - curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_grab_cb); - curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); - curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &sctx->sock); - curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - - int rc = curl_easy_perform(curl); - if (rc) { - applog(LOG_ERR, "Stratum connection failed: code: %d, text: %s", rc, sctx->curl_err_str); - curl_easy_cleanup(curl); - sctx->curl = NULL; - return false; - } - - return true; -} - - -/** - * @brief send_line - * @param sock - * @param s - * @return - */ -static bool send_line(curl_socket_t sock, char *s) -{ - ssize_t len, sent = 0; - - len = strlen(s); - s[len++] = '\n'; - - while (len > 0) { - struct pollfd pfd; - pfd.fd = sock; - pfd.events = POLLOUT; - - if (poll(&pfd, 1, 0) < 1) { - return false; - } - - ssize_t n = send(sock, s + sent, len, 0); - if (n < 0) { - if (!socket_blocks()) { - return false; - } - - n = 0; - } - - sent += n; - len -= n; - } - - return true; -} - - -/** - * @brief socket_full - * @param sock - * @param timeout - * @return - */ -static bool socket_full(curl_socket_t sock, int timeout) -{ - struct pollfd pfd; - pfd.fd = sock; - pfd.events = POLLIN; - - return poll(&pfd, 1, timeout * 1000) > 0; -} - - -/** - * @brief buffer_append - * @param sctx - * @param s - */ -static void buffer_append(struct stratum_ctx *sctx, const char *s) -{ - size_t old, new; - - old = strlen(sctx->sockbuf); - new = old + strlen(s) + 1; - - if (new >= sctx->sockbuf_size) { - sctx->sockbuf_size = new + (RBUFSIZE - (new % RBUFSIZE)); - sctx->sockbuf = realloc(sctx->sockbuf, sctx->sockbuf_size); - } - - strcpy(sctx->sockbuf + old, s); -} - - -/** - * @brief job - * @param sctx - * @param params - * @return - */ -static bool job(struct stratum_ctx *sctx, json_t *params) -{ - if (!job_decode(params)) { - return false; - } - - pthread_mutex_lock(&sctx->work_lock); - - if (sctx->work.target != work.target) { - stats_set_target(work.target); - } - - memcpy(&sctx->work, &work, sizeof(struct work)); - pthread_mutex_unlock(&sctx->work_lock); - - return true; -} - - -/** - * @brief sockopt_keepalive_cb - * @param userdata - * @param fd - * @param purpose - * @return - */ -static int sockopt_keepalive_cb(void *userdata, curl_socket_t fd, curlsocktype purpose) -{ - int keepalive = 1; - int tcp_keepcnt = 3; - int tcp_keepidle = 50; - int tcp_keepintvl = 50; - -#ifndef WIN32 - if (unlikely(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)))) { - return 1; - } - -# ifdef __linux - if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &tcp_keepcnt, sizeof(tcp_keepcnt)))) { - return 1; - } - - if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &tcp_keepidle, sizeof(tcp_keepidle)))) { - return 1; - } - - if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &tcp_keepintvl, sizeof(tcp_keepintvl)))) { - return 1; - } -# endif /* __linux */ - -# ifdef __APPLE_CC__ - if (unlikely(setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &tcp_keepintvl, sizeof(tcp_keepintvl)))) { - return 1; - } -# endif /* __APPLE_CC__ */ -#else /* WIN32 */ - struct tcp_keepalive vals; - vals.onoff = 1; - vals.keepalivetime = tcp_keepidle * 1000; - vals.keepaliveinterval = tcp_keepintvl * 1000; - DWORD outputBytes; - - if (unlikely(WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0, &outputBytes, NULL, NULL))) { - return 1; - } - -#endif /* WIN32 */ - - return 0; -} - - -static int closesocket_cb(void *clientp, curl_socket_t item) { - shutdown(item, SHUT_RDWR); - return closesocket(item); -} - - -/** - * @brief opensocket_grab_cb - * @param clientp - * @param purpose - * @param addr - * @return - */ -static curl_socket_t opensocket_grab_cb(void *clientp, curlsocktype purpose, struct curl_sockaddr *addr) -{ - curl_socket_t *sock = clientp; - *sock = socket(addr->family, addr->socktype, addr->protocol); - return *sock; -} - - -/** - * @brief login_decode - * @param sctx - * @param val - * @return - */ -static bool login_decode(struct stratum_ctx *sctx, const json_t *val) { - json_t *res = json_object_get(val, "result"); - if (!res) { - applog(LOG_ERR, "JSON invalid result"); - return false; - } - - const char *id = json_string_value(json_object_get(res, "id")); - if (!id || strlen(id) >= (sizeof(sctx->id))) { - applog(LOG_ERR, "JSON invalid id"); - return false; - } - - memset(&sctx->id, 0, sizeof(sctx->id)); - memcpy(&sctx->id, id, strlen(id)); - - const char *s = json_string_value(json_object_get(res, "status")); - if (!s) { - applog(LOG_ERR, "JSON invalid status"); - return false; - } - - if (strcmp(s, "OK")) { - applog(LOG_ERR, "JSON returned status \"%s\"", s); - return false; - } - - return true; -} - - -/** - * @brief job_decode - * @param sctx - * @param job - * @param work - * @return - */ -static bool job_decode(const json_t *job) { - const char *job_id = json_string_value(json_object_get(job, "job_id")); - if (!job_id || strlen(job_id) >= sizeof(work.job_id)) { - applog(LOG_ERR, "JSON invalid job id"); - return false; - } - - const char *blob = json_string_value(json_object_get(job, "blob")); - if (!blob) { - applog(LOG_ERR, "JSON invalid blob"); - return false; - } - - work.blob_size = strlen(blob); - if (work.blob_size % 2 != 0) { - applog(LOG_ERR, "JSON invalid blob length"); - return false; - } - - work.blob_size /= 2; - if (work.blob_size < 76 || work.blob_size > (sizeof(work.blob))) { - applog(LOG_ERR, "JSON invalid blob length"); - return false; - } - - if (!hex2bin((unsigned char *) work.blob, blob, work.blob_size)) { - applog(LOG_ERR, "JSON invalid blob"); - return false; - } - - jobj_binary(job, "target", &work.target, 4); - - memset(work.job_id, 0, sizeof(work.job_id)); - memcpy(work.job_id, job_id, strlen(job_id)); - - return true; -} - - -/** - * @brief jobj_binary - * @param obj - * @param key - * @param buf - * @param buflen - * @return - */ -static bool jobj_binary(const json_t *obj, const char *key, void *buf, size_t buflen) { - const char *hexstr; - json_t *tmp; - - tmp = json_object_get(obj, key); - if (unlikely(!tmp)) { - applog(LOG_ERR, "JSON key '%s' not found", key); - return false; - } - - hexstr = json_string_value(tmp); - if (unlikely(!hexstr)) { - applog(LOG_ERR, "JSON key '%s' is not a string", key); - return false; - } - - - if (!hex2bin(buf, hexstr, buflen)) { - return false; - } - - return true; -} diff --git a/stratum.h b/stratum.h deleted file mode 100644 index 48369567..00000000 --- a/stratum.h +++ /dev/null @@ -1,78 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __STRATUM_H__ -#define __STRATUM_H__ - -#include -#include -#include - - -/** - * 128tx exploit. - * - * Max blob size is 84 (75 fixed + 9 variable), aligned to 96. - * https://github.com/xmrig/xmrig/issues/1 Thanks fireice-uk. - */ -struct work { - uint32_t blob[21] __attribute__((aligned(16))); - size_t blob_size __attribute__((aligned(16))); - uint32_t target __attribute__((aligned(16))); - uint32_t hash[8] __attribute__((aligned(16))); - char job_id[64] __attribute__((aligned(16))); -}; - - -struct stratum_ctx { - char *url; - - CURL *curl; - char *curl_url; - char curl_err_str[CURL_ERROR_SIZE]; - curl_socket_t sock; - size_t sockbuf_size; - char *sockbuf; - pthread_mutex_t sock_lock; - bool ready; - - char id[64]; - - struct work work; - struct work g_work; - time_t g_work_time; - pthread_mutex_t work_lock; -}; - - -bool stratum_send_line(struct stratum_ctx *sctx, char *s); -bool stratum_socket_full(struct stratum_ctx *sctx, int timeout); -char *stratum_recv_line(struct stratum_ctx *sctx); -bool stratum_connect(struct stratum_ctx *sctx, const char *url); -void stratum_disconnect(struct stratum_ctx *sctx); -bool stratum_authorize(struct stratum_ctx *sctx, const char *user, const char *pass); -bool stratum_handle_method(struct stratum_ctx *sctx, const char *s); -bool stratum_handle_response(char *buf); -bool stratum_keepalived(struct stratum_ctx *sctx); - -#endif /* __STRATUM_H__ */ diff --git a/unix/memory_unix.c b/unix/memory_unix.c deleted file mode 100644 index 2e794a03..00000000 --- a/unix/memory_unix.c +++ /dev/null @@ -1,76 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "persistent_memory.h" -#include "options.h" -#include "utils/applog.h" - - -char *persistent_memory; -int persistent_memory_flags = 0; - - -const char * persistent_memory_allocate() { - const int ratio = (opt_double_hash && opt_algo != ALGO_CRYPTONIGHT_LITE) ? 2 : 1; - const int size = MEMORY * (opt_n_threads * ratio + 1); - persistent_memory_flags |= MEMORY_HUGEPAGES_AVAILABLE; - - persistent_memory = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); - - if (persistent_memory == MAP_FAILED) { - persistent_memory = _mm_malloc(size, 16); - return persistent_memory; - } - - persistent_memory_flags |= MEMORY_HUGEPAGES_ENABLED; - - if (madvise(persistent_memory, size, MADV_RANDOM | MADV_WILLNEED) != 0) { - applog(LOG_ERR, "madvise failed"); - } - - if (mlock(persistent_memory, size) == 0) { - persistent_memory_flags |= MEMORY_LOCK; - } - - return persistent_memory; -} - - -void persistent_memory_free() { - const int size = MEMORY * (opt_n_threads + 1); - - if (persistent_memory_flags & MEMORY_HUGEPAGES_ENABLED) { - if (persistent_memory_flags & MEMORY_LOCK) { - munlock(persistent_memory, size); - } - - munmap(persistent_memory, size); - } - else { - _mm_free(persistent_memory); - } -} diff --git a/unix/xmrig_unix.c b/unix/xmrig_unix.c deleted file mode 100644 index cd25934a..00000000 --- a/unix/xmrig_unix.c +++ /dev/null @@ -1,91 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "options.h" -#include "cpu.h" -#include "utils/applog.h" - - -static void signal_handler(int sig) -{ - switch (sig) { - case SIGHUP: - applog(LOG_WARNING, "SIGHUP received"); - break; - - case SIGINT: - applog(LOG_WARNING, "SIGINT received, exiting"); - proper_exit(0); - break; - - case SIGTERM: - applog(LOG_WARNING, "SIGTERM received, exiting"); - proper_exit(0); - break; - } -} - - -void proper_exit(int reason) { - exit(reason); -} - - -void os_specific_init() -{ - if (opt_affinity != -1) { - affine_to_cpu_mask(-1, opt_affinity); - } - - if (opt_background) { - int i = fork(); - if (i < 0) { - exit(1); - } - - if (i > 0) { - exit(0); - } - - i = setsid(); - - if (i < 0) { - applog(LOG_ERR, "setsid() failed (errno = %d)", errno); - } - - i = chdir("/"); - if (i < 0) { - applog(LOG_ERR, "chdir() failed (errno = %d)", errno); - } - - signal(SIGHUP, signal_handler); - signal(SIGTERM, signal_handler); - } - - signal(SIGINT, signal_handler); -} diff --git a/util.c b/util.c deleted file mode 100644 index 3dfec485..00000000 --- a/util.c +++ /dev/null @@ -1,270 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#include "util.h" -#include "elist.h" -#include "utils/applog.h" - - -struct tq_ent { - void *data; - struct list_head q_node; -}; - - -struct thread_q { - struct list_head q; - bool frozen; - pthread_mutex_t mutex; - pthread_cond_t cond; -}; - - -json_t *json_decode(const char *s) -{ - json_error_t err; - json_t *val = json_loads(s, 0, &err); - - if (!val) { - applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text); - } - - return val; -} - - -/** - * @brief bin2hex - * @param p - * @param len - * @return - */ -char *bin2hex(const unsigned char *p, size_t len) -{ - char *s = malloc((len * 2) + 1); - if (!s) { - return NULL; - } - - for (int i = 0; i < len; i++) { - sprintf(s + (i * 2), "%02x", (unsigned int) p[i]); - } - - return s; -} - - -/** - * @brief hex2bin - * @param p - * @param hexstr - * @param len - * @return - */ -bool hex2bin(unsigned char *p, const char *hexstr, size_t len) -{ - char hex_byte[3]; - char *ep; - - hex_byte[2] = '\0'; - - while (*hexstr && len) { - if (!hexstr[1]) { - applog(LOG_ERR, "hex2bin str truncated"); - return false; - } - - hex_byte[0] = hexstr[0]; - hex_byte[1] = hexstr[1]; - *p = (unsigned char) strtol(hex_byte, &ep, 16); - if (*ep) { - applog(LOG_ERR, "hex2bin failed on '%s'", hex_byte); - return false; - } - - p++; - hexstr += 2; - len--; - } - - return (len == 0 && *hexstr == 0) ? true : false; -} - - -/** - * @brief tq_new - * @return - */ -struct thread_q *tq_new(void) -{ - struct thread_q *tq; - - tq = calloc(1, sizeof(*tq)); - if (!tq) - return NULL; - - INIT_LIST_HEAD(&tq->q); - pthread_mutex_init(&tq->mutex, NULL); - pthread_cond_init(&tq->cond, NULL); - - return tq; -} - - -/** - * @brief tq_free - * @param tq - */ -void tq_free(struct thread_q *tq) -{ - struct tq_ent *ent, *iter; - - if (!tq) - return; - - list_for_each_entry_safe(ent, iter, &tq->q, q_node) { - list_del(&ent->q_node); - free(ent); - } - - pthread_cond_destroy(&tq->cond); - pthread_mutex_destroy(&tq->mutex); - - memset(tq, 0, sizeof(*tq)); /* poison */ - free(tq); -} - - -/** - * @brief tq_freezethaw - * @param tq - * @param frozen - */ -static void tq_freezethaw(struct thread_q *tq, bool frozen) -{ - pthread_mutex_lock(&tq->mutex); - - tq->frozen = frozen; - - pthread_cond_signal(&tq->cond); - pthread_mutex_unlock(&tq->mutex); -} - - -/** - * @brief tq_freeze - * @param tq - */ -void tq_freeze(struct thread_q *tq) -{ - tq_freezethaw(tq, true); -} - - -/** - * @brief tq_thaw - * @param tq - */ -void tq_thaw(struct thread_q *tq) -{ - tq_freezethaw(tq, false); -} - - -/** - * @brief tq_push - * @param tq - * @param data - * @return - */ -bool tq_push(struct thread_q *tq, void *data) -{ - struct tq_ent *ent; - bool rc = true; - - ent = calloc(1, sizeof(*ent)); - if (!ent) - return false; - - ent->data = data; - INIT_LIST_HEAD(&ent->q_node); - - pthread_mutex_lock(&tq->mutex); - - if (!tq->frozen) { - list_add_tail(&ent->q_node, &tq->q); - } else { - free(ent); - rc = false; - } - - pthread_cond_signal(&tq->cond); - pthread_mutex_unlock(&tq->mutex); - - return rc; -} - - -/** - * @brief tq_pop - * @param tq - * @param abstime - * @return - */ -void *tq_pop(struct thread_q *tq, const struct timespec *abstime) -{ - struct tq_ent *ent; - void *rval = NULL; - int rc; - - pthread_mutex_lock(&tq->mutex); - - if (!list_empty(&tq->q)) - goto pop; - - if (abstime) - rc = pthread_cond_timedwait(&tq->cond, &tq->mutex, abstime); - else - rc = pthread_cond_wait(&tq->cond, &tq->mutex); - if (rc) - goto out; - if (list_empty(&tq->q)) - goto out; - -pop: - ent = list_entry(tq->q.next, struct tq_ent, q_node); - rval = ent->data; - - list_del(&ent->q_node); - free(ent); - -out: - pthread_mutex_unlock(&tq->mutex); - return rval; -} diff --git a/utils/applog.c b/utils/applog.c deleted file mode 100644 index e0043c86..00000000 --- a/utils/applog.c +++ /dev/null @@ -1,151 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "xmrig.h" -#include "applog.h" -#include "threads.h" -#include -#include - -#ifdef WIN32 -# include "compat/winansi.h" -#endif - -#include "options.h" - - -MUTEX applog_mutex; - - -void applog_init() -{ - MUTEX_INIT(applog_mutex); -} - - -void applog(int prio, const char *fmt, ...) -{ - if (opt_background) { - return; - } - - va_list ap; - va_start(ap, fmt); - - struct tm tm; - struct tm *tm_p; - time_t now = time(NULL); - - MUTEX_LOCK(applog_mutex); - tm_p = localtime(&now); - memcpy(&tm, tm_p, sizeof(tm)); - MUTEX_UNLOCK(applog_mutex); - - const char* color = ""; - - if (opt_colors) { - switch (prio) { - case LOG_ERR: color = CL_RED; break; - case LOG_WARNING: color = CL_YLW; break; - case LOG_NOTICE: color = CL_WHT; break; - case LOG_INFO: color = ""; break; - case LOG_DEBUG: color = CL_GRY; break; - - case LOG_BLUE: - prio = LOG_NOTICE; - color = CL_CYN; - break; - - case LOG_GREEN: - prio = LOG_NOTICE; - color = CL_LGR; - break; - } - } - - const int len = 64 + strlen(fmt) + 2; - char *f = alloca(len); - - sprintf(f, "[%d-%02d-%02d %02d:%02d:%02d]%s %s%s\n", - tm.tm_year + 1900, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - color, - fmt, - opt_colors ? CL_N : "" - ); - - MUTEX_LOCK(applog_mutex); - vfprintf(stderr, f, ap); - fflush(stderr); - MUTEX_UNLOCK(applog_mutex); - - va_end(ap); -} - - -void applog_notime(int prio, const char *fmt, ...) -{ - if (opt_background) { - return; - } - - va_list ap; - va_start(ap, fmt); - - const char* color = ""; - - if (opt_colors) { - switch (prio) { - case LOG_ERR: color = CL_RED; break; - case LOG_WARNING: color = CL_LYL; break; - case LOG_NOTICE: color = CL_WHT; break; - case LOG_INFO: color = ""; break; - case LOG_DEBUG: color = CL_GRY; break; - - case LOG_BLUE: - prio = LOG_NOTICE; - color = CL_CYN; - break; - } - } - - const int len = 64 + strlen(fmt) + 2; - char *f = alloca(len); - - sprintf(f, "%s%s%s\n", - color, - fmt, - opt_colors ? CL_N : "" - ); - - MUTEX_LOCK(applog_mutex); - vfprintf(stderr, f, ap); - fflush(stderr); - MUTEX_UNLOCK(applog_mutex); - - va_end(ap); -} diff --git a/utils/applog.h b/utils/applog.h deleted file mode 100644 index 24a73226..00000000 --- a/utils/applog.h +++ /dev/null @@ -1,75 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __APPLOG_H__ -#define __APPLOG_H__ - -enum { - LOG_ERR, - LOG_WARNING, - LOG_NOTICE, - LOG_INFO, - LOG_DEBUG, - LOG_BLUE = 0x10, - LOG_GREEN -}; - -#define CL_N "\x1B[0m" -#define CL_RED "\x1B[31m" -#define CL_GRN "\x1B[32m" -#define CL_YLW "\x1B[33m" -#define CL_BLU "\x1B[34m" -#define CL_MAG "\x1B[35m" -#define CL_CYN "\x1B[36m" - -#define CL_BLK "\x1B[22;30m" /* black */ -#define CL_RD2 "\x1B[22;31m" /* red */ -#define CL_GR2 "\x1B[22;32m" /* green */ -#define CL_BRW "\x1B[22;33m" /* brown */ -#define CL_BL2 "\x1B[22;34m" /* blue */ -#define CL_MA2 "\x1B[22;35m" /* magenta */ -#define CL_CY2 "\x1B[22;36m" /* cyan */ -#define CL_SIL "\x1B[22;37m" /* gray */ - -#ifdef WIN32 -#define CL_GRY "\x1B[01;30m" /* dark gray */ -#else -#define CL_GRY "\x1B[90m" /* dark gray selectable in putty */ -#endif -#define CL_LRD "\x1B[01;31m" /* light red */ -#define CL_LGR "\x1B[01;32m" /* light green */ -#define CL_LYL "\x1B[01;33m" /* light yellow */ -#define CL_LBL "\x1B[01;34m" /* light blue */ -#define CL_LMA "\x1B[01;35m" /* light magenta */ -#define CL_LCY "\x1B[01;36m" /* light cyan */ - -#define CL_WHT "\x1B[01;37m" /* white */ - -#define OPT_COLOR(color, text) (opt_colors ? (color text CL_N) : text) - - -void applog_init(); -void applog(int prio, const char *fmt, ...); -void applog_notime(int prio, const char *fmt, ...); - -#endif /* __APPLOG_H__ */ diff --git a/utils/summary.c b/utils/summary.c deleted file mode 100644 index 65912bb0..00000000 --- a/utils/summary.c +++ /dev/null @@ -1,121 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "options.h" -#include "applog.h" -#include "version.h" -#include "persistent_memory.h" -#include "cpu.h" - - -static void print_memory() { - const char *t1 = (persistent_memory_flags & MEMORY_HUGEPAGES_AVAILABLE) ? OPT_COLOR(CL_LGR, "available") : OPT_COLOR(CL_LRD, "unavailable"); - const char *t2 = (persistent_memory_flags & MEMORY_HUGEPAGES_ENABLED) ? OPT_COLOR(CL_LGR, "enabled") : OPT_COLOR(CL_LRD, "disabled"); - - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "HUGE PAGES: %s, %s", t1, t2); - } - else { - applog_notime(LOG_INFO, " * HUGE PAGES: %s, %s", t1, t2); - } -} - - -static void print_cpu() { - const char *t1 = (cpu_info.flags & CPU_FLAG_X86_64) ? OPT_COLOR(CL_LGR, "x86_64") : OPT_COLOR(CL_LRD, "-x86_64"); - const char *t2 = (cpu_info.flags & CPU_FLAG_AES) ? OPT_COLOR(CL_LGR, "AES-NI") : OPT_COLOR(CL_LRD, "-AES-NI"); - - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "CPU: %s (%d)", cpu_info.brand, cpu_info.sockets); - } - else { - applog_notime(LOG_INFO, " * CPU: %s (%d)", cpu_info.brand, cpu_info.sockets); - } - - # ifndef XMRIG_NO_LIBCPUID - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "CPU L2/L3: %.1f MB/%.1f MB", cpu_info.l2_cache / 1024.0, cpu_info.l3_cache / 1024.0); - } - else { - applog_notime(LOG_INFO, " * CPU L2/L3: %.1f MB/%.1f MB", cpu_info.l2_cache / 1024.0, cpu_info.l3_cache / 1024.0); - } - # endif - - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "CPU FEATURES: %s %s", t1, t2); - } - else { - applog_notime(LOG_INFO, " * CPU FEATURES: %s %s", t1, t2); - } -} - - -static void print_threads() { - const char *extra = ""; - if (opt_nicehash) { - extra = ", nicehash"; - } - - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "THREADS: " CL_WHT "%d" CL_WHT ", av=%d, %s, donate=%d%%%s", opt_n_threads, opt_algo_variant, get_current_algo_name(), opt_donate_level, extra); - } - else { - applog_notime(LOG_INFO, " * THREADS: %d, av=%d, %s, donate=%d%%%s", opt_n_threads, opt_algo_variant, get_current_algo_name(), opt_donate_level, extra); - } -} - - -static void print_stratum() { - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "STRATUM URL: " CL_LCY "%s", opt_url); - - if (opt_backup_url) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "FAILOVER URL: " CL_LCY "%s", opt_backup_url); - } - else { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT "FAILOVER URL: " CL_LRD "none"); - } - } - else { - applog_notime(LOG_INFO, " * STRATUM URL: %s", opt_url); - applog_notime(LOG_INFO, " * FAILOVER URL: %s", opt_backup_url ? opt_backup_url : "none"); - } -} - - -void print_summary() { - if (opt_colors) { - applog_notime(LOG_INFO, CL_LGR " * " CL_WHT APP_NAME " " APP_VERSION " " CL_LCY APP_SITE); - } - else { - applog_notime(LOG_INFO, " * " APP_NAME " " APP_VERSION " " APP_SITE); - } - - print_memory(); - print_cpu(); - print_threads(); - print_stratum(); -} - - - diff --git a/win/xmrig_win.c b/win/xmrig_win.c deleted file mode 100644 index 96921faa..00000000 --- a/win/xmrig_win.c +++ /dev/null @@ -1,84 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "options.h" -#include "cpu.h" -#include "utils/applog.h" - - -BOOL WINAPI ConsoleHandler(DWORD dwType) -{ - switch (dwType) { - case CTRL_C_EVENT: - applog(LOG_WARNING, "CTRL_C_EVENT received, exiting"); - proper_exit(0); - break; - - case CTRL_BREAK_EVENT: - applog(LOG_WARNING, "CTRL_BREAK_EVENT received, exiting"); - proper_exit(0); - break; - - default: - return false; -} - - return true; -} - - -void proper_exit(int reason) { - if (opt_background) { - HWND hcon = GetConsoleWindow(); - if (hcon) { - // unhide parent command line windows - ShowWindow(hcon, SW_SHOWMINNOACTIVE); - } - } - - exit(reason); -} - - -void os_specific_init() -{ - if (opt_affinity != -1) { - affine_to_cpu_mask(-1, opt_affinity); - } - - SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE); - - if (opt_background) { - HWND hcon = GetConsoleWindow(); - if (hcon) { - // this method also hide parent command line window - ShowWindow(hcon, SW_HIDE); - } else { - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - CloseHandle(h); - FreeConsole(); - } - } -} diff --git a/xmrig.c b/xmrig.c deleted file mode 100644 index 7b14933b..00000000 --- a/xmrig.c +++ /dev/null @@ -1,677 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#ifdef WIN32 -# include -# include -#endif - -#include -#include -#include - -#include "compat.h" -#include "xmrig.h" -#include "algo/cryptonight/cryptonight.h" -#include "options.h" -#include "cpu.h" -#include "persistent_memory.h" -#include "stratum.h" -#include "stats.h" -#include "util.h" -#include "utils/summary.h" -#include "utils/applog.h" - -#define LP_SCANTIME 60 -#define JSON_BUF_LEN 345 - - -struct workio_cmd { - struct thr_info *thr; - struct work *work; -}; - - -struct thr_info *thr_info; -static int work_thr_id = -1; -static int timer_thr_id = -1; -static int stratum_thr_id = -1; -struct work_restart *work_restart = NULL; -static struct stratum_ctx *stratum_ctx = NULL; -static bool backup_active = false; -static bool g_want_donate = false; - - -static void workio_cmd_free(struct workio_cmd *wc); - - -/** - * @brief work_copy - * @param dest - * @param src - */ -static inline void work_copy(struct work *dest, const struct work *src) { - memcpy(dest, src, sizeof(struct work)); -} - - -/** - * @brief restart_threads - */ -static inline void restart_threads(void) { - for (int i = 0; i < opt_n_threads; i++) { - work_restart[i].restart = 1; - } -} - - -/** - * @brief gen_workify - * @param sctx - * @param work - */ -static inline void gen_workify(struct stratum_ctx *sctx) { - pthread_mutex_lock(&stratum_ctx->work_lock); - - if (stratum_ctx->work.job_id && (!stratum_ctx->g_work_time || strcmp(stratum_ctx->work.job_id, stratum_ctx->g_work.job_id))) { - memcpy(&sctx->g_work, &sctx->work, sizeof(struct work)); - time(&stratum_ctx->g_work_time); - - pthread_mutex_unlock(&stratum_ctx->work_lock); - - applog(LOG_DEBUG, "Stratum detected new block"); - restart_threads(); - - return; - } - - pthread_mutex_unlock(&stratum_ctx->work_lock); -} - - -/** - * @brief submit_upstream_work - * @param work - * @return - */ -static bool submit_upstream_work(struct work *work) { - char s[JSON_BUF_LEN]; - - /* pass if the previous hash is not the current previous hash */ - if (memcmp(work->blob + 1, stratum_ctx->g_work.blob + 1, 32)) { - return true; - } - - char *noncestr = bin2hex(((const unsigned char*) work->blob) + 39, 4); - char *hashhex = bin2hex((const unsigned char *) work->hash, 32); - - snprintf(s, JSON_BUF_LEN, - "{\"method\":\"submit\",\"params\":{\"id\":\"%s\",\"job_id\":\"%s\",\"nonce\":\"%s\",\"result\":\"%s\"},\"id\":1}", - stratum_ctx->id, work->job_id, noncestr, hashhex); - - free(hashhex); - free(noncestr); - - if (unlikely(!stratum_send_line(stratum_ctx, s))) { - return false; - } - - return true; -} - - - -/** - * @brief workio_cmd_free - * @param wc - */ -static void workio_cmd_free(struct workio_cmd *wc) { - if (!wc) { - return; - } - - free(wc->work); - - memset(wc, 0, sizeof(*wc)); /* poison */ - free(wc); -} - - -/** - * @brief workio_submit_work - * @param wc - * @param curl - * @return - */ -static bool workio_submit_work(struct workio_cmd *wc) { - while (!submit_upstream_work(wc->work)) { - sleep(opt_retry_pause); - } - - return true; -} - - -/** - * @brief workio_thread - * @param userdata - * @return - */ -static void *workio_thread(void *userdata) { - struct thr_info *mythr = userdata; - bool ok = true; - - while (ok) { - struct workio_cmd *wc; - - /* wait for workio_cmd sent to us, on our queue */ - wc = tq_pop(mythr->q, NULL ); - if (!wc) { - ok = false; - break; - } - - workio_submit_work(wc); - workio_cmd_free(wc); - } - - tq_freeze(mythr->q); - - return NULL ; -} - - -/** - * @brief submit_work - * @param thr - * @param work_in - * @return - */ -static bool submit_work(struct thr_info *thr, const struct work *work_in) { - struct workio_cmd *wc; - - /* fill out work request message */ - wc = calloc(1, sizeof(*wc)); - wc->work = malloc(sizeof(*work_in)); - - if (likely(wc->work)) { - wc->thr = thr; - work_copy(wc->work, work_in); - - if (likely(tq_push(thr_info[work_thr_id].q, wc))) { - return true; - } - } - - workio_cmd_free(wc); - return false; -} - - -static bool should_pause(int thr_id) { - bool ret = false; - - pthread_mutex_lock(&stratum_ctx->sock_lock); - - if (!stratum_ctx->ready) { - ret = true; - } - - pthread_mutex_unlock(&stratum_ctx->sock_lock); - - return ret; -} - - -/** - * @brief miner_thread - * @param userdata - * @return - */ -static void *miner_thread(void *userdata) { - struct thr_info *mythr = userdata; - const int thr_id = mythr->id; - struct work work = { { 0 } }; - uint32_t max_nonce; - uint32_t end_nonce = 0xffffffffU / opt_n_threads * (thr_id + 1) - 0x20; - - struct cryptonight_ctx *persistentctx = (struct cryptonight_ctx *) create_persistent_ctx(thr_id); - - if (cpu_info.total_logical_cpus > 1 && opt_affinity != -1L) { - affine_to_cpu_mask(thr_id, (unsigned long) opt_affinity); - } - - uint32_t *nonceptr = NULL; - uint32_t hash[8] __attribute__((aligned(32))); - - while (1) { - if (should_pause(thr_id)) { - sleep(1); - continue; - } - - pthread_mutex_lock(&stratum_ctx->work_lock); - - if (memcmp(work.job_id, stratum_ctx->g_work.job_id, 64)) { - work_copy(&work, &stratum_ctx->g_work); - nonceptr = (uint32_t*) (((char*) work.blob) + 39); - - if (opt_nicehash) { - end_nonce = (*nonceptr & 0xff000000U) + (0xffffffU / opt_n_threads * (thr_id + 1) - 0x20); - *nonceptr = (*nonceptr & 0xff000000U) + (0xffffffU / opt_n_threads * thr_id); - } - else { - *nonceptr = 0xffffffffU / opt_n_threads * thr_id; - } - } - - pthread_mutex_unlock(&stratum_ctx->work_lock); - - work_restart[thr_id].restart = 0; - - if (*nonceptr + LP_SCANTIME > end_nonce) { - max_nonce = end_nonce; - } else { - max_nonce = *nonceptr + LP_SCANTIME; - } - - unsigned long hashes_done = 0; - - struct timeval tv_start; - gettimeofday(&tv_start, NULL); - - /* scan nonces for a proof-of-work hash */ - const int rc = scanhash_cryptonight(thr_id, hash, work.blob, work.blob_size, work.target, max_nonce, &hashes_done, persistentctx); - stats_add_hashes(thr_id, &tv_start, hashes_done); - - if (!rc) { - continue; - } - - memcpy(work.hash, hash, 32); - submit_work(mythr, &work); - ++(*nonceptr); - } - - tq_freeze(mythr->q); - return NULL; -} - - -/** - * @brief miner_thread_double - * @param userdata - * @return - */ -static void *miner_thread_double(void *userdata) { - struct thr_info *mythr = userdata; - const int thr_id = mythr->id; - struct work work = { { 0 } }; - uint32_t max_nonce; - uint32_t end_nonce = 0xffffffffU / opt_n_threads * (thr_id + 1) - 0x20; - - struct cryptonight_ctx *persistentctx = (struct cryptonight_ctx *) create_persistent_ctx(thr_id); - - if (cpu_info.total_logical_cpus > 1 && opt_affinity != -1L) { - affine_to_cpu_mask(thr_id, (unsigned long) opt_affinity); - } - - uint32_t *nonceptr0 = NULL; - uint32_t *nonceptr1 = NULL; - uint8_t double_hash[64]; - uint8_t double_blob[sizeof(work.blob) * 2]; - - while (1) { - if (should_pause(thr_id)) { - sleep(1); - continue; - } - - pthread_mutex_lock(&stratum_ctx->work_lock); - - if (memcmp(work.job_id, stratum_ctx->g_work.job_id, 64)) { - work_copy(&work, &stratum_ctx->g_work); - - memcpy(double_blob, work.blob, work.blob_size); - memcpy(double_blob + work.blob_size, work.blob, work.blob_size); - - nonceptr0 = (uint32_t*) (((char*) double_blob) + 39); - nonceptr1 = (uint32_t*) (((char*) double_blob) + 39 + work.blob_size); - - if (opt_nicehash) { - end_nonce = (*nonceptr0 & 0xff000000U) + (0xffffffU / (opt_n_threads * 2) * (thr_id + 1) - 0x20); - *nonceptr0 = (*nonceptr0 & 0xff000000U) + (0xffffffU / (opt_n_threads * 2) * thr_id); - *nonceptr1 = (*nonceptr1 & 0xff000000U) + (0xffffffU / (opt_n_threads * 2) * (thr_id + opt_n_threads)); - } - else { - *nonceptr0 = 0xffffffffU / (opt_n_threads * 2) * thr_id; - *nonceptr1 = 0xffffffffU / (opt_n_threads * 2) * (thr_id + opt_n_threads); - } - } - - pthread_mutex_unlock(&stratum_ctx->work_lock); - - work_restart[thr_id].restart = 0; - - if (*nonceptr0 + (LP_SCANTIME / 2) > end_nonce) { - max_nonce = end_nonce; - } else { - max_nonce = *nonceptr0 + (LP_SCANTIME / 2); - } - - unsigned long hashes_done = 0; - - struct timeval tv_start; - gettimeofday(&tv_start, NULL); - - /* scan nonces for a proof-of-work hash */ - const int rc = scanhash_cryptonight_double(thr_id, (uint32_t *) double_hash, double_blob, work.blob_size, work.target, max_nonce, &hashes_done, persistentctx); - stats_add_hashes(thr_id, &tv_start, hashes_done); - - if (!rc) { - continue; - } - - if (rc & 1) { - memcpy(work.hash, double_hash, 32); - memcpy(work.blob, double_blob, work.blob_size); - submit_work(mythr, &work); - } - - if (rc & 2) { - memcpy(work.hash, double_hash + 32, 32); - memcpy(work.blob, double_blob + work.blob_size, work.blob_size); - submit_work(mythr, &work); - } - - ++(*nonceptr0); - ++(*nonceptr1); - } - - tq_freeze(mythr->q); - return NULL; -} - - - -/** - * @brief stratum_thread - * @param userdata - * @return - */ -static void *timer_thread(void *userdata) { - const int max_user_time = 100 - opt_donate_level; - int user_time_remaning = max_user_time; - int donate_time_remaning = 0; - - - while (1) { - sleep(60); - - if (user_time_remaning > 0) { - if (--user_time_remaning == 0) { - g_want_donate = true; - - donate_time_remaning = opt_donate_level; - stratum_disconnect(stratum_ctx); - continue; - } - } - - if (donate_time_remaning > 0) { - if (--donate_time_remaning == 0) { - g_want_donate = false; - - user_time_remaning = max_user_time; - stratum_disconnect(stratum_ctx); - continue; - } - } - } -} - - -static void switch_stratum() { - static bool want_donate = false; - - if (g_want_donate && !want_donate) { - stratum_ctx->url = opt_algo == ALGO_CRYPTONIGHT ? "stratum+tcp://donate.xmrig.com:443" : "stratum+tcp://donate.xmrig.com:3333"; - applog(LOG_NOTICE, "Switching to dev pool"); - want_donate = true; - } - - if (!g_want_donate && want_donate) { - stratum_ctx->url = backup_active ? opt_backup_url : opt_url; - applog(LOG_NOTICE, "Switching to user pool: \"%s\"", stratum_ctx->url); - want_donate = false; - } -} - - - -/** - * @brief stratum_thread - * @param userdata - * @return - */ -static void *stratum_thread(void *userdata) { - char *s; - - stratum_ctx->url = opt_url; - stratum_ctx->ready = false; - - while (1) { - int failures = 0; - switch_stratum(); - - while (!stratum_ctx->curl) { - pthread_mutex_lock(&stratum_ctx->work_lock); - stratum_ctx->g_work_time = 0; - pthread_mutex_unlock(&stratum_ctx->work_lock); - - restart_threads(); - switch_stratum(); - - if (!stratum_connect(stratum_ctx, stratum_ctx->url) || !stratum_authorize(stratum_ctx, opt_user, opt_pass)) { - stratum_disconnect(stratum_ctx); - failures++; - - if (failures > opt_retries && opt_backup_url) { - failures = 0; - - backup_active = !backup_active; - stratum_ctx->url = backup_active ? opt_backup_url : opt_url; - sleep(opt_retry_pause); - - applog(LOG_WARNING, "Switch to: \"%s\"", stratum_ctx->url); - continue; - } - - applog(LOG_ERR, "...retry after %d seconds", opt_retry_pause); - sleep(opt_retry_pause); - } - } - - gen_workify(stratum_ctx); - - if (opt_keepalive && !stratum_socket_full(stratum_ctx, 90)) { - stratum_keepalived(stratum_ctx); - } - - if (!stratum_socket_full(stratum_ctx, 300)) { - applog(LOG_ERR, "Stratum connection timed out"); - s = NULL; - } else { - s = stratum_recv_line(stratum_ctx); - } - - if (!s) { - stratum_disconnect(stratum_ctx); - applog(LOG_ERR, "Stratum connection interrupted"); - continue; - } - - if (!stratum_handle_method(stratum_ctx, s)) { - stratum_handle_response(s); - } - - free(s); - } - - return NULL ; -} - - -/** - * @brief start work I/O thread - * @return - */ -static bool start_workio() { - work_thr_id = opt_n_threads; - - struct thr_info *thr = &thr_info[work_thr_id]; - thr->id = work_thr_id; - thr->q = tq_new(); - - if (unlikely(!thr->q || pthread_create(&thr->pth, NULL, workio_thread, thr))) { - return false; - } - - return true; -} - - -/** - * @brief start_stratum - * @return - */ -static bool start_stratum() { - stratum_thr_id = opt_n_threads + 1; - - stratum_ctx = persistent_calloc(1, sizeof(struct stratum_ctx)); - pthread_mutex_init(&stratum_ctx->work_lock, NULL); - pthread_mutex_init(&stratum_ctx->sock_lock, NULL); - - struct thr_info *thr = &thr_info[stratum_thr_id]; - thr->id = stratum_thr_id; - thr->q = tq_new(); - - if (unlikely(!thr->q || pthread_create(&thr->pth, NULL, stratum_thread, thr))) { - return false; - } - - tq_push(thr_info[stratum_thr_id].q, strdup(opt_url)); - return true; -} - - -/** - * @brief start_timer - * @return - */ -static bool start_timer() { - timer_thr_id = opt_n_threads + 2; - - if (opt_donate_level < 1) { - return true; - } - - struct thr_info *thr = &thr_info[timer_thr_id]; - thr->id = timer_thr_id; - thr->q = tq_new(); - - if (unlikely(!thr->q || pthread_create(&thr->pth, NULL, timer_thread, thr))) { - return false; - } - - return true; -} - - -/** - * @brief start_mining - * @return - */ -static bool start_mining() { - for (int i = 0; i < opt_n_threads; i++) { - struct thr_info *thr = &thr_info[i]; - - thr->id = i; - thr->q = tq_new(); - - if (unlikely(!thr->q || pthread_create(&thr->pth, NULL, opt_double_hash ? miner_thread_double : miner_thread, thr))) { - applog(LOG_ERR, "thread %d create failed", i); - return false; - } - } - - return true; -} - - -/** - * @brief main - * @param argc - * @param argv - * @return - */ -int main(int argc, char *argv[]) { - applog_init(); - cpu_init(); - parse_cmdline(argc, argv); - persistent_memory_allocate(); - print_summary(); - - stats_init(); - os_specific_init(); - - work_restart = persistent_calloc(opt_n_threads, sizeof(*work_restart)); - thr_info = persistent_calloc(opt_n_threads + 3, sizeof(struct thr_info)); - - if (!start_workio()) { - applog(LOG_ERR, "workio thread create failed"); - return 1; - } - - if (!start_stratum()) { - applog(LOG_ERR, "stratum thread create failed"); - return 1; - } - - start_timer(); - - if (!start_mining()) { - return 1; - } - - pthread_join(thr_info[work_thr_id].pth, NULL); - applog(LOG_INFO, "workio thread dead, exiting."); - persistent_memory_free(); - return 0; -} -