diff --git a/.gitignore b/.gitignore index 0fbf883..b640b9a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /lizardfs-3.11.0.tar.gz /lizardfs-3.11.2.tar.gz /lizardfs-3.11.3.tar.gz +/lizardfs-3.12.0.tar.gz diff --git a/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch b/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch new file mode 100644 index 0000000..77dc7cf --- /dev/null +++ b/0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch @@ -0,0 +1,23784 @@ +From e8ce42bd1756361349b41b648e6f744934f8e44e Mon Sep 17 00:00:00 2001 +From: Jonathan Dieter +Date: Tue, 26 Dec 2017 17:27:46 +0200 +Subject: [PATCH] Put customized spdlog in source so we don't download during + build + +Signed-off-by: Jonathan Dieter +--- + external/spdlog-0.14.0/.gitignore | 64 + + external/spdlog-0.14.0/.travis.yml | 90 + + external/spdlog-0.14.0/CMakeLists.txt | 87 + + external/spdlog-0.14.0/INSTALL | 13 + + external/spdlog-0.14.0/LICENSE | 22 + + external/spdlog-0.14.0/README.md | 224 + + external/spdlog-0.14.0/astyle.sh | 5 + + external/spdlog-0.14.0/bench/Makefile.mingw | 57 + + external/spdlog-0.14.0/bench/boost-bench-mt.cpp | 84 + + external/spdlog-0.14.0/bench/boost-bench.cpp | 47 + + external/spdlog-0.14.0/bench/easyl.conf | 10 + + .../spdlog-0.14.0/bench/easylogging-bench-mt.cpp | 52 + + external/spdlog-0.14.0/bench/easylogging-bench.cpp | 22 + + external/spdlog-0.14.0/bench/g2log-async.cpp | 62 + + external/spdlog-0.14.0/bench/glog-bench-mt.cpp | 50 + + external/spdlog-0.14.0/bench/glog-bench.cpp | 21 + + external/spdlog-0.14.0/bench/latency/compare.sh | 13 + + .../spdlog-0.14.0/bench/latency/g3log-crush.cpp | 37 + + .../spdlog-0.14.0/bench/latency/g3log-latency.cpp | 129 + + .../spdlog-0.14.0/bench/latency/spdlog-latency.cpp | 128 + + external/spdlog-0.14.0/bench/latency/utils.h | 35 + + external/spdlog-0.14.0/bench/logs/.gitignore | 4 + + external/spdlog-0.14.0/bench/spdlog-async.cpp | 62 + + external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp | 55 + + external/spdlog-0.14.0/bench/spdlog-bench.cpp | 20 + + external/spdlog-0.14.0/bench/spdlog-null-async.cpp | 112 + + external/spdlog-0.14.0/bench/utils.h | 35 + + external/spdlog-0.14.0/cmake/Config.cmake.in | 24 + + external/spdlog-0.14.0/cmake/spdlog.pc.in | 6 + + external/spdlog-0.14.0/example/CMakeLists.txt | 49 + + external/spdlog-0.14.0/example/Makefile.clang | 32 + + external/spdlog-0.14.0/example/Makefile.mingw | 32 + + external/spdlog-0.14.0/example/bench.cpp | 144 + + external/spdlog-0.14.0/example/example.cpp | 174 + + external/spdlog-0.14.0/example/example.sln | 26 + + external/spdlog-0.14.0/example/example.vcxproj | 126 + + external/spdlog-0.14.0/example/jni/Android.mk | 15 + + external/spdlog-0.14.0/example/jni/Application.mk | 2 + + external/spdlog-0.14.0/example/jni/example.cpp | 1 + + external/spdlog-0.14.0/example/multisink.cpp | 47 + + external/spdlog-0.14.0/example/utils.h | 35 + + .../spdlog-0.14.0/include/spdlog/async_logger.h | 82 + + external/spdlog-0.14.0/include/spdlog/common.h | 160 + + .../include/spdlog/details/async_log_helper.h | 399 + + .../include/spdlog/details/async_logger_impl.h | 105 + + .../include/spdlog/details/file_helper.h | 117 + + .../spdlog-0.14.0/include/spdlog/details/log_msg.h | 50 + + .../include/spdlog/details/logger_impl.h | 564 ++ + .../include/spdlog/details/mpmc_bounded_q.h | 172 + + .../include/spdlog/details/null_mutex.h | 45 + + external/spdlog-0.14.0/include/spdlog/details/os.h | 469 + + .../spdlog/details/pattern_formatter_impl.h | 665 ++ + .../include/spdlog/details/registry.h | 214 + + .../include/spdlog/details/spdlog_impl.h | 263 + + .../include/spdlog/fmt/bundled/format.cc | 535 ++ + .../include/spdlog/fmt/bundled/format.h | 4012 +++++++++ + .../include/spdlog/fmt/bundled/ostream.cc | 35 + + .../include/spdlog/fmt/bundled/ostream.h | 105 + + .../include/spdlog/fmt/bundled/posix.cc | 241 + + .../include/spdlog/fmt/bundled/posix.h | 367 + + .../include/spdlog/fmt/bundled/time.h | 143 + + external/spdlog-0.14.0/include/spdlog/fmt/fmt.h | 28 + + external/spdlog-0.14.0/include/spdlog/fmt/ostr.h | 17 + + external/spdlog-0.14.0/include/spdlog/formatter.h | 47 + + external/spdlog-0.14.0/include/spdlog/logger.h | 132 + + .../include/spdlog/sinks/android_sink.h | 90 + + .../include/spdlog/sinks/ansicolor_sink.h | 133 + + .../spdlog-0.14.0/include/spdlog/sinks/base_sink.h | 50 + + .../spdlog-0.14.0/include/spdlog/sinks/dist_sink.h | 73 + + .../include/spdlog/sinks/file_sinks.h | 242 + + .../spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h | 51 + + .../spdlog-0.14.0/include/spdlog/sinks/null_sink.h | 34 + + .../include/spdlog/sinks/ostream_sink.h | 47 + + external/spdlog-0.14.0/include/spdlog/sinks/sink.h | 53 + + .../include/spdlog/sinks/stdout_sinks.h | 77 + + .../include/spdlog/sinks/syslog_sink.h | 81 + + .../include/spdlog/sinks/wincolor_sink.h | 121 + + external/spdlog-0.14.0/include/spdlog/spdlog.h | 189 + + external/spdlog-0.14.0/include/spdlog/tweakme.h | 141 + + external/spdlog-0.14.0/tests/CMakeLists.txt | 19 + + external/spdlog-0.14.0/tests/catch.hpp | 9427 ++++++++++++++++++++ + external/spdlog-0.14.0/tests/cond_logging.cpp | 154 + + external/spdlog-0.14.0/tests/errors.cpp | 113 + + external/spdlog-0.14.0/tests/file_helper.cpp | 78 + + external/spdlog-0.14.0/tests/file_log.cpp | 151 + + external/spdlog-0.14.0/tests/format.cpp | 56 + + external/spdlog-0.14.0/tests/includes.h | 16 + + external/spdlog-0.14.0/tests/install_libcxx.sh | 12 + + external/spdlog-0.14.0/tests/main.cpp | 2 + + external/spdlog-0.14.0/tests/registry.cpp | 84 + + external/spdlog-0.14.0/tests/tests.sln | 28 + + external/spdlog-0.14.0/tests/tests.vcxproj | 145 + + external/spdlog-0.14.0/tests/tests.vcxproj.filters | 54 + + external/spdlog-0.14.0/tests/utils.cpp | 48 + + external/spdlog-0.14.0/tests/utils.h | 15 + + 95 files changed, 23004 insertions(+) + create mode 100644 external/spdlog-0.14.0/.gitignore + create mode 100644 external/spdlog-0.14.0/.travis.yml + create mode 100644 external/spdlog-0.14.0/CMakeLists.txt + create mode 100644 external/spdlog-0.14.0/INSTALL + create mode 100644 external/spdlog-0.14.0/LICENSE + create mode 100644 external/spdlog-0.14.0/README.md + create mode 100755 external/spdlog-0.14.0/astyle.sh + create mode 100644 external/spdlog-0.14.0/bench/Makefile.mingw + create mode 100644 external/spdlog-0.14.0/bench/boost-bench-mt.cpp + create mode 100644 external/spdlog-0.14.0/bench/boost-bench.cpp + create mode 100644 external/spdlog-0.14.0/bench/easyl.conf + create mode 100644 external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp + create mode 100644 external/spdlog-0.14.0/bench/easylogging-bench.cpp + create mode 100644 external/spdlog-0.14.0/bench/g2log-async.cpp + create mode 100644 external/spdlog-0.14.0/bench/glog-bench-mt.cpp + create mode 100644 external/spdlog-0.14.0/bench/glog-bench.cpp + create mode 100755 external/spdlog-0.14.0/bench/latency/compare.sh + create mode 100644 external/spdlog-0.14.0/bench/latency/g3log-crush.cpp + create mode 100644 external/spdlog-0.14.0/bench/latency/g3log-latency.cpp + create mode 100644 external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp + create mode 100644 external/spdlog-0.14.0/bench/latency/utils.h + create mode 100644 external/spdlog-0.14.0/bench/logs/.gitignore + create mode 100644 external/spdlog-0.14.0/bench/spdlog-async.cpp + create mode 100644 external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp + create mode 100644 external/spdlog-0.14.0/bench/spdlog-bench.cpp + create mode 100644 external/spdlog-0.14.0/bench/spdlog-null-async.cpp + create mode 100644 external/spdlog-0.14.0/bench/utils.h + create mode 100644 external/spdlog-0.14.0/cmake/Config.cmake.in + create mode 100644 external/spdlog-0.14.0/cmake/spdlog.pc.in + create mode 100644 external/spdlog-0.14.0/example/CMakeLists.txt + create mode 100644 external/spdlog-0.14.0/example/Makefile.clang + create mode 100644 external/spdlog-0.14.0/example/Makefile.mingw + create mode 100644 external/spdlog-0.14.0/example/bench.cpp + create mode 100644 external/spdlog-0.14.0/example/example.cpp + create mode 100644 external/spdlog-0.14.0/example/example.sln + create mode 100644 external/spdlog-0.14.0/example/example.vcxproj + create mode 100644 external/spdlog-0.14.0/example/jni/Android.mk + create mode 100644 external/spdlog-0.14.0/example/jni/Application.mk + create mode 120000 external/spdlog-0.14.0/example/jni/example.cpp + create mode 100644 external/spdlog-0.14.0/example/multisink.cpp + create mode 100644 external/spdlog-0.14.0/example/utils.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/async_logger.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/common.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/file_helper.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/log_msg.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/logger_impl.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/null_mutex.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/os.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/registry.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/fmt.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/fmt/ostr.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/formatter.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/logger.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/spdlog.h + create mode 100644 external/spdlog-0.14.0/include/spdlog/tweakme.h + create mode 100644 external/spdlog-0.14.0/tests/CMakeLists.txt + create mode 100644 external/spdlog-0.14.0/tests/catch.hpp + create mode 100644 external/spdlog-0.14.0/tests/cond_logging.cpp + create mode 100644 external/spdlog-0.14.0/tests/errors.cpp + create mode 100644 external/spdlog-0.14.0/tests/file_helper.cpp + create mode 100644 external/spdlog-0.14.0/tests/file_log.cpp + create mode 100644 external/spdlog-0.14.0/tests/format.cpp + create mode 100644 external/spdlog-0.14.0/tests/includes.h + create mode 100755 external/spdlog-0.14.0/tests/install_libcxx.sh + create mode 100644 external/spdlog-0.14.0/tests/main.cpp + create mode 100644 external/spdlog-0.14.0/tests/registry.cpp + create mode 100644 external/spdlog-0.14.0/tests/tests.sln + create mode 100644 external/spdlog-0.14.0/tests/tests.vcxproj + create mode 100644 external/spdlog-0.14.0/tests/tests.vcxproj.filters + create mode 100644 external/spdlog-0.14.0/tests/utils.cpp + create mode 100644 external/spdlog-0.14.0/tests/utils.h + +diff --git a/external/spdlog-0.14.0/.gitignore b/external/spdlog-0.14.0/.gitignore +new file mode 100644 +index 00000000..b51a05b7 +--- /dev/null ++++ b/external/spdlog-0.14.0/.gitignore +@@ -0,0 +1,64 @@ ++# Auto generated files ++*.slo ++*.lo ++*.o ++*.obj ++*.suo ++*.tlog ++*.ilk ++*.log ++*.pdb ++*.idb ++*.iobj ++*.ipdb ++*.opensdf ++*.sdf ++ ++# Compiled Dynamic libraries ++*.so ++*.dylib ++*.dll ++ ++# Compiled Static libraries ++*.lai ++*.la ++*.a ++*.lib ++ ++# Executables ++*.exe ++*.out ++*.app ++ ++# Codelite ++.codelite ++ ++# .orig files ++*.orig ++ ++# example files ++example/* ++!example/example.cpp ++!example/bench.cpp ++!example/utils.h ++!example/Makefile* ++!example/example.sln ++!example/example.vcxproj ++!example/CMakeLists.txt ++!example/multisink.cpp ++!example/jni ++ ++# generated files ++generated ++ ++# Cmake ++CMakeCache.txt ++CMakeFiles ++CMakeScripts ++Makefile ++cmake_install.cmake ++install_manifest.txt ++/tests/tests.VC.VC.opendb ++/tests/tests.VC.db ++/tests/tests ++/tests/logs/file_helper_test.txt +diff --git a/external/spdlog-0.14.0/.travis.yml b/external/spdlog-0.14.0/.travis.yml +new file mode 100644 +index 00000000..c65e84d4 +--- /dev/null ++++ b/external/spdlog-0.14.0/.travis.yml +@@ -0,0 +1,90 @@ ++# Adapted from various sources, including: ++# - Louis Dionne's Hana: https://github.com/ldionne/hana ++# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit ++# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3 ++language: cpp ++ ++# Test matrix: ++# - Build matrix per compiler: C++11/C++14 + Debug/Release ++# - Optionally: AddressSanitizer (ASAN) ++# - Valgrind: all release builds are also tested with valgrind ++# - clang 3.4, 3.5, 3.6, trunk ++# - Note: 3.4 and trunk are tested with/without ASAN, ++# the rest is only tested with ASAN=On. ++# - gcc 4.9, 5.0 ++# ++matrix: ++ include: ++ ++# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off ++ - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off ++ os: linux ++ addons: &gcc48 ++ apt: ++ packages: ++ - g++-4.8 ++ - valgrind ++ sources: ++ - ubuntu-toolchain-r-test ++ ++ - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off ++ os: linux ++ addons: *gcc48 ++ ++ # Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off ++ - env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off ++ os: linux ++ addons: &gcc49 ++ apt: ++ packages: ++ - g++-4.9 ++ - valgrind ++ sources: ++ - ubuntu-toolchain-r-test ++ ++ - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off ++ os: linux ++ addons: *gcc49 ++ ++# Install dependencies ++before_install: ++ - export CHECKOUT_PATH=`pwd`; ++ - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi ++ - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi ++ - if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi ++ - which $CXX ++ - which $CC ++ - which valgrind ++ - if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi ++ ++install: ++ - cd $CHECKOUT_PATH ++ ++ # Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469. ++ # It is fixed in valgrind 3.10 so this won't be necessary if someone ++ # replaces the current valgrind (3.7) with valgrind-3.10 ++ - sed -i 's/march=native/msse4.2/' example/Makefile ++ ++ - if [ ! -d build ]; then mkdir build; fi ++ - export CXX_FLAGS="-I${CHECKOUT_PATH}/include" ++ - export CXX_LINKER_FLAGS="" ++ - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi ++ - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi ++ - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi ++ - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi ++ - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi ++ - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}" ++ ++ # Build examples ++ - cd example ++ - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi ++ - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi ++ ++ ++script: ++ - ./"${BIN}" ++ - valgrind --trace-children=yes --leak-check=full ./"${BIN}" ++ - cd $CHECKOUT_PATH/tests; make rebuild; ./tests ++ ++notifications: ++ email: false +diff --git a/external/spdlog-0.14.0/CMakeLists.txt b/external/spdlog-0.14.0/CMakeLists.txt +new file mode 100644 +index 00000000..61c45b5e +--- /dev/null ++++ b/external/spdlog-0.14.0/CMakeLists.txt +@@ -0,0 +1,87 @@ ++# ++# Copyright(c) 2015 Ruslan Baratov. ++# Distributed under the MIT License (http://opensource.org/licenses/MIT) ++# ++ ++cmake_minimum_required(VERSION 3.1) ++project(spdlog VERSION 1.0.0) ++include(CTest) ++ ++set(CMAKE_CXX_STANDARD 11) ++set(CMAKE_CXX_STANDARD_REQUIRED ON) ++ ++if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") ++ set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}") ++endif() ++ ++add_library(spdlog INTERFACE) ++ ++option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF) ++option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON) ++ ++target_include_directories( ++ spdlog ++ INTERFACE ++ "$" ++ "$" ++) ++ ++set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") ++ ++if(SPDLOG_BUILD_EXAMPLES) ++ add_subdirectory(example) ++endif() ++ ++if(SPDLOG_BUILD_TESTING) ++ add_subdirectory(tests) ++endif() ++ ++### Install ### ++# * https://github.com/forexample/package-example ++set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") ++ ++set(config_install_dir "lib/cmake/${PROJECT_NAME}") ++set(include_install_dir "include") ++set(pkgconfig_install_dir "lib/pkgconfig") ++ ++set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") ++set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") ++set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc") ++set(targets_export_name "${PROJECT_NAME}Targets") ++set(namespace "${PROJECT_NAME}::") ++ ++include(CMakePackageConfigHelpers) ++write_basic_package_version_file( ++ "${version_config}" COMPATIBILITY SameMajorVersion ++) ++ ++# Note: use 'targets_export_name' ++configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) ++configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) ++ ++install( ++ TARGETS spdlog ++ EXPORT "${targets_export_name}" ++ INCLUDES DESTINATION "${include_install_dir}" ++) ++ ++install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}") ++ ++install( ++ FILES "${project_config}" "${version_config}" ++ DESTINATION "${config_install_dir}" ++) ++ ++install( ++ FILES "${pkg_config}" ++ DESTINATION "${pkgconfig_install_dir}" ++) ++ ++install( ++ EXPORT "${targets_export_name}" ++ NAMESPACE "${namespace}" ++ DESTINATION "${config_install_dir}" ++) ++ ++file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h") ++add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS}) +diff --git a/external/spdlog-0.14.0/INSTALL b/external/spdlog-0.14.0/INSTALL +new file mode 100644 +index 00000000..664509d2 +--- /dev/null ++++ b/external/spdlog-0.14.0/INSTALL +@@ -0,0 +1,13 @@ ++spdlog is header only library. ++Just copy the files to your build tree and use a C++11 compiler ++ ++Tested on: ++gcc 4.8.1 and above ++clang 3.5 ++Visual Studio 2013 ++ ++gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed ++gcc 4.9 flags: --std=c++11 -pthread -O3 -flto ++ ++ ++see the makefile in the example folder +diff --git a/external/spdlog-0.14.0/LICENSE b/external/spdlog-0.14.0/LICENSE +new file mode 100644 +index 00000000..4b43e064 +--- /dev/null ++++ b/external/spdlog-0.14.0/LICENSE +@@ -0,0 +1,22 @@ ++The MIT License (MIT) ++ ++Copyright (c) 2016 Gabi Melman. ++ ++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/external/spdlog-0.14.0/README.md b/external/spdlog-0.14.0/README.md +new file mode 100644 +index 00000000..e2fd18ae +--- /dev/null ++++ b/external/spdlog-0.14.0/README.md +@@ -0,0 +1,224 @@ ++# spdlog ++ ++Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) ++ ++ ++## Install ++#### Just copy the headers: ++ ++* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler. ++ ++#### Or use your favourite package manager: ++ ++* Ubuntu: `apt-get install libspdlog-dev` ++* Homebrew: `brew install spdlog` ++* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean` ++* Fedora: `yum install spdlog` ++* Gentoo: `emerge dev-libs/spdlog` ++* Arch Linux: `pacman -S spdlog-git` ++* vcpkg: `vcpkg install spdlog` ++ ++ ++## Platforms ++ * Linux, FreeBSD, Solaris ++ * Windows (vc 2013+, cygwin/mingw) ++ * Mac OSX (clang 3.5+) ++ * Android ++ ++## Features ++* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). ++* Headers only, just copy and use. ++* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library. ++* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec. ++* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. ++* Conditional Logging ++* Multi/Single threaded loggers. ++* Various log targets: ++ * Rotating log files. ++ * Daily log files. ++ * Console logging (colors supported). ++ * syslog. ++ * Windows debugger (```OutputDebugString(..)```) ++ * Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). ++* Severity based filtering - threshold levels can be modified in runtime as well as in compile time. ++ ++ ++ ++## Benchmarks ++ ++Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz ++ ++#### Synchronous mode ++Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs): ++ ++|threads|boost log 1.54|glog |easylogging |spdlog| ++|-------|:-------:|:-----:|----------:|------:| ++|1| 4.169s |1.066s |0.975s |0.302s| ++|10| 6.180s |3.032s |2.857s |0.968s| ++|100| 5.981s |1.139s |4.512s |0.497s| ++ ++ ++#### Asynchronous mode ++Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs): ++ ++|threads|g2log async logger |spdlog async mode| ++|:-------|:-----:|-------------------------:| ++|1| 1.850s |0.216s | ++|10| 0.943s |0.173s| ++|100| 0.959s |0.202s| ++ ++ ++ ++ ++## Usage Example ++```c++ ++ ++#include "spdlog/spdlog.h" ++ ++#include ++#include ++ ++void async_example(); ++void syslog_example(); ++void user_defined_example(); ++void err_handler_example(); ++ ++namespace spd = spdlog; ++int main(int, char*[]) ++{ ++ try ++ { ++ // Console logger with color ++ auto console = spd::stdout_color_mt("console"); ++ console->info("Welcome to spdlog!"); ++ console->error("Some error message with arg{}..", 1); ++ ++ // Conditional logging example ++ auto i = 2; ++ console->warn_if(i != 0, "an important message"); ++ ++ // Formatting examples ++ console->warn("Easy padding in numbers like {:08d}", 12); ++ console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); ++ console->info("Support for floats {:03.2f}", 1.23456); ++ console->info("Positional args are {1} {0}..", "too", "supported"); ++ console->info("{:<30}", "left aligned"); ++ ++ ++ spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); ++ ++ // Create basic file logger (not rotated) ++ auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt"); ++ my_logger->info("Some log message"); ++ ++ // Create a file rotating logger with 5mb size max and 3 rotated files ++ auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3); ++ for (int i = 0; i < 10; ++i) ++ rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); ++ ++ // Create a daily logger - a new file is created every day on 2:30am ++ auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30); ++ // trigger flush if the log severity is error or higher ++ daily_logger->flush_on(spd::level::err); ++ daily_logger->info(123.44); ++ ++ // Customize msg format for all messages ++ spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); ++ rotating_logger->info("This is another message with custom format"); ++ ++ ++ // Runtime log levels ++ spd::set_level(spd::level::info); //Set global log level to info ++ console->debug("This message shold not be displayed!"); ++ console->set_level(spd::level::debug); // Set specific logger's log level ++ console->debug("This message shold be displayed.."); ++ ++ // Compile time log levels ++ // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON ++ SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); ++ SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); ++ ++ // Asynchronous logging is very fast.. ++ // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. ++ async_example(); ++ ++ // syslog example. linux/osx only ++ syslog_example(); ++ ++ // android example. compile with NDK ++ android_example(); ++ ++ // Log user-defined types example ++ user_defined_example(); ++ ++ // Change default log error handler ++ err_handler_example(); ++ ++ // Apply a function on all registered loggers ++ spd::apply_all([&](std::shared_ptr l) ++ { ++ l->info("End of example."); ++ }); ++ ++ // Release and close all loggers ++ spd::drop_all(); ++ } ++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging) ++ catch (const spd::spdlog_ex& ex) ++ { ++ std::cout << "Log init failed: " << ex.what() << std::endl; ++ return 1; ++ } ++} ++ ++void async_example() ++{ ++ size_t q_size = 4096; //queue size must be power of 2 ++ spd::set_async_mode(q_size); ++ auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); ++ for (int i = 0; i < 100; ++i) ++ async_file->info("Async message #{}", i); ++} ++ ++//syslog example ++void syslog_example() ++{ ++#ifdef SPDLOG_ENABLE_SYSLOG ++ std::string ident = "spdlog-example"; ++ auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); ++ syslog_logger->warn("This is warning that will end up in syslog.."); ++#endif ++} ++ ++// user defined types logging by implementing operator<< ++struct my_type ++{ ++ int i; ++ template ++ friend OStream& operator<<(OStream& os, const my_type &c) ++ { ++ return os << "[my_type i="< // must be included ++void user_defined_example() ++{ ++ spd::get("console")->info("user defined type: {}", my_type { 14 }); ++} ++ ++// ++//custom error handler ++// ++void err_handler_example() ++{ ++ spd::set_error_handler([](const std::string& msg) { ++ std::cerr << "my err handler: " << msg << std::endl; ++ }); ++ // (or logger->set_error_handler(..) to set for specific logger) ++} ++ ++``` ++ ++## Documentation ++Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. +diff --git a/external/spdlog-0.14.0/astyle.sh b/external/spdlog-0.14.0/astyle.sh +new file mode 100755 +index 00000000..a7a90510 +--- /dev/null ++++ b/external/spdlog-0.14.0/astyle.sh +@@ -0,0 +1,5 @@ ++#!/bin/bash ++find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix ++find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1 ++ ++ +diff --git a/external/spdlog-0.14.0/bench/Makefile.mingw b/external/spdlog-0.14.0/bench/Makefile.mingw +new file mode 100644 +index 00000000..b4357be4 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/Makefile.mingw +@@ -0,0 +1,57 @@ ++CXX ?= g++ ++CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include ++CXX_RELEASE_FLAGS = -O3 -flto ++ ++ ++binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt ++ ++all: $(binaries) ++ ++spdlog-bench: spdlog-bench.cpp ++ $(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++spdlog-bench-mt: spdlog-bench-mt.cpp ++ $(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++spdlog-async: spdlog-async.cpp ++ $(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/home/gabi/devel/boost_1_56_0/ -L/home/gabi/devel/boost_1_56_0/stage/lib -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono ++ ++boost-bench: boost-bench.cpp ++ $(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++boost-bench-mt: boost-bench-mt.cpp ++ $(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++GLOG_FLAGS = -lglog ++glog-bench: glog-bench.cpp ++ $(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++glog-bench-mt: glog-bench-mt.cpp ++ $(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger ++g2log-async: g2log-async.cpp ++ $(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++EASYL_FLAGS = -I../../easylogging/src/ ++easylogging-bench: easylogging-bench.cpp ++ $(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS) ++easylogging-bench-mt: easylogging-bench-mt.cpp ++ $(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS) ++ ++.PHONY: clean ++ ++clean: ++ rm -f *.o logs/* $(binaries) ++ ++ ++rebuild: clean all ++ ++ ++ +diff --git a/external/spdlog-0.14.0/bench/boost-bench-mt.cpp b/external/spdlog-0.14.0/bench/boost-bench-mt.cpp +new file mode 100644 +index 00000000..d845fcec +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/boost-bench-mt.cpp +@@ -0,0 +1,84 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace logging = boost::log; ++namespace src = boost::log::sources; ++namespace sinks = boost::log::sinks; ++namespace keywords = boost::log::keywords; ++ ++void init() ++{ ++ logging::add_file_log ++ ( ++ keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/ ++ keywords::auto_flush = false, ++ keywords::format = "[%TimeStamp%]: %Message%" ++ ); ++ ++ logging::core::get()->set_filter ++ ( ++ logging::trivial::severity >= logging::trivial::info ++ ); ++} ++ ++ ++ ++using namespace std; ++ ++int main(int argc, char* argv[]) ++{ ++ int thread_count = 10; ++ if(argc > 1) ++ thread_count = atoi(argv[1]); ++ ++ int howmany = 1000000; ++ ++ ++ init(); ++ logging::add_common_attributes(); ++ ++ ++ using namespace logging::trivial; ++ ++ src::severity_logger_mt< severity_level > lg; ++ ++ std::atomic msg_counter {0}; ++ vector threads; ++ ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure"; ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/boost-bench.cpp b/external/spdlog-0.14.0/bench/boost-bench.cpp +new file mode 100644 +index 00000000..32c5b692 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/boost-bench.cpp +@@ -0,0 +1,47 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace logging = boost::log; ++namespace src = boost::log::sources; ++namespace sinks = boost::log::sinks; ++namespace keywords = boost::log::keywords; ++ ++void init() ++{ ++ logging::add_file_log ++ ( ++ keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/ ++ keywords::auto_flush = false, ++ keywords::format = "[%TimeStamp%]: %Message%" ++ ); ++ ++ logging::core::get()->set_filter ++ ( ++ logging::trivial::severity >= logging::trivial::info ++ ); ++} ++ ++ ++int main(int argc, char* []) ++{ ++ int howmany = 1000000; ++ init(); ++ logging::add_common_attributes(); ++ ++ using namespace logging::trivial; ++ src::severity_logger_mt< severity_level > lg; ++ for(int i = 0 ; i < howmany; ++i) ++ BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure"; ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/easyl.conf b/external/spdlog-0.14.0/bench/easyl.conf +new file mode 100644 +index 00000000..3bfb5440 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/easyl.conf +@@ -0,0 +1,10 @@ ++* GLOBAL: ++ FORMAT = "[%datetime]: %msg" ++ FILENAME = ./logs/easylogging.log ++ ENABLED = true ++ TO_FILE = true ++ TO_STANDARD_OUTPUT = false ++ MILLISECONDS_WIDTH = 3 ++ PERFORMANCE_TRACKING = false ++ MAX_LOG_FILE_SIZE = 10485760 ++ Log_Flush_Threshold = 10485760 +diff --git a/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp b/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp +new file mode 100644 +index 00000000..98d1ae35 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/easylogging-bench-mt.cpp +@@ -0,0 +1,52 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++ ++#define _ELPP_THREAD_SAFE ++#include "easylogging++.h" ++_INITIALIZE_EASYLOGGINGPP ++ ++using namespace std; ++ ++int main(int argc, char* argv[]) ++{ ++ ++ int thread_count = 10; ++ if(argc > 1) ++ thread_count = atoi(argv[1]); ++ ++ int howmany = 1000000; ++ ++ // Load configuration from file ++ el::Configurations conf("easyl.conf"); ++ el::Loggers::reconfigureLogger("default", conf); ++ ++ std::atomic msg_counter {0}; ++ vector threads; ++ ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure"; ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/easylogging-bench.cpp b/external/spdlog-0.14.0/bench/easylogging-bench.cpp +new file mode 100644 +index 00000000..a952cbd5 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/easylogging-bench.cpp +@@ -0,0 +1,22 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++ ++#include "easylogging++.h" ++ ++_INITIALIZE_EASYLOGGINGPP ++ ++int main(int, char* []) ++{ ++ int howmany = 1000000; ++ ++ // Load configuration from file ++ el::Configurations conf("easyl.conf"); ++ el::Loggers::reconfigureLogger("default", conf); ++ ++ for(int i = 0 ; i < howmany; ++i) ++ LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure"; ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/g2log-async.cpp b/external/spdlog-0.14.0/bench/g2log-async.cpp +new file mode 100644 +index 00000000..9f9eb71e +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/g2log-async.cpp +@@ -0,0 +1,62 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "g2logworker.h" ++#include "g2log.h" ++ ++using namespace std; ++template std::string format(const T& value); ++ ++int main(int argc, char* argv[]) ++{ ++ using namespace std::chrono; ++ using clock=steady_clock; ++ int thread_count = 10; ++ ++ if(argc > 1) ++ thread_count = atoi(argv[1]); ++ int howmany = 1000000; ++ ++ g2LogWorker g2log(argv[0], "logs"); ++ g2::initializeLogging(&g2log); ++ ++ ++ std::atomic msg_counter {0}; ++ vector threads; ++ auto start = clock::now(); ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure"; ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ duration delta = clock::now() - start; ++ float deltaf = delta.count(); ++ auto rate = howmany/deltaf; ++ ++ cout << "Total: " << howmany << std::endl; ++ cout << "Threads: " << thread_count << std::endl; ++ std::cout << "Delta = " << deltaf << " seconds" << std::endl; ++ std::cout << "Rate = " << rate << "/sec" << std::endl; ++} +diff --git a/external/spdlog-0.14.0/bench/glog-bench-mt.cpp b/external/spdlog-0.14.0/bench/glog-bench-mt.cpp +new file mode 100644 +index 00000000..db193aeb +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/glog-bench-mt.cpp +@@ -0,0 +1,50 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++ ++#include "glog/logging.h" ++ ++using namespace std; ++ ++int main(int argc, char* argv[]) ++{ ++ ++ int thread_count = 10; ++ if(argc > 1) ++ thread_count = atoi(argv[1]); ++ ++ int howmany = 1000000; ++ ++ FLAGS_logtostderr = 0; ++ FLAGS_log_dir = "logs"; ++ google::InitGoogleLogging(argv[0]); ++ ++ std::atomic msg_counter {0}; ++ vector threads; ++ ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure"; ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/glog-bench.cpp b/external/spdlog-0.14.0/bench/glog-bench.cpp +new file mode 100644 +index 00000000..cf7e70a2 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/glog-bench.cpp +@@ -0,0 +1,21 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include "glog/logging.h" ++ ++ ++int main(int, char* argv[]) ++{ ++ int howmany = 1000000; ++ ++ ++ FLAGS_logtostderr = 0; ++ FLAGS_log_dir = "logs"; ++ google::InitGoogleLogging(argv[0]); ++ for(int i = 0 ; i < howmany; ++i) ++ LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure"; ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/latency/compare.sh b/external/spdlog-0.14.0/bench/latency/compare.sh +new file mode 100755 +index 00000000..0f0e4c97 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/latency/compare.sh +@@ -0,0 +1,13 @@ ++#!/bin/bash ++echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).." ++rm -f *.log ++for i in {1..10} ++ ++do ++ echo ++ sleep 0.5 ++ ./spdlog-latency ${1:-10} 2>/dev/null || exit ++ sleep 0.5 ++ ./g3log-latency ${1:-10} 2>/dev/null || exit ++ ++done +diff --git a/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp b/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp +new file mode 100644 +index 00000000..417b014c +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/latency/g3log-crush.cpp +@@ -0,0 +1,37 @@ ++#include ++ ++#include ++#include ++ ++void CrusherLoop() ++{ ++ size_t counter = 0; ++ while (true) ++ { ++ LOGF(INFO, "Some text to crush you machine. thread:"); ++ if(++counter % 1000000 == 0) ++ { ++ std::cout << "Wrote " << counter << " entries" << std::endl; ++ } ++ } ++} ++ ++ ++int main(int argc, char** argv) ++{ ++ std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl; ++ std::cout << "Are you sure you want to continue ? " << std::endl; ++ char c; ++ std::cin >> c; ++ if (toupper( c ) != 'Y') ++ return 0; ++ ++ auto worker = g3::LogWorker::createLogWorker(); ++ auto handle= worker->addDefaultLogger(argv[0], "g3log.txt"); ++ g3::initializeLogging(worker.get()); ++ CrusherLoop(); ++ ++ return 0; ++} ++ ++ +diff --git a/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp b/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp +new file mode 100644 +index 00000000..e96e421b +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/latency/g3log-latency.cpp +@@ -0,0 +1,129 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "utils.h" ++#include ++#include ++ ++ ++namespace ++{ ++const uint64_t g_iterations = 1000000; ++ ++ ++std::atomic g_counter = {0}; ++ ++ ++void MeasurePeakDuringLogWrites(const size_t id, std::vector& result) ++{ ++ ++ while (true) ++ { ++ const size_t value_now = ++g_counter; ++ if (value_now > g_iterations) ++ { ++ return; ++ } ++ ++ auto start_time = std::chrono::high_resolution_clock::now(); ++ LOGF(INFO, "Some text to log for thread: %ld", id); ++ auto stop_time = std::chrono::high_resolution_clock::now(); ++ uint64_t time_us = std::chrono::duration_cast(stop_time - start_time).count(); ++ result.push_back(time_us); ++ } ++} ++ ++ ++ ++void PrintResults(const std::map>& threads_result, size_t total_us) ++{ ++ ++ std::vector all_measurements; ++ all_measurements.reserve(g_iterations); ++ for (auto& t_result : threads_result) ++ { ++ all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end()); ++ } ++ ++ // calc worst latenct ++ auto worst = *std::max_element(all_measurements.begin(), all_measurements.end()); ++ ++ // calc avg ++ auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus()); ++ auto avg = double(total)/all_measurements.size(); ++ ++ std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl; ++ ++} ++}// anonymous ++ ++ ++// The purpose of this test is NOT to see how fast ++// each thread can possibly write. It is to see what ++// the worst latency is for writing a log entry ++// ++// In the test 1 million log entries will be written ++// an atomic counter is used to give each thread what ++// it is to write next. The overhead of atomic ++// synchronization between the threads are not counted in the worst case latency ++int main(int argc, char** argv) ++{ ++ size_t number_of_threads {0}; ++ if (argc == 2) ++ { ++ number_of_threads = atoi(argv[1]); ++ } ++ if (argc != 2 || number_of_threads == 0) ++ { ++ std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; ++ return 1; ++ } ++ ++ ++ std::vector threads(number_of_threads); ++ std::map> threads_result; ++ ++ for (size_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ // reserve to 1 million for all the result ++ // it's a test so let's not care about the wasted space ++ threads_result[idx].reserve(g_iterations); ++ } ++ ++ const std::string g_path = "./" ; ++ const std::string g_prefix_log_name = "g3log-performance-"; ++ const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt"; ++ ++ auto worker = g3::LogWorker::createLogWorker(); ++ auto handle= worker->addDefaultLogger(argv[0], "g3log.txt"); ++ g3::initializeLogging(worker.get()); ++ ++ auto start_time_application_total = std::chrono::high_resolution_clock::now(); ++ for (uint64_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx])); ++ } ++ for (size_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ threads[idx].join(); ++ } ++ auto stop_time_application_total = std::chrono::high_resolution_clock::now(); ++ ++ uint64_t total_time_in_us = std::chrono::duration_cast(stop_time_application_total - start_time_application_total).count(); ++ PrintResults(threads_result, total_time_in_us); ++ return 0; ++} ++ ++ +diff --git a/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp b/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp +new file mode 100644 +index 00000000..ed4966cc +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/latency/spdlog-latency.cpp +@@ -0,0 +1,128 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "utils.h" ++#include ++ ++#include "spdlog/spdlog.h" ++ ++namespace spd = spdlog; ++ ++namespace ++{ ++const uint64_t g_iterations = 1000000; ++ ++ ++std::atomic g_counter = {0}; ++ ++ ++void MeasurePeakDuringLogWrites(const size_t id, std::vector& result) ++{ ++ auto logger = spd::get("file_logger"); ++ while (true) ++ { ++ const size_t value_now = ++g_counter; ++ if (value_now > g_iterations) ++ { ++ return; ++ } ++ ++ auto start_time = std::chrono::high_resolution_clock::now(); ++ logger->info("Some text to log for thread: [somemore text...............................] {}", id); ++ auto stop_time = std::chrono::high_resolution_clock::now(); ++ uint64_t time_us = std::chrono::duration_cast(stop_time - start_time).count(); ++ result.push_back(time_us); ++ } ++} ++ ++ ++void PrintResults(const std::map>& threads_result, size_t total_us) ++{ ++ ++ std::vector all_measurements; ++ all_measurements.reserve(g_iterations); ++ for (auto& t_result : threads_result) ++ { ++ all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end()); ++ } ++ ++ // calc worst latenct ++ auto worst = *std::max_element(all_measurements.begin(), all_measurements.end()); ++ ++ // calc avg ++ auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus()); ++ auto avg = double(total)/all_measurements.size(); ++ ++ std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl; ++ ++} ++}// anonymous ++ ++ ++// The purpose of this test is NOT to see how fast ++// each thread can possibly write. It is to see what ++// the worst latency is for writing a log entry ++// ++// In the test 1 million log entries will be written ++// an atomic counter is used to give each thread what ++// it is to write next. The overhead of atomic ++// synchronization between the threads are not counted in the worst case latency ++int main(int argc, char** argv) ++{ ++ size_t number_of_threads {0}; ++ if (argc == 2) ++ { ++ number_of_threads = atoi(argv[1]); ++ } ++ if (argc != 2 || number_of_threads == 0) ++ { ++ std::cerr << "usage: " << argv[0] << " number_threads" << std::endl; ++ return 1; ++ } ++ ++ ++ std::vector threads(number_of_threads); ++ std::map> threads_result; ++ ++ for (size_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ // reserve to 1 million for all the result ++ // it's a test so let's not care about the wasted space ++ threads_result[idx].reserve(g_iterations); ++ } ++ ++ int queue_size = 1048576; // 2 ^ 20 ++ spdlog::set_async_mode(queue_size); ++ auto logger = spdlog::create("file_logger", "spdlog.log", true); ++ ++ //force flush on every call to compare with g3log ++ auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get(); ++ s->set_force_flush(true); ++ ++ auto start_time_application_total = std::chrono::high_resolution_clock::now(); ++ for (uint64_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx])); ++ } ++ for (size_t idx = 0; idx < number_of_threads; ++idx) ++ { ++ threads[idx].join(); ++ } ++ auto stop_time_application_total = std::chrono::high_resolution_clock::now(); ++ ++ uint64_t total_time_in_us = std::chrono::duration_cast(stop_time_application_total - start_time_application_total).count(); ++ ++ PrintResults(threads_result, total_time_in_us); ++ return 0; ++} ++ ++ +diff --git a/external/spdlog-0.14.0/bench/latency/utils.h b/external/spdlog-0.14.0/bench/latency/utils.h +new file mode 100644 +index 00000000..b260f724 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/latency/utils.h +@@ -0,0 +1,35 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++namespace utils ++{ ++ ++template ++inline std::string format(const T& value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << value; ++ return ss.str(); ++} ++ ++template<> ++inline std::string format(const double & value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << std::fixed << std::setprecision(1) << value; ++ return ss.str(); ++} ++ ++} +diff --git a/external/spdlog-0.14.0/bench/logs/.gitignore b/external/spdlog-0.14.0/bench/logs/.gitignore +new file mode 100644 +index 00000000..40637012 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/logs/.gitignore +@@ -0,0 +1,4 @@ ++# Ignore everything in this directory ++* ++# Except this file ++!.gitignore +diff --git a/external/spdlog-0.14.0/bench/spdlog-async.cpp b/external/spdlog-0.14.0/bench/spdlog-async.cpp +new file mode 100644 +index 00000000..f788e4df +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/spdlog-async.cpp +@@ -0,0 +1,62 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "spdlog/spdlog.h" ++ ++using namespace std; ++ ++int main(int argc, char* argv[]) ++{ ++ ++ using namespace std::chrono; ++ using clock=steady_clock; ++ namespace spd = spdlog; ++ ++ int thread_count = 10; ++ if(argc > 1) ++ thread_count = ::atoi(argv[1]); ++ int howmany = 1000000; ++ ++ spd::set_async_mode(1048576); ++ auto logger = spdlog::create("file_logger", "logs/spd-bench-async.txt", false); ++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); ++ ++ ++ std::atomic msg_counter {0}; ++ vector threads; ++ auto start = clock::now(); ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ logger->info("spdlog message #{}: This is some text for your pleasure", counter); ++ } ++ })); ++ } ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ duration delta = clock::now() - start; ++ float deltaf = delta.count(); ++ auto rate = howmany/deltaf; ++ ++ cout << "Total: " << howmany << std::endl; ++ cout << "Threads: " << thread_count << std::endl; ++ std::cout << "Delta = " << deltaf << " seconds" << std::endl; ++ std::cout << "Rate = " << rate << "/sec" << std::endl; ++} +diff --git a/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp b/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp +new file mode 100644 +index 00000000..e28e7bb8 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/spdlog-bench-mt.cpp +@@ -0,0 +1,55 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include ++#include ++#include ++#include ++#include "spdlog/spdlog.h" ++ ++ ++using namespace std; ++ ++int main(int argc, char* argv[]) ++{ ++ ++ int thread_count = 10; ++ if(argc > 1) ++ thread_count = std::atoi(argv[1]); ++ ++ int howmany = 1000000; ++ ++ namespace spd = spdlog; ++ ++ auto logger = spdlog::create("file_logger", "logs/spd-bench-mt.txt", false); ++ ++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); ++ ++ std::atomic msg_counter {0}; ++ std::vector threads; ++ ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ while (true) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ logger->info("spdlog message #{}: This is some text for your pleasure", counter); ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ ++ ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/spdlog-bench.cpp b/external/spdlog-0.14.0/bench/spdlog-bench.cpp +new file mode 100644 +index 00000000..4ac95f6a +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/spdlog-bench.cpp +@@ -0,0 +1,20 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#include "spdlog/spdlog.h" ++ ++ ++int main(int, char* []) ++{ ++ int howmany = 1000000; ++ namespace spd = spdlog; ++ ///Create a file rotating logger with 5mb size max and 3 rotated files ++ auto logger = spdlog::create("file_logger", "logs/spd-bench-st.txt", false); ++ ++ logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); ++ for(int i = 0 ; i < howmany; ++i) ++ logger->info("spdlog message #{} : This is some text for your pleasure", i); ++ return 0; ++} +diff --git a/external/spdlog-0.14.0/bench/spdlog-null-async.cpp b/external/spdlog-0.14.0/bench/spdlog-null-async.cpp +new file mode 100644 +index 00000000..3874371a +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/spdlog-null-async.cpp +@@ -0,0 +1,112 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++// ++// bench.cpp : spdlog benchmarks ++// ++#include ++#include // EXIT_FAILURE ++#include ++#include ++#include ++#include ++#include "spdlog/spdlog.h" ++#include "spdlog/async_logger.h" ++#include "spdlog/sinks/null_sink.h" ++#include "utils.h" ++ ++ ++using namespace std; ++using namespace std::chrono; ++using namespace spdlog; ++using namespace spdlog::sinks; ++using namespace utils; ++ ++ ++ ++size_t bench_as(int howmany, std::shared_ptr log, int thread_count); ++ ++int main(int argc, char* argv[]) ++{ ++ ++ int queue_size = 1048576; ++ int howmany = 1000000; ++ int threads = 10; ++ int iters = 10; ++ ++ try ++ { ++ ++ if(argc > 1) ++ howmany = atoi(argv[1]); ++ if (argc > 2) ++ threads = atoi(argv[2]); ++ if (argc > 3) ++ queue_size = atoi(argv[3]); ++ ++ ++ cout << "\n*******************************************************************************\n"; ++ cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl; ++ cout << "*******************************************************************************\n"; ++ ++ spdlog::set_async_mode(queue_size); ++ ++ size_t total_rate = 0; ++ ++ for(int i = 0; i < iters; ++i) ++ { ++ //auto as = spdlog::daily_logger_st("as", "logs/daily_async"); ++ auto as = spdlog::create("async(null-sink)"); ++ total_rate+= bench_as(howmany, as, threads); ++ spdlog::drop("async(null-sink)"); ++ } ++ std::cout << endl; ++ std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" < log, int thread_count) ++{ ++ cout << log->name() << "...\t\t" << flush; ++ std::atomic msg_counter {0}; ++ vector threads; ++ auto start = system_clock::now(); ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ for(;;) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ log->info("Hello logger: msg number {}", counter); ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ ++ auto delta = system_clock::now() - start; ++ auto delta_d = duration_cast> (delta).count(); ++ auto per_sec = size_t(howmany / delta_d); ++ cout << format(per_sec) << "/sec" << endl; ++ return per_sec; ++} +diff --git a/external/spdlog-0.14.0/bench/utils.h b/external/spdlog-0.14.0/bench/utils.h +new file mode 100644 +index 00000000..b260f724 +--- /dev/null ++++ b/external/spdlog-0.14.0/bench/utils.h +@@ -0,0 +1,35 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++namespace utils ++{ ++ ++template ++inline std::string format(const T& value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << value; ++ return ss.str(); ++} ++ ++template<> ++inline std::string format(const double & value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << std::fixed << std::setprecision(1) << value; ++ return ss.str(); ++} ++ ++} +diff --git a/external/spdlog-0.14.0/cmake/Config.cmake.in b/external/spdlog-0.14.0/cmake/Config.cmake.in +new file mode 100644 +index 00000000..ba0b36f2 +--- /dev/null ++++ b/external/spdlog-0.14.0/cmake/Config.cmake.in +@@ -0,0 +1,24 @@ ++# *************************************************************************/ ++# * Copyright (c) 2015 Ruslan Baratov. */ ++# * */ ++# * Permission is hereby granted, free of charge, to any person obtaining */ ++# * a copy of this software and associated documentation files (the */ ++# * "Software"), to deal in the Software without restriction, including */ ++# * without limitation the rights to use, copy, modify, merge, publish, */ ++# * distribute, sublicense, and/or sell copies of the Software, and to */ ++# * permit persons to whom the Software is furnished to do so, subject to */ ++# * the following conditions: */ ++# * */ ++# * The above copyright notice and this permission notice shall be */ ++# * included in all copies or substantial portions of the Software. */ ++# * */ ++# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ ++# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ ++# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ ++# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ ++# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ ++# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ ++# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ ++# *************************************************************************/ ++ ++include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") +diff --git a/external/spdlog-0.14.0/cmake/spdlog.pc.in b/external/spdlog-0.14.0/cmake/spdlog.pc.in +new file mode 100644 +index 00000000..262248a7 +--- /dev/null ++++ b/external/spdlog-0.14.0/cmake/spdlog.pc.in +@@ -0,0 +1,6 @@ ++prefix=@CMAKE_INSTALL_PREFIX@ ++includedir=${prefix}/include ++ ++Name: @PROJECT_NAME@ ++Description: Super fast C++ logging library. ++Version: @PROJECT_VERSION@ +diff --git a/external/spdlog-0.14.0/example/CMakeLists.txt b/external/spdlog-0.14.0/example/CMakeLists.txt +new file mode 100644 +index 00000000..7859e4d5 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/CMakeLists.txt +@@ -0,0 +1,49 @@ ++# *************************************************************************/ ++# * Copyright (c) 2015 Ruslan Baratov. */ ++# * */ ++# * 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. */ ++# *************************************************************************/ ++ ++cmake_minimum_required(VERSION 3.0) ++project(SpdlogExamples) ++ ++if(TARGET spdlog) ++ # Part of the main project ++ add_library(spdlog::spdlog ALIAS spdlog) ++else() ++ # Stand-alone build ++ find_package(spdlog CONFIG REQUIRED) ++endif() ++ ++find_package(Threads) ++ ++add_executable(example example.cpp) ++target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) ++ ++add_executable(benchmark bench.cpp) ++target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) ++ ++add_executable(multisink multisink.cpp) ++target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) ++ ++enable_testing() ++file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") ++add_test(NAME RunExample COMMAND example) ++add_test(NAME RunBenchmark COMMAND benchmark) +diff --git a/external/spdlog-0.14.0/example/Makefile.clang b/external/spdlog-0.14.0/example/Makefile.clang +new file mode 100644 +index 00000000..0ed004d0 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/Makefile.clang +@@ -0,0 +1,32 @@ ++CXX ?= clang++ ++CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include ++CXX_RELEASE_FLAGS = -O2 ++CXX_DEBUG_FLAGS= -g ++ ++ ++all: example bench ++debug: example-debug bench-debug ++ ++example: example.cpp ++ $(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++bench: bench.cpp ++ $(CXX) bench.cpp -o bench-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++example-debug: example.cpp ++ $(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) ++ ++bench-debug: bench.cpp ++ $(CXX) bench.cpp -o bench-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) ++ ++ ++ ++clean: ++ rm -f *.o logs/*.txt example-clang example-clang-debug bench-clang bench-clang-debug ++ ++ ++rebuild: clean all ++rebuild-debug: clean debug ++ ++ +diff --git a/external/spdlog-0.14.0/example/Makefile.mingw b/external/spdlog-0.14.0/example/Makefile.mingw +new file mode 100644 +index 00000000..b9ffd711 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/Makefile.mingw +@@ -0,0 +1,32 @@ ++CXX ?= g++ ++CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include ++CXX_RELEASE_FLAGS = -O3 ++CXX_DEBUG_FLAGS= -g ++ ++ ++all: example bench ++debug: example-debug bench-debug ++ ++example: example.cpp ++ $(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++bench: bench.cpp ++ $(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) ++ ++ ++example-debug: example.cpp ++ $(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) ++ ++bench-debug: bench.cpp ++ $(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) ++ ++ ++ ++clean: ++ rm -f *.o logs/*.txt example example-debug bench bench-debug ++ ++ ++rebuild: clean all ++rebuild-debug: clean debug ++ ++ +diff --git a/external/spdlog-0.14.0/example/bench.cpp b/external/spdlog-0.14.0/example/bench.cpp +new file mode 100644 +index 00000000..b21c4435 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/bench.cpp +@@ -0,0 +1,144 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++// ++// bench.cpp : spdlog benchmarks ++// ++#include ++#include // EXIT_FAILURE ++#include ++#include ++#include ++#include ++#include "spdlog/spdlog.h" ++#include "spdlog/async_logger.h" ++#include "spdlog/sinks/file_sinks.h" ++#include "spdlog/sinks/null_sink.h" ++#include "utils.h" ++ ++ ++using namespace std; ++using namespace std::chrono; ++using namespace spdlog; ++using namespace spdlog::sinks; ++using namespace utils; ++ ++ ++void bench(int howmany, std::shared_ptr log); ++void bench_mt(int howmany, std::shared_ptr log, int thread_count); ++ ++int main(int argc, char* argv[]) ++{ ++ ++ int queue_size = 1048576; ++ int howmany = 1000000; ++ int threads = 10; ++ int file_size = 30 * 1024 * 1024; ++ int rotating_files = 5; ++ ++ try ++ { ++ ++ if(argc > 1) ++ howmany = atoi(argv[1]); ++ if (argc > 2) ++ threads = atoi(argv[2]); ++ if (argc > 3) ++ queue_size = atoi(argv[3]); ++ ++ ++ cout << "*******************************************************************************\n"; ++ cout << "Single thread, " << format(howmany) << " iterations" << endl; ++ cout << "*******************************************************************************\n"; ++ ++ auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files); ++ bench(howmany, rotating_st); ++ auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st"); ++ bench(howmany, daily_st); ++ bench(howmany, spdlog::create("null_st")); ++ ++ cout << "\n*******************************************************************************\n"; ++ cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl; ++ cout << "*******************************************************************************\n"; ++ ++ auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files); ++ bench_mt(howmany, rotating_mt, threads); ++ ++ ++ auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt"); ++ bench_mt(howmany, daily_mt, threads); ++ bench(howmany, spdlog::create("null_mt")); ++ ++ cout << "\n*******************************************************************************\n"; ++ cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl; ++ cout << "*******************************************************************************\n"; ++ ++ ++ spdlog::set_async_mode(queue_size); ++ ++ for(int i = 0; i < 3; ++i) ++ { ++ auto as = spdlog::daily_logger_st("as", "logs/daily_async"); ++ bench_mt(howmany, as, threads); ++ spdlog::drop("as"); ++ } ++ } ++ catch (std::exception &ex) ++ { ++ std::cerr << "Error: " << ex.what() << std::endl; ++ perror("Last error"); ++ return EXIT_FAILURE; ++ } ++ return EXIT_SUCCESS; ++} ++ ++ ++void bench(int howmany, std::shared_ptr log) ++{ ++ cout << log->name() << "...\t\t" << flush; ++ auto start = system_clock::now(); ++ for (auto i = 0; i < howmany; ++i) ++ { ++ log->info("Hello logger: msg number {}", i); ++ } ++ ++ ++ auto delta = system_clock::now() - start; ++ auto delta_d = duration_cast> (delta).count(); ++ cout << format(int(howmany / delta_d)) << "/sec" << endl; ++} ++ ++ ++void bench_mt(int howmany, std::shared_ptr log, int thread_count) ++{ ++ ++ cout << log->name() << "...\t\t" << flush; ++ std::atomic msg_counter {0}; ++ vector threads; ++ auto start = system_clock::now(); ++ for (int t = 0; t < thread_count; ++t) ++ { ++ threads.push_back(std::thread([&]() ++ { ++ for(;;) ++ { ++ int counter = ++msg_counter; ++ if (counter > howmany) break; ++ log->info("Hello logger: msg number {}", counter); ++ } ++ })); ++ } ++ ++ ++ for(auto &t:threads) ++ { ++ t.join(); ++ }; ++ ++ ++ auto delta = system_clock::now() - start; ++ auto delta_d = duration_cast> (delta).count(); ++ cout << format(int(howmany / delta_d)) << "/sec" << endl; ++} +diff --git a/external/spdlog-0.14.0/example/example.cpp b/external/spdlog-0.14.0/example/example.cpp +new file mode 100644 +index 00000000..98231ff5 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/example.cpp +@@ -0,0 +1,174 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++// ++// spdlog usage example ++// ++// ++ ++#define SPDLOG_TRACE_ON ++#define SPDLOG_DEBUG_ON ++ ++#include "spdlog/spdlog.h" ++ ++#include ++#include ++ ++void async_example(); ++void syslog_example(); ++void android_example(); ++void user_defined_example(); ++void err_handler_example(); ++ ++namespace spd = spdlog; ++int main(int, char*[]) ++{ ++ try ++ { ++ // Console logger with color ++ auto console = spd::stdout_color_mt("console"); ++ console->info("Welcome to spdlog!"); ++ console->error("Some error message with arg{}..", 1); ++ ++ // Conditional logging example ++ console->info_if(true, "Welcome to spdlog conditional logging!"); ++ ++ // Formatting examples ++ console->warn("Easy padding in numbers like {:08d}", 12); ++ console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); ++ console->info("Support for floats {:03.2f}", 1.23456); ++ console->info("Positional args are {1} {0}..", "too", "supported"); ++ console->info("{:<30}", "left aligned"); ++ ++ SPDLOG_DEBUG_IF(console, true, "This is a debug log"); ++ ++ ++ spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); ++ ++ ++ // Create basic file logger (not rotated) ++ auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic"); ++ my_logger->info("Some log message"); ++ ++ // Create a file rotating logger with 5mb size max and 3 rotated files ++ auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3); ++ for (int i = 0; i < 10; ++i) ++ rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); ++ ++ // Create a daily logger - a new file is created every day on 2:30am ++ auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30); ++ // trigger flush if the log severity is error or higher ++ daily_logger->flush_on(spd::level::err); ++ daily_logger->info(123.44); ++ ++ // Customize msg format for all messages ++ spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); ++ rotating_logger->info("This is another message with custom format"); ++ ++ ++ // Runtime log levels ++ spd::set_level(spd::level::info); //Set global log level to info ++ console->debug("This message shold not be displayed!"); ++ console->set_level(spd::level::debug); // Set specific logger's log level ++ console->debug("This message shold be displayed.."); ++ ++ // Compile time log levels ++ // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON ++ SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); ++ SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); ++ SPDLOG_DEBUG_IF(console, true, "This is a debug log"); ++ ++ ++ // Asynchronous logging is very fast.. ++ // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. ++ async_example(); ++ ++ // syslog example. linux/osx only ++ syslog_example(); ++ ++ // android example. compile with NDK ++ android_example(); ++ ++ // Log user-defined types example ++ user_defined_example(); ++ ++ // Change default log error handler ++ err_handler_example(); ++ ++ // Apply a function on all registered loggers ++ spd::apply_all([&](std::shared_ptr l) ++ { ++ l->info("End of example."); ++ }); ++ ++ // Release and close all loggers ++ spdlog::drop_all(); ++ } ++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging) ++ catch (const spd::spdlog_ex& ex) ++ { ++ std::cout << "Log init failed: " << ex.what() << std::endl; ++ return 1; ++ } ++} ++ ++void async_example() ++{ ++ size_t q_size = 4096; //queue size must be power of 2 ++ spdlog::set_async_mode(q_size); ++ auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log"); ++ ++ for (int i = 0; i < 100; ++i) ++ async_file->info("Async message #{}", i); ++} ++ ++//syslog example (linux/osx/freebsd) ++void syslog_example() ++{ ++#ifdef SPDLOG_ENABLE_SYSLOG ++ std::string ident = "spdlog-example"; ++ auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); ++ syslog_logger->warn("This is warning that will end up in syslog."); ++#endif ++} ++ ++// Android example ++void android_example() ++{ ++#if defined(__ANDROID__) ++ std::string tag = "spdlog-android"; ++ auto android_logger = spd::android_logger("android", tag); ++ android_logger->critical("Use \"adb shell logcat\" to view this message."); ++#endif ++} ++ ++// user defined types logging by implementing operator<< ++struct my_type ++{ ++ int i; ++ template ++ friend OStream& operator<<(OStream& os, const my_type &c) ++ { ++ return os << "[my_type i="<info("user defined type: {}", my_type { 14 }); ++} ++ ++// ++//custom error handler ++// ++void err_handler_example() ++{ ++ //can be set globaly or per logger(logger->set_error_handler(..)) ++ spdlog::set_error_handler([](const std::string& msg) ++ { ++ std::cerr << "my err handler: " << msg << std::endl; ++ }); ++ spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); ++} +diff --git a/external/spdlog-0.14.0/example/example.sln b/external/spdlog-0.14.0/example/example.sln +new file mode 100644 +index 00000000..81f45629 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/example.sln +@@ -0,0 +1,26 @@ ++ ++Microsoft Visual Studio Solution File, Format Version 12.00 ++# Visual Studio 14 ++VisualStudioVersion = 14.0.25420.1 ++MinimumVisualStudioVersion = 10.0.40219.1 ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}" ++EndProject ++Global ++ GlobalSection(SolutionConfigurationPlatforms) = preSolution ++ Debug|Win32 = Debug|Win32 ++ Debug|x64 = Debug|x64 ++ Release|Win32 = Release|Win32 ++ Release|x64 = Release|x64 ++ EndGlobalSection ++ GlobalSection(ProjectConfigurationPlatforms) = postSolution ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32 ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|Win32 ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32 ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32 ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32 ++ EndGlobalSection ++ GlobalSection(SolutionProperties) = preSolution ++ HideSolutionNode = FALSE ++ EndGlobalSection ++EndGlobal +diff --git a/external/spdlog-0.14.0/example/example.vcxproj b/external/spdlog-0.14.0/example/example.vcxproj +new file mode 100644 +index 00000000..63db2b5d +--- /dev/null ++++ b/external/spdlog-0.14.0/example/example.vcxproj +@@ -0,0 +1,126 @@ ++ ++ ++ ++ ++ Debug ++ Win32 ++ ++ ++ Release ++ Win32 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2} ++ Win32Proj ++ . ++ 8.1 ++ ++ ++ ++ Application ++ true ++ v120 ++ Unicode ++ ++ ++ Application ++ false ++ v120 ++ true ++ Unicode ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ true ++ ++ ++ false ++ ++ ++ ++ ++ ++ Level3 ++ Disabled ++ WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ++ ..\include;%(AdditionalIncludeDirectories) ++ ++ ++ ++ ++ Console ++ true ++ ++ ++ ++ ++ Level3 ++ ++ ++ MaxSpeed ++ true ++ true ++ WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ++ ..\include;%(AdditionalIncludeDirectories) ++ ++ ++ ++ ++ Console ++ true ++ true ++ true ++ %(AdditionalLibraryDirectories) ++ %(AdditionalDependencies) ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/example/jni/Android.mk b/external/spdlog-0.14.0/example/jni/Android.mk +new file mode 100644 +index 00000000..7accbad3 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/jni/Android.mk +@@ -0,0 +1,15 @@ ++# Setup a project ++LOCAL_PATH := $(call my-dir) ++include $(CLEAR_VARS) ++ ++LOCAL_MODULE := example ++LOCAL_SRC_FILES := example.cpp ++LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie ++LOCAL_LDFLAGS += -fPIE -pie ++ ++# Add exception support and set path for spdlog's headers ++LOCAL_CPPFLAGS += -fexceptions -I../include ++# Use android's log library ++LOCAL_LDFLAGS += -llog ++ ++include $(BUILD_EXECUTABLE) +diff --git a/external/spdlog-0.14.0/example/jni/Application.mk b/external/spdlog-0.14.0/example/jni/Application.mk +new file mode 100644 +index 00000000..dccd2a5a +--- /dev/null ++++ b/external/spdlog-0.14.0/example/jni/Application.mk +@@ -0,0 +1,2 @@ ++# Exceptions are used in spdlog. Link to an exception-ready C++ runtime. ++APP_STL = gnustl_static +diff --git a/external/spdlog-0.14.0/example/jni/example.cpp b/external/spdlog-0.14.0/example/jni/example.cpp +new file mode 120000 +index 00000000..6170abce +--- /dev/null ++++ b/external/spdlog-0.14.0/example/jni/example.cpp +@@ -0,0 +1 @@ ++../example.cpp +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/example/multisink.cpp b/external/spdlog-0.14.0/example/multisink.cpp +new file mode 100644 +index 00000000..fe6539b5 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/multisink.cpp +@@ -0,0 +1,47 @@ ++#include "spdlog/spdlog.h" ++ ++#include ++#include ++ ++namespace spd = spdlog; ++int main(int, char*[]) ++{ ++ bool enable_debug = true; ++ try ++ { ++ // This other example use a single logger with multiple sinks. ++ // This means that the same log_msg is forwarded to multiple sinks; ++ // Each sink can have it's own log level and a message will be logged. ++ std::vector sinks; ++ sinks.push_back( std::make_shared() ); ++ sinks.push_back( std::make_shared("./log_regular_file.txt") ); ++ sinks.push_back( std::make_shared("./log_debug_file.txt") ); ++ ++ spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() ); ++ console_multisink.set_level( spdlog::level::warn); ++ ++ sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value ++ sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value ++ sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything. ++ ++ console_multisink.warn("warn: will print only on console and regular file"); ++ ++ if( enable_debug ) ++ { ++ console_multisink.set_level( spdlog::level::debug); // level of the logger ++ sinks[1]->set_level( spdlog::level::debug); // regular file ++ sinks[2]->set_level( spdlog::level::debug); // debug file ++ } ++ console_multisink.debug("Debug: you should see this on console and both files"); ++ ++ // Release and close all loggers ++ spdlog::drop_all(); ++ } ++ // Exceptions will only be thrown upon failed logger or sink construction (not during logging) ++ catch (const spd::spdlog_ex& ex) ++ { ++ std::cout << "Log init failed: " << ex.what() << std::endl; ++ return 1; ++ } ++} ++ +diff --git a/external/spdlog-0.14.0/example/utils.h b/external/spdlog-0.14.0/example/utils.h +new file mode 100644 +index 00000000..b260f724 +--- /dev/null ++++ b/external/spdlog-0.14.0/example/utils.h +@@ -0,0 +1,35 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++namespace utils ++{ ++ ++template ++inline std::string format(const T& value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << value; ++ return ss.str(); ++} ++ ++template<> ++inline std::string format(const double & value) ++{ ++ static std::locale loc(""); ++ std::stringstream ss; ++ ss.imbue(loc); ++ ss << std::fixed << std::setprecision(1) << value; ++ return ss.str(); ++} ++ ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/async_logger.h b/external/spdlog-0.14.0/include/spdlog/async_logger.h +new file mode 100644 +index 00000000..9d7e08fa +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/async_logger.h +@@ -0,0 +1,82 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// Very fast asynchronous logger (millions of logs per second on an average desktop) ++// Uses pre allocated lockfree queue for maximum throughput even under large number of threads. ++// Creates a single back thread to pop messages from the queue and log them. ++// ++// Upon each log write the logger: ++// 1. Checks if its log level is enough to log the message ++// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) ++// 3. will throw spdlog_ex upon log exceptions ++// Upon destruction, logs all remaining messages in the queue before destructing.. ++ ++#include "spdlog/common.h" ++#include "spdlog/logger.h" ++ ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++ ++namespace details ++{ ++class async_log_helper; ++} ++ ++class async_logger SPDLOG_FINAL :public logger ++{ ++public: ++ template ++ async_logger(const std::string& name, ++ const It& begin, ++ const It& end, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, ++ const std::function& worker_warmup_cb = nullptr, ++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), ++ const std::function& worker_teardown_cb = nullptr); ++ ++ async_logger(const std::string& logger_name, ++ sinks_init_list sinks, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, ++ const std::function& worker_warmup_cb = nullptr, ++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), ++ const std::function& worker_teardown_cb = nullptr); ++ ++ async_logger(const std::string& logger_name, ++ sink_ptr single_sink, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, ++ const std::function& worker_warmup_cb = nullptr, ++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), ++ const std::function& worker_teardown_cb = nullptr); ++ ++ //Wait for the queue to be empty, and flush synchronously ++ //Warning: this can potentially last forever as we wait it to complete ++ void flush() override; ++ ++ // Error handler ++ virtual void set_error_handler(log_err_handler) override; ++ virtual log_err_handler error_handler() override; ++ ++protected: ++ void _sink_it(details::log_msg& msg) override; ++ void _set_formatter(spdlog::formatter_ptr msg_formatter) override; ++ void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override; ++ ++private: ++ std::unique_ptr _async_log_helper; ++}; ++} ++ ++ ++#include "spdlog/details/async_logger_impl.h" +diff --git a/external/spdlog-0.14.0/include/spdlog/common.h b/external/spdlog-0.14.0/include/spdlog/common.h +new file mode 100644 +index 00000000..7e352fa0 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/common.h +@@ -0,0 +1,160 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) ++#include ++#include ++#endif ++ ++#include "spdlog/details/null_mutex.h" ++ ++//visual studio upto 2013 does not support noexcept nor constexpr ++#if defined(_MSC_VER) && (_MSC_VER < 1900) ++#define SPDLOG_NOEXCEPT throw() ++#define SPDLOG_CONSTEXPR ++#else ++#define SPDLOG_NOEXCEPT noexcept ++#define SPDLOG_CONSTEXPR constexpr ++#endif ++ ++// See tweakme.h ++#if !defined(SPDLOG_FINAL) ++#define SPDLOG_FINAL ++#endif ++ ++#if defined(__GNUC__) || defined(__clang__) ++#define SPDLOG_DEPRECATED __attribute__((deprecated)) ++#elif defined(_MSC_VER) ++#define SPDLOG_DEPRECATED __declspec(deprecated) ++#else ++#define SPDLOG_DEPRECATED ++#endif ++ ++ ++#include "spdlog/fmt/fmt.h" ++ ++namespace spdlog ++{ ++ ++class formatter; ++ ++namespace sinks ++{ ++class sink; ++} ++ ++using log_clock = std::chrono::system_clock; ++using sink_ptr = std::shared_ptr < sinks::sink >; ++using sinks_init_list = std::initializer_list < sink_ptr >; ++using formatter_ptr = std::shared_ptr; ++#if defined(SPDLOG_NO_ATOMIC_LEVELS) ++using level_t = details::null_atomic_int; ++#else ++using level_t = std::atomic; ++#endif ++ ++using log_err_handler = std::function; ++ ++//Log level enum ++namespace level ++{ ++typedef enum ++{ ++ trace = 0, ++ debug = 1, ++ info = 2, ++ warn = 3, ++ err = 4, ++ critical = 5, ++ off = 6 ++} level_enum; ++ ++#if !defined(SPDLOG_LEVEL_NAMES) ++#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }; ++#endif ++static const char* level_names[] SPDLOG_LEVEL_NAMES ++ ++static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" }; ++ ++inline const char* to_str(spdlog::level::level_enum l) ++{ ++ return level_names[l]; ++} ++ ++inline const char* to_short_str(spdlog::level::level_enum l) ++{ ++ return short_level_names[l]; ++} ++} //level ++ ++ ++// ++// Async overflow policy - block by default. ++// ++enum class async_overflow_policy ++{ ++ block_retry, // Block / yield / sleep until message can be enqueued ++ discard_log_msg // Discard the message it enqueue fails ++}; ++ ++// ++// Pattern time - specific time getting to use for pattern_formatter. ++// local time by default ++// ++enum class pattern_time_type ++{ ++ local, // log localtime ++ utc // log utc ++}; ++ ++// ++// Log exception ++// ++namespace details ++{ ++namespace os ++{ ++std::string errno_str(int err_num); ++} ++} ++class spdlog_ex: public std::exception ++{ ++public: ++ spdlog_ex(const std::string& msg):_msg(msg) ++ {} ++ spdlog_ex(const std::string& msg, int last_errno) ++ { ++ _msg = msg + ": " + details::os::errno_str(last_errno); ++ } ++ const char* what() const SPDLOG_NOEXCEPT override ++ { ++ return _msg.c_str(); ++ } ++private: ++ std::string _msg; ++ ++}; ++ ++// ++// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) ++// ++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) ++using filename_t = std::wstring; ++#else ++using filename_t = std::string; ++#endif ++ ++ ++} //spdlog +diff --git a/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h b/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h +new file mode 100644 +index 00000000..6145dfa6 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/async_log_helper.h +@@ -0,0 +1,399 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++// async log helper : ++// Process logs asynchronously using a back thread. ++// ++// If the internal queue of log messages reaches its max size, ++// then the client call will block until there is more room. ++// ++ ++#pragma once ++ ++#include "spdlog/common.h" ++#include "spdlog/sinks/sink.h" ++#include "spdlog/details/mpmc_bounded_q.h" ++#include "spdlog/details/log_msg.h" ++#include "spdlog/details/os.h" ++#include "spdlog/formatter.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++ ++class async_log_helper ++{ ++ // Async msg to move to/from the queue ++ // Movable only. should never be copied ++ enum class async_msg_type ++ { ++ log, ++ flush, ++ terminate ++ }; ++ struct async_msg ++ { ++ std::string logger_name; ++ level::level_enum level; ++ log_clock::time_point time; ++ size_t thread_id; ++ std::string txt; ++ async_msg_type msg_type; ++ size_t msg_id; ++ ++ async_msg() = default; ++ ~async_msg() = default; ++ ++ ++async_msg(async_msg&& other) SPDLOG_NOEXCEPT: ++ logger_name(std::move(other.logger_name)), ++ level(std::move(other.level)), ++ time(std::move(other.time)), ++ thread_id(other.thread_id), ++ txt(std::move(other.txt)), ++ msg_type(std::move(other.msg_type)), ++ msg_id(other.msg_id) ++ {} ++ ++ async_msg(async_msg_type m_type): ++ level(level::info), ++ thread_id(0), ++ msg_type(m_type), ++ msg_id(0) ++ {} ++ ++ async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT ++ { ++ logger_name = std::move(other.logger_name); ++ level = other.level; ++ time = std::move(other.time); ++ thread_id = other.thread_id; ++ txt = std::move(other.txt); ++ msg_type = other.msg_type; ++ msg_id = other.msg_id; ++ return *this; ++ } ++ ++ // never copy or assign. should only be moved.. ++ async_msg(const async_msg&) = delete; ++ async_msg& operator=(const async_msg& other) = delete; ++ ++ // construct from log_msg ++ async_msg(const details::log_msg& m): ++ level(m.level), ++ time(m.time), ++ thread_id(m.thread_id), ++ txt(m.raw.data(), m.raw.size()), ++ msg_type(async_msg_type::log), ++ msg_id(m.msg_id) ++ { ++#ifndef SPDLOG_NO_NAME ++ logger_name = *m.logger_name; ++#endif ++ } ++ ++ ++ // copy into log_msg ++ void fill_log_msg(log_msg &msg) ++ { ++ msg.logger_name = &logger_name; ++ msg.level = level; ++ msg.time = time; ++ msg.thread_id = thread_id; ++ msg.raw << txt; ++ msg.msg_id = msg_id; ++ } ++ }; ++ ++public: ++ ++ using item_type = async_msg; ++ using q_type = details::mpmc_bounded_queue; ++ ++ using clock = std::chrono::steady_clock; ++ ++ ++ async_log_helper(formatter_ptr formatter, ++ const std::vector& sinks, ++ size_t queue_size, ++ const log_err_handler err_handler, ++ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, ++ const std::function& worker_warmup_cb = nullptr, ++ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), ++ const std::function& worker_teardown_cb = nullptr); ++ ++ void log(const details::log_msg& msg); ++ ++ // stop logging and join the back thread ++ ~async_log_helper(); ++ ++ void set_formatter(formatter_ptr); ++ ++ void flush(bool wait_for_q); ++ ++ void set_error_handler(spdlog::log_err_handler err_handler); ++ ++private: ++ formatter_ptr _formatter; ++ std::vector> _sinks; ++ ++ // queue of messages to log ++ q_type _q; ++ ++ log_err_handler _err_handler; ++ ++ bool _flush_requested; ++ ++ bool _terminate_requested; ++ ++ ++ // overflow policy ++ const async_overflow_policy _overflow_policy; ++ ++ // worker thread warmup callback - one can set thread priority, affinity, etc ++ const std::function _worker_warmup_cb; ++ ++ // auto periodic sink flush parameter ++ const std::chrono::milliseconds _flush_interval_ms; ++ ++ // worker thread teardown callback ++ const std::function _worker_teardown_cb; ++ ++ // worker thread ++ std::thread _worker_thread; ++ ++ void push_msg(async_msg&& new_msg); ++ ++ // worker thread main loop ++ void worker_loop(); ++ ++ // pop next message from the queue and process it. will set the last_pop to the pop time ++ // return false if termination of the queue is required ++ bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush); ++ ++ void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); ++ ++ // sleep,yield or return immediately using the time passed since last message as a hint ++ static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); ++ ++ // wait until the queue is empty ++ void wait_empty_q(); ++ ++}; ++} ++} ++ ++/////////////////////////////////////////////////////////////////////////////// ++// async_sink class implementation ++/////////////////////////////////////////////////////////////////////////////// ++inline spdlog::details::async_log_helper::async_log_helper( ++ formatter_ptr formatter, ++ const std::vector& sinks, ++ size_t queue_size, ++ log_err_handler err_handler, ++ const async_overflow_policy overflow_policy, ++ const std::function& worker_warmup_cb, ++ const std::chrono::milliseconds& flush_interval_ms, ++ const std::function& worker_teardown_cb): ++ _formatter(formatter), ++ _sinks(sinks), ++ _q(queue_size), ++ _err_handler(err_handler), ++ _flush_requested(false), ++ _terminate_requested(false), ++ _overflow_policy(overflow_policy), ++ _worker_warmup_cb(worker_warmup_cb), ++ _flush_interval_ms(flush_interval_ms), ++ _worker_teardown_cb(worker_teardown_cb), ++ _worker_thread(&async_log_helper::worker_loop, this) ++{} ++ ++// Send to the worker thread termination message(level=off) ++// and wait for it to finish gracefully ++inline spdlog::details::async_log_helper::~async_log_helper() ++{ ++ try ++ { ++ push_msg(async_msg(async_msg_type::terminate)); ++ _worker_thread.join(); ++ } ++ catch (...) // don't crash in destructor ++ { ++ } ++} ++ ++ ++//Try to push and block until succeeded (if the policy is not to discard when the queue is full) ++inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) ++{ ++ push_msg(async_msg(msg)); ++} ++ ++inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) ++{ ++ if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) ++ { ++ auto last_op_time = details::os::now(); ++ auto now = last_op_time; ++ do ++ { ++ now = details::os::now(); ++ sleep_or_yield(now, last_op_time); ++ } ++ while (!_q.enqueue(std::move(new_msg))); ++ } ++} ++ ++// optionally wait for the queue be empty and request flush from the sinks ++inline void spdlog::details::async_log_helper::flush(bool wait_for_q) ++{ ++ push_msg(async_msg(async_msg_type::flush)); ++ if (wait_for_q) ++ wait_empty_q(); //return only make after the above flush message was processed ++} ++ ++inline void spdlog::details::async_log_helper::worker_loop() ++{ ++ if (_worker_warmup_cb) _worker_warmup_cb(); ++ auto last_pop = details::os::now(); ++ auto last_flush = last_pop; ++ auto active = true; ++ while (active) ++ { ++ try ++ { ++ active = process_next_msg(last_pop, last_flush); ++ } ++ catch (const std::exception &ex) ++ { ++ _err_handler(ex.what()); ++ } ++ catch (...) ++ { ++ _err_handler("Unknown exception"); ++ } ++ } ++ if (_worker_teardown_cb) _worker_teardown_cb(); ++ ++ ++} ++ ++// process next message in the queue ++// return true if this thread should still be active (while no terminate msg was received) ++inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) ++{ ++ async_msg incoming_async_msg; ++ ++ if (_q.dequeue(incoming_async_msg)) ++ { ++ last_pop = details::os::now(); ++ switch (incoming_async_msg.msg_type) ++ { ++ case async_msg_type::flush: ++ _flush_requested = true; ++ break; ++ ++ case async_msg_type::terminate: ++ _flush_requested = true; ++ _terminate_requested = true; ++ break; ++ ++ default: ++ log_msg incoming_log_msg; ++ incoming_async_msg.fill_log_msg(incoming_log_msg); ++ _formatter->format(incoming_log_msg); ++ for (auto &s : _sinks) ++ { ++ if (s->should_log(incoming_log_msg.level)) ++ { ++ s->log(incoming_log_msg); ++ } ++ } ++ } ++ return true; ++ } ++ ++ // Handle empty queue.. ++ // This is the only place where the queue can terminate or flush to avoid losing messages already in the queue ++ else ++ { ++ auto now = details::os::now(); ++ handle_flush_interval(now, last_flush); ++ sleep_or_yield(now, last_pop); ++ return !_terminate_requested; ++ } ++} ++ ++// flush all sinks if _flush_interval_ms has expired ++inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) ++{ ++ auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); ++ if (should_flush) ++ { ++ for (auto &s : _sinks) ++ s->flush(); ++ now = last_flush = details::os::now(); ++ _flush_requested = false; ++ } ++} ++ ++inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) ++{ ++ _formatter = msg_formatter; ++} ++ ++ ++// spin, yield or sleep. use the time passed since last message as a hint ++inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) ++{ ++ using namespace std::this_thread; ++ using std::chrono::milliseconds; ++ using std::chrono::microseconds; ++ ++ auto time_since_op = now - last_op_time; ++ ++ // spin upto 50 micros ++ if (time_since_op <= microseconds(50)) ++ return; ++ ++ // yield upto 150 micros ++ if (time_since_op <= microseconds(100)) ++ return std::this_thread::yield(); ++ ++ // sleep for 20 ms upto 200 ms ++ if (time_since_op <= milliseconds(200)) ++ return sleep_for(milliseconds(20)); ++ ++ // sleep for 200 ms ++ return sleep_for(milliseconds(200)); ++} ++ ++// wait for the queue to be empty ++inline void spdlog::details::async_log_helper::wait_empty_q() ++{ ++ auto last_op = details::os::now(); ++ while (_q.approx_size() > 0) ++ { ++ sleep_or_yield(details::os::now(), last_op); ++ } ++} ++ ++inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) ++{ ++ _err_handler = err_handler; ++} ++ ++ ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h b/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h +new file mode 100644 +index 00000000..33486c28 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/async_logger_impl.h +@@ -0,0 +1,105 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// Async Logger implementation ++// Use an async_sink (queue per logger) to perform the logging in a worker thread ++ ++#include "spdlog/details/async_log_helper.h" ++#include "spdlog/async_logger.h" ++ ++#include ++#include ++#include ++#include ++ ++template ++inline spdlog::async_logger::async_logger(const std::string& logger_name, ++ const It& begin, ++ const It& end, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy, ++ const std::function& worker_warmup_cb, ++ const std::chrono::milliseconds& flush_interval_ms, ++ const std::function& worker_teardown_cb) : ++ logger(logger_name, begin, end), ++ _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) ++{ ++} ++ ++inline spdlog::async_logger::async_logger(const std::string& logger_name, ++ sinks_init_list sinks_list, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy, ++ const std::function& worker_warmup_cb, ++ const std::chrono::milliseconds& flush_interval_ms, ++ const std::function& worker_teardown_cb) : ++ async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} ++ ++inline spdlog::async_logger::async_logger(const std::string& logger_name, ++ sink_ptr single_sink, ++ size_t queue_size, ++ const async_overflow_policy overflow_policy, ++ const std::function& worker_warmup_cb, ++ const std::chrono::milliseconds& flush_interval_ms, ++ const std::function& worker_teardown_cb) : ++ async_logger(logger_name, ++{ ++ single_sink ++}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} ++ ++ ++inline void spdlog::async_logger::flush() ++{ ++ _async_log_helper->flush(true); ++} ++ ++// Error handler ++inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) ++{ ++ _err_handler = err_handler; ++ _async_log_helper->set_error_handler(err_handler); ++ ++} ++inline spdlog::log_err_handler spdlog::async_logger::error_handler() ++{ ++ return _err_handler; ++} ++ ++ ++inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) ++{ ++ _formatter = msg_formatter; ++ _async_log_helper->set_formatter(_formatter); ++} ++ ++inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time) ++{ ++ _formatter = std::make_shared(pattern, pattern_time); ++ _async_log_helper->set_formatter(_formatter); ++} ++ ++ ++inline void spdlog::async_logger::_sink_it(details::log_msg& msg) ++{ ++ try ++ { ++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) ++ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); ++#endif ++ _async_log_helper->log(msg); ++ if (_should_flush_on(msg)) ++ _async_log_helper->flush(false); // do async flush ++ } ++ catch (const std::exception &ex) ++ { ++ _err_handler(ex.what()); ++ } ++ catch (...) ++ { ++ _err_handler("Unknown exception"); ++ } ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/file_helper.h b/external/spdlog-0.14.0/include/spdlog/details/file_helper.h +new file mode 100644 +index 00000000..d0d730e2 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/file_helper.h +@@ -0,0 +1,117 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// Helper class for file sink ++// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) ++// Throw spdlog_ex exception on errors ++ ++#include "spdlog/details/os.h" ++#include "spdlog/details/log_msg.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++ ++class file_helper ++{ ++ ++public: ++ const int open_tries = 5; ++ const int open_interval = 10; ++ ++ explicit file_helper() : ++ _fd(nullptr) ++ {} ++ ++ file_helper(const file_helper&) = delete; ++ file_helper& operator=(const file_helper&) = delete; ++ ++ ~file_helper() ++ { ++ close(); ++ } ++ ++ ++ void open(const filename_t& fname, bool truncate = false) ++ { ++ ++ close(); ++ auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); ++ _filename = fname; ++ for (int tries = 0; tries < open_tries; ++tries) ++ { ++ if (!os::fopen_s(&_fd, fname, mode)) ++ return; ++ ++ std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); ++ } ++ ++ throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); ++ } ++ ++ void reopen(bool truncate) ++ { ++ if (_filename.empty()) ++ throw spdlog_ex("Failed re opening file - was not opened before"); ++ open(_filename, truncate); ++ ++ } ++ ++ void flush() ++ { ++ std::fflush(_fd); ++ } ++ ++ void close() ++ { ++ if (_fd) ++ { ++ std::fclose(_fd); ++ _fd = nullptr; ++ } ++ } ++ ++ void write(const log_msg& msg) ++ { ++ ++ size_t msg_size = msg.formatted.size(); ++ auto data = msg.formatted.data(); ++ if (std::fwrite(data, 1, msg_size, _fd) != msg_size) ++ throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); ++ } ++ ++ size_t size() ++ { ++ if (!_fd) ++ throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); ++ return os::filesize(_fd); ++ } ++ ++ const filename_t& filename() const ++ { ++ return _filename; ++ } ++ ++ static bool file_exists(const filename_t& name) ++ { ++ ++ return os::file_exists(name); ++ } ++ ++private: ++ FILE* _fd; ++ filename_t _filename; ++}; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/log_msg.h b/external/spdlog-0.14.0/include/spdlog/details/log_msg.h +new file mode 100644 +index 00000000..0d7ce4ba +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/log_msg.h +@@ -0,0 +1,50 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/common.h" ++#include "spdlog/details/os.h" ++ ++ ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++struct log_msg ++{ ++ log_msg() = default; ++ log_msg(const std::string *loggers_name, level::level_enum lvl) : ++ logger_name(loggers_name), ++ level(lvl), ++ msg_id(0) ++ { ++#ifndef SPDLOG_NO_DATETIME ++ time = os::now(); ++#endif ++ ++#ifndef SPDLOG_NO_THREAD_ID ++ thread_id = os::thread_id(); ++#endif ++ } ++ ++ log_msg(const log_msg& other) = delete; ++ log_msg& operator=(log_msg&& other) = delete; ++ log_msg(log_msg&& other) = delete; ++ ++ ++ const std::string *logger_name; ++ level::level_enum level; ++ log_clock::time_point time; ++ size_t thread_id; ++ fmt::MemoryWriter raw; ++ fmt::MemoryWriter formatted; ++ size_t msg_id; ++}; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h b/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h +new file mode 100644 +index 00000000..16222909 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/logger_impl.h +@@ -0,0 +1,564 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/logger.h" ++#include "spdlog/sinks/stdout_sinks.h" ++ ++#include ++#include ++ ++ ++// create logger with given name, sinks and the default pattern formatter ++// all other ctors will call this one ++template ++inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): ++ _name(logger_name), ++ _sinks(begin, end), ++ _formatter(std::make_shared("%+")), ++ _level(level::info), ++ _flush_level(level::off), ++ _last_err_time(0), ++ _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages ++{ ++ _err_handler = [this](const std::string &msg) ++ { ++ this->_default_err_handler(msg); ++ }; ++} ++ ++// ctor with sinks as init list ++inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): ++ logger(logger_name, sinks_list.begin(), sinks_list.end()) ++{} ++ ++ ++// ctor with single sink ++inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): ++ logger(logger_name, ++{ ++ single_sink ++}) ++{} ++ ++ ++inline spdlog::logger::~logger() = default; ++ ++ ++inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) ++{ ++ _set_formatter(msg_formatter); ++} ++ ++inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time) ++{ ++ _set_pattern(pattern, pattern_time); ++} ++ ++ ++template ++inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) ++{ ++ if (!should_log(lvl)) return; ++ ++ try ++ { ++ details::log_msg log_msg(&_name, lvl); ++ log_msg.raw.write(fmt, args...); ++ _sink_it(log_msg); ++ } ++ catch (const std::exception &ex) ++ { ++ _err_handler(ex.what()); ++ } ++ catch (...) ++ { ++ _err_handler("Unknown exception"); ++ } ++} ++ ++template ++inline void spdlog::logger::log(level::level_enum lvl, const char* msg) ++{ ++ if (!should_log(lvl)) return; ++ try ++ { ++ details::log_msg log_msg(&_name, lvl); ++ log_msg.raw << msg; ++ _sink_it(log_msg); ++ } ++ catch (const std::exception &ex) ++ { ++ _err_handler(ex.what()); ++ } ++ catch (...) ++ { ++ _err_handler("Unknown exception"); ++ } ++ ++} ++ ++template ++inline void spdlog::logger::log(level::level_enum lvl, const T& msg) ++{ ++ if (!should_log(lvl)) return; ++ try ++ { ++ details::log_msg log_msg(&_name, lvl); ++ log_msg.raw << msg; ++ _sink_it(log_msg); ++ } ++ catch (const std::exception &ex) ++ { ++ _err_handler(ex.what()); ++ } ++ catch (...) ++ { ++ _err_handler("Unknown exception"); ++ } ++} ++ ++ ++template ++inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::trace, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::debug, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::info, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::warn, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::err, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ log(level::critical, fmt, arg1, args...); ++} ++ ++template ++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg) ++{ ++ if (flag) ++ { ++ log(lvl, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg) ++{ ++ if (flag) ++ { ++ log(lvl, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::trace, fmt, arg1, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::debug, fmt, arg1, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::info, fmt, arg1, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::warn, fmt, arg1, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::err, fmt, arg1, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::critical, fmt, arg1, args...); ++ } ++} ++ ++ ++template ++inline void spdlog::logger::trace(const T& msg) ++{ ++ log(level::trace, msg); ++} ++ ++template ++inline void spdlog::logger::debug(const T& msg) ++{ ++ log(level::debug, msg); ++} ++ ++ ++template ++inline void spdlog::logger::info(const T& msg) ++{ ++ log(level::info, msg); ++} ++ ++ ++template ++inline void spdlog::logger::warn(const T& msg) ++{ ++ log(level::warn, msg); ++} ++ ++template ++inline void spdlog::logger::error(const T& msg) ++{ ++ log(level::err, msg); ++} ++ ++template ++inline void spdlog::logger::critical(const T& msg) ++{ ++ log(level::critical, msg); ++} ++ ++template ++inline void spdlog::logger::trace_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::trace, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::debug_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::debug, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::info_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::info, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::warn_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::warn, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::error_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::err, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::critical_if(const bool flag, const T& msg) ++{ ++ if (flag) ++ { ++ log(level::critical, msg); ++ } ++} ++ ++ ++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT ++#include ++#include ++ ++template ++inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg) ++{ ++ std::wstring_convert > conv; ++ ++ log(lvl, conv.to_bytes(msg)); ++} ++ ++template ++inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args) ++{ ++ fmt::WMemoryWriter wWriter; ++ ++ wWriter.write(fmt, args...); ++ log(lvl, wWriter.c_str()); ++} ++ ++template ++inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::trace, fmt, args...); ++} ++ ++template ++inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::debug, fmt, args...); ++} ++ ++template ++inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::info, fmt, args...); ++} ++ ++ ++template ++inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::warn, fmt, args...); ++} ++ ++template ++inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::err, fmt, args...); ++} ++ ++template ++inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args) ++{ ++ log(level::critical, fmt, args...); ++} ++ ++// ++// conditional logging ++// ++ ++template ++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg) ++{ ++ if (flag) ++ { ++ log(lvl, msg); ++ } ++} ++ ++template ++inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(lvl, fmt, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::trace, fmt, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::debug, fmt, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::info, fmt, args...); ++ } ++} ++ ++ ++template ++inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::warn, fmt, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::err, fmt, args...); ++ } ++} ++ ++template ++inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args) ++{ ++ if (flag) ++ { ++ log(level::critical, fmt, args...); ++ } ++} ++ ++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ ++ ++ ++// ++// name and level ++// ++inline const std::string& spdlog::logger::name() const ++{ ++ return _name; ++} ++ ++inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) ++{ ++ _level.store(log_level); ++} ++ ++inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) ++{ ++ _err_handler = err_handler; ++} ++ ++inline spdlog::log_err_handler spdlog::logger::error_handler() ++{ ++ return _err_handler; ++} ++ ++ ++inline void spdlog::logger::flush_on(level::level_enum log_level) ++{ ++ _flush_level.store(log_level); ++} ++ ++inline spdlog::level::level_enum spdlog::logger::level() const ++{ ++ return static_cast(_level.load(std::memory_order_relaxed)); ++} ++ ++inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const ++{ ++ return msg_level >= _level.load(std::memory_order_relaxed); ++} ++ ++// ++// protected virtual called at end of each user log call (if enabled) by the line_logger ++// ++inline void spdlog::logger::_sink_it(details::log_msg& msg) ++{ ++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) ++ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); ++#endif ++ _formatter->format(msg); ++ for (auto &sink : _sinks) ++ { ++ if( sink->should_log( msg.level)) ++ { ++ sink->log(msg); ++ } ++ } ++ ++ if(_should_flush_on(msg)) ++ flush(); ++} ++ ++inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time) ++{ ++ _formatter = std::make_shared(pattern, pattern_time); ++} ++inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) ++{ ++ _formatter = msg_formatter; ++} ++ ++inline void spdlog::logger::flush() ++{ ++ for (auto& sink : _sinks) ++ sink->flush(); ++} ++ ++inline void spdlog::logger::_default_err_handler(const std::string &msg) ++{ ++ auto now = time(nullptr); ++ if (now - _last_err_time < 60) ++ return; ++ auto tm_time = details::os::localtime(now); ++ char date_buf[100]; ++ std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); ++ details::log_msg err_msg; ++ err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); ++ sinks::stderr_sink_mt::instance()->log(err_msg); ++ _last_err_time = now; ++} ++ ++inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) ++{ ++ const auto flush_level = _flush_level.load(std::memory_order_relaxed); ++ return (msg.level >= flush_level) && (msg.level != level::off); ++} ++ ++inline const std::vector& spdlog::logger::sinks() const ++{ ++ return _sinks; ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h b/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h +new file mode 100644 +index 00000000..afd4c881 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/mpmc_bounded_q.h +@@ -0,0 +1,172 @@ ++/* ++A modified version of Bounded MPMC queue by Dmitry Vyukov. ++ ++Original code from: ++http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue ++ ++licensed by Dmitry Vyukov under the terms below: ++ ++Simplified BSD license ++ ++Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. ++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 DMITRY VYUKOV "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 DMITRY VYUKOV 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. ++ ++The views and conclusions contained in the software and documentation are those of the authors and ++should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. ++*/ ++ ++/* ++The code in its current form adds the license below: ++ ++Copyright(c) 2015 Gabi Melman. ++Distributed under the MIT License (http://opensource.org/licenses/MIT) ++ ++*/ ++ ++#pragma once ++ ++#include "spdlog/common.h" ++ ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++ ++template ++class mpmc_bounded_queue ++{ ++public: ++ ++ using item_type = T; ++ mpmc_bounded_queue(size_t buffer_size) ++ :max_size_(buffer_size), ++ buffer_(new cell_t [buffer_size]), ++ buffer_mask_(buffer_size - 1) ++ { ++ //queue size must be power of two ++ if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) ++ throw spdlog_ex("async logger queue size must be power of two"); ++ ++ for (size_t i = 0; i != buffer_size; i += 1) ++ buffer_[i].sequence_.store(i, std::memory_order_relaxed); ++ enqueue_pos_.store(0, std::memory_order_relaxed); ++ dequeue_pos_.store(0, std::memory_order_relaxed); ++ } ++ ++ ~mpmc_bounded_queue() ++ { ++ delete [] buffer_; ++ } ++ ++ ++ bool enqueue(T&& data) ++ { ++ cell_t* cell; ++ size_t pos = enqueue_pos_.load(std::memory_order_relaxed); ++ for (;;) ++ { ++ cell = &buffer_[pos & buffer_mask_]; ++ size_t seq = cell->sequence_.load(std::memory_order_acquire); ++ intptr_t dif = (intptr_t)seq - (intptr_t)pos; ++ if (dif == 0) ++ { ++ if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) ++ break; ++ } ++ else if (dif < 0) ++ { ++ return false; ++ } ++ else ++ { ++ pos = enqueue_pos_.load(std::memory_order_relaxed); ++ } ++ } ++ cell->data_ = std::move(data); ++ cell->sequence_.store(pos + 1, std::memory_order_release); ++ return true; ++ } ++ ++ bool dequeue(T& data) ++ { ++ cell_t* cell; ++ size_t pos = dequeue_pos_.load(std::memory_order_relaxed); ++ for (;;) ++ { ++ cell = &buffer_[pos & buffer_mask_]; ++ size_t seq = ++ cell->sequence_.load(std::memory_order_acquire); ++ intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); ++ if (dif == 0) ++ { ++ if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) ++ break; ++ } ++ else if (dif < 0) ++ return false; ++ else ++ pos = dequeue_pos_.load(std::memory_order_relaxed); ++ } ++ data = std::move(cell->data_); ++ cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); ++ return true; ++ } ++ ++ size_t approx_size() ++ { ++ size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed); ++ size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed); ++ if (last_pos <= first_pos) ++ return 0; ++ auto size = last_pos - first_pos; ++ return size < max_size_ ? size : max_size_; ++ } ++ ++private: ++ struct cell_t ++ { ++ std::atomic sequence_; ++ T data_; ++ }; ++ ++ size_t const max_size_; ++ ++ static size_t const cacheline_size = 64; ++ typedef char cacheline_pad_t [cacheline_size]; ++ ++ cacheline_pad_t pad0_; ++ cell_t* const buffer_; ++ size_t const buffer_mask_; ++ cacheline_pad_t pad1_; ++ std::atomic enqueue_pos_; ++ cacheline_pad_t pad2_; ++ std::atomic dequeue_pos_; ++ cacheline_pad_t pad3_; ++ ++ mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; ++ void operator= (mpmc_bounded_queue const&) = delete; ++}; ++ ++} // ns details ++} // ns spdlog +diff --git a/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h b/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h +new file mode 100644 +index 00000000..67b0aeee +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/null_mutex.h +@@ -0,0 +1,45 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include ++// null, no cost dummy "mutex" and dummy "atomic" int ++ ++namespace spdlog ++{ ++namespace details ++{ ++struct null_mutex ++{ ++ void lock() {} ++ void unlock() {} ++ bool try_lock() ++ { ++ return true; ++ } ++}; ++ ++struct null_atomic_int ++{ ++ int value; ++ null_atomic_int() = default; ++ ++ null_atomic_int(int val):value(val) ++ {} ++ ++ int load(std::memory_order) const ++ { ++ return value; ++ } ++ ++ void store(int val) ++ { ++ value = val; ++ } ++}; ++ ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/os.h b/external/spdlog-0.14.0/include/spdlog/details/os.h +new file mode 100644 +index 00000000..6241aa5f +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/os.h +@@ -0,0 +1,469 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++#pragma once ++ ++#include "spdlog/common.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef _WIN32 ++ ++#ifndef NOMINMAX ++#define NOMINMAX //prevent windows redefining min/max ++#endif ++ ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#include ++#include // _get_pid support ++#include // _get_osfhandle and _isatty support ++ ++#ifdef __MINGW32__ ++#include ++#endif ++ ++#else // unix ++ ++#include ++#include ++ ++#ifdef __linux__ ++#include //Use gettid() syscall under linux to get thread id ++ ++#elif __FreeBSD__ ++#include //Use thr_self() syscall under FreeBSD to get thread id ++#endif ++ ++#endif //unix ++ ++#ifndef __has_feature // Clang - feature checking macros. ++#define __has_feature(x) 0 // Compatibility with non-clang compilers. ++#endif ++ ++ ++namespace spdlog ++{ ++namespace details ++{ ++namespace os ++{ ++ ++inline spdlog::log_clock::time_point now() ++{ ++ ++#if defined __linux__ && defined SPDLOG_CLOCK_COARSE ++ timespec ts; ++ ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ++ return std::chrono::time_point( ++ std::chrono::duration_cast( ++ std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); ++ ++ ++#else ++ return log_clock::now(); ++#endif ++ ++} ++inline std::tm localtime(const std::time_t &time_tt) ++{ ++ ++#ifdef _WIN32 ++ std::tm tm; ++ localtime_s(&tm, &time_tt); ++#else ++ std::tm tm; ++ localtime_r(&time_tt, &tm); ++#endif ++ return tm; ++} ++ ++inline std::tm localtime() ++{ ++ std::time_t now_t = time(nullptr); ++ return localtime(now_t); ++} ++ ++ ++inline std::tm gmtime(const std::time_t &time_tt) ++{ ++ ++#ifdef _WIN32 ++ std::tm tm; ++ gmtime_s(&tm, &time_tt); ++#else ++ std::tm tm; ++ gmtime_r(&time_tt, &tm); ++#endif ++ return tm; ++} ++ ++inline std::tm gmtime() ++{ ++ std::time_t now_t = time(nullptr); ++ return gmtime(now_t); ++} ++inline bool operator==(const std::tm& tm1, const std::tm& tm2) ++{ ++ return (tm1.tm_sec == tm2.tm_sec && ++ tm1.tm_min == tm2.tm_min && ++ tm1.tm_hour == tm2.tm_hour && ++ tm1.tm_mday == tm2.tm_mday && ++ tm1.tm_mon == tm2.tm_mon && ++ tm1.tm_year == tm2.tm_year && ++ tm1.tm_isdst == tm2.tm_isdst); ++} ++ ++inline bool operator!=(const std::tm& tm1, const std::tm& tm2) ++{ ++ return !(tm1 == tm2); ++} ++ ++// eol definition ++#if !defined (SPDLOG_EOL) ++#ifdef _WIN32 ++#define SPDLOG_EOL "\r\n" ++#else ++#define SPDLOG_EOL "\n" ++#endif ++#endif ++ ++SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; ++SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; ++ ++inline void prevent_child_fd(FILE *f) ++{ ++#ifdef _WIN32 ++ auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); ++ if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) ++ throw spdlog_ex("SetHandleInformation failed", errno); ++#else ++ auto fd = fileno(f); ++ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) ++ throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); ++#endif ++} ++ ++ ++//fopen_s on non windows for writing ++inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) ++{ ++#ifdef _WIN32 ++#ifdef SPDLOG_WCHAR_FILENAMES ++ *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); ++#else ++ *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); ++#endif ++#else //unix ++ *fp = fopen((filename.c_str()), mode.c_str()); ++#endif ++ ++#ifdef SPDLOG_PREVENT_CHILD_FD ++ if (*fp != nullptr) ++ prevent_child_fd(*fp); ++#endif ++ return *fp == nullptr; ++} ++ ++ ++inline int remove(const filename_t &filename) ++{ ++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) ++ return _wremove(filename.c_str()); ++#else ++ return std::remove(filename.c_str()); ++#endif ++} ++ ++inline int rename(const filename_t& filename1, const filename_t& filename2) ++{ ++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) ++ return _wrename(filename1.c_str(), filename2.c_str()); ++#else ++ return std::rename(filename1.c_str(), filename2.c_str()); ++#endif ++} ++ ++ ++//Return if file exists ++inline bool file_exists(const filename_t& filename) ++{ ++#ifdef _WIN32 ++#ifdef SPDLOG_WCHAR_FILENAMES ++ auto attribs = GetFileAttributesW(filename.c_str()); ++#else ++ auto attribs = GetFileAttributesA(filename.c_str()); ++#endif ++ return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); ++#else //common linux/unix all have the stat system call ++ struct stat buffer; ++ return (stat(filename.c_str(), &buffer) == 0); ++#endif ++} ++ ++ ++ ++ ++//Return file size according to open FILE* object ++inline size_t filesize(FILE *f) ++{ ++ if (f == nullptr) ++ throw spdlog_ex("Failed getting file size. fd is null"); ++#ifdef _WIN32 ++ int fd = _fileno(f); ++#if _WIN64 //64 bits ++ struct _stat64 st; ++ if (_fstat64(fd, &st) == 0) ++ return st.st_size; ++ ++#else //windows 32 bits ++ long ret = _filelength(fd); ++ if (ret >= 0) ++ return static_cast(ret); ++#endif ++ ++#else // unix ++ int fd = fileno(f); ++ //64 bits(but not in osx, where fstat64 is deprecated) ++#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) ++ struct stat64 st; ++ if (fstat64(fd, &st) == 0) ++ return static_cast(st.st_size); ++#else // unix 32 bits or osx ++ struct stat st; ++ if (fstat(fd, &st) == 0) ++ return static_cast(st.st_size); ++#endif ++#endif ++ throw spdlog_ex("Failed getting file size from fd", errno); ++} ++ ++ ++ ++ ++//Return utc offset in minutes or throw spdlog_ex on failure ++inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) ++{ ++ ++#ifdef _WIN32 ++#if _WIN32_WINNT < _WIN32_WINNT_WS08 ++ TIME_ZONE_INFORMATION tzinfo; ++ auto rv = GetTimeZoneInformation(&tzinfo); ++#else ++ DYNAMIC_TIME_ZONE_INFORMATION tzinfo; ++ auto rv = GetDynamicTimeZoneInformation(&tzinfo); ++#endif ++ if (rv == TIME_ZONE_ID_INVALID) ++ throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); ++ ++ int offset = -tzinfo.Bias; ++ if (tm.tm_isdst) ++ offset -= tzinfo.DaylightBias; ++ else ++ offset -= tzinfo.StandardBias; ++ return offset; ++#else ++ ++#if defined(sun) || defined(__sun) ++ // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris ++ struct helper ++ { ++ static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) ++ { ++ int local_year = localtm.tm_year + (1900 - 1); ++ int gmt_year = gmtm.tm_year + (1900 - 1); ++ ++ long int days = ( ++ // difference in day of year ++ localtm.tm_yday - gmtm.tm_yday ++ ++ // + intervening leap days ++ + ((local_year >> 2) - (gmt_year >> 2)) ++ - (local_year / 100 - gmt_year / 100) ++ + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) ++ ++ // + difference in years * 365 */ ++ + (long int)(local_year - gmt_year) * 365 ++ ); ++ ++ long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); ++ long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); ++ long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); ++ ++ return secs; ++ } ++ }; ++ ++ long int offset_seconds = helper::calculate_gmt_offset(tm); ++#else ++ long int offset_seconds = tm.tm_gmtoff; ++#endif ++ ++ return static_cast(offset_seconds / 60); ++#endif ++} ++ ++//Return current thread id as size_t ++//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) ++inline size_t _thread_id() ++{ ++#ifdef _WIN32 ++ return static_cast(::GetCurrentThreadId()); ++#elif __linux__ ++# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) ++# define SYS_gettid __NR_gettid ++# endif ++ return static_cast(syscall(SYS_gettid)); ++#elif __FreeBSD__ ++ long tid; ++ thr_self(&tid); ++ return static_cast(tid); ++#elif __APPLE__ ++ uint64_t tid; ++ pthread_threadid_np(nullptr, &tid); ++ return static_cast(tid); ++#else //Default to standard C++11 (other Unix) ++ return static_cast(std::hash()(std::this_thread::get_id())); ++#endif ++} ++ ++//Return current thread id as size_t (from thread local storage) ++inline size_t thread_id() ++{ ++#if !defined(LIZARDFS_HAVE_THREAD_LOCAL) || defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local) ++ return _thread_id(); ++#else ++ static thread_local const size_t tid = _thread_id(); ++ return tid; ++#endif ++} ++ ++ ++ ++ ++// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) ++#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) ++#define SPDLOG_FILENAME_T(s) L ## s ++inline std::string filename_to_str(const filename_t& filename) ++{ ++ std::wstring_convert, wchar_t> c; ++ return c.to_bytes(filename); ++} ++#else ++#define SPDLOG_FILENAME_T(s) s ++inline std::string filename_to_str(const filename_t& filename) ++{ ++ return filename; ++} ++#endif ++ ++inline std::string errno_to_string(char[256], char* res) ++{ ++ return std::string(res); ++} ++ ++inline std::string errno_to_string(char buf[256], int res) ++{ ++ if (res == 0) ++ { ++ return std::string(buf); ++ } ++ else ++ { ++ return "Unknown error"; ++ } ++} ++ ++// Return errno string (thread safe) ++inline std::string errno_str(int err_num) ++{ ++ char buf[256]; ++ SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); ++ ++#ifdef _WIN32 ++ if (strerror_s(buf, buf_size, err_num) == 0) ++ return std::string(buf); ++ else ++ return "Unknown error"; ++ ++#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ ++ ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version ++ ++ if (strerror_r(err_num, buf, buf_size) == 0) ++ return std::string(buf); ++ else ++ return "Unknown error"; ++ ++#else // gnu version (might not use the given buf, so its retval pointer must be used) ++ auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type ++ return errno_to_string(buf, err); // use overloading to select correct stringify function ++#endif ++} ++ ++inline int pid() ++{ ++ ++#ifdef _WIN32 ++ return ::_getpid(); ++#else ++ return static_cast(::getpid()); ++#endif ++ ++} ++ ++ ++// Detrmine if the terminal supports colors ++// Source: https://github.com/agauniyal/rang/ ++inline bool is_color_terminal() ++{ ++#ifdef _WIN32 ++ return true; ++#else ++ static constexpr const char* Terms[] = ++ { ++ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", ++ "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm" ++ }; ++ ++ const char *env_p = std::getenv("TERM"); ++ if (env_p == nullptr) ++ { ++ return false; ++ } ++ ++ static const bool result = std::any_of( ++ std::begin(Terms), std::end(Terms), [&](const char* term) ++ { ++ return std::strstr(env_p, term) != nullptr; ++ }); ++ return result; ++#endif ++} ++ ++ ++// Detrmine if the terminal attached ++// Source: https://github.com/agauniyal/rang/ ++inline bool in_terminal(FILE* file) ++{ ++ ++#ifdef _WIN32 ++ return _isatty(_fileno(file)) ? true : false; ++#else ++ return isatty(fileno(file)) ? true : false; ++#endif ++} ++} //os ++} //details ++} //spdlog +diff --git a/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h b/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h +new file mode 100644 +index 00000000..bd2ebc71 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/pattern_formatter_impl.h +@@ -0,0 +1,665 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/formatter.h" ++#include "spdlog/details/log_msg.h" ++#include "spdlog/details/os.h" ++#include "spdlog/fmt/fmt.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++class flag_formatter ++{ ++public: ++ virtual ~flag_formatter() ++ {} ++ virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; ++}; ++ ++/////////////////////////////////////////////////////////////////////// ++// name & level pattern appenders ++/////////////////////////////////////////////////////////////////////// ++namespace ++{ ++class name_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << *msg.logger_name; ++ } ++}; ++} ++ ++// log level appender ++class level_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << level::to_str(msg.level); ++ } ++}; ++ ++// short log level appender ++class short_level_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << level::to_short_str(msg.level); ++ } ++}; ++ ++/////////////////////////////////////////////////////////////////////// ++// Date time pattern appenders ++/////////////////////////////////////////////////////////////////////// ++ ++static const char* ampm(const tm& t) ++{ ++ return t.tm_hour >= 12 ? "PM" : "AM"; ++} ++ ++static int to12h(const tm& t) ++{ ++ return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; ++} ++ ++//Abbreviated weekday name ++static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; ++class a_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << days[tm_time.tm_wday]; ++ } ++}; ++ ++//Full weekday name ++static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; ++class A_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << full_days[tm_time.tm_wday]; ++ } ++}; ++ ++//Abbreviated month ++static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; ++class b_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << months[tm_time.tm_mon]; ++ } ++}; ++ ++//Full month name ++static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; ++class B_formatter:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << full_months[tm_time.tm_mon]; ++ } ++}; ++ ++ ++//write 2 ints seperated by sep with padding of 2 ++static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep) ++{ ++ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0'); ++ return w; ++} ++ ++//write 3 ints seperated by sep with padding of 2 ++static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep) ++{ ++ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0'); ++ return w; ++} ++ ++ ++//Date and time representation (Thu Aug 23 15:35:46 2014) ++class c_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; ++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; ++ } ++}; ++ ++ ++// year - 2 digit ++class C_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0'); ++ } ++}; ++ ++ ++ ++// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 ++class D_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/'); ++ } ++}; ++ ++ ++// year - 4 digit ++class Y_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << tm_time.tm_year + 1900; ++ } ++}; ++ ++// month 1-12 ++class m_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0'); ++ } ++}; ++ ++// day of month 1-31 ++class d_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0'); ++ } ++}; ++ ++// hours in 24 format 0-23 ++class H_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0'); ++ } ++}; ++ ++// hours in 12 format 1-12 ++class I_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); ++ } ++}; ++ ++// minutes 0-59 ++class M_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_min, 2, '0'); ++ } ++}; ++ ++// seconds 0-59 ++class S_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0'); ++ } ++}; ++ ++// milliseconds ++class e_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ auto duration = msg.time.time_since_epoch(); ++ auto millis = std::chrono::duration_cast(duration).count() % 1000; ++ msg.formatted << fmt::pad(static_cast(millis), 3, '0'); ++ } ++}; ++ ++// microseconds ++class f_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ auto duration = msg.time.time_since_epoch(); ++ auto micros = std::chrono::duration_cast(duration).count() % 1000000; ++ msg.formatted << fmt::pad(static_cast(micros), 6, '0'); ++ } ++}; ++ ++// nanoseconds ++class F_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ auto duration = msg.time.time_since_epoch(); ++ auto ns = std::chrono::duration_cast(duration).count() % 1000000000; ++ msg.formatted << fmt::pad(static_cast(ns), 9, '0'); ++ } ++}; ++ ++// AM/PM ++class p_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ msg.formatted << ampm(tm_time); ++ } ++}; ++ ++ ++// 12 hour clock 02:55:02 pm ++class r_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time); ++ } ++}; ++ ++// 24-hour HH:MM time, equivalent to %H:%M ++class R_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':'); ++ } ++}; ++ ++// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S ++class T_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':'); ++ } ++}; ++ ++// ISO 8601 offset from UTC in timezone (+-HH:MM) ++class z_formatter SPDLOG_FINAL:public flag_formatter ++{ ++public: ++ const std::chrono::seconds cache_refresh = std::chrono::seconds(5); ++ ++ z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0) ++ {} ++ z_formatter(const z_formatter&) = delete; ++ z_formatter& operator=(const z_formatter&) = delete; ++ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++#ifdef _WIN32 ++ int total_minutes = get_cached_offset(msg, tm_time); ++#else ++ // No need to chache under gcc, ++ // it is very fast (already stored in tm.tm_gmtoff) ++ int total_minutes = os::utc_minutes_offset(tm_time); ++#endif ++ bool is_negative = total_minutes < 0; ++ char sign; ++ if (is_negative) ++ { ++ total_minutes = -total_minutes; ++ sign = '-'; ++ } ++ else ++ { ++ sign = '+'; ++ } ++ ++ int h = total_minutes / 60; ++ int m = total_minutes % 60; ++ msg.formatted << sign; ++ pad_n_join(msg.formatted, h, m, ':'); ++ } ++private: ++ log_clock::time_point _last_update; ++ int _offset_minutes; ++ std::mutex _mutex; ++ ++ int get_cached_offset(const log_msg& msg, const std::tm& tm_time) ++ { ++ using namespace std::chrono; ++ std::lock_guard l(_mutex); ++ if (msg.time - _last_update >= cache_refresh) ++ { ++ _offset_minutes = os::utc_minutes_offset(tm_time); ++ _last_update = msg.time; ++ } ++ return _offset_minutes; ++ } ++}; ++ ++ ++ ++// Thread id ++class t_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << msg.thread_id; ++ } ++}; ++ ++// Current pid ++class pid_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << details::os::pid(); ++ } ++}; ++ ++ ++class v_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); ++ } ++}; ++ ++class ch_formatter SPDLOG_FINAL:public flag_formatter ++{ ++public: ++ explicit ch_formatter(char ch): _ch(ch) ++ {} ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << _ch; ++ } ++private: ++ char _ch; ++}; ++ ++ ++//aggregate user chars to display as is ++class aggregate_formatter SPDLOG_FINAL:public flag_formatter ++{ ++public: ++ aggregate_formatter() ++ {} ++ void add_ch(char ch) ++ { ++ _str += ch; ++ } ++ void format(details::log_msg& msg, const std::tm&) override ++ { ++ msg.formatted << _str; ++ } ++private: ++ std::string _str; ++}; ++ ++// Full info formatter ++// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v ++class full_formatter SPDLOG_FINAL:public flag_formatter ++{ ++ void format(details::log_msg& msg, const std::tm& tm_time) override ++ { ++#ifndef SPDLOG_NO_DATETIME ++ auto duration = msg.time.time_since_epoch(); ++ auto millis = std::chrono::duration_cast(duration).count() % 1000; ++ ++ /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads), ++ msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ", ++ tm_time.tm_year + 1900, ++ tm_time.tm_mon + 1, ++ tm_time.tm_mday, ++ tm_time.tm_hour, ++ tm_time.tm_min, ++ tm_time.tm_sec, ++ static_cast(millis), ++ msg.logger_name, ++ level::to_str(msg.level), ++ msg.raw.str());*/ ++ ++ ++ // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads) ++ msg.formatted << '[' << static_cast(tm_time.tm_year + 1900) << '-' ++ << fmt::pad(static_cast(tm_time.tm_mon + 1), 2, '0') << '-' ++ << fmt::pad(static_cast(tm_time.tm_mday), 2, '0') << ' ' ++ << fmt::pad(static_cast(tm_time.tm_hour), 2, '0') << ':' ++ << fmt::pad(static_cast(tm_time.tm_min), 2, '0') << ':' ++ << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' ++ << fmt::pad(static_cast(millis), 3, '0') << "] "; ++ ++ //no datetime needed ++#else ++ (void)tm_time; ++#endif ++ ++#ifndef SPDLOG_NO_NAME ++ msg.formatted << '[' << *msg.logger_name << "] "; ++#endif ++ ++ msg.formatted << '[' << level::to_str(msg.level) << "] "; ++ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); ++ } ++}; ++ ++ ++ ++} ++} ++/////////////////////////////////////////////////////////////////////////////// ++// pattern_formatter inline impl ++/////////////////////////////////////////////////////////////////////////////// ++inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time) ++ : _pattern_time(pattern_time) ++{ ++ compile_pattern(pattern); ++} ++ ++inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern) ++{ ++ auto end = pattern.end(); ++ std::unique_ptr user_chars; ++ for (auto it = pattern.begin(); it != end; ++it) ++ { ++ if (*it == '%') ++ { ++ if (user_chars) //append user chars found so far ++ _formatters.push_back(std::move(user_chars)); ++ ++ if (++it != end) ++ handle_flag(*it); ++ else ++ break; ++ } ++ else // chars not following the % sign should be displayed as is ++ { ++ if (!user_chars) ++ user_chars = std::unique_ptr(new details::aggregate_formatter()); ++ user_chars->add_ch(*it); ++ } ++ } ++ if (user_chars) //append raw chars found so far ++ { ++ _formatters.push_back(std::move(user_chars)); ++ } ++ ++} ++inline void spdlog::pattern_formatter::handle_flag(char flag) ++{ ++ switch (flag) ++ { ++ // logger name ++ case 'n': ++ _formatters.push_back(std::unique_ptr(new details::name_formatter())); ++ break; ++ ++ case 'l': ++ _formatters.push_back(std::unique_ptr(new details::level_formatter())); ++ break; ++ ++ case 'L': ++ _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); ++ break; ++ ++ case('t'): ++ _formatters.push_back(std::unique_ptr(new details::t_formatter())); ++ break; ++ ++ case('v'): ++ _formatters.push_back(std::unique_ptr(new details::v_formatter())); ++ break; ++ ++ case('a'): ++ _formatters.push_back(std::unique_ptr(new details::a_formatter())); ++ break; ++ ++ case('A'): ++ _formatters.push_back(std::unique_ptr(new details::A_formatter())); ++ break; ++ ++ case('b'): ++ case('h'): ++ _formatters.push_back(std::unique_ptr(new details::b_formatter())); ++ break; ++ ++ case('B'): ++ _formatters.push_back(std::unique_ptr(new details::B_formatter())); ++ break; ++ case('c'): ++ _formatters.push_back(std::unique_ptr(new details::c_formatter())); ++ break; ++ ++ case('C'): ++ _formatters.push_back(std::unique_ptr(new details::C_formatter())); ++ break; ++ ++ case('Y'): ++ _formatters.push_back(std::unique_ptr(new details::Y_formatter())); ++ break; ++ ++ case('D'): ++ case('x'): ++ ++ _formatters.push_back(std::unique_ptr(new details::D_formatter())); ++ break; ++ ++ case('m'): ++ _formatters.push_back(std::unique_ptr(new details::m_formatter())); ++ break; ++ ++ case('d'): ++ _formatters.push_back(std::unique_ptr(new details::d_formatter())); ++ break; ++ ++ case('H'): ++ _formatters.push_back(std::unique_ptr(new details::H_formatter())); ++ break; ++ ++ case('I'): ++ _formatters.push_back(std::unique_ptr(new details::I_formatter())); ++ break; ++ ++ case('M'): ++ _formatters.push_back(std::unique_ptr(new details::M_formatter())); ++ break; ++ ++ case('S'): ++ _formatters.push_back(std::unique_ptr(new details::S_formatter())); ++ break; ++ ++ case('e'): ++ _formatters.push_back(std::unique_ptr(new details::e_formatter())); ++ break; ++ ++ case('f'): ++ _formatters.push_back(std::unique_ptr(new details::f_formatter())); ++ break; ++ case('F'): ++ _formatters.push_back(std::unique_ptr(new details::F_formatter())); ++ break; ++ ++ case('p'): ++ _formatters.push_back(std::unique_ptr(new details::p_formatter())); ++ break; ++ ++ case('r'): ++ _formatters.push_back(std::unique_ptr(new details::r_formatter())); ++ break; ++ ++ case('R'): ++ _formatters.push_back(std::unique_ptr(new details::R_formatter())); ++ break; ++ ++ case('T'): ++ case('X'): ++ _formatters.push_back(std::unique_ptr(new details::T_formatter())); ++ break; ++ ++ case('z'): ++ _formatters.push_back(std::unique_ptr(new details::z_formatter())); ++ break; ++ ++ case ('+'): ++ _formatters.push_back(std::unique_ptr(new details::full_formatter())); ++ break; ++ ++ case ('P'): ++ _formatters.push_back(std::unique_ptr(new details::pid_formatter())); ++ break; ++ ++#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) ++ case ('i'): ++ _formatters.push_back(std::unique_ptr(new details::i_formatter())); ++ break; ++#endif ++ ++ default: //Unknown flag appears as is ++ _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); ++ _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); ++ break; ++ } ++} ++ ++inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg) ++{ ++ if (_pattern_time == pattern_time_type::local) ++ return details::os::localtime(log_clock::to_time_t(msg.time)); ++ else ++ return details::os::gmtime(log_clock::to_time_t(msg.time)); ++} ++ ++inline void spdlog::pattern_formatter::format(details::log_msg& msg) ++{ ++ ++#ifndef SPDLOG_NO_DATETIME ++ auto tm_time = get_time(msg); ++#else ++ std::tm tm_time; ++#endif ++ for (auto &f : _formatters) ++ { ++ f->format(msg, tm_time); ++ } ++ //write eol ++ msg.formatted.write(details::os::eol, details::os::eol_size); ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/registry.h b/external/spdlog-0.14.0/include/spdlog/details/registry.h +new file mode 100644 +index 00000000..b518990a +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/registry.h +@@ -0,0 +1,214 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// Loggers registy of unique name->logger pointer ++// An attempt to create a logger with an already existing name will be ignored ++// If user requests a non existing logger, nullptr will be returned ++// This class is thread safe ++ ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/logger.h" ++#include "spdlog/async_logger.h" ++#include "spdlog/common.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++template class registry_t ++{ ++public: ++ ++ void register_logger(std::shared_ptr logger) ++ { ++ std::lock_guard lock(_mutex); ++ auto logger_name = logger->name(); ++ throw_if_exists(logger_name); ++ _loggers[logger_name] = logger; ++ } ++ ++ ++ std::shared_ptr get(const std::string& logger_name) ++ { ++ std::lock_guard lock(_mutex); ++ auto found = _loggers.find(logger_name); ++ return found == _loggers.end() ? nullptr : found->second; ++ } ++ ++ template ++ std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) ++ { ++ std::lock_guard lock(_mutex); ++ throw_if_exists(logger_name); ++ std::shared_ptr new_logger; ++ if (_async_mode) ++ new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); ++ else ++ new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); ++ ++ if (_formatter) ++ new_logger->set_formatter(_formatter); ++ ++ if (_err_handler) ++ new_logger->set_error_handler(_err_handler); ++ ++ new_logger->set_level(_level); ++ ++ ++ //Add to registry ++ _loggers[logger_name] = new_logger; ++ return new_logger; ++ } ++ ++ template ++ std::shared_ptr create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb, const It& sinks_begin, const It& sinks_end) ++ { ++ std::lock_guard lock(_mutex); ++ throw_if_exists(logger_name); ++ auto new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); ++ ++ if (_formatter) ++ new_logger->set_formatter(_formatter); ++ ++ if (_err_handler) ++ new_logger->set_error_handler(_err_handler); ++ ++ new_logger->set_level(_level); ++ ++ //Add to registry ++ _loggers[logger_name] = new_logger; ++ return new_logger; ++ } ++ ++ void apply_all(std::function)> fun) ++ { ++ std::lock_guard lock(_mutex); ++ for (auto &l : _loggers) ++ fun(l.second); ++ } ++ ++ void drop(const std::string& logger_name) ++ { ++ std::lock_guard lock(_mutex); ++ _loggers.erase(logger_name); ++ } ++ ++ void drop_all() ++ { ++ std::lock_guard lock(_mutex); ++ _loggers.clear(); ++ } ++ std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks) ++ { ++ return create(logger_name, sinks.begin(), sinks.end()); ++ } ++ ++ std::shared_ptr create(const std::string& logger_name, sink_ptr sink) ++ { ++ return create(logger_name, { sink }); ++ } ++ ++ std::shared_ptr create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb, sinks_init_list sinks) ++ { ++ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end()); ++ } ++ ++ std::shared_ptr create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb, sink_ptr sink) ++ { ++ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink }); ++ } ++ ++ void formatter(formatter_ptr f) ++ { ++ std::lock_guard lock(_mutex); ++ _formatter = f; ++ for (auto& l : _loggers) ++ l.second->set_formatter(_formatter); ++ } ++ ++ void set_pattern(const std::string& pattern) ++ { ++ std::lock_guard lock(_mutex); ++ _formatter = std::make_shared(pattern); ++ for (auto& l : _loggers) ++ l.second->set_formatter(_formatter); ++ } ++ ++ void set_level(level::level_enum log_level) ++ { ++ std::lock_guard lock(_mutex); ++ for (auto& l : _loggers) ++ l.second->set_level(log_level); ++ _level = log_level; ++ } ++ ++ void set_error_handler(log_err_handler handler) ++ { ++ for (auto& l : _loggers) ++ l.second->set_error_handler(handler); ++ _err_handler = handler; ++ } ++ ++ void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) ++ { ++ std::lock_guard lock(_mutex); ++ _async_mode = true; ++ _async_q_size = q_size; ++ _overflow_policy = overflow_policy; ++ _worker_warmup_cb = worker_warmup_cb; ++ _flush_interval_ms = flush_interval_ms; ++ _worker_teardown_cb = worker_teardown_cb; ++ } ++ ++ void set_sync_mode() ++ { ++ std::lock_guard lock(_mutex); ++ _async_mode = false; ++ } ++ ++ static registry_t& instance() ++ { ++ static registry_t s_instance; ++ return s_instance; ++ } ++ ++private: ++ registry_t() {} ++ registry_t(const registry_t&) = delete; ++ registry_t& operator=(const registry_t&) = delete; ++ ++ void throw_if_exists(const std::string &logger_name) ++ { ++ if (_loggers.find(logger_name) != _loggers.end()) ++ throw spdlog_ex("logger with name '" + logger_name + "' already exists"); ++ } ++ Mutex _mutex; ++ std::unordered_map > _loggers; ++ formatter_ptr _formatter; ++ level::level_enum _level = level::info; ++ log_err_handler _err_handler; ++ bool _async_mode = false; ++ size_t _async_q_size = 0; ++ async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; ++ std::function _worker_warmup_cb = nullptr; ++ std::chrono::milliseconds _flush_interval_ms; ++ std::function _worker_teardown_cb = nullptr; ++}; ++#ifdef SPDLOG_NO_REGISTRY_MUTEX ++typedef registry_t registry; ++#else ++typedef registry_t registry; ++#endif ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h b/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h +new file mode 100644 +index 00000000..7fe9ab40 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/details/spdlog_impl.h +@@ -0,0 +1,263 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// ++// Global registry functions ++// ++#include "spdlog/spdlog.h" ++#include "spdlog/details/registry.h" ++#include "spdlog/sinks/file_sinks.h" ++#include "spdlog/sinks/stdout_sinks.h" ++#ifdef SPDLOG_ENABLE_SYSLOG ++#include "spdlog/sinks/syslog_sink.h" ++#endif ++ ++#ifdef _WIN32 ++#include "spdlog/sinks/wincolor_sink.h" ++#else ++#include "spdlog/sinks/ansicolor_sink.h" ++#endif ++ ++ ++#ifdef __ANDROID__ ++#include "spdlog/sinks/android_sink.h" ++#endif ++ ++#include ++#include ++#include ++#include ++ ++inline void spdlog::register_logger(std::shared_ptr logger) ++{ ++ return details::registry::instance().register_logger(logger); ++} ++ ++inline std::shared_ptr spdlog::get(const std::string& name) ++{ ++ return details::registry::instance().get(name); ++} ++ ++inline void spdlog::drop(const std::string &name) ++{ ++ details::registry::instance().drop(name); ++} ++ ++// Create multi/single threaded simple file logger ++inline std::shared_ptr spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate) ++{ ++ return create(logger_name, filename, truncate); ++} ++ ++inline std::shared_ptr spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate) ++{ ++ return create(logger_name, filename, truncate); ++} ++ ++// Create multi/single threaded rotating file logger ++inline std::shared_ptr spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) ++{ ++ return create(logger_name, filename, max_file_size, max_files); ++} ++ ++inline std::shared_ptr spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) ++{ ++ return create(logger_name, filename, max_file_size, max_files); ++} ++ ++// Create file logger which creates new file at midnight): ++inline std::shared_ptr spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute) ++{ ++ return create(logger_name, filename, hour, minute); ++} ++ ++inline std::shared_ptr spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute) ++{ ++ return create(logger_name, filename, hour, minute); ++} ++ ++ ++// ++// stdout/stderr loggers ++// ++inline std::shared_ptr spdlog::stdout_logger_mt(const std::string& logger_name) ++{ ++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); ++} ++ ++inline std::shared_ptr spdlog::stdout_logger_st(const std::string& logger_name) ++{ ++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); ++} ++ ++inline std::shared_ptr spdlog::stderr_logger_mt(const std::string& logger_name) ++{ ++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); ++} ++ ++inline std::shared_ptr spdlog::stderr_logger_st(const std::string& logger_name) ++{ ++ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); ++} ++ ++// ++// stdout/stderr color loggers ++// ++#ifdef _WIN32 ++inline std::shared_ptr spdlog::stdout_color_mt(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++inline std::shared_ptr spdlog::stdout_color_st(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++inline std::shared_ptr spdlog::stderr_color_mt(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++ ++inline std::shared_ptr spdlog::stderr_color_st(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++#else //ansi terminal colors ++ ++inline std::shared_ptr spdlog::stdout_color_mt(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++inline std::shared_ptr spdlog::stdout_color_st(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++inline std::shared_ptr spdlog::stderr_color_mt(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++ ++inline std::shared_ptr spdlog::stderr_color_st(const std::string& logger_name) ++{ ++ auto sink = std::make_shared(); ++ return spdlog::details::registry::instance().create(logger_name, sink); ++} ++#endif ++ ++#ifdef SPDLOG_ENABLE_SYSLOG ++// Create syslog logger ++inline std::shared_ptr spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option) ++{ ++ return create(logger_name, syslog_ident, syslog_option); ++} ++#endif ++ ++#ifdef __ANDROID__ ++inline std::shared_ptr spdlog::android_logger(const std::string& logger_name, const std::string& tag) ++{ ++ return create(logger_name, tag); ++} ++#endif ++ ++// Create and register a logger a single sink ++inline std::shared_ptr spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink) ++{ ++ return details::registry::instance().create(logger_name, sink); ++} ++ ++//Create logger with multiple sinks ++ ++inline std::shared_ptr spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks) ++{ ++ return details::registry::instance().create(logger_name, sinks); ++} ++ ++ ++template ++inline std::shared_ptr spdlog::create(const std::string& logger_name, Args... args) ++{ ++ sink_ptr sink = std::make_shared(args...); ++ return details::registry::instance().create(logger_name, { sink }); ++} ++ ++ ++template ++inline std::shared_ptr spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) ++{ ++ return details::registry::instance().create(logger_name, sinks_begin, sinks_end); ++} ++ ++// Create and register an async logger with a single sink ++inline std::shared_ptr spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) ++{ ++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink); ++} ++ ++// Create and register an async logger with multiple sinks ++inline std::shared_ptr spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb ) ++{ ++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks); ++} ++ ++template ++inline std::shared_ptr spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) ++{ ++ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end); ++} ++ ++inline void spdlog::set_formatter(spdlog::formatter_ptr f) ++{ ++ details::registry::instance().formatter(f); ++} ++ ++inline void spdlog::set_pattern(const std::string& format_string) ++{ ++ return details::registry::instance().set_pattern(format_string); ++} ++ ++inline void spdlog::set_level(level::level_enum log_level) ++{ ++ return details::registry::instance().set_level(log_level); ++} ++ ++inline void spdlog::set_error_handler(log_err_handler handler) ++{ ++ return details::registry::instance().set_error_handler(handler); ++} ++ ++ ++inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) ++{ ++ details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); ++} ++ ++inline void spdlog::set_sync_mode() ++{ ++ details::registry::instance().set_sync_mode(); ++} ++ ++inline void spdlog::apply_all(std::function)> fun) ++{ ++ details::registry::instance().apply_all(fun); ++} ++ ++inline void spdlog::drop_all() ++{ ++ details::registry::instance().drop_all(); ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc +new file mode 100644 +index 00000000..09d2ea9f +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.cc +@@ -0,0 +1,535 @@ ++/* ++ Formatting library for C++ ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ++ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "format.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include // for std::ptrdiff_t ++ ++#if defined(_WIN32) && defined(__MINGW32__) ++# include ++#endif ++ ++#if FMT_USE_WINDOWS_H ++# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) ++# define WIN32_LEAN_AND_MEAN ++# endif ++# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) ++# include ++# else ++# define NOMINMAX ++# include ++# undef NOMINMAX ++# endif ++#endif ++ ++#if FMT_EXCEPTIONS ++# define FMT_TRY try ++# define FMT_CATCH(x) catch (x) ++#else ++# define FMT_TRY if (true) ++# define FMT_CATCH(x) if (false) ++#endif ++ ++#ifdef _MSC_VER ++# pragma warning(push) ++# pragma warning(disable: 4127) // conditional expression is constant ++# pragma warning(disable: 4702) // unreachable code ++// Disable deprecation warning for strerror. The latter is not called but ++// MSVC fails to detect it. ++# pragma warning(disable: 4996) ++#endif ++ ++// Dummy implementations of strerror_r and strerror_s called if corresponding ++// system functions are not available. ++static inline fmt::internal::Null<> strerror_r(int, char *, ...) { ++ return fmt::internal::Null<>(); ++} ++static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { ++ return fmt::internal::Null<>(); ++} ++ ++namespace fmt { ++ ++FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} ++FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} ++FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} ++ ++namespace { ++ ++#ifndef _MSC_VER ++# define FMT_SNPRINTF snprintf ++#else // _MSC_VER ++inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { ++ va_list args; ++ va_start(args, format); ++ int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); ++ va_end(args); ++ return result; ++} ++# define FMT_SNPRINTF fmt_snprintf ++#endif // _MSC_VER ++ ++#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) ++# define FMT_SWPRINTF snwprintf ++#else ++# define FMT_SWPRINTF swprintf ++#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) ++ ++const char RESET_COLOR[] = "\x1b[0m"; ++ ++typedef void (*FormatFunc)(Writer &, int, StringRef); ++ ++// Portable thread-safe version of strerror. ++// Sets buffer to point to a string describing the error code. ++// This can be either a pointer to a string stored in buffer, ++// or a pointer to some static immutable string. ++// Returns one of the following values: ++// 0 - success ++// ERANGE - buffer is not large enough to store the error message ++// other - failure ++// Buffer should be at least of size 1. ++int safe_strerror( ++ int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { ++ FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); ++ ++ class StrError { ++ private: ++ int error_code_; ++ char *&buffer_; ++ std::size_t buffer_size_; ++ ++ // A noop assignment operator to avoid bogus warnings. ++ void operator=(const StrError &) {} ++ ++ // Handle the result of XSI-compliant version of strerror_r. ++ int handle(int result) { ++ // glibc versions before 2.13 return result in errno. ++ return result == -1 ? errno : result; ++ } ++ ++ // Handle the result of GNU-specific version of strerror_r. ++ int handle(char *message) { ++ // If the buffer is full then the message is probably truncated. ++ if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) ++ return ERANGE; ++ buffer_ = message; ++ return 0; ++ } ++ ++ // Handle the case when strerror_r is not available. ++ int handle(internal::Null<>) { ++ return fallback(strerror_s(buffer_, buffer_size_, error_code_)); ++ } ++ ++ // Fallback to strerror_s when strerror_r is not available. ++ int fallback(int result) { ++ // If the buffer is full then the message is probably truncated. ++ return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ++ ERANGE : result; ++ } ++ ++ // Fallback to strerror if strerror_r and strerror_s are not available. ++ int fallback(internal::Null<>) { ++ errno = 0; ++ buffer_ = strerror(error_code_); ++ return errno; ++ } ++ ++ public: ++ StrError(int err_code, char *&buf, std::size_t buf_size) ++ : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} ++ ++ int run() { ++ // Suppress a warning about unused strerror_r. ++ strerror_r(0, FMT_NULL, ""); ++ return handle(strerror_r(error_code_, buffer_, buffer_size_)); ++ } ++ }; ++ return StrError(error_code, buffer, buffer_size).run(); ++} ++ ++void format_error_code(Writer &out, int error_code, ++ StringRef message) FMT_NOEXCEPT { ++ // Report error code making sure that the output fits into ++ // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential ++ // bad_alloc. ++ out.clear(); ++ static const char SEP[] = ": "; ++ static const char ERROR_STR[] = "error "; ++ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. ++ std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; ++ typedef internal::IntTraits::MainType MainType; ++ MainType abs_value = static_cast(error_code); ++ if (internal::is_negative(error_code)) { ++ abs_value = 0 - abs_value; ++ ++error_code_size; ++ } ++ error_code_size += internal::count_digits(abs_value); ++ if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) ++ out << message << SEP; ++ out << ERROR_STR << error_code; ++ assert(out.size() <= internal::INLINE_BUFFER_SIZE); ++} ++ ++void report_error(FormatFunc func, int error_code, ++ StringRef message) FMT_NOEXCEPT { ++ MemoryWriter full_message; ++ func(full_message, error_code, message); ++ // Use Writer::data instead of Writer::c_str to avoid potential memory ++ // allocation. ++ std::fwrite(full_message.data(), full_message.size(), 1, stderr); ++ std::fputc('\n', stderr); ++} ++} // namespace ++ ++FMT_FUNC void SystemError::init( ++ int err_code, CStringRef format_str, ArgList args) { ++ error_code_ = err_code; ++ MemoryWriter w; ++ format_system_error(w, err_code, format(format_str, args)); ++ std::runtime_error &base = *this; ++ base = std::runtime_error(w.str()); ++} ++ ++template ++int internal::CharTraits::format_float( ++ char *buffer, std::size_t size, const char *format, ++ unsigned width, int precision, T value) { ++ if (width == 0) { ++ return precision < 0 ? ++ FMT_SNPRINTF(buffer, size, format, value) : ++ FMT_SNPRINTF(buffer, size, format, precision, value); ++ } ++ return precision < 0 ? ++ FMT_SNPRINTF(buffer, size, format, width, value) : ++ FMT_SNPRINTF(buffer, size, format, width, precision, value); ++} ++ ++template ++int internal::CharTraits::format_float( ++ wchar_t *buffer, std::size_t size, const wchar_t *format, ++ unsigned width, int precision, T value) { ++ if (width == 0) { ++ return precision < 0 ? ++ FMT_SWPRINTF(buffer, size, format, value) : ++ FMT_SWPRINTF(buffer, size, format, precision, value); ++ } ++ return precision < 0 ? ++ FMT_SWPRINTF(buffer, size, format, width, value) : ++ FMT_SWPRINTF(buffer, size, format, width, precision, value); ++} ++ ++template ++const char internal::BasicData::DIGITS[] = ++ "0001020304050607080910111213141516171819" ++ "2021222324252627282930313233343536373839" ++ "4041424344454647484950515253545556575859" ++ "6061626364656667686970717273747576777879" ++ "8081828384858687888990919293949596979899"; ++ ++#define FMT_POWERS_OF_10(factor) \ ++ factor * 10, \ ++ factor * 100, \ ++ factor * 1000, \ ++ factor * 10000, \ ++ factor * 100000, \ ++ factor * 1000000, \ ++ factor * 10000000, \ ++ factor * 100000000, \ ++ factor * 1000000000 ++ ++template ++const uint32_t internal::BasicData::POWERS_OF_10_32[] = { ++ 0, FMT_POWERS_OF_10(1) ++}; ++ ++template ++const uint64_t internal::BasicData::POWERS_OF_10_64[] = { ++ 0, ++ FMT_POWERS_OF_10(1), ++ FMT_POWERS_OF_10(ULongLong(1000000000)), ++ // Multiply several constants instead of using a single long long constant ++ // to avoid warnings about C++98 not supporting long long. ++ ULongLong(1000000000) * ULongLong(1000000000) * 10 ++}; ++ ++FMT_FUNC void internal::report_unknown_type(char code, const char *type) { ++ (void)type; ++ if (std::isprint(static_cast(code))) { ++ FMT_THROW(FormatError( ++ format("unknown format code '{}' for {}", code, type))); ++ } ++ FMT_THROW(FormatError( ++ format("unknown format code '\\x{:02x}' for {}", ++ static_cast(code), type))); ++} ++ ++#if FMT_USE_WINDOWS_H ++ ++FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { ++ static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; ++ if (s.size() > INT_MAX) ++ FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); ++ int s_size = static_cast(s.size()); ++ int length = MultiByteToWideChar( ++ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); ++ if (length == 0) ++ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); ++ buffer_.resize(length + 1); ++ length = MultiByteToWideChar( ++ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); ++ if (length == 0) ++ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); ++ buffer_[length] = 0; ++} ++ ++FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { ++ if (int error_code = convert(s)) { ++ FMT_THROW(WindowsError(error_code, ++ "cannot convert string from UTF-16 to UTF-8")); ++ } ++} ++ ++FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { ++ if (s.size() > INT_MAX) ++ return ERROR_INVALID_PARAMETER; ++ int s_size = static_cast(s.size()); ++ int length = WideCharToMultiByte( ++ CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); ++ if (length == 0) ++ return GetLastError(); ++ buffer_.resize(length + 1); ++ length = WideCharToMultiByte( ++ CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); ++ if (length == 0) ++ return GetLastError(); ++ buffer_[length] = 0; ++ return 0; ++} ++ ++FMT_FUNC void WindowsError::init( ++ int err_code, CStringRef format_str, ArgList args) { ++ error_code_ = err_code; ++ MemoryWriter w; ++ internal::format_windows_error(w, err_code, format(format_str, args)); ++ std::runtime_error &base = *this; ++ base = std::runtime_error(w.str()); ++} ++ ++FMT_FUNC void internal::format_windows_error( ++ Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { ++ FMT_TRY { ++ MemoryBuffer buffer; ++ buffer.resize(INLINE_BUFFER_SIZE); ++ for (;;) { ++ wchar_t *system_message = &buffer[0]; ++ int result = FormatMessageW( ++ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, ++ FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ++ system_message, static_cast(buffer.size()), FMT_NULL); ++ if (result != 0) { ++ UTF16ToUTF8 utf8_message; ++ if (utf8_message.convert(system_message) == ERROR_SUCCESS) { ++ out << message << ": " << utf8_message; ++ return; ++ } ++ break; ++ } ++ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) ++ break; // Can't get error message, report error code instead. ++ buffer.resize(buffer.size() * 2); ++ } ++ } FMT_CATCH(...) {} ++ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. ++} ++ ++#endif // FMT_USE_WINDOWS_H ++ ++FMT_FUNC void format_system_error( ++ Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { ++ FMT_TRY { ++ internal::MemoryBuffer buffer; ++ buffer.resize(internal::INLINE_BUFFER_SIZE); ++ for (;;) { ++ char *system_message = &buffer[0]; ++ int result = safe_strerror(error_code, system_message, buffer.size()); ++ if (result == 0) { ++ out << message << ": " << system_message; ++ return; ++ } ++ if (result != ERANGE) ++ break; // Can't get error message, report error code instead. ++ buffer.resize(buffer.size() * 2); ++ } ++ } FMT_CATCH(...) {} ++ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. ++} ++ ++template ++void internal::ArgMap::init(const ArgList &args) { ++ if (!map_.empty()) ++ return; ++ typedef internal::NamedArg NamedArg; ++ const NamedArg *named_arg = FMT_NULL; ++ bool use_values = ++ args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; ++ if (use_values) { ++ for (unsigned i = 0;/*nothing*/; ++i) { ++ internal::Arg::Type arg_type = args.type(i); ++ switch (arg_type) { ++ case internal::Arg::NONE: ++ return; ++ case internal::Arg::NAMED_ARG: ++ named_arg = static_cast(args.values_[i].pointer); ++ map_.push_back(Pair(named_arg->name, *named_arg)); ++ break; ++ default: ++ /*nothing*/; ++ } ++ } ++ return; ++ } ++ for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { ++ internal::Arg::Type arg_type = args.type(i); ++ if (arg_type == internal::Arg::NAMED_ARG) { ++ named_arg = static_cast(args.args_[i].pointer); ++ map_.push_back(Pair(named_arg->name, *named_arg)); ++ } ++ } ++ for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { ++ switch (args.args_[i].type) { ++ case internal::Arg::NONE: ++ return; ++ case internal::Arg::NAMED_ARG: ++ named_arg = static_cast(args.args_[i].pointer); ++ map_.push_back(Pair(named_arg->name, *named_arg)); ++ break; ++ default: ++ /*nothing*/; ++ } ++ } ++} ++ ++template ++void internal::FixedBuffer::grow(std::size_t) { ++ FMT_THROW(std::runtime_error("buffer overflow")); ++} ++ ++FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg( ++ unsigned arg_index, const char *&error) { ++ internal::Arg arg = args_[arg_index]; ++ switch (arg.type) { ++ case internal::Arg::NONE: ++ error = "argument index out of range"; ++ break; ++ case internal::Arg::NAMED_ARG: ++ arg = *static_cast(arg.pointer); ++ break; ++ default: ++ /*nothing*/; ++ } ++ return arg; ++} ++ ++FMT_FUNC void report_system_error( ++ int error_code, fmt::StringRef message) FMT_NOEXCEPT { ++ // 'fmt::' is for bcc32. ++ report_error(format_system_error, error_code, message); ++} ++ ++#if FMT_USE_WINDOWS_H ++FMT_FUNC void report_windows_error( ++ int error_code, fmt::StringRef message) FMT_NOEXCEPT { ++ // 'fmt::' is for bcc32. ++ report_error(internal::format_windows_error, error_code, message); ++} ++#endif ++ ++FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { ++ MemoryWriter w; ++ w.write(format_str, args); ++ std::fwrite(w.data(), 1, w.size(), f); ++} ++ ++FMT_FUNC void print(CStringRef format_str, ArgList args) { ++ print(stdout, format_str, args); ++} ++ ++FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { ++ char escape[] = "\x1b[30m"; ++ escape[3] = static_cast('0' + c); ++ std::fputs(escape, stdout); ++ print(format, args); ++ std::fputs(RESET_COLOR, stdout); ++} ++ ++#ifndef FMT_HEADER_ONLY ++ ++template struct internal::BasicData; ++ ++// Explicit instantiations for char. ++ ++template void internal::FixedBuffer::grow(std::size_t); ++ ++template void internal::ArgMap::init(const ArgList &args); ++ ++template FMT_API int internal::CharTraits::format_float( ++ char *buffer, std::size_t size, const char *format, ++ unsigned width, int precision, double value); ++ ++template FMT_API int internal::CharTraits::format_float( ++ char *buffer, std::size_t size, const char *format, ++ unsigned width, int precision, long double value); ++ ++// Explicit instantiations for wchar_t. ++ ++template void internal::FixedBuffer::grow(std::size_t); ++ ++template void internal::ArgMap::init(const ArgList &args); ++ ++template FMT_API int internal::CharTraits::format_float( ++ wchar_t *buffer, std::size_t size, const wchar_t *format, ++ unsigned width, int precision, double value); ++ ++template FMT_API int internal::CharTraits::format_float( ++ wchar_t *buffer, std::size_t size, const wchar_t *format, ++ unsigned width, int precision, long double value); ++ ++#endif // FMT_HEADER_ONLY ++ ++} // namespace fmt ++ ++#ifdef _MSC_VER ++# pragma warning(pop) ++#endif +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h +new file mode 100644 +index 00000000..6ee9d2a2 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/format.h +@@ -0,0 +1,4012 @@ ++/* ++ Formatting library for C++ ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ++ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef FMT_FORMAT_H_ ++#define FMT_FORMAT_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include // for std::pair ++ ++// The fmt library version in the form major * 10000 + minor * 100 + patch. ++#define FMT_VERSION 40000 ++ ++#ifdef _SECURE_SCL ++# define FMT_SECURE_SCL _SECURE_SCL ++#else ++# define FMT_SECURE_SCL 0 ++#endif ++ ++#if FMT_SECURE_SCL ++# include ++#endif ++ ++#ifdef _MSC_VER ++# define FMT_MSC_VER _MSC_VER ++#else ++# define FMT_MSC_VER 0 ++#endif ++ ++#if FMT_MSC_VER && FMT_MSC_VER <= 1500 ++typedef unsigned __int32 uint32_t; ++typedef unsigned __int64 uint64_t; ++typedef __int64 intmax_t; ++#else ++#include ++#endif ++ ++#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) ++# ifdef FMT_EXPORT ++# define FMT_API __declspec(dllexport) ++# elif defined(FMT_SHARED) ++# define FMT_API __declspec(dllimport) ++# endif ++#endif ++#ifndef FMT_API ++# define FMT_API ++#endif ++ ++#ifdef __GNUC__ ++# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) ++# define FMT_GCC_EXTENSION __extension__ ++# if FMT_GCC_VERSION >= 406 ++# pragma GCC diagnostic push ++// Disable the warning about "long long" which is sometimes reported even ++// when using __extension__. ++# pragma GCC diagnostic ignored "-Wlong-long" ++// Disable the warning about declaration shadowing because it affects too ++// many valid cases. ++# pragma GCC diagnostic ignored "-Wshadow" ++// Disable the warning about implicit conversions that may change the sign of ++// an integer; silencing it otherwise would require many explicit casts. ++# pragma GCC diagnostic ignored "-Wsign-conversion" ++# endif ++# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ ++# define FMT_HAS_GXX_CXX11 1 ++# endif ++#else ++# define FMT_GCC_EXTENSION ++#endif ++ ++#if defined(__INTEL_COMPILER) ++# define FMT_ICC_VERSION __INTEL_COMPILER ++#elif defined(__ICL) ++# define FMT_ICC_VERSION __ICL ++#endif ++ ++#if defined(__clang__) && !defined(FMT_ICC_VERSION) ++# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) ++# pragma clang diagnostic push ++# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" ++# pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++#ifdef __GNUC_LIBSTD__ ++# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) ++#endif ++ ++#ifdef __has_feature ++# define FMT_HAS_FEATURE(x) __has_feature(x) ++#else ++# define FMT_HAS_FEATURE(x) 0 ++#endif ++ ++#ifdef __has_builtin ++# define FMT_HAS_BUILTIN(x) __has_builtin(x) ++#else ++# define FMT_HAS_BUILTIN(x) 0 ++#endif ++ ++#ifdef __has_cpp_attribute ++# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) ++#else ++# define FMT_HAS_CPP_ATTRIBUTE(x) 0 ++#endif ++ ++#ifndef FMT_USE_VARIADIC_TEMPLATES ++// Variadic templates are available in GCC since version 4.4 ++// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ ++// since version 2013. ++# define FMT_USE_VARIADIC_TEMPLATES \ ++ (FMT_HAS_FEATURE(cxx_variadic_templates) || \ ++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) ++#endif ++ ++#ifndef FMT_USE_RVALUE_REFERENCES ++// Don't use rvalue references when compiling with clang and an old libstdc++ ++// as the latter doesn't provide std::move. ++# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 ++# define FMT_USE_RVALUE_REFERENCES 0 ++# else ++# define FMT_USE_RVALUE_REFERENCES \ ++ (FMT_HAS_FEATURE(cxx_rvalue_references) || \ ++ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) ++# endif ++#endif ++ ++// Check if exceptions are disabled. ++#if defined(__GNUC__) && !defined(__EXCEPTIONS) ++# define FMT_EXCEPTIONS 0 ++#endif ++#if FMT_MSC_VER && !_HAS_EXCEPTIONS ++# define FMT_EXCEPTIONS 0 ++#endif ++#ifndef FMT_EXCEPTIONS ++# define FMT_EXCEPTIONS 1 ++#endif ++ ++#ifndef FMT_THROW ++# if FMT_EXCEPTIONS ++# define FMT_THROW(x) throw x ++# else ++# define FMT_THROW(x) assert(false) ++# endif ++#endif ++ ++// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). ++#ifndef FMT_USE_NOEXCEPT ++# define FMT_USE_NOEXCEPT 0 ++#endif ++ ++#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ ++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ ++ FMT_MSC_VER >= 1900 ++# define FMT_DETECTED_NOEXCEPT noexcept ++#else ++# define FMT_DETECTED_NOEXCEPT throw() ++#endif ++ ++#ifndef FMT_NOEXCEPT ++# if FMT_EXCEPTIONS ++# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT ++# else ++# define FMT_NOEXCEPT ++# endif ++#endif ++ ++// This is needed because GCC still uses throw() in its headers when exceptions ++// are disabled. ++#if FMT_GCC_VERSION ++# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT ++#else ++# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT ++#endif ++ ++#ifndef FMT_OVERRIDE ++# if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \ ++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ ++ FMT_MSC_VER >= 1900 ++# define FMT_OVERRIDE override ++# else ++# define FMT_OVERRIDE ++# endif ++#endif ++ ++#ifndef FMT_NULL ++# if FMT_HAS_FEATURE(cxx_nullptr) || \ ++ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ ++ FMT_MSC_VER >= 1600 ++# define FMT_NULL nullptr ++# else ++# define FMT_NULL NULL ++# endif ++#endif ++ ++// A macro to disallow the copy constructor and operator= functions ++// This should be used in the private: declarations for a class ++#ifndef FMT_USE_DELETED_FUNCTIONS ++# define FMT_USE_DELETED_FUNCTIONS 0 ++#endif ++ ++#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ ++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 ++# define FMT_DELETED_OR_UNDEFINED = delete ++# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ ++ TypeName(const TypeName&) = delete; \ ++ TypeName& operator=(const TypeName&) = delete ++#else ++# define FMT_DELETED_OR_UNDEFINED ++# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ ++ TypeName(const TypeName&); \ ++ TypeName& operator=(const TypeName&) ++#endif ++ ++#ifndef FMT_USE_DEFAULTED_FUNCTIONS ++# define FMT_USE_DEFAULTED_FUNCTIONS 0 ++#endif ++ ++#ifndef FMT_DEFAULTED_COPY_CTOR ++# if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || \ ++ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 ++# define FMT_DEFAULTED_COPY_CTOR(TypeName) \ ++ TypeName(const TypeName&) = default; ++# else ++# define FMT_DEFAULTED_COPY_CTOR(TypeName) ++# endif ++#endif ++ ++#ifndef FMT_USE_USER_DEFINED_LITERALS ++// All compilers which support UDLs also support variadic templates. This ++// makes the fmt::literals implementation easier. However, an explicit check ++// for variadic templates is added here just in case. ++// For Intel's compiler both it and the system gcc/msc must support UDLs. ++# define FMT_USE_USER_DEFINED_LITERALS \ ++ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ ++ (FMT_HAS_FEATURE(cxx_user_literals) || \ ++ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ ++ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) ++#endif ++ ++#ifndef FMT_USE_EXTERN_TEMPLATES ++# define FMT_USE_EXTERN_TEMPLATES \ ++ (FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) ++#endif ++ ++#ifdef FMT_HEADER_ONLY ++// If header only do not use extern templates. ++# undef FMT_USE_EXTERN_TEMPLATES ++# define FMT_USE_EXTERN_TEMPLATES 0 ++#endif ++ ++#ifndef FMT_ASSERT ++# define FMT_ASSERT(condition, message) assert((condition) && message) ++#endif ++ ++// __builtin_clz is broken in clang with Microsoft CodeGen: ++// https://github.com/fmtlib/fmt/issues/519 ++#ifndef _MSC_VER ++# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) ++# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) ++# endif ++ ++# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) ++# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) ++# endif ++#endif ++ ++// Some compilers masquerade as both MSVC and GCC-likes or ++// otherwise support __builtin_clz and __builtin_clzll, so ++// only define FMT_BUILTIN_CLZ using the MSVC intrinsics ++// if the clz and clzll builtins are not available. ++#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) ++# include // _BitScanReverse, _BitScanReverse64 ++ ++namespace fmt { ++namespace internal { ++# pragma intrinsic(_BitScanReverse) ++inline uint32_t clz(uint32_t x) { ++ unsigned long r = 0; ++ _BitScanReverse(&r, x); ++ ++ assert(x != 0); ++ // Static analysis complains about using uninitialized data ++ // "r", but the only way that can happen is if "x" is 0, ++ // which the callers guarantee to not happen. ++# pragma warning(suppress: 6102) ++ return 31 - r; ++} ++# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) ++ ++# ifdef _WIN64 ++# pragma intrinsic(_BitScanReverse64) ++# endif ++ ++inline uint32_t clzll(uint64_t x) { ++ unsigned long r = 0; ++# ifdef _WIN64 ++ _BitScanReverse64(&r, x); ++# else ++ // Scan the high 32 bits. ++ if (_BitScanReverse(&r, static_cast(x >> 32))) ++ return 63 - (r + 32); ++ ++ // Scan the low 32 bits. ++ _BitScanReverse(&r, static_cast(x)); ++# endif ++ ++ assert(x != 0); ++ // Static analysis complains about using uninitialized data ++ // "r", but the only way that can happen is if "x" is 0, ++ // which the callers guarantee to not happen. ++# pragma warning(suppress: 6102) ++ return 63 - r; ++} ++# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) ++} ++} ++#endif ++ ++namespace fmt { ++namespace internal { ++struct DummyInt { ++ int data[2]; ++ operator int() const { return 0; } ++}; ++typedef std::numeric_limits FPUtil; ++ ++// Dummy implementations of system functions such as signbit and ecvt called ++// if the latter are not available. ++inline DummyInt signbit(...) { return DummyInt(); } ++inline DummyInt _ecvt_s(...) { return DummyInt(); } ++inline DummyInt isinf(...) { return DummyInt(); } ++inline DummyInt _finite(...) { return DummyInt(); } ++inline DummyInt isnan(...) { return DummyInt(); } ++inline DummyInt _isnan(...) { return DummyInt(); } ++ ++// A helper function to suppress bogus "conditional expression is constant" ++// warnings. ++template ++inline T const_check(T value) { return value; } ++} ++} // namespace fmt ++ ++namespace std { ++// Standard permits specialization of std::numeric_limits. This specialization ++// is used to resolve ambiguity between isinf and std::isinf in glibc: ++// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 ++// and the same for isnan and signbit. ++template <> ++class numeric_limits : ++ public std::numeric_limits { ++ public: ++ // Portable version of isinf. ++ template ++ static bool isinfinity(T x) { ++ using namespace fmt::internal; ++ // The resolution "priority" is: ++ // isinf macro > std::isinf > ::isinf > fmt::internal::isinf ++ if (const_check(sizeof(isinf(x)) == sizeof(bool) || ++ sizeof(isinf(x)) == sizeof(int))) { ++ return isinf(x) != 0; ++ } ++ return !_finite(static_cast(x)); ++ } ++ ++ // Portable version of isnan. ++ template ++ static bool isnotanumber(T x) { ++ using namespace fmt::internal; ++ if (const_check(sizeof(isnan(x)) == sizeof(bool) || ++ sizeof(isnan(x)) == sizeof(int))) { ++ return isnan(x) != 0; ++ } ++ return _isnan(static_cast(x)) != 0; ++ } ++ ++ // Portable version of signbit. ++ static bool isnegative(double x) { ++ using namespace fmt::internal; ++ if (const_check(sizeof(signbit(x)) == sizeof(bool) || ++ sizeof(signbit(x)) == sizeof(int))) { ++ return signbit(x) != 0; ++ } ++ if (x < 0) return true; ++ if (!isnotanumber(x)) return false; ++ int dec = 0, sign = 0; ++ char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. ++ _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); ++ return sign != 0; ++ } ++}; ++} // namespace std ++ ++namespace fmt { ++ ++// Fix the warning about long long on older versions of GCC ++// that don't support the diagnostic pragma. ++FMT_GCC_EXTENSION typedef long long LongLong; ++FMT_GCC_EXTENSION typedef unsigned long long ULongLong; ++ ++#if FMT_USE_RVALUE_REFERENCES ++using std::move; ++#endif ++ ++template ++class BasicWriter; ++ ++typedef BasicWriter Writer; ++typedef BasicWriter WWriter; ++ ++template ++class ArgFormatter; ++ ++struct FormatSpec; ++ ++template ++class BasicPrintfArgFormatter; ++ ++template > ++class BasicFormatter; ++ ++/** ++ \rst ++ A string reference. It can be constructed from a C string or ++ ``std::basic_string``. ++ ++ You can use one of the following typedefs for common character types: ++ ++ +------------+-------------------------+ ++ | Type | Definition | ++ +============+=========================+ ++ | StringRef | BasicStringRef | ++ +------------+-------------------------+ ++ | WStringRef | BasicStringRef | ++ +------------+-------------------------+ ++ ++ This class is most useful as a parameter type to allow passing ++ different types of strings to a function, for example:: ++ ++ template ++ std::string format(StringRef format_str, const Args & ... args); ++ ++ format("{}", 42); ++ format(std::string("{}"), 42); ++ \endrst ++ */ ++template ++class BasicStringRef { ++ private: ++ const Char *data_; ++ std::size_t size_; ++ ++ public: ++ /** Constructs a string reference object from a C string and a size. */ ++ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} ++ ++ /** ++ \rst ++ Constructs a string reference object from a C string computing ++ the size with ``std::char_traits::length``. ++ \endrst ++ */ ++ BasicStringRef(const Char *s) ++ : data_(s), size_(std::char_traits::length(s)) {} ++ ++ /** ++ \rst ++ Constructs a string reference from a ``std::basic_string`` object. ++ \endrst ++ */ ++ template ++ BasicStringRef( ++ const std::basic_string, Allocator> &s) ++ : data_(s.c_str()), size_(s.size()) {} ++ ++ /** ++ \rst ++ Converts a string reference to an ``std::string`` object. ++ \endrst ++ */ ++ std::basic_string to_string() const { ++ return std::basic_string(data_, size_); ++ } ++ ++ /** Returns a pointer to the string data. */ ++ const Char *data() const { return data_; } ++ ++ /** Returns the string size. */ ++ std::size_t size() const { return size_; } ++ ++ // Lexicographically compare this string reference to other. ++ int compare(BasicStringRef other) const { ++ std::size_t size = size_ < other.size_ ? size_ : other.size_; ++ int result = std::char_traits::compare(data_, other.data_, size); ++ if (result == 0) ++ result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); ++ return result; ++ } ++ ++ friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) == 0; ++ } ++ friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) != 0; ++ } ++ friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) < 0; ++ } ++ friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) <= 0; ++ } ++ friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) > 0; ++ } ++ friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) { ++ return lhs.compare(rhs) >= 0; ++ } ++}; ++ ++typedef BasicStringRef StringRef; ++typedef BasicStringRef WStringRef; ++ ++/** ++ \rst ++ A reference to a null terminated string. It can be constructed from a C ++ string or ``std::basic_string``. ++ ++ You can use one of the following typedefs for common character types: ++ ++ +-------------+--------------------------+ ++ | Type | Definition | ++ +=============+==========================+ ++ | CStringRef | BasicCStringRef | ++ +-------------+--------------------------+ ++ | WCStringRef | BasicCStringRef | ++ +-------------+--------------------------+ ++ ++ This class is most useful as a parameter type to allow passing ++ different types of strings to a function, for example:: ++ ++ template ++ std::string format(CStringRef format_str, const Args & ... args); ++ ++ format("{}", 42); ++ format(std::string("{}"), 42); ++ \endrst ++ */ ++template ++class BasicCStringRef { ++ private: ++ const Char *data_; ++ ++ public: ++ /** Constructs a string reference object from a C string. */ ++ BasicCStringRef(const Char *s) : data_(s) {} ++ ++ /** ++ \rst ++ Constructs a string reference from a ``std::basic_string`` object. ++ \endrst ++ */ ++ template ++ BasicCStringRef( ++ const std::basic_string, Allocator> &s) ++ : data_(s.c_str()) {} ++ ++ /** Returns the pointer to a C string. */ ++ const Char *c_str() const { return data_; } ++}; ++ ++typedef BasicCStringRef CStringRef; ++typedef BasicCStringRef WCStringRef; ++ ++/** A formatting error such as invalid format string. */ ++class FormatError : public std::runtime_error { ++ public: ++ explicit FormatError(CStringRef message) ++ : std::runtime_error(message.c_str()) {} ++ FormatError(const FormatError &ferr) : std::runtime_error(ferr) {} ++ FMT_API ~FormatError() FMT_DTOR_NOEXCEPT; ++}; ++ ++namespace internal { ++ ++// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. ++template ++struct MakeUnsigned { typedef T Type; }; ++ ++#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ ++ template <> \ ++ struct MakeUnsigned { typedef U Type; } ++ ++FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); ++FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); ++FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); ++FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); ++FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); ++FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); ++ ++// Casts nonnegative integer to unsigned. ++template ++inline typename MakeUnsigned::Type to_unsigned(Int value) { ++ FMT_ASSERT(value >= 0, "negative value"); ++ return static_cast::Type>(value); ++} ++ ++// The number of characters to store in the MemoryBuffer object itself ++// to avoid dynamic memory allocation. ++enum { INLINE_BUFFER_SIZE = 500 }; ++ ++#if FMT_SECURE_SCL ++// Use checked iterator to avoid warnings on MSVC. ++template ++inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { ++ return stdext::checked_array_iterator(ptr, size); ++} ++#else ++template ++inline T *make_ptr(T *ptr, std::size_t) { return ptr; } ++#endif ++} // namespace internal ++ ++/** ++ \rst ++ A buffer supporting a subset of ``std::vector``'s operations. ++ \endrst ++ */ ++template ++class Buffer { ++ private: ++ FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); ++ ++ protected: ++ T *ptr_; ++ std::size_t size_; ++ std::size_t capacity_; ++ ++ Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0) ++ : ptr_(ptr), size_(0), capacity_(capacity) {} ++ ++ /** ++ \rst ++ Increases the buffer capacity to hold at least *size* elements updating ++ ``ptr_`` and ``capacity_``. ++ \endrst ++ */ ++ virtual void grow(std::size_t size) = 0; ++ ++ public: ++ virtual ~Buffer() {} ++ ++ /** Returns the size of this buffer. */ ++ std::size_t size() const { return size_; } ++ ++ /** Returns the capacity of this buffer. */ ++ std::size_t capacity() const { return capacity_; } ++ ++ /** ++ Resizes the buffer. If T is a POD type new elements may not be initialized. ++ */ ++ void resize(std::size_t new_size) { ++ if (new_size > capacity_) ++ grow(new_size); ++ size_ = new_size; ++ } ++ ++ /** ++ \rst ++ Reserves space to store at least *capacity* elements. ++ \endrst ++ */ ++ void reserve(std::size_t capacity) { ++ if (capacity > capacity_) ++ grow(capacity); ++ } ++ ++ void clear() FMT_NOEXCEPT { size_ = 0; } ++ ++ void push_back(const T &value) { ++ if (size_ == capacity_) ++ grow(size_ + 1); ++ ptr_[size_++] = value; ++ } ++ ++ /** Appends data to the end of the buffer. */ ++ template ++ void append(const U *begin, const U *end); ++ ++ T &operator[](std::size_t index) { return ptr_[index]; } ++ const T &operator[](std::size_t index) const { return ptr_[index]; } ++}; ++ ++template ++template ++void Buffer::append(const U *begin, const U *end) { ++ FMT_ASSERT(end >= begin, "negative value"); ++ std::size_t new_size = size_ + (end - begin); ++ if (new_size > capacity_) ++ grow(new_size); ++ std::uninitialized_copy(begin, end, ++ internal::make_ptr(ptr_, capacity_) + size_); ++ size_ = new_size; ++} ++ ++namespace internal { ++ ++// A memory buffer for trivially copyable/constructible types with the first ++// SIZE elements stored in the object itself. ++template > ++class MemoryBuffer : private Allocator, public Buffer { ++ private: ++ T data_[SIZE]; ++ ++ // Deallocate memory allocated by the buffer. ++ void deallocate() { ++ if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_); ++ } ++ ++ protected: ++ void grow(std::size_t size) FMT_OVERRIDE; ++ ++ public: ++ explicit MemoryBuffer(const Allocator &alloc = Allocator()) ++ : Allocator(alloc), Buffer(data_, SIZE) {} ++ ~MemoryBuffer() { deallocate(); } ++ ++#if FMT_USE_RVALUE_REFERENCES ++ private: ++ // Move data from other to this buffer. ++ void move(MemoryBuffer &other) { ++ Allocator &this_alloc = *this, &other_alloc = other; ++ this_alloc = std::move(other_alloc); ++ this->size_ = other.size_; ++ this->capacity_ = other.capacity_; ++ if (other.ptr_ == other.data_) { ++ this->ptr_ = data_; ++ std::uninitialized_copy(other.data_, other.data_ + this->size_, ++ make_ptr(data_, this->capacity_)); ++ } else { ++ this->ptr_ = other.ptr_; ++ // Set pointer to the inline array so that delete is not called ++ // when deallocating. ++ other.ptr_ = other.data_; ++ } ++ } ++ ++ public: ++ MemoryBuffer(MemoryBuffer &&other) { ++ move(other); ++ } ++ ++ MemoryBuffer &operator=(MemoryBuffer &&other) { ++ assert(this != &other); ++ deallocate(); ++ move(other); ++ return *this; ++ } ++#endif ++ ++ // Returns a copy of the allocator associated with this buffer. ++ Allocator get_allocator() const { return *this; } ++}; ++ ++template ++void MemoryBuffer::grow(std::size_t size) { ++ std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; ++ if (size > new_capacity) ++ new_capacity = size; ++ T *new_ptr = this->allocate(new_capacity, FMT_NULL); ++ // The following code doesn't throw, so the raw pointer above doesn't leak. ++ std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, ++ make_ptr(new_ptr, new_capacity)); ++ std::size_t old_capacity = this->capacity_; ++ T *old_ptr = this->ptr_; ++ this->capacity_ = new_capacity; ++ this->ptr_ = new_ptr; ++ // deallocate may throw (at least in principle), but it doesn't matter since ++ // the buffer already uses the new storage and will deallocate it in case ++ // of exception. ++ if (old_ptr != data_) ++ Allocator::deallocate(old_ptr, old_capacity); ++} ++ ++// A fixed-size buffer. ++template ++class FixedBuffer : public fmt::Buffer { ++ public: ++ FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} ++ ++ protected: ++ FMT_API void grow(std::size_t size) FMT_OVERRIDE; ++}; ++ ++template ++class BasicCharTraits { ++ public: ++#if FMT_SECURE_SCL ++ typedef stdext::checked_array_iterator CharPtr; ++#else ++ typedef Char *CharPtr; ++#endif ++ static Char cast(int value) { return static_cast(value); } ++}; ++ ++template ++class CharTraits; ++ ++template <> ++class CharTraits : public BasicCharTraits { ++ private: ++ // Conversion from wchar_t to char is not allowed. ++ static char convert(wchar_t); ++ ++ public: ++ static char convert(char value) { return value; } ++ ++ // Formats a floating-point number. ++ template ++ FMT_API static int format_float(char *buffer, std::size_t size, ++ const char *format, unsigned width, int precision, T value); ++}; ++ ++#if FMT_USE_EXTERN_TEMPLATES ++extern template int CharTraits::format_float ++ (char *buffer, std::size_t size, ++ const char* format, unsigned width, int precision, double value); ++extern template int CharTraits::format_float ++ (char *buffer, std::size_t size, ++ const char* format, unsigned width, int precision, long double value); ++#endif ++ ++template <> ++class CharTraits : public BasicCharTraits { ++ public: ++ static wchar_t convert(char value) { return value; } ++ static wchar_t convert(wchar_t value) { return value; } ++ ++ template ++ FMT_API static int format_float(wchar_t *buffer, std::size_t size, ++ const wchar_t *format, unsigned width, int precision, T value); ++}; ++ ++#if FMT_USE_EXTERN_TEMPLATES ++extern template int CharTraits::format_float ++ (wchar_t *buffer, std::size_t size, ++ const wchar_t* format, unsigned width, int precision, double value); ++extern template int CharTraits::format_float ++ (wchar_t *buffer, std::size_t size, ++ const wchar_t* format, unsigned width, int precision, long double value); ++#endif ++ ++// Checks if a number is negative - used to avoid warnings. ++template ++struct SignChecker { ++ template ++ static bool is_negative(T value) { return value < 0; } ++}; ++ ++template <> ++struct SignChecker { ++ template ++ static bool is_negative(T) { return false; } ++}; ++ ++// Returns true if value is negative, false otherwise. ++// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. ++template ++inline bool is_negative(T value) { ++ return SignChecker::is_signed>::is_negative(value); ++} ++ ++// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. ++template ++struct TypeSelector { typedef uint32_t Type; }; ++ ++template <> ++struct TypeSelector { typedef uint64_t Type; }; ++ ++template ++struct IntTraits { ++ // Smallest of uint32_t and uint64_t that is large enough to represent ++ // all values of T. ++ typedef typename ++ TypeSelector::digits <= 32>::Type MainType; ++}; ++ ++FMT_API void report_unknown_type(char code, const char *type); ++ ++// Static data is placed in this class template to allow header-only ++// configuration. ++template ++struct FMT_API BasicData { ++ static const uint32_t POWERS_OF_10_32[]; ++ static const uint64_t POWERS_OF_10_64[]; ++ static const char DIGITS[]; ++}; ++ ++#if FMT_USE_EXTERN_TEMPLATES ++extern template struct BasicData; ++#endif ++ ++typedef BasicData<> Data; ++ ++#ifdef FMT_BUILTIN_CLZLL ++// Returns the number of decimal digits in n. Leading zeros are not counted ++// except for n == 0 in which case count_digits returns 1. ++inline unsigned count_digits(uint64_t n) { ++ // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 ++ // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. ++ int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; ++ return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; ++} ++#else ++// Fallback version of count_digits used when __builtin_clz is not available. ++inline unsigned count_digits(uint64_t n) { ++ unsigned count = 1; ++ for (;;) { ++ // Integer division is slow so do it for a group of four digits instead ++ // of for every digit. The idea comes from the talk by Alexandrescu ++ // "Three Optimization Tips for C++". See speed-test for a comparison. ++ if (n < 10) return count; ++ if (n < 100) return count + 1; ++ if (n < 1000) return count + 2; ++ if (n < 10000) return count + 3; ++ n /= 10000u; ++ count += 4; ++ } ++} ++#endif ++ ++#ifdef FMT_BUILTIN_CLZ ++// Optional version of count_digits for better performance on 32-bit platforms. ++inline unsigned count_digits(uint32_t n) { ++ int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; ++ return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; ++} ++#endif ++ ++// A functor that doesn't add a thousands separator. ++struct NoThousandsSep { ++ template ++ void operator()(Char *) {} ++}; ++ ++// A functor that adds a thousands separator. ++class ThousandsSep { ++ private: ++ fmt::StringRef sep_; ++ ++ // Index of a decimal digit with the least significant digit having index 0. ++ unsigned digit_index_; ++ ++ public: ++ explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} ++ ++ template ++ void operator()(Char *&buffer) { ++ if (++digit_index_ % 3 != 0) ++ return; ++ buffer -= sep_.size(); ++ std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), ++ internal::make_ptr(buffer, sep_.size())); ++ } ++}; ++ ++// Formats a decimal unsigned integer value writing into buffer. ++// thousands_sep is a functor that is called after writing each char to ++// add a thousands separator if necessary. ++template ++inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, ++ ThousandsSep thousands_sep) { ++ buffer += num_digits; ++ while (value >= 100) { ++ // Integer division is slow so do it for a group of two digits instead ++ // of for every digit. The idea comes from the talk by Alexandrescu ++ // "Three Optimization Tips for C++". See speed-test for a comparison. ++ unsigned index = static_cast((value % 100) * 2); ++ value /= 100; ++ *--buffer = Data::DIGITS[index + 1]; ++ thousands_sep(buffer); ++ *--buffer = Data::DIGITS[index]; ++ thousands_sep(buffer); ++ } ++ if (value < 10) { ++ *--buffer = static_cast('0' + value); ++ return; ++ } ++ unsigned index = static_cast(value * 2); ++ *--buffer = Data::DIGITS[index + 1]; ++ thousands_sep(buffer); ++ *--buffer = Data::DIGITS[index]; ++} ++ ++template ++inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { ++ format_decimal(buffer, value, num_digits, NoThousandsSep()); ++ return; ++} ++ ++#ifndef _WIN32 ++# define FMT_USE_WINDOWS_H 0 ++#elif !defined(FMT_USE_WINDOWS_H) ++# define FMT_USE_WINDOWS_H 1 ++#endif ++ ++// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. ++// All the functionality that relies on it will be disabled too. ++#if FMT_USE_WINDOWS_H ++// A converter from UTF-8 to UTF-16. ++// It is only provided for Windows since other systems support UTF-8 natively. ++class UTF8ToUTF16 { ++ private: ++ MemoryBuffer buffer_; ++ ++ public: ++ FMT_API explicit UTF8ToUTF16(StringRef s); ++ operator WStringRef() const { return WStringRef(&buffer_[0], size()); } ++ size_t size() const { return buffer_.size() - 1; } ++ const wchar_t *c_str() const { return &buffer_[0]; } ++ std::wstring str() const { return std::wstring(&buffer_[0], size()); } ++}; ++ ++// A converter from UTF-16 to UTF-8. ++// It is only provided for Windows since other systems support UTF-8 natively. ++class UTF16ToUTF8 { ++ private: ++ MemoryBuffer buffer_; ++ ++ public: ++ UTF16ToUTF8() {} ++ FMT_API explicit UTF16ToUTF8(WStringRef s); ++ operator StringRef() const { return StringRef(&buffer_[0], size()); } ++ size_t size() const { return buffer_.size() - 1; } ++ const char *c_str() const { return &buffer_[0]; } ++ std::string str() const { return std::string(&buffer_[0], size()); } ++ ++ // Performs conversion returning a system error code instead of ++ // throwing exception on conversion error. This method may still throw ++ // in case of memory allocation error. ++ FMT_API int convert(WStringRef s); ++}; ++ ++FMT_API void format_windows_error(fmt::Writer &out, int error_code, ++ fmt::StringRef message) FMT_NOEXCEPT; ++#endif ++ ++// A formatting argument value. ++struct Value { ++ template ++ struct StringValue { ++ const Char *value; ++ std::size_t size; ++ }; ++ ++ typedef void (*FormatFunc)( ++ void *formatter, const void *arg, void *format_str_ptr); ++ ++ struct CustomValue { ++ const void *value; ++ FormatFunc format; ++ }; ++ ++ union { ++ int int_value; ++ unsigned uint_value; ++ LongLong long_long_value; ++ ULongLong ulong_long_value; ++ double double_value; ++ long double long_double_value; ++ const void *pointer; ++ StringValue string; ++ StringValue sstring; ++ StringValue ustring; ++ StringValue wstring; ++ CustomValue custom; ++ }; ++ ++ enum Type { ++ NONE, NAMED_ARG, ++ // Integer types should go first, ++ INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, ++ // followed by floating-point types. ++ DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, ++ CSTRING, STRING, WSTRING, POINTER, CUSTOM ++ }; ++}; ++ ++// A formatting argument. It is a trivially copyable/constructible type to ++// allow storage in internal::MemoryBuffer. ++struct Arg : Value { ++ Type type; ++}; ++ ++template ++struct NamedArg; ++template ++struct NamedArgWithType; ++ ++template ++struct Null {}; ++ ++// A helper class template to enable or disable overloads taking wide ++// characters and strings in MakeValue. ++template ++struct WCharHelper { ++ typedef Null Supported; ++ typedef T Unsupported; ++}; ++ ++template ++struct WCharHelper { ++ typedef T Supported; ++ typedef Null Unsupported; ++}; ++ ++typedef char Yes[1]; ++typedef char No[2]; ++ ++template ++T &get(); ++ ++// These are non-members to workaround an overload resolution bug in bcc32. ++Yes &convert(fmt::ULongLong); ++No &convert(...); ++ ++template ++struct ConvertToIntImpl { ++ enum { value = ENABLE_CONVERSION }; ++}; ++ ++template ++struct ConvertToIntImpl2 { ++ enum { value = false }; ++}; ++ ++template ++struct ConvertToIntImpl2 { ++ enum { ++ // Don't convert numeric types. ++ value = ConvertToIntImpl::is_specialized>::value ++ }; ++}; ++ ++template ++struct ConvertToInt { ++ enum { ++ enable_conversion = sizeof(fmt::internal::convert(get())) == sizeof(Yes) ++ }; ++ enum { value = ConvertToIntImpl2::value }; ++}; ++ ++#define FMT_DISABLE_CONVERSION_TO_INT(Type) \ ++ template <> \ ++ struct ConvertToInt { enum { value = 0 }; } ++ ++// Silence warnings about convering float to int. ++FMT_DISABLE_CONVERSION_TO_INT(float); ++FMT_DISABLE_CONVERSION_TO_INT(double); ++FMT_DISABLE_CONVERSION_TO_INT(long double); ++ ++template ++struct EnableIf {}; ++ ++template ++struct EnableIf { typedef T type; }; ++ ++template ++struct Conditional { typedef T type; }; ++ ++template ++struct Conditional { typedef F type; }; ++ ++// For bcc32 which doesn't understand ! in template arguments. ++template ++struct Not { enum { value = 0 }; }; ++ ++template <> ++struct Not { enum { value = 1 }; }; ++ ++template ++struct FalseType { enum { value = 0 }; }; ++ ++template struct LConvCheck { ++ LConvCheck(int) {} ++}; ++ ++// Returns the thousands separator for the current locale. ++// We check if ``lconv`` contains ``thousands_sep`` because on Android ++// ``lconv`` is stubbed as an empty struct. ++template ++inline StringRef thousands_sep( ++ LConv *lc, LConvCheck = 0) { ++ return lc->thousands_sep; ++} ++ ++inline fmt::StringRef thousands_sep(...) { return ""; } ++ ++#define FMT_CONCAT(a, b) a##b ++ ++#if FMT_GCC_VERSION >= 303 ++# define FMT_UNUSED __attribute__((unused)) ++#else ++# define FMT_UNUSED ++#endif ++ ++#ifndef FMT_USE_STATIC_ASSERT ++# define FMT_USE_STATIC_ASSERT 0 ++#endif ++ ++#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ ++ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 ++# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) ++#else ++# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) ++# define FMT_STATIC_ASSERT(cond, message) \ ++ typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED ++#endif ++ ++template ++void format_arg(Formatter &, const Char *, const T &) { ++ FMT_STATIC_ASSERT(FalseType::value, ++ "Cannot format argument. To enable the use of ostream " ++ "operator<< include fmt/ostream.h. Otherwise provide " ++ "an overload of format_arg."); ++} ++ ++// Makes an Arg object from any type. ++template ++class MakeValue : public Arg { ++ public: ++ typedef typename Formatter::Char Char; ++ ++ private: ++ // The following two methods are private to disallow formatting of ++ // arbitrary pointers. If you want to output a pointer cast it to ++ // "void *" or "const void *". In particular, this forbids formatting ++ // of "[const] volatile char *" which is printed as bool by iostreams. ++ // Do not implement! ++ template ++ MakeValue(const T *value); ++ template ++ MakeValue(T *value); ++ ++ // The following methods are private to disallow formatting of wide ++ // characters and strings into narrow strings as in ++ // fmt::format("{}", L"test"); ++ // To fix this, use a wide format string: fmt::format(L"{}", L"test"). ++#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) ++ MakeValue(typename WCharHelper::Unsupported); ++#endif ++ MakeValue(typename WCharHelper::Unsupported); ++ MakeValue(typename WCharHelper::Unsupported); ++ MakeValue(typename WCharHelper::Unsupported); ++ MakeValue(typename WCharHelper::Unsupported); ++ ++ void set_string(StringRef str) { ++ string.value = str.data(); ++ string.size = str.size(); ++ } ++ ++ void set_string(WStringRef str) { ++ wstring.value = str.data(); ++ wstring.size = str.size(); ++ } ++ ++ // Formats an argument of a custom type, such as a user-defined class. ++ template ++ static void format_custom_arg( ++ void *formatter, const void *arg, void *format_str_ptr) { ++ format_arg(*static_cast(formatter), ++ *static_cast(format_str_ptr), ++ *static_cast(arg)); ++ } ++ ++ public: ++ MakeValue() {} ++ ++#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ ++ MakeValue(Type value) { field = rhs; } \ ++ static uint64_t type(Type) { return Arg::TYPE; } ++ ++#define FMT_MAKE_VALUE(Type, field, TYPE) \ ++ FMT_MAKE_VALUE_(Type, field, TYPE, value) ++ ++ FMT_MAKE_VALUE(bool, int_value, BOOL) ++ FMT_MAKE_VALUE(short, int_value, INT) ++ FMT_MAKE_VALUE(unsigned short, uint_value, UINT) ++ FMT_MAKE_VALUE(int, int_value, INT) ++ FMT_MAKE_VALUE(unsigned, uint_value, UINT) ++ ++ MakeValue(long value) { ++ // To minimize the number of types we need to deal with, long is ++ // translated either to int or to long long depending on its size. ++ if (const_check(sizeof(long) == sizeof(int))) ++ int_value = static_cast(value); ++ else ++ long_long_value = value; ++ } ++ static uint64_t type(long) { ++ return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; ++ } ++ ++ MakeValue(unsigned long value) { ++ if (const_check(sizeof(unsigned long) == sizeof(unsigned))) ++ uint_value = static_cast(value); ++ else ++ ulong_long_value = value; ++ } ++ static uint64_t type(unsigned long) { ++ return sizeof(unsigned long) == sizeof(unsigned) ? ++ Arg::UINT : Arg::ULONG_LONG; ++ } ++ ++ FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) ++ FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) ++ FMT_MAKE_VALUE(float, double_value, DOUBLE) ++ FMT_MAKE_VALUE(double, double_value, DOUBLE) ++ FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) ++ FMT_MAKE_VALUE(signed char, int_value, INT) ++ FMT_MAKE_VALUE(unsigned char, uint_value, UINT) ++ FMT_MAKE_VALUE(char, int_value, CHAR) ++ ++#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) ++ MakeValue(typename WCharHelper::Supported value) { ++ int_value = value; ++ } ++ static uint64_t type(wchar_t) { return Arg::CHAR; } ++#endif ++ ++#define FMT_MAKE_STR_VALUE(Type, TYPE) \ ++ MakeValue(Type value) { set_string(value); } \ ++ static uint64_t type(Type) { return Arg::TYPE; } ++ ++ FMT_MAKE_VALUE(char *, string.value, CSTRING) ++ FMT_MAKE_VALUE(const char *, string.value, CSTRING) ++ FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) ++ FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) ++ FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) ++ FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) ++ FMT_MAKE_STR_VALUE(const std::string &, STRING) ++ FMT_MAKE_STR_VALUE(StringRef, STRING) ++ FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) ++ ++#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ ++ MakeValue(typename WCharHelper::Supported value) { \ ++ set_string(value); \ ++ } \ ++ static uint64_t type(Type) { return Arg::TYPE; } ++ ++ FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) ++ FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) ++ FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) ++ FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) ++ ++ FMT_MAKE_VALUE(void *, pointer, POINTER) ++ FMT_MAKE_VALUE(const void *, pointer, POINTER) ++ ++ template ++ MakeValue(const T &value, ++ typename EnableIf::value>::value, int>::type = 0) { ++ custom.value = &value; ++ custom.format = &format_custom_arg; ++ } ++ ++ template ++ static typename EnableIf::value>::value, uint64_t>::type ++ type(const T &) { ++ return Arg::CUSTOM; ++ } ++ ++ // Additional template param `Char_` is needed here because make_type always ++ // uses char. ++ template ++ MakeValue(const NamedArg &value) { pointer = &value; } ++ template ++ MakeValue(const NamedArgWithType &value) { pointer = &value; } ++ ++ template ++ static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } ++ template ++ static uint64_t type(const NamedArgWithType &) { return Arg::NAMED_ARG; } ++}; ++ ++template ++class MakeArg : public Arg { ++public: ++ MakeArg() { ++ type = Arg::NONE; ++ } ++ ++ template ++ MakeArg(const T &value) ++ : Arg(MakeValue(value)) { ++ type = static_cast(MakeValue::type(value)); ++ } ++}; ++ ++template ++struct NamedArg : Arg { ++ BasicStringRef name; ++ ++ template ++ NamedArg(BasicStringRef argname, const T &value) ++ : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} ++}; ++ ++template ++struct NamedArgWithType : NamedArg { ++ NamedArgWithType(BasicStringRef argname, const T &value) ++ : NamedArg(argname, value) {} ++}; ++ ++class RuntimeError : public std::runtime_error { ++ protected: ++ RuntimeError() : std::runtime_error("") {} ++ RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {} ++ FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT; ++}; ++ ++template ++class ArgMap; ++} // namespace internal ++ ++/** An argument list. */ ++class ArgList { ++ private: ++ // To reduce compiled code size per formatting function call, types of first ++ // MAX_PACKED_ARGS arguments are passed in the types_ field. ++ uint64_t types_; ++ union { ++ // If the number of arguments is less than MAX_PACKED_ARGS, the argument ++ // values are stored in values_, otherwise they are stored in args_. ++ // This is done to reduce compiled code size as storing larger objects ++ // may require more code (at least on x86-64) even if the same amount of ++ // data is actually copied to stack. It saves ~10% on the bloat test. ++ const internal::Value *values_; ++ const internal::Arg *args_; ++ }; ++ ++ internal::Arg::Type type(unsigned index) const { ++ return type(types_, index); ++ } ++ ++ template ++ friend class internal::ArgMap; ++ ++ public: ++ // Maximum number of arguments with packed types. ++ enum { MAX_PACKED_ARGS = 16 }; ++ ++ ArgList() : types_(0) {} ++ ++ ArgList(ULongLong types, const internal::Value *values) ++ : types_(types), values_(values) {} ++ ArgList(ULongLong types, const internal::Arg *args) ++ : types_(types), args_(args) {} ++ ++ uint64_t types() const { return types_; } ++ ++ /** Returns the argument at specified index. */ ++ internal::Arg operator[](unsigned index) const { ++ using internal::Arg; ++ Arg arg; ++ bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; ++ if (index < MAX_PACKED_ARGS) { ++ Arg::Type arg_type = type(index); ++ internal::Value &val = arg; ++ if (arg_type != Arg::NONE) ++ val = use_values ? values_[index] : args_[index]; ++ arg.type = arg_type; ++ return arg; ++ } ++ if (use_values) { ++ // The index is greater than the number of arguments that can be stored ++ // in values, so return a "none" argument. ++ arg.type = Arg::NONE; ++ return arg; ++ } ++ for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { ++ if (args_[i].type == Arg::NONE) ++ return args_[i]; ++ } ++ return args_[index]; ++ } ++ ++ static internal::Arg::Type type(uint64_t types, unsigned index) { ++ unsigned shift = index * 4; ++ uint64_t mask = 0xf; ++ return static_cast( ++ (types & (mask << shift)) >> shift); ++ } ++}; ++ ++#define FMT_DISPATCH(call) static_cast(this)->call ++ ++/** ++ \rst ++ An argument visitor based on the `curiously recurring template pattern ++ `_. ++ ++ To use `~fmt::ArgVisitor` define a subclass that implements some or all of the ++ visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, ++ for example, `~fmt::ArgVisitor::visit_int()`. ++ Pass the subclass as the *Impl* template parameter. Then calling ++ `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method ++ specific to the argument type. For example, if the argument type is ++ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass ++ will be called. If the subclass doesn't contain a method with this signature, ++ then a corresponding method of `~fmt::ArgVisitor` will be called. ++ ++ **Example**:: ++ ++ class MyArgVisitor : public fmt::ArgVisitor { ++ public: ++ void visit_int(int value) { fmt::print("{}", value); } ++ void visit_double(double value) { fmt::print("{}", value ); } ++ }; ++ \endrst ++ */ ++template ++class ArgVisitor { ++ private: ++ typedef internal::Arg Arg; ++ ++ public: ++ void report_unhandled_arg() {} ++ ++ Result visit_unhandled_arg() { ++ FMT_DISPATCH(report_unhandled_arg()); ++ return Result(); ++ } ++ ++ /** Visits an ``int`` argument. **/ ++ Result visit_int(int value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits a ``long long`` argument. **/ ++ Result visit_long_long(LongLong value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits an ``unsigned`` argument. **/ ++ Result visit_uint(unsigned value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits an ``unsigned long long`` argument. **/ ++ Result visit_ulong_long(ULongLong value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits a ``bool`` argument. **/ ++ Result visit_bool(bool value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits a ``char`` or ``wchar_t`` argument. **/ ++ Result visit_char(int value) { ++ return FMT_DISPATCH(visit_any_int(value)); ++ } ++ ++ /** Visits an argument of any integral type. **/ ++ template ++ Result visit_any_int(T) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits a ``double`` argument. **/ ++ Result visit_double(double value) { ++ return FMT_DISPATCH(visit_any_double(value)); ++ } ++ ++ /** Visits a ``long double`` argument. **/ ++ Result visit_long_double(long double value) { ++ return FMT_DISPATCH(visit_any_double(value)); ++ } ++ ++ /** Visits a ``double`` or ``long double`` argument. **/ ++ template ++ Result visit_any_double(T) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits a null-terminated C string (``const char *``) argument. **/ ++ Result visit_cstring(const char *) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits a string argument. **/ ++ Result visit_string(Arg::StringValue) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits a wide string argument. **/ ++ Result visit_wstring(Arg::StringValue) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits a pointer argument. **/ ++ Result visit_pointer(const void *) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** Visits an argument of a custom (user-defined) type. **/ ++ Result visit_custom(Arg::CustomValue) { ++ return FMT_DISPATCH(visit_unhandled_arg()); ++ } ++ ++ /** ++ \rst ++ Visits an argument dispatching to the appropriate visit method based on ++ the argument type. For example, if the argument type is ``double`` then ++ the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be ++ called. ++ \endrst ++ */ ++ Result visit(const Arg &arg) { ++ switch (arg.type) { ++ case Arg::NONE: ++ case Arg::NAMED_ARG: ++ FMT_ASSERT(false, "invalid argument type"); ++ break; ++ case Arg::INT: ++ return FMT_DISPATCH(visit_int(arg.int_value)); ++ case Arg::UINT: ++ return FMT_DISPATCH(visit_uint(arg.uint_value)); ++ case Arg::LONG_LONG: ++ return FMT_DISPATCH(visit_long_long(arg.long_long_value)); ++ case Arg::ULONG_LONG: ++ return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); ++ case Arg::BOOL: ++ return FMT_DISPATCH(visit_bool(arg.int_value != 0)); ++ case Arg::CHAR: ++ return FMT_DISPATCH(visit_char(arg.int_value)); ++ case Arg::DOUBLE: ++ return FMT_DISPATCH(visit_double(arg.double_value)); ++ case Arg::LONG_DOUBLE: ++ return FMT_DISPATCH(visit_long_double(arg.long_double_value)); ++ case Arg::CSTRING: ++ return FMT_DISPATCH(visit_cstring(arg.string.value)); ++ case Arg::STRING: ++ return FMT_DISPATCH(visit_string(arg.string)); ++ case Arg::WSTRING: ++ return FMT_DISPATCH(visit_wstring(arg.wstring)); ++ case Arg::POINTER: ++ return FMT_DISPATCH(visit_pointer(arg.pointer)); ++ case Arg::CUSTOM: ++ return FMT_DISPATCH(visit_custom(arg.custom)); ++ } ++ return Result(); ++ } ++}; ++ ++enum Alignment { ++ ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC ++}; ++ ++// Flags. ++enum { ++ SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, ++ CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. ++}; ++ ++// An empty format specifier. ++struct EmptySpec {}; ++ ++// A type specifier. ++template ++struct TypeSpec : EmptySpec { ++ Alignment align() const { return ALIGN_DEFAULT; } ++ unsigned width() const { return 0; } ++ int precision() const { return -1; } ++ bool flag(unsigned) const { return false; } ++ char type() const { return TYPE; } ++ char type_prefix() const { return TYPE; } ++ char fill() const { return ' '; } ++}; ++ ++// A width specifier. ++struct WidthSpec { ++ unsigned width_; ++ // Fill is always wchar_t and cast to char if necessary to avoid having ++ // two specialization of WidthSpec and its subclasses. ++ wchar_t fill_; ++ ++ WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} ++ ++ unsigned width() const { return width_; } ++ wchar_t fill() const { return fill_; } ++}; ++ ++// An alignment specifier. ++struct AlignSpec : WidthSpec { ++ Alignment align_; ++ ++ AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) ++ : WidthSpec(width, fill), align_(align) {} ++ ++ Alignment align() const { return align_; } ++ ++ int precision() const { return -1; } ++}; ++ ++// An alignment and type specifier. ++template ++struct AlignTypeSpec : AlignSpec { ++ AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} ++ ++ bool flag(unsigned) const { return false; } ++ char type() const { return TYPE; } ++ char type_prefix() const { return TYPE; } ++}; ++ ++// A full format specifier. ++struct FormatSpec : AlignSpec { ++ unsigned flags_; ++ int precision_; ++ char type_; ++ ++ FormatSpec( ++ unsigned width = 0, char type = 0, wchar_t fill = ' ') ++ : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} ++ ++ bool flag(unsigned f) const { return (flags_ & f) != 0; } ++ int precision() const { return precision_; } ++ char type() const { return type_; } ++ char type_prefix() const { return type_; } ++}; ++ ++// An integer format specifier. ++template , typename Char = char> ++class IntFormatSpec : public SpecT { ++ private: ++ T value_; ++ ++ public: ++ IntFormatSpec(T val, const SpecT &spec = SpecT()) ++ : SpecT(spec), value_(val) {} ++ ++ T value() const { return value_; } ++}; ++ ++// A string format specifier. ++template ++class StrFormatSpec : public AlignSpec { ++ private: ++ const Char *str_; ++ ++ public: ++ template ++ StrFormatSpec(const Char *str, unsigned width, FillChar fill) ++ : AlignSpec(width, fill), str_(str) { ++ internal::CharTraits::convert(FillChar()); ++ } ++ ++ const Char *str() const { return str_; } ++}; ++ ++/** ++ Returns an integer format specifier to format the value in base 2. ++ */ ++IntFormatSpec > bin(int value); ++ ++/** ++ Returns an integer format specifier to format the value in base 8. ++ */ ++IntFormatSpec > oct(int value); ++ ++/** ++ Returns an integer format specifier to format the value in base 16 using ++ lower-case letters for the digits above 9. ++ */ ++IntFormatSpec > hex(int value); ++ ++/** ++ Returns an integer formatter format specifier to format in base 16 using ++ upper-case letters for the digits above 9. ++ */ ++IntFormatSpec > hexu(int value); ++ ++/** ++ \rst ++ Returns an integer format specifier to pad the formatted argument with the ++ fill character to the specified width using the default (right) numeric ++ alignment. ++ ++ **Example**:: ++ ++ MemoryWriter out; ++ out << pad(hex(0xcafe), 8, '0'); ++ // out.str() == "0000cafe" ++ ++ \endrst ++ */ ++template ++IntFormatSpec, Char> pad( ++ int value, unsigned width, Char fill = ' '); ++ ++#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ ++inline IntFormatSpec > bin(TYPE value) { \ ++ return IntFormatSpec >(value, TypeSpec<'b'>()); \ ++} \ ++ \ ++inline IntFormatSpec > oct(TYPE value) { \ ++ return IntFormatSpec >(value, TypeSpec<'o'>()); \ ++} \ ++ \ ++inline IntFormatSpec > hex(TYPE value) { \ ++ return IntFormatSpec >(value, TypeSpec<'x'>()); \ ++} \ ++ \ ++inline IntFormatSpec > hexu(TYPE value) { \ ++ return IntFormatSpec >(value, TypeSpec<'X'>()); \ ++} \ ++ \ ++template \ ++inline IntFormatSpec > pad( \ ++ IntFormatSpec > f, unsigned width) { \ ++ return IntFormatSpec >( \ ++ f.value(), AlignTypeSpec(width, ' ')); \ ++} \ ++ \ ++/* For compatibility with older compilers we provide two overloads for pad, */ \ ++/* one that takes a fill character and one that doesn't. In the future this */ \ ++/* can be replaced with one overload making the template argument Char */ \ ++/* default to char (C++11). */ \ ++template \ ++inline IntFormatSpec, Char> pad( \ ++ IntFormatSpec, Char> f, \ ++ unsigned width, Char fill) { \ ++ return IntFormatSpec, Char>( \ ++ f.value(), AlignTypeSpec(width, fill)); \ ++} \ ++ \ ++inline IntFormatSpec > pad( \ ++ TYPE value, unsigned width) { \ ++ return IntFormatSpec >( \ ++ value, AlignTypeSpec<0>(width, ' ')); \ ++} \ ++ \ ++template \ ++inline IntFormatSpec, Char> pad( \ ++ TYPE value, unsigned width, Char fill) { \ ++ return IntFormatSpec, Char>( \ ++ value, AlignTypeSpec<0>(width, fill)); \ ++} ++ ++FMT_DEFINE_INT_FORMATTERS(int) ++FMT_DEFINE_INT_FORMATTERS(long) ++FMT_DEFINE_INT_FORMATTERS(unsigned) ++FMT_DEFINE_INT_FORMATTERS(unsigned long) ++FMT_DEFINE_INT_FORMATTERS(LongLong) ++FMT_DEFINE_INT_FORMATTERS(ULongLong) ++ ++/** ++ \rst ++ Returns a string formatter that pads the formatted argument with the fill ++ character to the specified width using the default (left) string alignment. ++ ++ **Example**:: ++ ++ std::string s = str(MemoryWriter() << pad("abc", 8)); ++ // s == "abc " ++ ++ \endrst ++ */ ++template ++inline StrFormatSpec pad( ++ const Char *str, unsigned width, Char fill = ' ') { ++ return StrFormatSpec(str, width, fill); ++} ++ ++inline StrFormatSpec pad( ++ const wchar_t *str, unsigned width, char fill = ' ') { ++ return StrFormatSpec(str, width, fill); ++} ++ ++namespace internal { ++ ++template ++class ArgMap { ++ private: ++ typedef std::vector< ++ std::pair, internal::Arg> > MapType; ++ typedef typename MapType::value_type Pair; ++ ++ MapType map_; ++ ++ public: ++ FMT_API void init(const ArgList &args); ++ ++ const internal::Arg *find(const fmt::BasicStringRef &name) const { ++ // The list is unsorted, so just return the first matching name. ++ for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); ++ it != end; ++it) { ++ if (it->first == name) ++ return &it->second; ++ } ++ return FMT_NULL; ++ } ++}; ++ ++template ++class ArgFormatterBase : public ArgVisitor { ++ private: ++ BasicWriter &writer_; ++ Spec &spec_; ++ ++ FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); ++ ++ void write_pointer(const void *p) { ++ spec_.flags_ = HASH_FLAG; ++ spec_.type_ = 'x'; ++ writer_.write_int(reinterpret_cast(p), spec_); ++ } ++ ++ // workaround MSVC two-phase lookup issue ++ typedef internal::Arg Arg; ++ ++ protected: ++ BasicWriter &writer() { return writer_; } ++ Spec &spec() { return spec_; } ++ ++ void write(bool value) { ++ const char *str_value = value ? "true" : "false"; ++ Arg::StringValue str = { str_value, std::strlen(str_value) }; ++ writer_.write_str(str, spec_); ++ } ++ ++ void write(const char *value) { ++ Arg::StringValue str = {value, value ? std::strlen(value) : 0}; ++ writer_.write_str(str, spec_); ++ } ++ ++ public: ++ typedef Spec SpecType; ++ ++ ArgFormatterBase(BasicWriter &w, Spec &s) ++ : writer_(w), spec_(s) {} ++ ++ template ++ void visit_any_int(T value) { writer_.write_int(value, spec_); } ++ ++ template ++ void visit_any_double(T value) { writer_.write_double(value, spec_); } ++ ++ void visit_bool(bool value) { ++ if (spec_.type_) { ++ visit_any_int(value); ++ return; ++ } ++ write(value); ++ } ++ ++ void visit_char(int value) { ++ if (spec_.type_ && spec_.type_ != 'c') { ++ spec_.flags_ |= CHAR_FLAG; ++ writer_.write_int(value, spec_); ++ return; ++ } ++ if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) ++ FMT_THROW(FormatError("invalid format specifier for char")); ++ typedef typename BasicWriter::CharPtr CharPtr; ++ Char fill = internal::CharTraits::cast(spec_.fill()); ++ CharPtr out = CharPtr(); ++ const unsigned CHAR_SIZE = 1; ++ if (spec_.width_ > CHAR_SIZE) { ++ out = writer_.grow_buffer(spec_.width_); ++ if (spec_.align_ == ALIGN_RIGHT) { ++ std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); ++ out += spec_.width_ - CHAR_SIZE; ++ } else if (spec_.align_ == ALIGN_CENTER) { ++ out = writer_.fill_padding(out, spec_.width_, ++ internal::const_check(CHAR_SIZE), fill); ++ } else { ++ std::uninitialized_fill_n(out + CHAR_SIZE, ++ spec_.width_ - CHAR_SIZE, fill); ++ } ++ } else { ++ out = writer_.grow_buffer(CHAR_SIZE); ++ } ++ *out = internal::CharTraits::cast(value); ++ } ++ ++ void visit_cstring(const char *value) { ++ if (spec_.type_ == 'p') ++ return write_pointer(value); ++ write(value); ++ } ++ ++ // Qualification with "internal" here and below is a workaround for nvcc. ++ void visit_string(internal::Arg::StringValue value) { ++ writer_.write_str(value, spec_); ++ } ++ ++ using ArgVisitor::visit_wstring; ++ ++ void visit_wstring(internal::Arg::StringValue value) { ++ writer_.write_str(value, spec_); ++ } ++ ++ void visit_pointer(const void *value) { ++ if (spec_.type_ && spec_.type_ != 'p') ++ report_unknown_type(spec_.type_, "pointer"); ++ write_pointer(value); ++ } ++}; ++ ++class FormatterBase { ++ private: ++ ArgList args_; ++ int next_arg_index_; ++ ++ // Returns the argument with specified index. ++ FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); ++ ++ protected: ++ const ArgList &args() const { return args_; } ++ ++ explicit FormatterBase(const ArgList &args) { ++ args_ = args; ++ next_arg_index_ = 0; ++ } ++ ++ // Returns the next argument. ++ Arg next_arg(const char *&error) { ++ if (next_arg_index_ >= 0) ++ return do_get_arg(internal::to_unsigned(next_arg_index_++), error); ++ error = "cannot switch from manual to automatic argument indexing"; ++ return Arg(); ++ } ++ ++ // Checks if manual indexing is used and returns the argument with ++ // specified index. ++ Arg get_arg(unsigned arg_index, const char *&error) { ++ return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); ++ } ++ ++ bool check_no_auto_index(const char *&error) { ++ if (next_arg_index_ > 0) { ++ error = "cannot switch from automatic to manual argument indexing"; ++ return false; ++ } ++ next_arg_index_ = -1; ++ return true; ++ } ++ ++ template ++ void write(BasicWriter &w, const Char *start, const Char *end) { ++ if (start != end) ++ w << BasicStringRef(start, internal::to_unsigned(end - start)); ++ } ++}; ++} // namespace internal ++ ++/** ++ \rst ++ An argument formatter based on the `curiously recurring template pattern ++ `_. ++ ++ To use `~fmt::BasicArgFormatter` define a subclass that implements some or ++ all of the visit methods with the same signatures as the methods in ++ `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. ++ Pass the subclass as the *Impl* template parameter. When a formatting ++ function processes an argument, it will dispatch to a visit method ++ specific to the argument type. For example, if the argument type is ++ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass ++ will be called. If the subclass doesn't contain a method with this signature, ++ then a corresponding method of `~fmt::BasicArgFormatter` or its superclass ++ will be called. ++ \endrst ++ */ ++template ++class BasicArgFormatter : public internal::ArgFormatterBase { ++ private: ++ BasicFormatter &formatter_; ++ const Char *format_; ++ ++ public: ++ /** ++ \rst ++ Constructs an argument formatter object. ++ *formatter* is a reference to the main formatter object, *spec* contains ++ format specifier information for standard argument types, and *fmt* points ++ to the part of the format string being parsed for custom argument types. ++ \endrst ++ */ ++ BasicArgFormatter(BasicFormatter &formatter, ++ Spec &spec, const Char *fmt) ++ : internal::ArgFormatterBase(formatter.writer(), spec), ++ formatter_(formatter), format_(fmt) {} ++ ++ /** Formats an argument of a custom (user-defined) type. */ ++ void visit_custom(internal::Arg::CustomValue c) { ++ c.format(&formatter_, c.value, &format_); ++ } ++}; ++ ++/** The default argument formatter. */ ++template ++class ArgFormatter : ++ public BasicArgFormatter, Char, FormatSpec> { ++ public: ++ /** Constructs an argument formatter object. */ ++ ArgFormatter(BasicFormatter &formatter, ++ FormatSpec &spec, const Char *fmt) ++ : BasicArgFormatter, ++ Char, FormatSpec>(formatter, spec, fmt) {} ++}; ++ ++/** This template formats data and writes the output to a writer. */ ++template ++class BasicFormatter : private internal::FormatterBase { ++ public: ++ /** The character type for the output. */ ++ typedef CharType Char; ++ ++ private: ++ BasicWriter &writer_; ++ internal::ArgMap map_; ++ ++ FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); ++ ++ using internal::FormatterBase::get_arg; ++ ++ // Checks if manual indexing is used and returns the argument with ++ // specified name. ++ internal::Arg get_arg(BasicStringRef arg_name, const char *&error); ++ ++ // Parses argument index and returns corresponding argument. ++ internal::Arg parse_arg_index(const Char *&s); ++ ++ // Parses argument name and returns corresponding argument. ++ internal::Arg parse_arg_name(const Char *&s); ++ ++ public: ++ /** ++ \rst ++ Constructs a ``BasicFormatter`` object. References to the arguments and ++ the writer are stored in the formatter object so make sure they have ++ appropriate lifetimes. ++ \endrst ++ */ ++ BasicFormatter(const ArgList &args, BasicWriter &w) ++ : internal::FormatterBase(args), writer_(w) {} ++ ++ /** Returns a reference to the writer associated with this formatter. */ ++ BasicWriter &writer() { return writer_; } ++ ++ /** Formats stored arguments and writes the output to the writer. */ ++ void format(BasicCStringRef format_str); ++ ++ // Formats a single argument and advances format_str, a format string pointer. ++ const Char *format(const Char *&format_str, const internal::Arg &arg); ++}; ++ ++// Generates a comma-separated list with results of applying f to ++// numbers 0..n-1. ++# define FMT_GEN(n, f) FMT_GEN##n(f) ++# define FMT_GEN1(f) f(0) ++# define FMT_GEN2(f) FMT_GEN1(f), f(1) ++# define FMT_GEN3(f) FMT_GEN2(f), f(2) ++# define FMT_GEN4(f) FMT_GEN3(f), f(3) ++# define FMT_GEN5(f) FMT_GEN4(f), f(4) ++# define FMT_GEN6(f) FMT_GEN5(f), f(5) ++# define FMT_GEN7(f) FMT_GEN6(f), f(6) ++# define FMT_GEN8(f) FMT_GEN7(f), f(7) ++# define FMT_GEN9(f) FMT_GEN8(f), f(8) ++# define FMT_GEN10(f) FMT_GEN9(f), f(9) ++# define FMT_GEN11(f) FMT_GEN10(f), f(10) ++# define FMT_GEN12(f) FMT_GEN11(f), f(11) ++# define FMT_GEN13(f) FMT_GEN12(f), f(12) ++# define FMT_GEN14(f) FMT_GEN13(f), f(13) ++# define FMT_GEN15(f) FMT_GEN14(f), f(14) ++ ++namespace internal { ++inline uint64_t make_type() { return 0; } ++ ++template ++inline uint64_t make_type(const T &arg) { ++ return MakeValue< BasicFormatter >::type(arg); ++} ++ ++template ++struct ArgArray; ++ ++template ++struct ArgArray { ++ typedef Value Type[N > 0 ? N : 1]; ++ ++ template ++ static Value make(const T &value) { ++#ifdef __clang__ ++ Value result = MakeValue(value); ++ // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: ++ // https://github.com/fmtlib/fmt/issues/276 ++ (void)result.custom.format; ++ return result; ++#else ++ return MakeValue(value); ++#endif ++ } ++}; ++ ++template ++struct ArgArray { ++ typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE ++ ++ template ++ static Arg make(const T &value) { return MakeArg(value); } ++}; ++ ++#if FMT_USE_VARIADIC_TEMPLATES ++template ++inline uint64_t make_type(const Arg &first, const Args & ... tail) { ++ return make_type(first) | (make_type(tail...) << 4); ++} ++ ++#else ++ ++struct ArgType { ++ uint64_t type; ++ ++ ArgType() : type(0) {} ++ ++ template ++ ArgType(const T &arg) : type(make_type(arg)) {} ++}; ++ ++# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() ++ ++inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { ++ return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | ++ (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | ++ (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | ++ (t12.type << 48) | (t13.type << 52) | (t14.type << 56); ++} ++#endif ++} // namespace internal ++ ++# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n ++# define FMT_MAKE_ARG_TYPE(n) T##n ++# define FMT_MAKE_ARG(n) const T##n &v##n ++# define FMT_ASSIGN_char(n) \ ++ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) ++# define FMT_ASSIGN_wchar_t(n) \ ++ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) ++ ++#if FMT_USE_VARIADIC_TEMPLATES ++// Defines a variadic function returning void. ++# define FMT_VARIADIC_VOID(func, arg_type) \ ++ template \ ++ void func(arg_type arg0, const Args & ... args) { \ ++ typedef fmt::internal::ArgArray ArgArray; \ ++ typename ArgArray::Type array{ \ ++ ArgArray::template make >(args)...}; \ ++ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ ++ } ++ ++// Defines a variadic constructor. ++# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ ++ template \ ++ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ ++ typedef fmt::internal::ArgArray ArgArray; \ ++ typename ArgArray::Type array{ \ ++ ArgArray::template make >(args)...}; \ ++ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ ++ } ++ ++#else ++ ++# define FMT_MAKE_REF(n) \ ++ fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) ++# define FMT_MAKE_REF2(n) v##n ++ ++// Defines a wrapper for a function taking one argument of type arg_type ++// and n additional arguments of arbitrary types. ++# define FMT_WRAP1(func, arg_type, n) \ ++ template \ ++ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ ++ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ ++ func(arg1, fmt::ArgList( \ ++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ ++ } ++ ++// Emulates a variadic function returning void on a pre-C++11 compiler. ++# define FMT_VARIADIC_VOID(func, arg_type) \ ++ inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ ++ FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ ++ FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ ++ FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ ++ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ ++ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) ++ ++# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ ++ template \ ++ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ ++ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ ++ func(arg0, arg1, fmt::ArgList( \ ++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ ++ } ++ ++// Emulates a variadic constructor on a pre-C++11 compiler. ++# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ ++ FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) ++#endif ++ ++// Generates a comma-separated list with results of applying f to pairs ++// (argument, index). ++#define FMT_FOR_EACH1(f, x0) f(x0, 0) ++#define FMT_FOR_EACH2(f, x0, x1) \ ++ FMT_FOR_EACH1(f, x0), f(x1, 1) ++#define FMT_FOR_EACH3(f, x0, x1, x2) \ ++ FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) ++#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ ++ FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) ++#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ ++ FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) ++#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ ++ FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) ++#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ ++ FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) ++#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ ++ FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) ++#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ ++ FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) ++#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ ++ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) ++ ++/** ++ An error returned by an operating system or a language runtime, ++ for example a file opening error. ++*/ ++class SystemError : public internal::RuntimeError { ++ private: ++ FMT_API void init(int err_code, CStringRef format_str, ArgList args); ++ ++ protected: ++ int error_code_; ++ ++ typedef char Char; // For FMT_VARIADIC_CTOR. ++ ++ SystemError() {} ++ ++ public: ++ /** ++ \rst ++ Constructs a :class:`fmt::SystemError` object with a description ++ formatted with `fmt::format_system_error`. *message* and additional ++ arguments passed into the constructor are formatted similarly to ++ `fmt::format`. ++ ++ **Example**:: ++ ++ // This throws a SystemError with the description ++ // cannot open file 'madeup': No such file or directory ++ // or similar (system message may vary). ++ const char *filename = "madeup"; ++ std::FILE *file = std::fopen(filename, "r"); ++ if (!file) ++ throw fmt::SystemError(errno, "cannot open file '{}'", filename); ++ \endrst ++ */ ++ SystemError(int error_code, CStringRef message) { ++ init(error_code, message, ArgList()); ++ } ++ FMT_DEFAULTED_COPY_CTOR(SystemError) ++ FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) ++ ++ FMT_API ~SystemError() FMT_DTOR_NOEXCEPT; ++ ++ int error_code() const { return error_code_; } ++}; ++ ++/** ++ \rst ++ Formats an error returned by an operating system or a language runtime, ++ for example a file opening error, and writes it to *out* in the following ++ form: ++ ++ .. parsed-literal:: ++ **: ** ++ ++ where ** is the passed message and ** is ++ the system message corresponding to the error code. ++ *error_code* is a system error code as given by ``errno``. ++ If *error_code* is not a valid error code such as -1, the system message ++ may look like "Unknown error -1" and is platform-dependent. ++ \endrst ++ */ ++FMT_API void format_system_error(fmt::Writer &out, int error_code, ++ fmt::StringRef message) FMT_NOEXCEPT; ++ ++/** ++ \rst ++ This template provides operations for formatting and writing data into ++ a character stream. The output is stored in a buffer provided by a subclass ++ such as :class:`fmt::BasicMemoryWriter`. ++ ++ You can use one of the following typedefs for common character types: ++ ++ +---------+----------------------+ ++ | Type | Definition | ++ +=========+======================+ ++ | Writer | BasicWriter | ++ +---------+----------------------+ ++ | WWriter | BasicWriter | ++ +---------+----------------------+ ++ ++ \endrst ++ */ ++template ++class BasicWriter { ++ private: ++ // Output buffer. ++ Buffer &buffer_; ++ ++ FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); ++ ++ typedef typename internal::CharTraits::CharPtr CharPtr; ++ ++#if FMT_SECURE_SCL ++ // Returns pointer value. ++ static Char *get(CharPtr p) { return p.base(); } ++#else ++ static Char *get(Char *p) { return p; } ++#endif ++ ++ // Fills the padding around the content and returns the pointer to the ++ // content area. ++ static CharPtr fill_padding(CharPtr buffer, ++ unsigned total_size, std::size_t content_size, wchar_t fill); ++ ++ // Grows the buffer by n characters and returns a pointer to the newly ++ // allocated area. ++ CharPtr grow_buffer(std::size_t n) { ++ std::size_t size = buffer_.size(); ++ buffer_.resize(size + n); ++ return internal::make_ptr(&buffer_[size], n); ++ } ++ ++ // Writes an unsigned decimal integer. ++ template ++ Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) { ++ unsigned num_digits = internal::count_digits(value); ++ Char *ptr = get(grow_buffer(prefix_size + num_digits)); ++ internal::format_decimal(ptr + prefix_size, value, num_digits); ++ return ptr; ++ } ++ ++ // Writes a decimal integer. ++ template ++ void write_decimal(Int value) { ++ typedef typename internal::IntTraits::MainType MainType; ++ MainType abs_value = static_cast(value); ++ if (internal::is_negative(value)) { ++ abs_value = 0 - abs_value; ++ *write_unsigned_decimal(abs_value, 1) = '-'; ++ } else { ++ write_unsigned_decimal(abs_value, 0); ++ } ++ } ++ ++ // Prepare a buffer for integer formatting. ++ CharPtr prepare_int_buffer(unsigned num_digits, ++ const EmptySpec &, const char *prefix, unsigned prefix_size) { ++ unsigned size = prefix_size + num_digits; ++ CharPtr p = grow_buffer(size); ++ std::uninitialized_copy(prefix, prefix + prefix_size, p); ++ return p + size - 1; ++ } ++ ++ template ++ CharPtr prepare_int_buffer(unsigned num_digits, ++ const Spec &spec, const char *prefix, unsigned prefix_size); ++ ++ // Formats an integer. ++ template ++ void write_int(T value, Spec spec); ++ ++ // Formats a floating-point number (double or long double). ++ template ++ void write_double(T value, const Spec &spec); ++ ++ // Writes a formatted string. ++ template ++ CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); ++ ++ template ++ void write_str(const internal::Arg::StringValue &str, ++ const Spec &spec); ++ ++ // This following methods are private to disallow writing wide characters ++ // and strings to a char stream. If you want to print a wide string as a ++ // pointer as std::ostream does, cast it to const void*. ++ // Do not implement! ++ void operator<<(typename internal::WCharHelper::Unsupported); ++ void operator<<( ++ typename internal::WCharHelper::Unsupported); ++ ++ // Appends floating-point length specifier to the format string. ++ // The second argument is only used for overload resolution. ++ void append_float_length(Char *&format_ptr, long double) { ++ *format_ptr++ = 'L'; ++ } ++ ++ template ++ void append_float_length(Char *&, T) {} ++ ++ template ++ friend class internal::ArgFormatterBase; ++ ++ template ++ friend class BasicPrintfArgFormatter; ++ ++ protected: ++ /** ++ Constructs a ``BasicWriter`` object. ++ */ ++ explicit BasicWriter(Buffer &b) : buffer_(b) {} ++ ++ public: ++ /** ++ \rst ++ Destroys a ``BasicWriter`` object. ++ \endrst ++ */ ++ virtual ~BasicWriter() {} ++ ++ /** ++ Returns the total number of characters written. ++ */ ++ std::size_t size() const { return buffer_.size(); } ++ ++ /** ++ Returns a pointer to the output buffer content. No terminating null ++ character is appended. ++ */ ++ const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } ++ ++ /** ++ Returns a pointer to the output buffer content with terminating null ++ character appended. ++ */ ++ const Char *c_str() const { ++ std::size_t size = buffer_.size(); ++ buffer_.reserve(size + 1); ++ buffer_[size] = '\0'; ++ return &buffer_[0]; ++ } ++ ++ /** ++ \rst ++ Returns the content of the output buffer as an `std::string`. ++ \endrst ++ */ ++ std::basic_string str() const { ++ return std::basic_string(&buffer_[0], buffer_.size()); ++ } ++ ++ /** ++ \rst ++ Writes formatted data. ++ ++ *args* is an argument list representing arbitrary arguments. ++ ++ **Example**:: ++ ++ MemoryWriter out; ++ out.write("Current point:\n"); ++ out.write("({:+f}, {:+f})", -3.14, 3.14); ++ ++ This will write the following output to the ``out`` object: ++ ++ .. code-block:: none ++ ++ Current point: ++ (-3.140000, +3.140000) ++ ++ The output can be accessed using :func:`data()`, :func:`c_str` or ++ :func:`str` methods. ++ ++ See also :ref:`syntax`. ++ \endrst ++ */ ++ void write(BasicCStringRef format, ArgList args) { ++ BasicFormatter(args, *this).format(format); ++ } ++ FMT_VARIADIC_VOID(write, BasicCStringRef) ++ ++ BasicWriter &operator<<(int value) { ++ write_decimal(value); ++ return *this; ++ } ++ BasicWriter &operator<<(unsigned value) { ++ return *this << IntFormatSpec(value); ++ } ++ BasicWriter &operator<<(long value) { ++ write_decimal(value); ++ return *this; ++ } ++ BasicWriter &operator<<(unsigned long value) { ++ return *this << IntFormatSpec(value); ++ } ++ BasicWriter &operator<<(LongLong value) { ++ write_decimal(value); ++ return *this; ++ } ++ ++ /** ++ \rst ++ Formats *value* and writes it to the stream. ++ \endrst ++ */ ++ BasicWriter &operator<<(ULongLong value) { ++ return *this << IntFormatSpec(value); ++ } ++ ++ BasicWriter &operator<<(double value) { ++ write_double(value, FormatSpec()); ++ return *this; ++ } ++ ++ /** ++ \rst ++ Formats *value* using the general format for floating-point numbers ++ (``'g'``) and writes it to the stream. ++ \endrst ++ */ ++ BasicWriter &operator<<(long double value) { ++ write_double(value, FormatSpec()); ++ return *this; ++ } ++ ++ /** ++ Writes a character to the stream. ++ */ ++ BasicWriter &operator<<(char value) { ++ buffer_.push_back(value); ++ return *this; ++ } ++ ++ BasicWriter &operator<<( ++ typename internal::WCharHelper::Supported value) { ++ buffer_.push_back(value); ++ return *this; ++ } ++ ++ /** ++ \rst ++ Writes *value* to the stream. ++ \endrst ++ */ ++ BasicWriter &operator<<(fmt::BasicStringRef value) { ++ const Char *str = value.data(); ++ buffer_.append(str, str + value.size()); ++ return *this; ++ } ++ ++ BasicWriter &operator<<( ++ typename internal::WCharHelper::Supported value) { ++ const char *str = value.data(); ++ buffer_.append(str, str + value.size()); ++ return *this; ++ } ++ ++ template ++ BasicWriter &operator<<(IntFormatSpec spec) { ++ internal::CharTraits::convert(FillChar()); ++ write_int(spec.value(), spec); ++ return *this; ++ } ++ ++ template ++ BasicWriter &operator<<(const StrFormatSpec &spec) { ++ const StrChar *s = spec.str(); ++ write_str(s, std::char_traits::length(s), spec); ++ return *this; ++ } ++ ++ void clear() FMT_NOEXCEPT { buffer_.clear(); } ++ ++ Buffer &buffer() FMT_NOEXCEPT { return buffer_; } ++}; ++ ++template ++template ++typename BasicWriter::CharPtr BasicWriter::write_str( ++ const StrChar *s, std::size_t size, const AlignSpec &spec) { ++ CharPtr out = CharPtr(); ++ if (spec.width() > size) { ++ out = grow_buffer(spec.width()); ++ Char fill = internal::CharTraits::cast(spec.fill()); ++ if (spec.align() == ALIGN_RIGHT) { ++ std::uninitialized_fill_n(out, spec.width() - size, fill); ++ out += spec.width() - size; ++ } else if (spec.align() == ALIGN_CENTER) { ++ out = fill_padding(out, spec.width(), size, fill); ++ } else { ++ std::uninitialized_fill_n(out + size, spec.width() - size, fill); ++ } ++ } else { ++ out = grow_buffer(size); ++ } ++ std::uninitialized_copy(s, s + size, out); ++ return out; ++} ++ ++template ++template ++void BasicWriter::write_str( ++ const internal::Arg::StringValue &s, const Spec &spec) { ++ // Check if StrChar is convertible to Char. ++ internal::CharTraits::convert(StrChar()); ++ if (spec.type_ && spec.type_ != 's') ++ internal::report_unknown_type(spec.type_, "string"); ++ const StrChar *str_value = s.value; ++ std::size_t str_size = s.size; ++ if (str_size == 0) { ++ if (!str_value) { ++ FMT_THROW(FormatError("string pointer is null")); ++ } ++ } ++ std::size_t precision = static_cast(spec.precision_); ++ if (spec.precision_ >= 0 && precision < str_size) ++ str_size = precision; ++ write_str(str_value, str_size, spec); ++} ++ ++template ++typename BasicWriter::CharPtr ++ BasicWriter::fill_padding( ++ CharPtr buffer, unsigned total_size, ++ std::size_t content_size, wchar_t fill) { ++ std::size_t padding = total_size - content_size; ++ std::size_t left_padding = padding / 2; ++ Char fill_char = internal::CharTraits::cast(fill); ++ std::uninitialized_fill_n(buffer, left_padding, fill_char); ++ buffer += left_padding; ++ CharPtr content = buffer; ++ std::uninitialized_fill_n(buffer + content_size, ++ padding - left_padding, fill_char); ++ return content; ++} ++ ++template ++template ++typename BasicWriter::CharPtr ++ BasicWriter::prepare_int_buffer( ++ unsigned num_digits, const Spec &spec, ++ const char *prefix, unsigned prefix_size) { ++ unsigned width = spec.width(); ++ Alignment align = spec.align(); ++ Char fill = internal::CharTraits::cast(spec.fill()); ++ if (spec.precision() > static_cast(num_digits)) { ++ // Octal prefix '0' is counted as a digit, so ignore it if precision ++ // is specified. ++ if (prefix_size > 0 && prefix[prefix_size - 1] == '0') ++ --prefix_size; ++ unsigned number_size = ++ prefix_size + internal::to_unsigned(spec.precision()); ++ AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); ++ if (number_size >= width) ++ return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); ++ buffer_.reserve(width); ++ unsigned fill_size = width - number_size; ++ if (align != ALIGN_LEFT) { ++ CharPtr p = grow_buffer(fill_size); ++ std::uninitialized_fill(p, p + fill_size, fill); ++ } ++ CharPtr result = prepare_int_buffer( ++ num_digits, subspec, prefix, prefix_size); ++ if (align == ALIGN_LEFT) { ++ CharPtr p = grow_buffer(fill_size); ++ std::uninitialized_fill(p, p + fill_size, fill); ++ } ++ return result; ++ } ++ unsigned size = prefix_size + num_digits; ++ if (width <= size) { ++ CharPtr p = grow_buffer(size); ++ std::uninitialized_copy(prefix, prefix + prefix_size, p); ++ return p + size - 1; ++ } ++ CharPtr p = grow_buffer(width); ++ CharPtr end = p + width; ++ if (align == ALIGN_LEFT) { ++ std::uninitialized_copy(prefix, prefix + prefix_size, p); ++ p += size; ++ std::uninitialized_fill(p, end, fill); ++ } else if (align == ALIGN_CENTER) { ++ p = fill_padding(p, width, size, fill); ++ std::uninitialized_copy(prefix, prefix + prefix_size, p); ++ p += size; ++ } else { ++ if (align == ALIGN_NUMERIC) { ++ if (prefix_size != 0) { ++ p = std::uninitialized_copy(prefix, prefix + prefix_size, p); ++ size -= prefix_size; ++ } ++ } else { ++ std::uninitialized_copy(prefix, prefix + prefix_size, end - size); ++ } ++ std::uninitialized_fill(p, end - size, fill); ++ p = end; ++ } ++ return p - 1; ++} ++ ++template ++template ++void BasicWriter::write_int(T value, Spec spec) { ++ unsigned prefix_size = 0; ++ typedef typename internal::IntTraits::MainType UnsignedType; ++ UnsignedType abs_value = static_cast(value); ++ char prefix[4] = ""; ++ if (internal::is_negative(value)) { ++ prefix[0] = '-'; ++ ++prefix_size; ++ abs_value = 0 - abs_value; ++ } else if (spec.flag(SIGN_FLAG)) { ++ prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; ++ ++prefix_size; ++ } ++ switch (spec.type()) { ++ case 0: case 'd': { ++ unsigned num_digits = internal::count_digits(abs_value); ++ CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; ++ internal::format_decimal(get(p), abs_value, 0); ++ break; ++ } ++ case 'x': case 'X': { ++ UnsignedType n = abs_value; ++ if (spec.flag(HASH_FLAG)) { ++ prefix[prefix_size++] = '0'; ++ prefix[prefix_size++] = spec.type_prefix(); ++ } ++ unsigned num_digits = 0; ++ do { ++ ++num_digits; ++ } while ((n >>= 4) != 0); ++ Char *p = get(prepare_int_buffer( ++ num_digits, spec, prefix, prefix_size)); ++ n = abs_value; ++ const char *digits = spec.type() == 'x' ? ++ "0123456789abcdef" : "0123456789ABCDEF"; ++ do { ++ *p-- = digits[n & 0xf]; ++ } while ((n >>= 4) != 0); ++ break; ++ } ++ case 'b': case 'B': { ++ UnsignedType n = abs_value; ++ if (spec.flag(HASH_FLAG)) { ++ prefix[prefix_size++] = '0'; ++ prefix[prefix_size++] = spec.type_prefix(); ++ } ++ unsigned num_digits = 0; ++ do { ++ ++num_digits; ++ } while ((n >>= 1) != 0); ++ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); ++ n = abs_value; ++ do { ++ *p-- = static_cast('0' + (n & 1)); ++ } while ((n >>= 1) != 0); ++ break; ++ } ++ case 'o': { ++ UnsignedType n = abs_value; ++ if (spec.flag(HASH_FLAG)) ++ prefix[prefix_size++] = '0'; ++ unsigned num_digits = 0; ++ do { ++ ++num_digits; ++ } while ((n >>= 3) != 0); ++ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); ++ n = abs_value; ++ do { ++ *p-- = static_cast('0' + (n & 7)); ++ } while ((n >>= 3) != 0); ++ break; ++ } ++ case 'n': { ++ unsigned num_digits = internal::count_digits(abs_value); ++ fmt::StringRef sep = ""; ++#if !(defined(ANDROID) || defined(__ANDROID__)) ++ sep = internal::thousands_sep(std::localeconv()); ++#endif ++ unsigned size = static_cast( ++ num_digits + sep.size() * ((num_digits - 1) / 3)); ++ CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; ++ internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); ++ break; ++ } ++ default: ++ internal::report_unknown_type( ++ spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); ++ break; ++ } ++} ++ ++template ++template ++void BasicWriter::write_double(T value, const Spec &spec) { ++ // Check type. ++ char type = spec.type(); ++ bool upper = false; ++ switch (type) { ++ case 0: ++ type = 'g'; ++ break; ++ case 'e': case 'f': case 'g': case 'a': ++ break; ++ case 'F': ++#if FMT_MSC_VER ++ // MSVC's printf doesn't support 'F'. ++ type = 'f'; ++#endif ++ // Fall through. ++ case 'E': case 'G': case 'A': ++ upper = true; ++ break; ++ default: ++ internal::report_unknown_type(type, "double"); ++ break; ++ } ++ ++ char sign = 0; ++ // Use isnegative instead of value < 0 because the latter is always ++ // false for NaN. ++ if (internal::FPUtil::isnegative(static_cast(value))) { ++ sign = '-'; ++ value = -value; ++ } else if (spec.flag(SIGN_FLAG)) { ++ sign = spec.flag(PLUS_FLAG) ? '+' : ' '; ++ } ++ ++ if (internal::FPUtil::isnotanumber(value)) { ++ // Format NaN ourselves because sprintf's output is not consistent ++ // across platforms. ++ std::size_t nan_size = 4; ++ const char *nan = upper ? " NAN" : " nan"; ++ if (!sign) { ++ --nan_size; ++ ++nan; ++ } ++ CharPtr out = write_str(nan, nan_size, spec); ++ if (sign) ++ *out = sign; ++ return; ++ } ++ ++ if (internal::FPUtil::isinfinity(value)) { ++ // Format infinity ourselves because sprintf's output is not consistent ++ // across platforms. ++ std::size_t inf_size = 4; ++ const char *inf = upper ? " INF" : " inf"; ++ if (!sign) { ++ --inf_size; ++ ++inf; ++ } ++ CharPtr out = write_str(inf, inf_size, spec); ++ if (sign) ++ *out = sign; ++ return; ++ } ++ ++ std::size_t offset = buffer_.size(); ++ unsigned width = spec.width(); ++ if (sign) { ++ buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); ++ if (width > 0) ++ --width; ++ ++offset; ++ } ++ ++ // Build format string. ++ enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg ++ Char format[MAX_FORMAT_SIZE]; ++ Char *format_ptr = format; ++ *format_ptr++ = '%'; ++ unsigned width_for_sprintf = width; ++ if (spec.flag(HASH_FLAG)) ++ *format_ptr++ = '#'; ++ if (spec.align() == ALIGN_CENTER) { ++ width_for_sprintf = 0; ++ } else { ++ if (spec.align() == ALIGN_LEFT) ++ *format_ptr++ = '-'; ++ if (width != 0) ++ *format_ptr++ = '*'; ++ } ++ if (spec.precision() >= 0) { ++ *format_ptr++ = '.'; ++ *format_ptr++ = '*'; ++ } ++ ++ append_float_length(format_ptr, value); ++ *format_ptr++ = type; ++ *format_ptr = '\0'; ++ ++ // Format using snprintf. ++ Char fill = internal::CharTraits::cast(spec.fill()); ++ unsigned n = 0; ++ Char *start = FMT_NULL; ++ for (;;) { ++ std::size_t buffer_size = buffer_.capacity() - offset; ++#if FMT_MSC_VER ++ // MSVC's vsnprintf_s doesn't work with zero size, so reserve ++ // space for at least one extra character to make the size non-zero. ++ // Note that the buffer's capacity will increase by more than 1. ++ if (buffer_size == 0) { ++ buffer_.reserve(offset + 1); ++ buffer_size = buffer_.capacity() - offset; ++ } ++#endif ++ start = &buffer_[offset]; ++ int result = internal::CharTraits::format_float( ++ start, buffer_size, format, width_for_sprintf, spec.precision(), value); ++ if (result >= 0) { ++ n = internal::to_unsigned(result); ++ if (offset + n < buffer_.capacity()) ++ break; // The buffer is large enough - continue with formatting. ++ buffer_.reserve(offset + n + 1); ++ } else { ++ // If result is negative we ask to increase the capacity by at least 1, ++ // but as std::vector, the buffer grows exponentially. ++ buffer_.reserve(buffer_.capacity() + 1); ++ } ++ } ++ if (sign) { ++ if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || ++ *start != ' ') { ++ *(start - 1) = sign; ++ sign = 0; ++ } else { ++ *(start - 1) = fill; ++ } ++ ++n; ++ } ++ if (spec.align() == ALIGN_CENTER && spec.width() > n) { ++ width = spec.width(); ++ CharPtr p = grow_buffer(width); ++ std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); ++ fill_padding(p, spec.width(), n, fill); ++ return; ++ } ++ if (spec.fill() != ' ' || sign) { ++ while (*start == ' ') ++ *start++ = fill; ++ if (sign) ++ *(start - 1) = sign; ++ } ++ grow_buffer(n); ++} ++ ++/** ++ \rst ++ This class template provides operations for formatting and writing data ++ into a character stream. The output is stored in a memory buffer that grows ++ dynamically. ++ ++ You can use one of the following typedefs for common character types ++ and the standard allocator: ++ ++ +---------------+-----------------------------------------------------+ ++ | Type | Definition | ++ +===============+=====================================================+ ++ | MemoryWriter | BasicMemoryWriter> | ++ +---------------+-----------------------------------------------------+ ++ | WMemoryWriter | BasicMemoryWriter> | ++ +---------------+-----------------------------------------------------+ ++ ++ **Example**:: ++ ++ MemoryWriter out; ++ out << "The answer is " << 42 << "\n"; ++ out.write("({:+f}, {:+f})", -3.14, 3.14); ++ ++ This will write the following output to the ``out`` object: ++ ++ .. code-block:: none ++ ++ The answer is 42 ++ (-3.140000, +3.140000) ++ ++ The output can be converted to an ``std::string`` with ``out.str()`` or ++ accessed as a C string with ``out.c_str()``. ++ \endrst ++ */ ++template > ++class BasicMemoryWriter : public BasicWriter { ++ private: ++ internal::MemoryBuffer buffer_; ++ ++ public: ++ explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) ++ : BasicWriter(buffer_), buffer_(alloc) {} ++ ++#if FMT_USE_RVALUE_REFERENCES ++ /** ++ \rst ++ Constructs a :class:`fmt::BasicMemoryWriter` object moving the content ++ of the other object to it. ++ \endrst ++ */ ++ BasicMemoryWriter(BasicMemoryWriter &&other) ++ : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { ++ } ++ ++ /** ++ \rst ++ Moves the content of the other ``BasicMemoryWriter`` object to this one. ++ \endrst ++ */ ++ BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { ++ buffer_ = std::move(other.buffer_); ++ return *this; ++ } ++#endif ++}; ++ ++typedef BasicMemoryWriter MemoryWriter; ++typedef BasicMemoryWriter WMemoryWriter; ++ ++/** ++ \rst ++ This class template provides operations for formatting and writing data ++ into a fixed-size array. For writing into a dynamically growing buffer ++ use :class:`fmt::BasicMemoryWriter`. ++ ++ Any write method will throw ``std::runtime_error`` if the output doesn't fit ++ into the array. ++ ++ You can use one of the following typedefs for common character types: ++ ++ +--------------+---------------------------+ ++ | Type | Definition | ++ +==============+===========================+ ++ | ArrayWriter | BasicArrayWriter | ++ +--------------+---------------------------+ ++ | WArrayWriter | BasicArrayWriter | ++ +--------------+---------------------------+ ++ \endrst ++ */ ++template ++class BasicArrayWriter : public BasicWriter { ++ private: ++ internal::FixedBuffer buffer_; ++ ++ public: ++ /** ++ \rst ++ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the ++ given size. ++ \endrst ++ */ ++ BasicArrayWriter(Char *array, std::size_t size) ++ : BasicWriter(buffer_), buffer_(array, size) {} ++ ++ /** ++ \rst ++ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the ++ size known at compile time. ++ \endrst ++ */ ++ template ++ explicit BasicArrayWriter(Char (&array)[SIZE]) ++ : BasicWriter(buffer_), buffer_(array, SIZE) {} ++}; ++ ++typedef BasicArrayWriter ArrayWriter; ++typedef BasicArrayWriter WArrayWriter; ++ ++// Reports a system error without throwing an exception. ++// Can be used to report errors from destructors. ++FMT_API void report_system_error(int error_code, ++ StringRef message) FMT_NOEXCEPT; ++ ++#if FMT_USE_WINDOWS_H ++ ++/** A Windows error. */ ++class WindowsError : public SystemError { ++ private: ++ FMT_API void init(int error_code, CStringRef format_str, ArgList args); ++ ++ public: ++ /** ++ \rst ++ Constructs a :class:`fmt::WindowsError` object with the description ++ of the form ++ ++ .. parsed-literal:: ++ **: ** ++ ++ where ** is the formatted message and ** is the ++ system message corresponding to the error code. ++ *error_code* is a Windows error code as given by ``GetLastError``. ++ If *error_code* is not a valid error code such as -1, the system message ++ will look like "error -1". ++ ++ **Example**:: ++ ++ // This throws a WindowsError with the description ++ // cannot open file 'madeup': The system cannot find the file specified. ++ // or similar (system message may vary). ++ const char *filename = "madeup"; ++ LPOFSTRUCT of = LPOFSTRUCT(); ++ HFILE file = OpenFile(filename, &of, OF_READ); ++ if (file == HFILE_ERROR) { ++ throw fmt::WindowsError(GetLastError(), ++ "cannot open file '{}'", filename); ++ } ++ \endrst ++ */ ++ WindowsError(int error_code, CStringRef message) { ++ init(error_code, message, ArgList()); ++ } ++ FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef) ++}; ++ ++// Reports a Windows error without throwing an exception. ++// Can be used to report errors from destructors. ++FMT_API void report_windows_error(int error_code, ++ StringRef message) FMT_NOEXCEPT; ++ ++#endif ++ ++enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; ++ ++/** ++ Formats a string and prints it to stdout using ANSI escape sequences ++ to specify color (experimental). ++ Example: ++ print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); ++ */ ++FMT_API void print_colored(Color c, CStringRef format, ArgList args); ++ ++/** ++ \rst ++ Formats arguments and returns the result as a string. ++ ++ **Example**:: ++ ++ std::string message = format("The answer is {}", 42); ++ \endrst ++*/ ++inline std::string format(CStringRef format_str, ArgList args) { ++ MemoryWriter w; ++ w.write(format_str, args); ++ return w.str(); ++} ++ ++inline std::wstring format(WCStringRef format_str, ArgList args) { ++ WMemoryWriter w; ++ w.write(format_str, args); ++ return w.str(); ++} ++ ++/** ++ \rst ++ Prints formatted data to the file *f*. ++ ++ **Example**:: ++ ++ print(stderr, "Don't {}!", "panic"); ++ \endrst ++ */ ++FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); ++ ++/** ++ \rst ++ Prints formatted data to ``stdout``. ++ ++ **Example**:: ++ ++ print("Elapsed time: {0:.2f} seconds", 1.23); ++ \endrst ++ */ ++FMT_API void print(CStringRef format_str, ArgList args); ++ ++/** ++ Fast integer formatter. ++ */ ++class FormatInt { ++ private: ++ // Buffer should be large enough to hold all digits (digits10 + 1), ++ // a sign and a null character. ++ enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; ++ mutable char buffer_[BUFFER_SIZE]; ++ char *str_; ++ ++ // Formats value in reverse and returns the number of digits. ++ char *format_decimal(ULongLong value) { ++ char *buffer_end = buffer_ + BUFFER_SIZE - 1; ++ while (value >= 100) { ++ // Integer division is slow so do it for a group of two digits instead ++ // of for every digit. The idea comes from the talk by Alexandrescu ++ // "Three Optimization Tips for C++". See speed-test for a comparison. ++ unsigned index = static_cast((value % 100) * 2); ++ value /= 100; ++ *--buffer_end = internal::Data::DIGITS[index + 1]; ++ *--buffer_end = internal::Data::DIGITS[index]; ++ } ++ if (value < 10) { ++ *--buffer_end = static_cast('0' + value); ++ return buffer_end; ++ } ++ unsigned index = static_cast(value * 2); ++ *--buffer_end = internal::Data::DIGITS[index + 1]; ++ *--buffer_end = internal::Data::DIGITS[index]; ++ return buffer_end; ++ } ++ ++ void FormatSigned(LongLong value) { ++ ULongLong abs_value = static_cast(value); ++ bool negative = value < 0; ++ if (negative) ++ abs_value = 0 - abs_value; ++ str_ = format_decimal(abs_value); ++ if (negative) ++ *--str_ = '-'; ++ } ++ ++ public: ++ explicit FormatInt(int value) { FormatSigned(value); } ++ explicit FormatInt(long value) { FormatSigned(value); } ++ explicit FormatInt(LongLong value) { FormatSigned(value); } ++ explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} ++ explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} ++ explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} ++ ++ /** Returns the number of characters written to the output buffer. */ ++ std::size_t size() const { ++ return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); ++ } ++ ++ /** ++ Returns a pointer to the output buffer content. No terminating null ++ character is appended. ++ */ ++ const char *data() const { return str_; } ++ ++ /** ++ Returns a pointer to the output buffer content with terminating null ++ character appended. ++ */ ++ const char *c_str() const { ++ buffer_[BUFFER_SIZE - 1] = '\0'; ++ return str_; ++ } ++ ++ /** ++ \rst ++ Returns the content of the output buffer as an ``std::string``. ++ \endrst ++ */ ++ std::string str() const { return std::string(str_, size()); } ++}; ++ ++// Formats a decimal integer value writing into buffer and returns ++// a pointer to the end of the formatted string. This function doesn't ++// write a terminating null character. ++template ++inline void format_decimal(char *&buffer, T value) { ++ typedef typename internal::IntTraits::MainType MainType; ++ MainType abs_value = static_cast(value); ++ if (internal::is_negative(value)) { ++ *buffer++ = '-'; ++ abs_value = 0 - abs_value; ++ } ++ if (abs_value < 100) { ++ if (abs_value < 10) { ++ *buffer++ = static_cast('0' + abs_value); ++ return; ++ } ++ unsigned index = static_cast(abs_value * 2); ++ *buffer++ = internal::Data::DIGITS[index]; ++ *buffer++ = internal::Data::DIGITS[index + 1]; ++ return; ++ } ++ unsigned num_digits = internal::count_digits(abs_value); ++ internal::format_decimal(buffer, abs_value, num_digits); ++ buffer += num_digits; ++} ++ ++/** ++ \rst ++ Returns a named argument for formatting functions. ++ ++ **Example**:: ++ ++ print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); ++ ++ \endrst ++ */ ++template ++inline internal::NamedArgWithType arg(StringRef name, const T &arg) { ++ return internal::NamedArgWithType(name, arg); ++} ++ ++template ++inline internal::NamedArgWithType arg(WStringRef name, const T &arg) { ++ return internal::NamedArgWithType(name, arg); ++} ++ ++// The following two functions are deleted intentionally to disable ++// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. ++template ++void arg(StringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; ++template ++void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; ++} ++ ++#if FMT_GCC_VERSION ++// Use the system_header pragma to suppress warnings about variadic macros ++// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't ++// work. It is used at the end because we want to suppress as little warnings ++// as possible. ++# pragma GCC system_header ++#endif ++ ++// This is used to work around VC++ bugs in handling variadic macros. ++#define FMT_EXPAND(args) args ++ ++// Returns the number of arguments. ++// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. ++#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) ++#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) ++#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N ++#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ++ ++#define FMT_FOR_EACH_(N, f, ...) \ ++ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) ++#define FMT_FOR_EACH(f, ...) \ ++ FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) ++ ++#define FMT_ADD_ARG_NAME(type, index) type arg##index ++#define FMT_GET_ARG_NAME(type, index) arg##index ++ ++#if FMT_USE_VARIADIC_TEMPLATES ++# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ ++ template \ ++ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ ++ const Args & ... args) { \ ++ typedef fmt::internal::ArgArray ArgArray; \ ++ typename ArgArray::Type array{ \ ++ ArgArray::template make >(args)...}; \ ++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ ++ fmt::ArgList(fmt::internal::make_type(args...), array)); \ ++ } ++#else ++// Defines a wrapper for a function taking __VA_ARGS__ arguments ++// and n additional arguments of arbitrary types. ++# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ ++ template \ ++ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ ++ FMT_GEN(n, FMT_MAKE_ARG)) { \ ++ fmt::internal::ArgArray::Type arr; \ ++ FMT_GEN(n, FMT_ASSIGN_##Char); \ ++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ ++ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ ++ } ++ ++# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ ++ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ ++ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ ++ } \ ++ FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ ++ FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) ++#endif // FMT_USE_VARIADIC_TEMPLATES ++ ++/** ++ \rst ++ Defines a variadic function with the specified return type, function name ++ and argument types passed as variable arguments to this macro. ++ ++ **Example**:: ++ ++ void print_error(const char *file, int line, const char *format, ++ fmt::ArgList args) { ++ fmt::print("{}: {}: ", file, line); ++ fmt::print(format, args); ++ } ++ FMT_VARIADIC(void, print_error, const char *, int, const char *) ++ ++ ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that ++ don't implement variadic templates. You don't have to use this macro if ++ you don't need legacy compiler support and can use variadic templates ++ directly:: ++ ++ template ++ void print_error(const char *file, int line, const char *format, ++ const Args & ... args) { ++ fmt::print("{}: {}: ", file, line); ++ fmt::print(format, args...); ++ } ++ \endrst ++ */ ++#define FMT_VARIADIC(ReturnType, func, ...) \ ++ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) ++ ++#define FMT_VARIADIC_W(ReturnType, func, ...) \ ++ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) ++ ++#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) ++ ++#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) ++ ++/** ++ \rst ++ Convenient macro to capture the arguments' names and values into several ++ ``fmt::arg(name, value)``. ++ ++ **Example**:: ++ ++ int x = 1, y = 2; ++ print("point: ({x}, {y})", FMT_CAPTURE(x, y)); ++ // same as: ++ // print("point: ({x}, {y})", arg("x", x), arg("y", y)); ++ ++ \endrst ++ */ ++#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) ++ ++#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) ++ ++namespace fmt { ++FMT_VARIADIC(std::string, format, CStringRef) ++FMT_VARIADIC_W(std::wstring, format, WCStringRef) ++FMT_VARIADIC(void, print, CStringRef) ++FMT_VARIADIC(void, print, std::FILE *, CStringRef) ++FMT_VARIADIC(void, print_colored, Color, CStringRef) ++ ++namespace internal { ++template ++inline bool is_name_start(Char c) { ++ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; ++} ++ ++// Parses an unsigned integer advancing s to the end of the parsed input. ++// This function assumes that the first character of s is a digit. ++template ++unsigned parse_nonnegative_int(const Char *&s) { ++ assert('0' <= *s && *s <= '9'); ++ unsigned value = 0; ++ do { ++ unsigned new_value = value * 10 + (*s++ - '0'); ++ // Check if value wrapped around. ++ if (new_value < value) { ++ value = (std::numeric_limits::max)(); ++ break; ++ } ++ value = new_value; ++ } while ('0' <= *s && *s <= '9'); ++ // Convert to unsigned to prevent a warning. ++ unsigned max_int = (std::numeric_limits::max)(); ++ if (value > max_int) ++ FMT_THROW(FormatError("number is too big")); ++ return value; ++} ++ ++inline void require_numeric_argument(const Arg &arg, char spec) { ++ if (arg.type > Arg::LAST_NUMERIC_TYPE) { ++ std::string message = ++ fmt::format("format specifier '{}' requires numeric argument", spec); ++ FMT_THROW(fmt::FormatError(message)); ++ } ++} ++ ++template ++void check_sign(const Char *&s, const Arg &arg) { ++ char sign = static_cast(*s); ++ require_numeric_argument(arg, sign); ++ if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { ++ FMT_THROW(FormatError(fmt::format( ++ "format specifier '{}' requires signed argument", sign))); ++ } ++ ++s; ++} ++} // namespace internal ++ ++template ++inline internal::Arg BasicFormatter::get_arg( ++ BasicStringRef arg_name, const char *&error) { ++ if (check_no_auto_index(error)) { ++ map_.init(args()); ++ const internal::Arg *arg = map_.find(arg_name); ++ if (arg) ++ return *arg; ++ error = "argument not found"; ++ } ++ return internal::Arg(); ++} ++ ++template ++inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) { ++ const char *error = FMT_NULL; ++ internal::Arg arg = *s < '0' || *s > '9' ? ++ next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); ++ if (error) { ++ FMT_THROW(FormatError( ++ *s != '}' && *s != ':' ? "invalid format string" : error)); ++ } ++ return arg; ++} ++ ++template ++inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) { ++ assert(internal::is_name_start(*s)); ++ const Char *start = s; ++ Char c; ++ do { ++ c = *++s; ++ } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); ++ const char *error = FMT_NULL; ++ internal::Arg arg = get_arg(BasicStringRef(start, s - start), error); ++ if (error) ++ FMT_THROW(FormatError(error)); ++ return arg; ++} ++ ++template ++const Char *BasicFormatter::format( ++ const Char *&format_str, const internal::Arg &arg) { ++ using internal::Arg; ++ const Char *s = format_str; ++ typename ArgFormatter::SpecType spec; ++ if (*s == ':') { ++ if (arg.type == Arg::CUSTOM) { ++ arg.custom.format(this, arg.custom.value, &s); ++ return s; ++ } ++ ++s; ++ // Parse fill and alignment. ++ if (Char c = *s) { ++ const Char *p = s + 1; ++ spec.align_ = ALIGN_DEFAULT; ++ do { ++ switch (*p) { ++ case '<': ++ spec.align_ = ALIGN_LEFT; ++ break; ++ case '>': ++ spec.align_ = ALIGN_RIGHT; ++ break; ++ case '=': ++ spec.align_ = ALIGN_NUMERIC; ++ break; ++ case '^': ++ spec.align_ = ALIGN_CENTER; ++ break; ++ } ++ if (spec.align_ != ALIGN_DEFAULT) { ++ if (p != s) { ++ if (c == '}') break; ++ if (c == '{') ++ FMT_THROW(FormatError("invalid fill character '{'")); ++ s += 2; ++ spec.fill_ = c; ++ } else ++s; ++ if (spec.align_ == ALIGN_NUMERIC) ++ require_numeric_argument(arg, '='); ++ break; ++ } ++ } while (--p >= s); ++ } ++ ++ // Parse sign. ++ switch (*s) { ++ case '+': ++ check_sign(s, arg); ++ spec.flags_ |= SIGN_FLAG | PLUS_FLAG; ++ break; ++ case '-': ++ check_sign(s, arg); ++ spec.flags_ |= MINUS_FLAG; ++ break; ++ case ' ': ++ check_sign(s, arg); ++ spec.flags_ |= SIGN_FLAG; ++ break; ++ } ++ ++ if (*s == '#') { ++ require_numeric_argument(arg, '#'); ++ spec.flags_ |= HASH_FLAG; ++ ++s; ++ } ++ ++ // Parse zero flag. ++ if (*s == '0') { ++ require_numeric_argument(arg, '0'); ++ spec.align_ = ALIGN_NUMERIC; ++ spec.fill_ = '0'; ++ ++s; ++ } ++ ++ // Parse width. ++ if ('0' <= *s && *s <= '9') { ++ spec.width_ = internal::parse_nonnegative_int(s); ++ } else if (*s == '{') { ++ ++s; ++ Arg width_arg = internal::is_name_start(*s) ? ++ parse_arg_name(s) : parse_arg_index(s); ++ if (*s++ != '}') ++ FMT_THROW(FormatError("invalid format string")); ++ ULongLong value = 0; ++ switch (width_arg.type) { ++ case Arg::INT: ++ if (width_arg.int_value < 0) ++ FMT_THROW(FormatError("negative width")); ++ value = width_arg.int_value; ++ break; ++ case Arg::UINT: ++ value = width_arg.uint_value; ++ break; ++ case Arg::LONG_LONG: ++ if (width_arg.long_long_value < 0) ++ FMT_THROW(FormatError("negative width")); ++ value = width_arg.long_long_value; ++ break; ++ case Arg::ULONG_LONG: ++ value = width_arg.ulong_long_value; ++ break; ++ default: ++ FMT_THROW(FormatError("width is not integer")); ++ } ++ if (value > (std::numeric_limits::max)()) ++ FMT_THROW(FormatError("number is too big")); ++ spec.width_ = static_cast(value); ++ } ++ ++ // Parse precision. ++ if (*s == '.') { ++ ++s; ++ spec.precision_ = 0; ++ if ('0' <= *s && *s <= '9') { ++ spec.precision_ = internal::parse_nonnegative_int(s); ++ } else if (*s == '{') { ++ ++s; ++ Arg precision_arg = internal::is_name_start(*s) ? ++ parse_arg_name(s) : parse_arg_index(s); ++ if (*s++ != '}') ++ FMT_THROW(FormatError("invalid format string")); ++ ULongLong value = 0; ++ switch (precision_arg.type) { ++ case Arg::INT: ++ if (precision_arg.int_value < 0) ++ FMT_THROW(FormatError("negative precision")); ++ value = precision_arg.int_value; ++ break; ++ case Arg::UINT: ++ value = precision_arg.uint_value; ++ break; ++ case Arg::LONG_LONG: ++ if (precision_arg.long_long_value < 0) ++ FMT_THROW(FormatError("negative precision")); ++ value = precision_arg.long_long_value; ++ break; ++ case Arg::ULONG_LONG: ++ value = precision_arg.ulong_long_value; ++ break; ++ default: ++ FMT_THROW(FormatError("precision is not integer")); ++ } ++ if (value > (std::numeric_limits::max)()) ++ FMT_THROW(FormatError("number is too big")); ++ spec.precision_ = static_cast(value); ++ } else { ++ FMT_THROW(FormatError("missing precision specifier")); ++ } ++ if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { ++ FMT_THROW(FormatError( ++ fmt::format("precision not allowed in {} format specifier", ++ arg.type == Arg::POINTER ? "pointer" : "integer"))); ++ } ++ } ++ ++ // Parse type. ++ if (*s != '}' && *s) ++ spec.type_ = static_cast(*s++); ++ } ++ ++ if (*s++ != '}') ++ FMT_THROW(FormatError("missing '}' in format string")); ++ ++ // Format argument. ++ ArgFormatter(*this, spec, s - 1).visit(arg); ++ return s; ++} ++ ++template ++void BasicFormatter::format(BasicCStringRef format_str) { ++ const Char *s = format_str.c_str(); ++ const Char *start = s; ++ while (*s) { ++ Char c = *s++; ++ if (c != '{' && c != '}') continue; ++ if (*s == c) { ++ write(writer_, start, s); ++ start = ++s; ++ continue; ++ } ++ if (c == '}') ++ FMT_THROW(FormatError("unmatched '}' in format string")); ++ write(writer_, start, s - 1); ++ internal::Arg arg = internal::is_name_start(*s) ? ++ parse_arg_name(s) : parse_arg_index(s); ++ start = s = format(s, arg); ++ } ++ write(writer_, start, s); ++} ++ ++template ++struct ArgJoin { ++ It first; ++ It last; ++ BasicCStringRef sep; ++ ++ ArgJoin(It first, It last, const BasicCStringRef& sep) : ++ first(first), ++ last(last), ++ sep(sep) {} ++}; ++ ++template ++ArgJoin join(It first, It last, const BasicCStringRef& sep) { ++ return ArgJoin(first, last, sep); ++} ++ ++template ++ArgJoin join(It first, It last, const BasicCStringRef& sep) { ++ return ArgJoin(first, last, sep); ++} ++ ++#if FMT_HAS_GXX_CXX11 ++template ++auto join(const Range& range, const BasicCStringRef& sep) ++ -> ArgJoin { ++ return join(std::begin(range), std::end(range), sep); ++} ++ ++template ++auto join(const Range& range, const BasicCStringRef& sep) ++ -> ArgJoin { ++ return join(std::begin(range), std::end(range), sep); ++} ++#endif ++ ++template ++void format_arg(fmt::BasicFormatter &f, ++ const Char *&format_str, const ArgJoin& e) { ++ const Char* end = format_str; ++ if (*end == ':') ++ ++end; ++ while (*end && *end != '}') ++ ++end; ++ if (*end != '}') ++ FMT_THROW(FormatError("missing '}' in format string")); ++ ++ It it = e.first; ++ if (it != e.last) { ++ const Char* save = format_str; ++ f.format(format_str, internal::MakeArg >(*it++)); ++ while (it != e.last) { ++ f.writer().write(e.sep); ++ format_str = save; ++ f.format(format_str, internal::MakeArg >(*it++)); ++ } ++ } ++ format_str = end + 1; ++} ++} // namespace fmt ++ ++#if FMT_USE_USER_DEFINED_LITERALS ++namespace fmt { ++namespace internal { ++ ++template ++struct UdlFormat { ++ const Char *str; ++ ++ template ++ auto operator()(Args && ... args) const ++ -> decltype(format(str, std::forward(args)...)) { ++ return format(str, std::forward(args)...); ++ } ++}; ++ ++template ++struct UdlArg { ++ const Char *str; ++ ++ template ++ NamedArgWithType operator=(T &&value) const { ++ return {str, std::forward(value)}; ++ } ++}; ++ ++} // namespace internal ++ ++inline namespace literals { ++ ++/** ++ \rst ++ C++11 literal equivalent of :func:`fmt::format`. ++ ++ **Example**:: ++ ++ using namespace fmt::literals; ++ std::string message = "The answer is {}"_format(42); ++ \endrst ++ */ ++inline internal::UdlFormat ++operator"" _format(const char *s, std::size_t) { return {s}; } ++inline internal::UdlFormat ++operator"" _format(const wchar_t *s, std::size_t) { return {s}; } ++ ++/** ++ \rst ++ C++11 literal equivalent of :func:`fmt::arg`. ++ ++ **Example**:: ++ ++ using namespace fmt::literals; ++ print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); ++ \endrst ++ */ ++inline internal::UdlArg ++operator"" _a(const char *s, std::size_t) { return {s}; } ++inline internal::UdlArg ++operator"" _a(const wchar_t *s, std::size_t) { return {s}; } ++ ++} // inline namespace literals ++} // namespace fmt ++#endif // FMT_USE_USER_DEFINED_LITERALS ++ ++// Restore warnings. ++#if FMT_GCC_VERSION >= 406 ++# pragma GCC diagnostic pop ++#endif ++ ++#if defined(__clang__) && !defined(FMT_ICC_VERSION) ++# pragma clang diagnostic pop ++#endif ++ ++#ifdef FMT_HEADER_ONLY ++# define FMT_FUNC inline ++# include "format.cc" ++#else ++# define FMT_FUNC ++#endif ++ ++#endif // FMT_FORMAT_H_ +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc +new file mode 100644 +index 00000000..2d443f73 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.cc +@@ -0,0 +1,35 @@ ++/* ++ Formatting library for C++ - std::ostream support ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ For the license information refer to format.h. ++ */ ++ ++#include "ostream.h" ++ ++namespace fmt { ++ ++namespace internal { ++FMT_FUNC void write(std::ostream &os, Writer &w) { ++ const char *data = w.data(); ++ typedef internal::MakeUnsigned::Type UnsignedStreamSize; ++ UnsignedStreamSize size = w.size(); ++ UnsignedStreamSize max_size = ++ internal::to_unsigned((std::numeric_limits::max)()); ++ do { ++ UnsignedStreamSize n = size <= max_size ? size : max_size; ++ os.write(data, static_cast(n)); ++ data += n; ++ size -= n; ++ } while (size != 0); ++} ++} ++ ++FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { ++ MemoryWriter w; ++ w.write(format_str, args); ++ internal::write(os, w); ++} ++} // namespace fmt +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h +new file mode 100644 +index 00000000..84a02d17 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/ostream.h +@@ -0,0 +1,105 @@ ++/* ++ Formatting library for C++ - std::ostream support ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ For the license information refer to format.h. ++ */ ++ ++#ifndef FMT_OSTREAM_H_ ++#define FMT_OSTREAM_H_ ++ ++#include "format.h" ++#include ++ ++namespace fmt { ++ ++namespace internal { ++ ++template ++class FormatBuf : public std::basic_streambuf { ++ private: ++ typedef typename std::basic_streambuf::int_type int_type; ++ typedef typename std::basic_streambuf::traits_type traits_type; ++ ++ Buffer &buffer_; ++ ++ public: ++ FormatBuf(Buffer &buffer) : buffer_(buffer) {} ++ ++ protected: ++ // The put-area is actually always empty. This makes the implementation ++ // simpler and has the advantage that the streambuf and the buffer are always ++ // in sync and sputc never writes into uninitialized memory. The obvious ++ // disadvantage is that each call to sputc always results in a (virtual) call ++ // to overflow. There is no disadvantage here for sputn since this always ++ // results in a call to xsputn. ++ ++ int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { ++ if (!traits_type::eq_int_type(ch, traits_type::eof())) ++ buffer_.push_back(static_cast(ch)); ++ return ch; ++ } ++ ++ std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { ++ buffer_.append(s, s + count); ++ return count; ++ } ++}; ++ ++Yes &convert(std::ostream &); ++ ++struct DummyStream : std::ostream { ++ DummyStream(); // Suppress a bogus warning in MSVC. ++ // Hide all operator<< overloads from std::ostream. ++ void operator<<(Null<>); ++}; ++ ++No &operator<<(std::ostream &, int); ++ ++template ++struct ConvertToIntImpl { ++ // Convert to int only if T doesn't have an overloaded operator<<. ++ enum { ++ value = sizeof(convert(get() << get())) == sizeof(No) ++ }; ++}; ++ ++// Write the content of w to os. ++FMT_API void write(std::ostream &os, Writer &w); ++} // namespace internal ++ ++// Formats a value. ++template ++void format_arg(BasicFormatter &f, ++ const Char *&format_str, const T &value) { ++ internal::MemoryBuffer buffer; ++ ++ internal::FormatBuf format_buf(buffer); ++ std::basic_ostream output(&format_buf); ++ output << value; ++ ++ BasicStringRef str(&buffer[0], buffer.size()); ++ typedef internal::MakeArg< BasicFormatter > MakeArg; ++ format_str = f.format(format_str, MakeArg(str)); ++} ++ ++/** ++ \rst ++ Prints formatted data to the stream *os*. ++ ++ **Example**:: ++ ++ print(cerr, "Don't {}!", "panic"); ++ \endrst ++ */ ++FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); ++FMT_VARIADIC(void, print, std::ostream &, CStringRef) ++} // namespace fmt ++ ++#ifdef FMT_HEADER_ONLY ++# include "ostream.cc" ++#endif ++ ++#endif // FMT_OSTREAM_H_ +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc +new file mode 100644 +index 00000000..356668c1 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.cc +@@ -0,0 +1,241 @@ ++/* ++ A C++ interface to POSIX functions. ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ For the license information refer to format.h. ++ */ ++ ++// Disable bogus MSVC warnings. ++#ifndef _CRT_SECURE_NO_WARNINGS ++# define _CRT_SECURE_NO_WARNINGS ++#endif ++ ++#include "posix.h" ++ ++#include ++#include ++#include ++ ++#ifndef _WIN32 ++# include ++#else ++# ifndef WIN32_LEAN_AND_MEAN ++# define WIN32_LEAN_AND_MEAN ++# endif ++# include ++# include ++ ++# define O_CREAT _O_CREAT ++# define O_TRUNC _O_TRUNC ++ ++# ifndef S_IRUSR ++# define S_IRUSR _S_IREAD ++# endif ++ ++# ifndef S_IWUSR ++# define S_IWUSR _S_IWRITE ++# endif ++ ++# ifdef __MINGW32__ ++# define _SH_DENYNO 0x40 ++# endif ++ ++#endif // _WIN32 ++ ++#ifdef fileno ++# undef fileno ++#endif ++ ++namespace { ++#ifdef _WIN32 ++// Return type of read and write functions. ++typedef int RWResult; ++ ++// On Windows the count argument to read and write is unsigned, so convert ++// it from size_t preventing integer overflow. ++inline unsigned convert_rwcount(std::size_t count) { ++ return count <= UINT_MAX ? static_cast(count) : UINT_MAX; ++} ++#else ++// Return type of read and write functions. ++typedef ssize_t RWResult; ++ ++inline std::size_t convert_rwcount(std::size_t count) { return count; } ++#endif ++} ++ ++fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { ++ if (file_ && FMT_SYSTEM(fclose(file_)) != 0) ++ fmt::report_system_error(errno, "cannot close file"); ++} ++ ++fmt::BufferedFile::BufferedFile( ++ fmt::CStringRef filename, fmt::CStringRef mode) { ++ FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); ++ if (!file_) ++ FMT_THROW(SystemError(errno, "cannot open file {}", filename)); ++} ++ ++void fmt::BufferedFile::close() { ++ if (!file_) ++ return; ++ int result = FMT_SYSTEM(fclose(file_)); ++ file_ = FMT_NULL; ++ if (result != 0) ++ FMT_THROW(SystemError(errno, "cannot close file")); ++} ++ ++// A macro used to prevent expansion of fileno on broken versions of MinGW. ++#define FMT_ARGS ++ ++int fmt::BufferedFile::fileno() const { ++ int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); ++ if (fd == -1) ++ FMT_THROW(SystemError(errno, "cannot get file descriptor")); ++ return fd; ++} ++ ++fmt::File::File(fmt::CStringRef path, int oflag) { ++ int mode = S_IRUSR | S_IWUSR; ++#if defined(_WIN32) && !defined(__MINGW32__) ++ fd_ = -1; ++ FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); ++#else ++ FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); ++#endif ++ if (fd_ == -1) ++ FMT_THROW(SystemError(errno, "cannot open file {}", path)); ++} ++ ++fmt::File::~File() FMT_NOEXCEPT { ++ // Don't retry close in case of EINTR! ++ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html ++ if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) ++ fmt::report_system_error(errno, "cannot close file"); ++} ++ ++void fmt::File::close() { ++ if (fd_ == -1) ++ return; ++ // Don't retry close in case of EINTR! ++ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html ++ int result = FMT_POSIX_CALL(close(fd_)); ++ fd_ = -1; ++ if (result != 0) ++ FMT_THROW(SystemError(errno, "cannot close file")); ++} ++ ++fmt::LongLong fmt::File::size() const { ++#ifdef _WIN32 ++ // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT ++ // is less than 0x0500 as is the case with some default MinGW builds. ++ // Both functions support large file sizes. ++ DWORD size_upper = 0; ++ HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); ++ DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); ++ if (size_lower == INVALID_FILE_SIZE) { ++ DWORD error = GetLastError(); ++ if (error != NO_ERROR) ++ FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); ++ } ++ fmt::ULongLong long_size = size_upper; ++ return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; ++#else ++ typedef struct stat Stat; ++ Stat file_stat = Stat(); ++ if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) ++ FMT_THROW(SystemError(errno, "cannot get file attributes")); ++ FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), ++ "return type of File::size is not large enough"); ++ return file_stat.st_size; ++#endif ++} ++ ++std::size_t fmt::File::read(void *buffer, std::size_t count) { ++ RWResult result = 0; ++ FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); ++ if (result < 0) ++ FMT_THROW(SystemError(errno, "cannot read from file")); ++ return internal::to_unsigned(result); ++} ++ ++std::size_t fmt::File::write(const void *buffer, std::size_t count) { ++ RWResult result = 0; ++ FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); ++ if (result < 0) ++ FMT_THROW(SystemError(errno, "cannot write to file")); ++ return internal::to_unsigned(result); ++} ++ ++fmt::File fmt::File::dup(int fd) { ++ // Don't retry as dup doesn't return EINTR. ++ // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html ++ int new_fd = FMT_POSIX_CALL(dup(fd)); ++ if (new_fd == -1) ++ FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); ++ return File(new_fd); ++} ++ ++void fmt::File::dup2(int fd) { ++ int result = 0; ++ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); ++ if (result == -1) { ++ FMT_THROW(SystemError(errno, ++ "cannot duplicate file descriptor {} to {}", fd_, fd)); ++ } ++} ++ ++void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { ++ int result = 0; ++ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); ++ if (result == -1) ++ ec = ErrorCode(errno); ++} ++ ++void fmt::File::pipe(File &read_end, File &write_end) { ++ // Close the descriptors first to make sure that assignments don't throw ++ // and there are no leaks. ++ read_end.close(); ++ write_end.close(); ++ int fds[2] = {}; ++#ifdef _WIN32 ++ // Make the default pipe capacity same as on Linux 2.6.11+. ++ enum { DEFAULT_CAPACITY = 65536 }; ++ int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); ++#else ++ // Don't retry as the pipe function doesn't return EINTR. ++ // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html ++ int result = FMT_POSIX_CALL(pipe(fds)); ++#endif ++ if (result != 0) ++ FMT_THROW(SystemError(errno, "cannot create pipe")); ++ // The following assignments don't throw because read_fd and write_fd ++ // are closed. ++ read_end = File(fds[0]); ++ write_end = File(fds[1]); ++} ++ ++fmt::BufferedFile fmt::File::fdopen(const char *mode) { ++ // Don't retry as fdopen doesn't return EINTR. ++ FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); ++ if (!f) ++ FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); ++ BufferedFile file(f); ++ fd_ = -1; ++ return file; ++} ++ ++long fmt::getpagesize() { ++#ifdef _WIN32 ++ SYSTEM_INFO si; ++ GetSystemInfo(&si); ++ return si.dwPageSize; ++#else ++ long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); ++ if (size < 0) ++ FMT_THROW(SystemError(errno, "cannot get memory page size")); ++ return size; ++#endif ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h +new file mode 100644 +index 00000000..88512de5 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/posix.h +@@ -0,0 +1,367 @@ ++/* ++ A C++ interface to POSIX functions. ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ For the license information refer to format.h. ++ */ ++ ++#ifndef FMT_POSIX_H_ ++#define FMT_POSIX_H_ ++ ++#if defined(__MINGW32__) || defined(__CYGWIN__) ++// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. ++# undef __STRICT_ANSI__ ++#endif ++ ++#include ++#include // for O_RDONLY ++#include // for locale_t ++#include ++#include // for strtod_l ++ ++#include ++ ++#if defined __APPLE__ || defined(__FreeBSD__) ++# include // for LC_NUMERIC_MASK on OS X ++#endif ++ ++#include "format.h" ++ ++#ifndef FMT_POSIX ++# if defined(_WIN32) && !defined(__MINGW32__) ++// Fix warnings about deprecated symbols. ++# define FMT_POSIX(call) _##call ++# else ++# define FMT_POSIX(call) call ++# endif ++#endif ++ ++// Calls to system functions are wrapped in FMT_SYSTEM for testability. ++#ifdef FMT_SYSTEM ++# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) ++#else ++# define FMT_SYSTEM(call) call ++# ifdef _WIN32 ++// Fix warnings about deprecated symbols. ++# define FMT_POSIX_CALL(call) ::_##call ++# else ++# define FMT_POSIX_CALL(call) ::call ++# endif ++#endif ++ ++// Retries the expression while it evaluates to error_result and errno ++// equals to EINTR. ++#ifndef _WIN32 ++# define FMT_RETRY_VAL(result, expression, error_result) \ ++ do { \ ++ result = (expression); \ ++ } while (result == error_result && errno == EINTR) ++#else ++# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) ++#endif ++ ++#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) ++ ++namespace fmt { ++ ++// An error code. ++class ErrorCode { ++ private: ++ int value_; ++ ++ public: ++ explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} ++ ++ int get() const FMT_NOEXCEPT { return value_; } ++}; ++ ++// A buffered file. ++class BufferedFile { ++ private: ++ FILE *file_; ++ ++ friend class File; ++ ++ explicit BufferedFile(FILE *f) : file_(f) {} ++ ++ public: ++ // Constructs a BufferedFile object which doesn't represent any file. ++ BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} ++ ++ // Destroys the object closing the file it represents if any. ++ FMT_API ~BufferedFile() FMT_NOEXCEPT; ++ ++#if !FMT_USE_RVALUE_REFERENCES ++ // Emulate a move constructor and a move assignment operator if rvalue ++ // references are not supported. ++ ++ private: ++ // A proxy object to emulate a move constructor. ++ // It is private to make it impossible call operator Proxy directly. ++ struct Proxy { ++ FILE *file; ++ }; ++ ++public: ++ // A "move constructor" for moving from a temporary. ++ BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} ++ ++ // A "move constructor" for moving from an lvalue. ++ BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { ++ f.file_ = FMT_NULL; ++ } ++ ++ // A "move assignment operator" for moving from a temporary. ++ BufferedFile &operator=(Proxy p) { ++ close(); ++ file_ = p.file; ++ return *this; ++ } ++ ++ // A "move assignment operator" for moving from an lvalue. ++ BufferedFile &operator=(BufferedFile &other) { ++ close(); ++ file_ = other.file_; ++ other.file_ = FMT_NULL; ++ return *this; ++ } ++ ++ // Returns a proxy object for moving from a temporary: ++ // BufferedFile file = BufferedFile(...); ++ operator Proxy() FMT_NOEXCEPT { ++ Proxy p = {file_}; ++ file_ = FMT_NULL; ++ return p; ++ } ++ ++#else ++ private: ++ FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); ++ ++ public: ++ BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { ++ other.file_ = FMT_NULL; ++ } ++ ++ BufferedFile& operator=(BufferedFile &&other) { ++ close(); ++ file_ = other.file_; ++ other.file_ = FMT_NULL; ++ return *this; ++ } ++#endif ++ ++ // Opens a file. ++ FMT_API BufferedFile(CStringRef filename, CStringRef mode); ++ ++ // Closes the file. ++ FMT_API void close(); ++ ++ // Returns the pointer to a FILE object representing this file. ++ FILE *get() const FMT_NOEXCEPT { return file_; } ++ ++ // We place parentheses around fileno to workaround a bug in some versions ++ // of MinGW that define fileno as a macro. ++ FMT_API int (fileno)() const; ++ ++ void print(CStringRef format_str, const ArgList &args) { ++ fmt::print(file_, format_str, args); ++ } ++ FMT_VARIADIC(void, print, CStringRef) ++}; ++ ++// A file. Closed file is represented by a File object with descriptor -1. ++// Methods that are not declared with FMT_NOEXCEPT may throw ++// fmt::SystemError in case of failure. Note that some errors such as ++// closing the file multiple times will cause a crash on Windows rather ++// than an exception. You can get standard behavior by overriding the ++// invalid parameter handler with _set_invalid_parameter_handler. ++class File { ++ private: ++ int fd_; // File descriptor. ++ ++ // Constructs a File object with a given descriptor. ++ explicit File(int fd) : fd_(fd) {} ++ ++ public: ++ // Possible values for the oflag argument to the constructor. ++ enum { ++ RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. ++ WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. ++ RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. ++ }; ++ ++ // Constructs a File object which doesn't represent any file. ++ File() FMT_NOEXCEPT : fd_(-1) {} ++ ++ // Opens a file and constructs a File object representing this file. ++ FMT_API File(CStringRef path, int oflag); ++ ++#if !FMT_USE_RVALUE_REFERENCES ++ // Emulate a move constructor and a move assignment operator if rvalue ++ // references are not supported. ++ ++ private: ++ // A proxy object to emulate a move constructor. ++ // It is private to make it impossible call operator Proxy directly. ++ struct Proxy { ++ int fd; ++ }; ++ ++ public: ++ // A "move constructor" for moving from a temporary. ++ File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} ++ ++ // A "move constructor" for moving from an lvalue. ++ File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { ++ other.fd_ = -1; ++ } ++ ++ // A "move assignment operator" for moving from a temporary. ++ File &operator=(Proxy p) { ++ close(); ++ fd_ = p.fd; ++ return *this; ++ } ++ ++ // A "move assignment operator" for moving from an lvalue. ++ File &operator=(File &other) { ++ close(); ++ fd_ = other.fd_; ++ other.fd_ = -1; ++ return *this; ++ } ++ ++ // Returns a proxy object for moving from a temporary: ++ // File file = File(...); ++ operator Proxy() FMT_NOEXCEPT { ++ Proxy p = {fd_}; ++ fd_ = -1; ++ return p; ++ } ++ ++#else ++ private: ++ FMT_DISALLOW_COPY_AND_ASSIGN(File); ++ ++ public: ++ File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { ++ other.fd_ = -1; ++ } ++ ++ File& operator=(File &&other) { ++ close(); ++ fd_ = other.fd_; ++ other.fd_ = -1; ++ return *this; ++ } ++#endif ++ ++ // Destroys the object closing the file it represents if any. ++ FMT_API ~File() FMT_NOEXCEPT; ++ ++ // Returns the file descriptor. ++ int descriptor() const FMT_NOEXCEPT { return fd_; } ++ ++ // Closes the file. ++ FMT_API void close(); ++ ++ // Returns the file size. The size has signed type for consistency with ++ // stat::st_size. ++ FMT_API LongLong size() const; ++ ++ // Attempts to read count bytes from the file into the specified buffer. ++ FMT_API std::size_t read(void *buffer, std::size_t count); ++ ++ // Attempts to write count bytes from the specified buffer to the file. ++ FMT_API std::size_t write(const void *buffer, std::size_t count); ++ ++ // Duplicates a file descriptor with the dup function and returns ++ // the duplicate as a file object. ++ FMT_API static File dup(int fd); ++ ++ // Makes fd be the copy of this file descriptor, closing fd first if ++ // necessary. ++ FMT_API void dup2(int fd); ++ ++ // Makes fd be the copy of this file descriptor, closing fd first if ++ // necessary. ++ FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; ++ ++ // Creates a pipe setting up read_end and write_end file objects for reading ++ // and writing respectively. ++ FMT_API static void pipe(File &read_end, File &write_end); ++ ++ // Creates a BufferedFile object associated with this file and detaches ++ // this File object from the file. ++ FMT_API BufferedFile fdopen(const char *mode); ++}; ++ ++// Returns the memory page size. ++long getpagesize(); ++ ++#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \ ++ !defined(__ANDROID__) && !defined(__CYGWIN__) ++# define FMT_LOCALE ++#endif ++ ++#ifdef FMT_LOCALE ++// A "C" numeric locale. ++class Locale { ++ private: ++# ifdef _MSC_VER ++ typedef _locale_t locale_t; ++ ++ enum { LC_NUMERIC_MASK = LC_NUMERIC }; ++ ++ static locale_t newlocale(int category_mask, const char *locale, locale_t) { ++ return _create_locale(category_mask, locale); ++ } ++ ++ static void freelocale(locale_t locale) { ++ _free_locale(locale); ++ } ++ ++ static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { ++ return _strtod_l(nptr, endptr, locale); ++ } ++# endif ++ ++ locale_t locale_; ++ ++ FMT_DISALLOW_COPY_AND_ASSIGN(Locale); ++ ++ public: ++ typedef locale_t Type; ++ ++ Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { ++ if (!locale_) ++ FMT_THROW(fmt::SystemError(errno, "cannot create locale")); ++ } ++ ~Locale() { freelocale(locale_); } ++ ++ Type get() const { return locale_; } ++ ++ // Converts string to floating-point number and advances str past the end ++ // of the parsed input. ++ double strtod(const char *&str) const { ++ char *end = FMT_NULL; ++ double result = strtod_l(str, &end, locale_); ++ str = end; ++ return result; ++ } ++}; ++#endif // FMT_LOCALE ++} // namespace fmt ++ ++#if !FMT_USE_RVALUE_REFERENCES ++namespace std { ++// For compatibility with C++98. ++inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } ++inline fmt::File &move(fmt::File &f) { return f; } ++} ++#endif ++ ++#endif // FMT_POSIX_H_ +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h +new file mode 100644 +index 00000000..c98b0e01 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/bundled/time.h +@@ -0,0 +1,143 @@ ++/* ++ Formatting library for C++ - time formatting ++ ++ Copyright (c) 2012 - 2016, Victor Zverovich ++ All rights reserved. ++ ++ For the license information refer to format.h. ++ */ ++ ++#ifndef FMT_TIME_H_ ++#define FMT_TIME_H_ ++ ++#include "format.h" ++#include ++ ++#ifdef _MSC_VER ++# pragma warning(push) ++# pragma warning(disable: 4702) // unreachable code ++# pragma warning(disable: 4996) // "deprecated" functions ++#endif ++ ++namespace fmt { ++template ++void format_arg(BasicFormatter &f, ++ const char *&format_str, const std::tm &tm) { ++ if (*format_str == ':') ++ ++format_str; ++ const char *end = format_str; ++ while (*end && *end != '}') ++ ++end; ++ if (*end != '}') ++ FMT_THROW(FormatError("missing '}' in format string")); ++ internal::MemoryBuffer format; ++ format.append(format_str, end + 1); ++ format[format.size() - 1] = '\0'; ++ Buffer &buffer = f.writer().buffer(); ++ std::size_t start = buffer.size(); ++ for (;;) { ++ std::size_t size = buffer.capacity() - start; ++ std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); ++ if (count != 0) { ++ buffer.resize(start + count); ++ break; ++ } ++ if (size >= format.size() * 256) { ++ // If the buffer is 256 times larger than the format string, assume ++ // that `strftime` gives an empty result. There doesn't seem to be a ++ // better way to distinguish the two cases: ++ // https://github.com/fmtlib/fmt/issues/367 ++ break; ++ } ++ const std::size_t MIN_GROWTH = 10; ++ buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); ++ } ++ format_str = end + 1; ++} ++ ++namespace internal{ ++inline Null<> localtime_r(...) { return Null<>(); } ++inline Null<> localtime_s(...) { return Null<>(); } ++inline Null<> gmtime_r(...) { return Null<>(); } ++inline Null<> gmtime_s(...) { return Null<>(); } ++} ++ ++// Thread-safe replacement for std::localtime ++inline std::tm localtime(std::time_t time) { ++ struct LocalTime { ++ std::time_t time_; ++ std::tm tm_; ++ ++ LocalTime(std::time_t t): time_(t) {} ++ ++ bool run() { ++ using namespace fmt::internal; ++ return handle(localtime_r(&time_, &tm_)); ++ } ++ ++ bool handle(std::tm *tm) { return tm != FMT_NULL; } ++ ++ bool handle(internal::Null<>) { ++ using namespace fmt::internal; ++ return fallback(localtime_s(&tm_, &time_)); ++ } ++ ++ bool fallback(int res) { return res == 0; } ++ ++ bool fallback(internal::Null<>) { ++ using namespace fmt::internal; ++ std::tm *tm = std::localtime(&time_); ++ if (tm) tm_ = *tm; ++ return tm != FMT_NULL; ++ } ++ }; ++ LocalTime lt(time); ++ if (lt.run()) ++ return lt.tm_; ++ // Too big time values may be unsupported. ++ FMT_THROW(fmt::FormatError("time_t value out of range")); ++ return std::tm(); ++} ++ ++// Thread-safe replacement for std::gmtime ++inline std::tm gmtime(std::time_t time) { ++ struct GMTime { ++ std::time_t time_; ++ std::tm tm_; ++ ++ GMTime(std::time_t t): time_(t) {} ++ ++ bool run() { ++ using namespace fmt::internal; ++ return handle(gmtime_r(&time_, &tm_)); ++ } ++ ++ bool handle(std::tm *tm) { return tm != FMT_NULL; } ++ ++ bool handle(internal::Null<>) { ++ using namespace fmt::internal; ++ return fallback(gmtime_s(&tm_, &time_)); ++ } ++ ++ bool fallback(int res) { return res == 0; } ++ ++ bool fallback(internal::Null<>) { ++ std::tm *tm = std::gmtime(&time_); ++ if (tm != FMT_NULL) tm_ = *tm; ++ return tm != FMT_NULL; ++ } ++ }; ++ GMTime gt(time); ++ if (gt.run()) ++ return gt.tm_; ++ // Too big time values may be unsupported. ++ FMT_THROW(fmt::FormatError("time_t value out of range")); ++ return std::tm(); ++} ++} //namespace fmt ++ ++#ifdef _MSC_VER ++# pragma warning(pop) ++#endif ++ ++#endif // FMT_TIME_H_ +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h b/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h +new file mode 100644 +index 00000000..a4ee4673 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/fmt.h +@@ -0,0 +1,28 @@ ++// ++// Copyright(c) 2016 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// ++// Include a bundled header-only copy of fmtlib or an external one. ++// By default spdlog include its own copy. ++// ++ ++#if !defined(SPDLOG_FMT_EXTERNAL) ++ ++#ifndef FMT_HEADER_ONLY ++#define FMT_HEADER_ONLY ++#endif ++#ifndef FMT_USE_WINDOWS_H ++#define FMT_USE_WINDOWS_H 0 ++#endif ++#include "spdlog/fmt/bundled/format.h" ++ ++#else //external fmtlib ++ ++#include ++ ++#endif ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h b/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h +new file mode 100644 +index 00000000..49b5e98c +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/fmt/ostr.h +@@ -0,0 +1,17 @@ ++// ++// Copyright(c) 2016 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// include external or bundled copy of fmtlib's ostream support ++// ++#if !defined(SPDLOG_FMT_EXTERNAL) ++#include "spdlog/fmt/fmt.h" ++#include "spdlog/fmt/bundled/ostream.h" ++#else ++#include ++#endif ++ ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/formatter.h b/external/spdlog-0.14.0/include/spdlog/formatter.h +new file mode 100644 +index 00000000..6bba9025 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/formatter.h +@@ -0,0 +1,47 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/details/log_msg.h" ++ ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace details ++{ ++class flag_formatter; ++} ++ ++class formatter ++{ ++public: ++ virtual ~formatter() {} ++ virtual void format(details::log_msg& msg) = 0; ++}; ++ ++class pattern_formatter SPDLOG_FINAL : public formatter ++{ ++ ++public: ++ explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local); ++ pattern_formatter(const pattern_formatter&) = delete; ++ pattern_formatter& operator=(const pattern_formatter&) = delete; ++ void format(details::log_msg& msg) override; ++private: ++ const std::string _pattern; ++ const pattern_time_type _pattern_time; ++ std::vector> _formatters; ++ std::tm get_time(details::log_msg& msg); ++ void handle_flag(char flag); ++ void compile_pattern(const std::string& pattern); ++}; ++} ++ ++#include "spdlog/details/pattern_formatter_impl.h" ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/logger.h b/external/spdlog-0.14.0/include/spdlog/logger.h +new file mode 100644 +index 00000000..642208eb +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/logger.h +@@ -0,0 +1,132 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler()) ++// Has name, log level, vector of std::shared sink pointers and formatter ++// Upon each log write the logger: ++// 1. Checks if its log level is enough to log the message ++// 2. Format the message using the formatter function ++// 3. Pass the formatted message to its sinks to performa the actual logging ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/common.h" ++ ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++ ++class logger ++{ ++public: ++ logger(const std::string& logger_name, sink_ptr single_sink); ++ logger(const std::string& name, sinks_init_list); ++ template ++ logger(const std::string& name, const It& begin, const It& end); ++ ++ virtual ~logger(); ++ logger(const logger&) = delete; ++ logger& operator=(const logger&) = delete; ++ ++ ++ template void log(level::level_enum lvl, const char* fmt, const Args&... args); ++ template void log(level::level_enum lvl, const char* msg); ++ template void trace(const char* fmt, const Arg1&, const Args&... args); ++ template void debug(const char* fmt, const Arg1&, const Args&... args); ++ template void info(const char* fmt, const Arg1&, const Args&... args); ++ template void warn(const char* fmt, const Arg1&, const Args&... args); ++ template void error(const char* fmt, const Arg1&, const Args&... args); ++ template void critical(const char* fmt, const Arg1&, const Args&... args); ++ ++ template void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args); ++ template void log_if(const bool flag, level::level_enum lvl, const char* msg); ++ template void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ template void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ template void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ template void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ template void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ template void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); ++ ++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ template void log(level::level_enum lvl, const wchar_t* msg); ++ template void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args); ++ template void trace(const wchar_t* fmt, const Args&... args); ++ template void debug(const wchar_t* fmt, const Args&... args); ++ template void info(const wchar_t* fmt, const Args&... args); ++ template void warn(const wchar_t* fmt, const Args&... args); ++ template void error(const wchar_t* fmt, const Args&... args); ++ template void critical(const wchar_t* fmt, const Args&... args); ++ ++ template void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg); ++ template void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args); ++ template void trace_if(const bool flag, const wchar_t* fmt, const Args&... args); ++ template void debug_if(const bool flag, const wchar_t* fmt, const Args&... args); ++ template void info_if(const bool flag, const wchar_t* fmt, const Args&... args); ++ template void warn_if(const bool flag, const wchar_t* fmt, const Args&... args); ++ template void error_if(const bool flag, const wchar_t* fmt, const Args&... args); ++ template void critical_if(const bool flag, const wchar_t* fmt, const Args&... args); ++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ ++ template void log(level::level_enum lvl, const T&); ++ template void trace(const T&); ++ template void debug(const T&); ++ template void info(const T&); ++ template void warn(const T&); ++ template void error(const T&); ++ template void critical(const T&); ++ ++ template void log_if(const bool flag, level::level_enum lvl, const T&); ++ template void trace_if(const bool flag, const T&); ++ template void debug_if(const bool flag, const T&); ++ template void info_if(const bool flag, const T&); ++ template void warn_if(const bool flag, const T&); ++ template void error_if(const bool flag, const T&); ++ template void critical_if(const bool flag, const T&); ++ ++ bool should_log(level::level_enum) const; ++ void set_level(level::level_enum); ++ level::level_enum level() const; ++ const std::string& name() const; ++ void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local); ++ void set_formatter(formatter_ptr); ++ ++ // automatically call flush() if message level >= log_level ++ void flush_on(level::level_enum log_level); ++ ++ virtual void flush(); ++ ++ const std::vector& sinks() const; ++ ++ // error handler ++ virtual void set_error_handler(log_err_handler); ++ virtual log_err_handler error_handler(); ++ ++protected: ++ virtual void _sink_it(details::log_msg&); ++ virtual void _set_pattern(const std::string&, pattern_time_type); ++ virtual void _set_formatter(formatter_ptr); ++ ++ // default error handler: print the error to stderr with the max rate of 1 message/minute ++ virtual void _default_err_handler(const std::string &msg); ++ ++ // return true if the given message level should trigger a flush ++ bool _should_flush_on(const details::log_msg&); ++ ++ const std::string _name; ++ std::vector _sinks; ++ formatter_ptr _formatter; ++ spdlog::level_t _level; ++ spdlog::level_t _flush_level; ++ log_err_handler _err_handler; ++ std::atomic _last_err_time; ++ std::atomic _msg_counter; ++}; ++} ++ ++#include "spdlog/details/logger_impl.h" +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h +new file mode 100644 +index 00000000..239f2d28 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/android_sink.h +@@ -0,0 +1,90 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#if defined(__ANDROID__) ++ ++#include "spdlog/sinks/sink.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if !defined(SPDLOG_ANDROID_RETRIES) ++#define SPDLOG_ANDROID_RETRIES 2 ++#endif ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++ ++/* ++* Android sink (logging using __android_log_write) ++* __android_log_write is thread-safe. No lock is needed. ++*/ ++class android_sink : public sink ++{ ++public: ++ explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {} ++ ++ void log(const details::log_msg& msg) override ++ { ++ const android_LogPriority priority = convert_to_android(msg.level); ++ const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str()); ++ ++ // See system/core/liblog/logger_write.c for explanation of return value ++ int ret = __android_log_write(priority, _tag.c_str(), msg_output); ++ int retry_count = 0; ++ while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) ++ { ++ std::this_thread::sleep_for(std::chrono::milliseconds(5)); ++ ret = __android_log_write(priority, _tag.c_str(), msg_output); ++ retry_count++; ++ } ++ ++ if (ret < 0) ++ { ++ throw spdlog_ex("__android_log_write() failed", ret); ++ } ++ } ++ ++ void flush() override ++ { ++ } ++ ++private: ++ static android_LogPriority convert_to_android(spdlog::level::level_enum level) ++ { ++ switch(level) ++ { ++ case spdlog::level::trace: ++ return ANDROID_LOG_VERBOSE; ++ case spdlog::level::debug: ++ return ANDROID_LOG_DEBUG; ++ case spdlog::level::info: ++ return ANDROID_LOG_INFO; ++ case spdlog::level::warn: ++ return ANDROID_LOG_WARN; ++ case spdlog::level::err: ++ return ANDROID_LOG_ERROR; ++ case spdlog::level::critical: ++ return ANDROID_LOG_FATAL; ++ default: ++ return ANDROID_LOG_DEFAULT; ++ } ++ } ++ ++ std::string _tag; ++ bool _use_raw_msg; ++}; ++ ++} ++} ++ ++#endif +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h +new file mode 100644 +index 00000000..56fd3fd5 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/ansicolor_sink.h +@@ -0,0 +1,133 @@ ++// ++// Copyright(c) 2017 spdlog authors. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/common.h" ++#include "spdlog/details/os.h" ++ ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++ ++/** ++ * This sink prefixes the output with an ANSI escape sequence color code depending on the severity ++ * of the message. ++ * If no color terminal detected, omit the escape codes. ++ */ ++template ++class ansicolor_sink: public base_sink ++{ ++public: ++ ansicolor_sink(FILE* file): target_file_(file) ++ { ++ should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal(); ++ colors_[level::trace] = cyan; ++ colors_[level::debug] = cyan; ++ colors_[level::info] = reset; ++ colors_[level::warn] = yellow + bold; ++ colors_[level::err] = red + bold; ++ colors_[level::critical] = bold + on_red; ++ colors_[level::off] = reset; ++ } ++ virtual ~ansicolor_sink() ++ { ++ _flush(); ++ } ++ ++ void set_color(level::level_enum color_level, const std::string& color) ++ { ++ std::lock_guard lock(base_sink::_mutex); ++ colors_[color_level] = color; ++ } ++ ++ /// Formatting codes ++ const std::string reset = "\033[00m"; ++ const std::string bold = "\033[1m"; ++ const std::string dark = "\033[2m"; ++ const std::string underline = "\033[4m"; ++ const std::string blink = "\033[5m"; ++ const std::string reverse = "\033[7m"; ++ const std::string concealed = "\033[8m"; ++ ++ // Foreground colors ++ const std::string grey = "\033[30m"; ++ const std::string red = "\033[31m"; ++ const std::string green = "\033[32m"; ++ const std::string yellow = "\033[33m"; ++ const std::string blue = "\033[34m"; ++ const std::string magenta = "\033[35m"; ++ const std::string cyan = "\033[36m"; ++ const std::string white = "\033[37m"; ++ ++ /// Background colors ++ const std::string on_grey = "\033[40m"; ++ const std::string on_red = "\033[41m"; ++ const std::string on_green = "\033[42m"; ++ const std::string on_yellow = "\033[43m"; ++ const std::string on_blue = "\033[44m"; ++ const std::string on_magenta = "\033[45m"; ++ const std::string on_cyan = "\033[46m"; ++ const std::string on_white = "\033[47m"; ++ ++protected: ++ virtual void _sink_it(const details::log_msg& msg) override ++ { ++ // Wrap the originally formatted message in color codes. ++ // If color is not supported in the terminal, log as is instead. ++ if (should_do_colors_) ++ { ++ const std::string& prefix = colors_[msg.level]; ++ fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_); ++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_); ++ fwrite(reset.data(), sizeof(char), reset.size(), target_file_); ++ } ++ else ++ { ++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_); ++ } ++ _flush(); ++ } ++ ++ void _flush() override ++ { ++ fflush(target_file_); ++ } ++ FILE* target_file_; ++ bool should_do_colors_; ++ std::map colors_; ++}; ++ ++ ++template ++class ansicolor_stdout_sink: public ansicolor_sink ++{ ++public: ++ ansicolor_stdout_sink(): ansicolor_sink(stdout) ++ {} ++}; ++ ++template ++class ansicolor_stderr_sink: public ansicolor_sink ++{ ++public: ++ ansicolor_stderr_sink(): ansicolor_sink(stderr) ++ {} ++}; ++ ++typedef ansicolor_stdout_sink ansicolor_stdout_sink_mt; ++typedef ansicolor_stdout_sink ansicolor_stdout_sink_st; ++ ++typedef ansicolor_stderr_sink ansicolor_stderr_sink_mt; ++typedef ansicolor_stderr_sink ansicolor_stderr_sink_st; ++ ++} // namespace sinks ++} // namespace spdlog ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h +new file mode 100644 +index 00000000..926f4931 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/base_sink.h +@@ -0,0 +1,50 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++// ++// base sink templated over a mutex (either dummy or real) ++// concrete implementation should only override the _sink_it method. ++// all locking is taken care of here so no locking needed by the implementers.. ++// ++ ++#include "spdlog/sinks/sink.h" ++#include "spdlog/formatter.h" ++#include "spdlog/common.h" ++#include "spdlog/details/log_msg.h" ++ ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++template ++class base_sink:public sink ++{ ++public: ++ base_sink():_mutex() {} ++ virtual ~base_sink() = default; ++ ++ base_sink(const base_sink&) = delete; ++ base_sink& operator=(const base_sink&) = delete; ++ ++ void log(const details::log_msg& msg) SPDLOG_FINAL override ++ { ++ std::lock_guard lock(_mutex); ++ _sink_it(msg); ++ } ++ void flush() SPDLOG_FINAL override ++ { ++ _flush(); ++ } ++ ++protected: ++ virtual void _sink_it(const details::log_msg& msg) = 0; ++ virtual void _flush() = 0; ++ Mutex _mutex; ++}; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h +new file mode 100644 +index 00000000..4d4b6b61 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/dist_sink.h +@@ -0,0 +1,73 @@ ++// ++// Copyright (c) 2015 David Schury, Gabi Melman ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/details/log_msg.h" ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/sinks/sink.h" ++ ++#include ++#include ++#include ++#include ++ ++// Distribution sink (mux). Stores a vector of sinks which get called when log is called ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++template ++class dist_sink: public base_sink ++{ ++public: ++ explicit dist_sink() :_sinks() {} ++ dist_sink(const dist_sink&) = delete; ++ dist_sink& operator=(const dist_sink&) = delete; ++ virtual ~dist_sink() = default; ++ ++protected: ++ std::vector> _sinks; ++ ++ void _sink_it(const details::log_msg& msg) override ++ { ++ for (auto &sink : _sinks) ++ { ++ if( sink->should_log( msg.level)) ++ { ++ sink->log(msg); ++ } ++ } ++ } ++ ++ void _flush() override ++ { ++ std::lock_guard lock(base_sink::_mutex); ++ for (auto &sink : _sinks) ++ sink->flush(); ++ } ++ ++public: ++ ++ ++ void add_sink(std::shared_ptr sink) ++ { ++ std::lock_guard lock(base_sink::_mutex); ++ _sinks.push_back(sink); ++ } ++ ++ void remove_sink(std::shared_ptr sink) ++ { ++ std::lock_guard lock(base_sink::_mutex); ++ _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); ++ } ++}; ++ ++typedef dist_sink dist_sink_mt; ++typedef dist_sink dist_sink_st; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h b/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h +new file mode 100644 +index 00000000..421acc8a +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/file_sinks.h +@@ -0,0 +1,242 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/details/file_helper.h" ++#include "spdlog/fmt/fmt.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++/* ++ * Trivial file sink with single file as target ++ */ ++template ++class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex > ++{ ++public: ++ explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false) ++ { ++ _file_helper.open(filename, truncate); ++ } ++ ++ void set_force_flush(bool force_flush) ++ { ++ _force_flush = force_flush; ++ } ++ ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ _file_helper.write(msg); ++ if(_force_flush) ++ _file_helper.flush(); ++ } ++ void _flush() override ++ { ++ _file_helper.flush(); ++ } ++private: ++ details::file_helper _file_helper; ++ bool _force_flush; ++}; ++ ++typedef simple_file_sink simple_file_sink_mt; ++typedef simple_file_sink simple_file_sink_st; ++ ++/* ++ * Rotating file sink based on size ++ */ ++template ++class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex > ++{ ++public: ++ rotating_file_sink(const filename_t &base_filename, ++ std::size_t max_size, std::size_t max_files) : ++ _base_filename(base_filename), ++ _max_size(max_size), ++ _max_files(max_files), ++ _current_size(0), ++ _file_helper() ++ { ++ _file_helper.open(calc_filename(_base_filename, 0)); ++ _current_size = _file_helper.size(); //expensive. called only once ++ } ++ ++ ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ _current_size += msg.formatted.size(); ++ if (_current_size > _max_size) ++ { ++ _rotate(); ++ _current_size = msg.formatted.size(); ++ } ++ _file_helper.write(msg); ++ } ++ ++ void _flush() override ++ { ++ _file_helper.flush(); ++ } ++ ++private: ++ static filename_t calc_filename(const filename_t& filename, std::size_t index) ++ { ++ std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; ++ if (index) ++ w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index); ++ else ++ w.write(SPDLOG_FILENAME_T("{}"), filename); ++ return w.str(); ++ } ++ ++ // Rotate files: ++ // log.txt -> log.txt.1 ++ // log.txt.1 -> log.txt.2 ++ // log.txt.2 -> log.txt.3 ++ // lo3.txt.3 -> delete ++ ++ void _rotate() ++ { ++ using details::os::filename_to_str; ++ _file_helper.close(); ++ for (auto i = _max_files; i > 0; --i) ++ { ++ filename_t src = calc_filename(_base_filename, i - 1); ++ filename_t target = calc_filename(_base_filename, i); ++ ++ if (details::file_helper::file_exists(target)) ++ { ++ if (details::os::remove(target) != 0) ++ { ++ throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); ++ } ++ } ++ if (details::file_helper::file_exists(src) && details::os::rename(src, target)) ++ { ++ throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); ++ } ++ } ++ _file_helper.reopen(true); ++ } ++ filename_t _base_filename; ++ std::size_t _max_size; ++ std::size_t _max_files; ++ std::size_t _current_size; ++ details::file_helper _file_helper; ++}; ++ ++typedef rotating_file_sink rotating_file_sink_mt; ++typedef rotating_file_sinkrotating_file_sink_st; ++ ++/* ++ * Default generator of daily log file names. ++ */ ++struct default_daily_file_name_calculator ++{ ++ // Create filename for the form basename.YYYY-MM-DD_hh-mm ++ static filename_t calc_filename(const filename_t& basename) ++ { ++ std::tm tm = spdlog::details::os::localtime(); ++ std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; ++ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min); ++ return w.str(); ++ } ++}; ++ ++/* ++ * Generator of daily log file names in format basename.YYYY-MM-DD ++ */ ++struct dateonly_daily_file_name_calculator ++{ ++ // Create filename for the form basename.YYYY-MM-DD ++ static filename_t calc_filename(const filename_t& basename) ++ { ++ std::tm tm = spdlog::details::os::localtime(); ++ std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; ++ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ return w.str(); ++ } ++}; ++ ++/* ++ * Rotating file sink based on date. rotates at midnight ++ */ ++template ++class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex > ++{ ++public: ++ //create daily file sink which rotates on given time ++ daily_file_sink( ++ const filename_t& base_filename, ++ int rotation_hour, ++ int rotation_minute) : _base_filename(base_filename), ++ _rotation_h(rotation_hour), ++ _rotation_m(rotation_minute) ++ { ++ if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) ++ throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); ++ _rotation_tp = _next_rotation_tp(); ++ _file_helper.open(FileNameCalc::calc_filename(_base_filename)); ++ } ++ ++ ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ if (std::chrono::system_clock::now() >= _rotation_tp) ++ { ++ _file_helper.open(FileNameCalc::calc_filename(_base_filename)); ++ _rotation_tp = _next_rotation_tp(); ++ } ++ _file_helper.write(msg); ++ } ++ ++ void _flush() override ++ { ++ _file_helper.flush(); ++ } ++ ++private: ++ std::chrono::system_clock::time_point _next_rotation_tp() ++ { ++ auto now = std::chrono::system_clock::now(); ++ time_t tnow = std::chrono::system_clock::to_time_t(now); ++ tm date = spdlog::details::os::localtime(tnow); ++ date.tm_hour = _rotation_h; ++ date.tm_min = _rotation_m; ++ date.tm_sec = 0; ++ auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date)); ++ if (rotation_time > now) ++ return rotation_time; ++ else ++ return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24)); ++ } ++ ++ filename_t _base_filename; ++ int _rotation_h; ++ int _rotation_m; ++ std::chrono::system_clock::time_point _rotation_tp; ++ details::file_helper _file_helper; ++}; ++ ++typedef daily_file_sink daily_file_sink_mt; ++typedef daily_file_sink daily_file_sink_st; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h +new file mode 100644 +index 00000000..68b02556 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/msvc_sink.h +@@ -0,0 +1,51 @@ ++// ++// Copyright(c) 2016 Alexander Dalshov. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#if defined(_MSC_VER) ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/details/null_mutex.h" ++ ++#include ++ ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++/* ++* MSVC sink (logging using OutputDebugStringA) ++*/ ++template ++class msvc_sink : public base_sink < Mutex > ++{ ++public: ++ explicit msvc_sink() ++ { ++ } ++ ++ ++ ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ OutputDebugStringA(msg.formatted.c_str()); ++ } ++ ++ void _flush() override ++ {} ++}; ++ ++typedef msvc_sink msvc_sink_mt; ++typedef msvc_sink msvc_sink_st; ++ ++} ++} ++ ++#endif +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h +new file mode 100644 +index 00000000..ed4b5e47 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/null_sink.h +@@ -0,0 +1,34 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/details/null_mutex.h" ++ ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++ ++template ++class null_sink : public base_sink < Mutex > ++{ ++protected: ++ void _sink_it(const details::log_msg&) override ++ {} ++ ++ void _flush() override ++ {} ++ ++}; ++typedef null_sink null_sink_st; ++typedef null_sink null_sink_mt; ++ ++} ++} ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h +new file mode 100644 +index 00000000..f056107f +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/ostream_sink.h +@@ -0,0 +1,47 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/sinks/base_sink.h" ++ ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++template ++class ostream_sink: public base_sink ++{ ++public: ++ explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {} ++ ostream_sink(const ostream_sink&) = delete; ++ ostream_sink& operator=(const ostream_sink&) = delete; ++ virtual ~ostream_sink() = default; ++ ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ _ostream.write(msg.formatted.data(), msg.formatted.size()); ++ if (_force_flush) ++ _ostream.flush(); ++ } ++ ++ void _flush() override ++ { ++ _ostream.flush(); ++ } ++ ++ std::ostream& _ostream; ++ bool _force_flush; ++}; ++ ++typedef ostream_sink ostream_sink_mt; ++typedef ostream_sink ostream_sink_st; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/sink.h +new file mode 100644 +index 00000000..0974f337 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/sink.h +@@ -0,0 +1,53 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++ ++#pragma once ++ ++#include "spdlog/details/log_msg.h" ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++class sink ++{ ++public: ++ sink() ++ { ++ _level = level::trace; ++ } ++ ++ virtual ~sink() {} ++ virtual void log(const details::log_msg& msg) = 0; ++ virtual void flush() = 0; ++ ++ bool should_log(level::level_enum msg_level) const; ++ void set_level(level::level_enum log_level); ++ level::level_enum level() const; ++ ++private: ++ level_t _level; ++ ++}; ++ ++inline bool sink::should_log(level::level_enum msg_level) const ++{ ++ return msg_level >= _level.load(std::memory_order_relaxed); ++} ++ ++inline void sink::set_level(level::level_enum log_level) ++{ ++ _level.store(log_level); ++} ++ ++inline level::level_enum sink::level() const ++{ ++ return static_cast(_level.load(std::memory_order_relaxed)); ++} ++ ++} ++} ++ +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h b/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h +new file mode 100644 +index 00000000..dcdcc7c1 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/stdout_sinks.h +@@ -0,0 +1,77 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/sinks/base_sink.h" ++ ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++ ++template ++class stdout_sink SPDLOG_FINAL : public base_sink ++{ ++ using MyType = stdout_sink; ++public: ++ stdout_sink() ++ {} ++ static std::shared_ptr instance() ++ { ++ static std::shared_ptr instance = std::make_shared(); ++ return instance; ++ } ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); ++ _flush(); ++ } ++ ++ void _flush() override ++ { ++ fflush(stdout); ++ } ++}; ++ ++typedef stdout_sink stdout_sink_st; ++typedef stdout_sink stdout_sink_mt; ++ ++ ++template ++class stderr_sink SPDLOG_FINAL : public base_sink ++{ ++ using MyType = stderr_sink; ++public: ++ stderr_sink() ++ {} ++ static std::shared_ptr instance() ++ { ++ static std::shared_ptr instance = std::make_shared(); ++ return instance; ++ } ++protected: ++ void _sink_it(const details::log_msg& msg) override ++ { ++ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); ++ _flush(); ++ } ++ ++ void _flush() override ++ { ++ fflush(stderr); ++ } ++}; ++ ++typedef stderr_sink stderr_sink_mt; ++typedef stderr_sink stderr_sink_st; ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h +new file mode 100644 +index 00000000..0b509c4a +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/syslog_sink.h +@@ -0,0 +1,81 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/common.h" ++ ++#ifdef SPDLOG_ENABLE_SYSLOG ++ ++#include "spdlog/sinks/sink.h" ++#include "spdlog/details/log_msg.h" ++ ++#include ++#include ++#include ++ ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++/** ++ * Sink that write to syslog using the `syscall()` library call. ++ * ++ * Locking is not needed, as `syslog()` itself is thread-safe. ++ */ ++class syslog_sink : public sink ++{ ++public: ++ // ++ syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER): ++ _ident(ident) ++ { ++ _priorities[static_cast(level::trace)] = LOG_DEBUG; ++ _priorities[static_cast(level::debug)] = LOG_DEBUG; ++ _priorities[static_cast(level::info)] = LOG_INFO; ++ _priorities[static_cast(level::warn)] = LOG_WARNING; ++ _priorities[static_cast(level::err)] = LOG_ERR; ++ _priorities[static_cast(level::critical)] = LOG_CRIT; ++ _priorities[static_cast(level::off)] = LOG_INFO; ++ ++ //set ident to be program name if empty ++ ::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility); ++ } ++ ~syslog_sink() ++ { ++ ::closelog(); ++ } ++ ++ syslog_sink(const syslog_sink&) = delete; ++ syslog_sink& operator=(const syslog_sink&) = delete; ++ ++ void log(const details::log_msg &msg) override ++ { ++ ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); ++ } ++ ++ void flush() override ++ { ++ } ++ ++ ++private: ++ std::array _priorities; ++ //must store the ident because the man says openlog might use the pointer as is and not a string copy ++ const std::string _ident; ++ ++ // ++ // Simply maps spdlog's log level to syslog priority level. ++ // ++ int syslog_prio_from_level(const details::log_msg &msg) const ++ { ++ return _priorities[static_cast(msg.level)]; ++ } ++}; ++} ++} ++ ++#endif +diff --git a/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h b/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h +new file mode 100644 +index 00000000..5b92bf8a +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/sinks/wincolor_sink.h +@@ -0,0 +1,121 @@ ++// ++// Copyright(c) 2016 spdlog ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++#include "spdlog/sinks/base_sink.h" ++#include "spdlog/details/null_mutex.h" ++#include "spdlog/common.h" ++ ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++namespace sinks ++{ ++/* ++ * Windows color console sink. Uses WriteConsoleA to write to the console with colors ++ */ ++template ++class wincolor_sink: public base_sink ++{ ++public: ++ const WORD BOLD = FOREGROUND_INTENSITY; ++ const WORD RED = FOREGROUND_RED; ++ const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; ++ const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; ++ const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; ++ ++ wincolor_sink(HANDLE std_handle): out_handle_(std_handle) ++ { ++ colors_[level::trace] = CYAN; ++ colors_[level::debug] = CYAN; ++ colors_[level::info] = WHITE | BOLD; ++ colors_[level::warn] = YELLOW | BOLD; ++ colors_[level::err] = RED | BOLD; // red bold ++ colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background ++ colors_[level::off] = 0; ++ } ++ ++ virtual ~wincolor_sink() ++ { ++ this->flush(); ++ } ++ ++ wincolor_sink(const wincolor_sink& other) = delete; ++ wincolor_sink& operator=(const wincolor_sink& other) = delete; ++ ++protected: ++ virtual void _sink_it(const details::log_msg& msg) override ++ { ++ auto color = colors_[msg.level]; ++ auto orig_attribs = set_console_attribs(color); ++ WriteConsoleA(out_handle_, msg.formatted.data(), static_cast(msg.formatted.size()), nullptr, nullptr); ++ SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors ++ } ++ ++ virtual void _flush() override ++ { ++ // windows console always flushed? ++ } ++ ++ // change the color for the given level ++ void set_color(level::level_enum level, WORD color) ++ { ++ std::lock_guard lock(base_sink::_mutex); ++ colors_[level] = color; ++ } ++ ++private: ++ HANDLE out_handle_; ++ std::map colors_; ++ ++ // set color and return the orig console attributes (for resetting later) ++ WORD set_console_attribs(WORD attribs) ++ { ++ CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; ++ GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); ++ WORD back_color = orig_buffer_info.wAttributes; ++ // retrieve the current background color ++ back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); ++ // keep the background color unchanged ++ SetConsoleTextAttribute(out_handle_, attribs | back_color); ++ return orig_buffer_info.wAttributes; //return orig attribs ++ } ++}; ++ ++// ++// windows color console to stdout ++// ++template ++class wincolor_stdout_sink: public wincolor_sink ++{ ++public: ++ wincolor_stdout_sink() : wincolor_sink(GetStdHandle(STD_OUTPUT_HANDLE)) ++ {} ++}; ++ ++typedef wincolor_stdout_sink wincolor_stdout_sink_mt; ++typedef wincolor_stdout_sink wincolor_stdout_sink_st; ++ ++// ++// windows color console to stderr ++// ++template ++class wincolor_stderr_sink: public wincolor_sink ++{ ++public: ++ wincolor_stderr_sink() : wincolor_sink(GetStdHandle(STD_ERROR_HANDLE)) ++ {} ++}; ++ ++typedef wincolor_stderr_sink wincolor_stderr_sink_mt; ++typedef wincolor_stderr_sink wincolor_stderr_sink_st; ++ ++} ++} +diff --git a/external/spdlog-0.14.0/include/spdlog/spdlog.h b/external/spdlog-0.14.0/include/spdlog/spdlog.h +new file mode 100644 +index 00000000..8fec6431 +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/spdlog.h +@@ -0,0 +1,189 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++// spdlog main header file. ++// see example.cpp for usage example ++ ++#pragma once ++ ++#define SPDLOG_VERSION "0.14.0" ++ ++#include "spdlog/tweakme.h" ++#include "spdlog/common.h" ++#include "spdlog/logger.h" ++ ++#include ++#include ++#include ++#include ++ ++namespace spdlog ++{ ++ ++// ++// Return an existing logger or nullptr if a logger with such name doesn't exist. ++// example: spdlog::get("my_logger")->info("hello {}", "world"); ++// ++std::shared_ptr get(const std::string& name); ++ ++ ++// ++// Set global formatting ++// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); ++// ++void set_pattern(const std::string& format_string); ++void set_formatter(formatter_ptr f); ++ ++// ++// Set global logging level for ++// ++void set_level(level::level_enum log_level); ++ ++// ++// Set global error handler ++// ++void set_error_handler(log_err_handler); ++ ++// ++// Turn on async mode (off by default) and set the queue size for each async_logger. ++// effective only for loggers created after this call. ++// queue_size: size of queue (must be power of 2): ++// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction. ++// ++// async_overflow_policy (optional, block_retry by default): ++// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry. ++// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows. ++// ++// worker_warmup_cb (optional): ++// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) ++// ++// worker_teardown_cb (optional): ++// callback function that will be called in worker thread upon exit ++// ++void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); ++ ++// Turn off async mode ++void set_sync_mode(); ++ ++ ++// ++// Create and register multi/single threaded basic file logger. ++// Basic logger simply writes to given file without any limitations or rotations. ++// ++std::shared_ptr basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false); ++std::shared_ptr basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false); ++ ++// ++// Create and register multi/single threaded rotating file logger ++// ++std::shared_ptr rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); ++std::shared_ptr rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); ++ ++// ++// Create file logger which creates new file on the given time (default in midnight): ++// ++std::shared_ptr daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); ++std::shared_ptr daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); ++ ++// ++// Create and register stdout/stderr loggers ++// ++std::shared_ptr stdout_logger_mt(const std::string& logger_name); ++std::shared_ptr stdout_logger_st(const std::string& logger_name); ++std::shared_ptr stderr_logger_mt(const std::string& logger_name); ++std::shared_ptr stderr_logger_st(const std::string& logger_name); ++// ++// Create and register colored stdout/stderr loggers ++// ++std::shared_ptr stdout_color_mt(const std::string& logger_name); ++std::shared_ptr stdout_color_st(const std::string& logger_name); ++std::shared_ptr stderr_color_mt(const std::string& logger_name); ++std::shared_ptr stderr_color_st(const std::string& logger_name); ++ ++ ++// ++// Create and register a syslog logger ++// ++#ifdef SPDLOG_ENABLE_SYSLOG ++std::shared_ptr syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); ++#endif ++ ++#if defined(__ANDROID__) ++std::shared_ptr android_logger(const std::string& logger_name, const std::string& tag = "spdlog"); ++#endif ++ ++// Create and register a logger with a single sink ++std::shared_ptr create(const std::string& logger_name, const sink_ptr& sink); ++ ++// Create and register a logger with multiple sinks ++std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks); ++template ++std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end); ++ ++ ++// Create and register a logger with templated sink type ++// Example: ++// spdlog::create("mylog", "dailylog_filename"); ++template ++std::shared_ptr create(const std::string& logger_name, Args...); ++ ++// Create and register an async logger with a single sink ++std::shared_ptr create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); ++ ++// Create and register an async logger with multiple sinks ++std::shared_ptr create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); ++template ++std::shared_ptr create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); ++ ++// Register the given logger with the given name ++void register_logger(std::shared_ptr logger); ++ ++// Apply a user defined function on all registered loggers ++// Example: ++// spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); ++void apply_all(std::function)> fun); ++ ++// Drop the reference to the given logger ++void drop(const std::string &name); ++ ++// Drop all references from the registry ++void drop_all(); ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// ++// Trace & Debug can be switched on/off at compile time for zero cost debug statements. ++// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable. ++// SPDLOG_TRACE(..) will also print current file and line. ++// ++// Example: ++// spdlog::set_level(spdlog::level::trace); ++// SPDLOG_TRACE(my_logger, "some trace message"); ++// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); ++// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); ++// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4); ++/////////////////////////////////////////////////////////////////////////////// ++ ++#ifdef SPDLOG_TRACE_ON ++#define SPDLOG_STR_H(x) #x ++#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) ++#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) ++#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) ++#else ++#define SPDLOG_TRACE(logger, ...) ++#define SPDLOG_TRACE_IF(logger, flag, ...) ++#endif ++ ++#ifdef SPDLOG_DEBUG_ON ++#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) ++#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__) ++#else ++#define SPDLOG_DEBUG(logger, ...) ++#define SPDLOG_DEBUG_IF(logger, flag, ...) ++#endif ++ ++} ++ ++ ++#include "spdlog/details/spdlog_impl.h" +diff --git a/external/spdlog-0.14.0/include/spdlog/tweakme.h b/external/spdlog-0.14.0/include/spdlog/tweakme.h +new file mode 100644 +index 00000000..53f5cf7e +--- /dev/null ++++ b/external/spdlog-0.14.0/include/spdlog/tweakme.h +@@ -0,0 +1,141 @@ ++// ++// Copyright(c) 2015 Gabi Melman. ++// Distributed under the MIT License (http://opensource.org/licenses/MIT) ++// ++ ++#pragma once ++ ++/////////////////////////////////////////////////////////////////////////////// ++// ++// Edit this file to squeeze more performance, and to customize supported features ++// ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. ++// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ. ++// Uncomment to use it instead of the regular clock. ++// ++// #define SPDLOG_CLOCK_COARSE ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment if date/time logging is not needed and never appear in the log pattern. ++// This will prevent spdlog from quering the clock on each log call. ++// ++// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined. ++// You must set new pattern(spdlog::set_pattern(..") without any date/time in it ++// ++// #define SPDLOG_NO_DATETIME ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). ++// This will prevent spdlog from quering the thread id on each log call. ++// ++// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined. ++// ++// #define SPDLOG_NO_THREAD_ID ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment if logger name logging is not needed. ++// This will prevent spdlog from copying the logger name on each log call. ++// ++// #define SPDLOG_NO_NAME ++/////////////////////////////////////////////////////////////////////////////// ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. ++// ++// #define SPDLOG_DEBUG_ON ++// #define SPDLOG_TRACE_ON ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). ++// Use only if your code never modifes concurrently the registry. ++// Note that upon creating a logger the registry is modified by spdlog.. ++// ++// #define SPDLOG_NO_REGISTRY_MUTEX ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to avoid spdlog's usage of atomic log levels ++// Use only if your code never modifies a logger's log levels concurrently by different threads. ++// ++// #define SPDLOG_NO_ATOMIC_LEVELS ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable usage of wchar_t for file names on Windows. ++// ++// #define SPDLOG_WCHAR_FILENAMES ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) ++// ++// #define SPDLOG_EOL ";-)\n" ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to use your own copy of the fmt library instead of spdlog's copy. ++// In this case spdlog will try to include so set your -I flag accordingly. ++// ++// #define SPDLOG_FMT_EXTERNAL ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable syslog (disabled by default) ++// ++// #define SPDLOG_ENABLE_SYSLOG ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable wchar_t support (convert to utf8) ++// ++// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to prevent child processes from inheriting log file descriptors ++// ++// #define SPDLOG_PREVENT_CHILD_FD ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to mark some types as final, allowing more optimizations in release ++// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types ++// for instance. ++// ++// #define SPDLOG_FINAL final ++/////////////////////////////////////////////////////////////////////////////// ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable message counting feature. Adds %i logger pattern that ++// prints log message sequence id. ++// ++// #define SPDLOG_ENABLE_MESSAGE_COUNTER ++/////////////////////////////////////////////////////////////////////////////// ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Uncomment to enable user defined tag names ++// ++// #define SPDLOG_LEVEL_NAMES { " TRACE", " DEBUG", " INFO", ++// " WARNING", " ERROR", "CRITICAL", "OFF" }; ++/////////////////////////////////////////////////////////////////////////////// +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/tests/CMakeLists.txt b/external/spdlog-0.14.0/tests/CMakeLists.txt +new file mode 100644 +index 00000000..22329b4e +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/CMakeLists.txt +@@ -0,0 +1,19 @@ ++# ++# Tests ++# ++ ++enable_testing() ++ ++find_package(Threads) ++ ++# Build Catch unit tests ++add_library(catch INTERFACE) ++target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) ++ ++file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp) ++ ++add_executable(catch_tests ${catch_tests}) ++target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT}) ++add_test(NAME catch_tests COMMAND catch_tests) ++file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") ++ +diff --git a/external/spdlog-0.14.0/tests/catch.hpp b/external/spdlog-0.14.0/tests/catch.hpp +new file mode 100644 +index 00000000..925c6bff +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/catch.hpp +@@ -0,0 +1,9427 @@ ++/* ++ * CATCH v1.1 build 1 (master branch) ++ * Generated: 2015-03-27 18:00:16.346230 ++ * ---------------------------------------------------------- ++ * This file has been merged from multiple headers. Please don't edit it directly ++ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. ++ * ++ * Distributed under the Boost Software License, Version 1.0. (See accompanying ++ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ++ */ ++#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED ++#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED ++ ++#define TWOBLUECUBES_CATCH_HPP_INCLUDED ++ ++// #included from: internal/catch_suppress_warnings.h ++ ++#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED ++ ++#ifdef __clang__ ++# ifdef __ICC // icpc defines the __clang__ macro ++# pragma warning(push) ++# pragma warning(disable: 161 1682) ++# else // __ICC ++# pragma clang diagnostic ignored "-Wglobal-constructors" ++# pragma clang diagnostic ignored "-Wvariadic-macros" ++# pragma clang diagnostic ignored "-Wc99-extensions" ++# pragma clang diagnostic ignored "-Wunused-variable" ++# pragma clang diagnostic push ++# pragma clang diagnostic ignored "-Wpadded" ++# pragma clang diagnostic ignored "-Wc++98-compat" ++# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" ++# endif ++#elif defined __GNUC__ ++# pragma GCC diagnostic ignored "-Wvariadic-macros" ++# pragma GCC diagnostic ignored "-Wunused-variable" ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wpadded" ++#endif ++ ++#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) ++# define CATCH_IMPL ++#endif ++ ++#ifdef CATCH_IMPL ++# ifndef CLARA_CONFIG_MAIN ++# define CLARA_CONFIG_MAIN_NOT_DEFINED ++# define CLARA_CONFIG_MAIN ++# endif ++#endif ++ ++// #included from: internal/catch_notimplemented_exception.h ++#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED ++ ++// #included from: catch_common.h ++#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED ++ ++#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line ++#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) ++#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) ++ ++#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr ++#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) ++ ++#include ++#include ++#include ++ ++// #included from: catch_compiler_capabilities.h ++#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED ++ ++// Much of the following code is based on Boost (1.53) ++ ++#ifdef __clang__ ++ ++# if __has_feature(cxx_nullptr) ++# define CATCH_CONFIG_CPP11_NULLPTR ++# endif ++ ++# if __has_feature(cxx_noexcept) ++# define CATCH_CONFIG_CPP11_NOEXCEPT ++# endif ++ ++#endif // __clang__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Borland ++#ifdef __BORLANDC__ ++ ++#if (__BORLANDC__ > 0x582 ) ++//#define CATCH_CONFIG_SFINAE // Not confirmed ++#endif ++ ++#endif // __BORLANDC__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// EDG ++#ifdef __EDG_VERSION__ ++ ++#if (__EDG_VERSION__ > 238 ) ++//#define CATCH_CONFIG_SFINAE // Not confirmed ++#endif ++ ++#endif // __EDG_VERSION__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Digital Mars ++#ifdef __DMC__ ++ ++#if (__DMC__ > 0x840 ) ++//#define CATCH_CONFIG_SFINAE // Not confirmed ++#endif ++ ++#endif // __DMC__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// GCC ++#ifdef __GNUC__ ++ ++#if __GNUC__ < 3 ++ ++#if (__GNUC_MINOR__ >= 96 ) ++//#define CATCH_CONFIG_SFINAE ++#endif ++ ++#elif __GNUC__ >= 3 ++ ++// #define CATCH_CONFIG_SFINAE // Taking this out completely for now ++ ++#endif // __GNUC__ < 3 ++ ++#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) ++ ++#define CATCH_CONFIG_CPP11_NULLPTR ++#endif ++ ++#endif // __GNUC__ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// Visual C++ ++#ifdef _MSC_VER ++ ++#if (_MSC_VER >= 1600) ++#define CATCH_CONFIG_CPP11_NULLPTR ++#endif ++ ++#if (_MSC_VER >= 1310 ) // (VC++ 7.0+) ++//#define CATCH_CONFIG_SFINAE // Not confirmed ++#endif ++ ++#endif // _MSC_VER ++ ++// Use variadic macros if the compiler supports them ++#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ ++ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ++ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ++ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) ++ ++#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS ++#define CATCH_CONFIG_VARIADIC_MACROS ++#endif ++ ++#endif ++ ++//////////////////////////////////////////////////////////////////////////////// ++// C++ language feature support ++ ++// detect language version: ++#if (__cplusplus == 201103L) ++# define CATCH_CPP11 ++# define CATCH_CPP11_OR_GREATER ++#elif (__cplusplus >= 201103L) ++# define CATCH_CPP11_OR_GREATER ++#endif ++ ++// noexcept support: ++#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) ++# define CATCH_NOEXCEPT noexcept ++# define CATCH_NOEXCEPT_IS(x) noexcept(x) ++#else ++# define CATCH_NOEXCEPT throw() ++# define CATCH_NOEXCEPT_IS(x) ++#endif ++ ++namespace Catch { ++ ++ class NonCopyable { ++#ifdef CATCH_CPP11_OR_GREATER ++ NonCopyable( NonCopyable const& ) = delete; ++ NonCopyable( NonCopyable && ) = delete; ++ NonCopyable& operator = ( NonCopyable const& ) = delete; ++ NonCopyable& operator = ( NonCopyable && ) = delete; ++#else ++ NonCopyable( NonCopyable const& info ); ++ NonCopyable& operator = ( NonCopyable const& ); ++#endif ++ ++ protected: ++ NonCopyable() {} ++ virtual ~NonCopyable(); ++ }; ++ ++ class SafeBool { ++ public: ++ typedef void (SafeBool::*type)() const; ++ ++ static type makeSafe( bool value ) { ++ return value ? &SafeBool::trueValue : 0; ++ } ++ private: ++ void trueValue() const {} ++ }; ++ ++ template ++ inline void deleteAll( ContainerT& container ) { ++ typename ContainerT::const_iterator it = container.begin(); ++ typename ContainerT::const_iterator itEnd = container.end(); ++ for(; it != itEnd; ++it ) ++ delete *it; ++ } ++ template ++ inline void deleteAllValues( AssociativeContainerT& container ) { ++ typename AssociativeContainerT::const_iterator it = container.begin(); ++ typename AssociativeContainerT::const_iterator itEnd = container.end(); ++ for(; it != itEnd; ++it ) ++ delete it->second; ++ } ++ ++ bool startsWith( std::string const& s, std::string const& prefix ); ++ bool endsWith( std::string const& s, std::string const& suffix ); ++ bool contains( std::string const& s, std::string const& infix ); ++ void toLowerInPlace( std::string& s ); ++ std::string toLower( std::string const& s ); ++ std::string trim( std::string const& str ); ++ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); ++ ++ struct pluralise { ++ pluralise( std::size_t count, std::string const& label ); ++ ++ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); ++ ++ std::size_t m_count; ++ std::string m_label; ++ }; ++ ++ struct SourceLineInfo { ++ ++ SourceLineInfo(); ++ SourceLineInfo( char const* _file, std::size_t _line ); ++ SourceLineInfo( SourceLineInfo const& other ); ++# ifdef CATCH_CPP11_OR_GREATER ++ SourceLineInfo( SourceLineInfo && ) = default; ++ SourceLineInfo& operator = ( SourceLineInfo const& ) = default; ++ SourceLineInfo& operator = ( SourceLineInfo && ) = default; ++# endif ++ bool empty() const; ++ bool operator == ( SourceLineInfo const& other ) const; ++ bool operator < ( SourceLineInfo const& other ) const; ++ ++ std::string file; ++ std::size_t line; ++ }; ++ ++ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); ++ ++ // This is just here to avoid compiler warnings with macro constants and boolean literals ++ inline bool isTrue( bool value ){ return value; } ++ inline bool alwaysTrue() { return true; } ++ inline bool alwaysFalse() { return false; } ++ ++ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); ++ ++ // Use this in variadic streaming macros to allow ++ // >> +StreamEndStop ++ // as well as ++ // >> stuff +StreamEndStop ++ struct StreamEndStop { ++ std::string operator+() { ++ return std::string(); ++ } ++ }; ++ template ++ T const& operator + ( T const& value, StreamEndStop ) { ++ return value; ++ } ++} ++ ++#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) ++#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); ++ ++#include ++ ++namespace Catch { ++ ++ class NotImplementedException : public std::exception ++ { ++ public: ++ NotImplementedException( SourceLineInfo const& lineInfo ); ++ NotImplementedException( NotImplementedException const& ) {} ++ ++ virtual ~NotImplementedException() CATCH_NOEXCEPT {} ++ ++ virtual const char* what() const CATCH_NOEXCEPT; ++ ++ private: ++ std::string m_what; ++ SourceLineInfo m_lineInfo; ++ }; ++ ++} // end namespace Catch ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) ++ ++// #included from: internal/catch_context.h ++#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED ++ ++// #included from: catch_interfaces_generators.h ++#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct IGeneratorInfo { ++ virtual ~IGeneratorInfo(); ++ virtual bool moveNext() = 0; ++ virtual std::size_t getCurrentIndex() const = 0; ++ }; ++ ++ struct IGeneratorsForTest { ++ virtual ~IGeneratorsForTest(); ++ ++ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; ++ virtual bool moveNext() = 0; ++ }; ++ ++ IGeneratorsForTest* createGeneratorsForTest(); ++ ++} // end namespace Catch ++ ++// #included from: catch_ptr.hpp ++#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++namespace Catch { ++ ++ // An intrusive reference counting smart pointer. ++ // T must implement addRef() and release() methods ++ // typically implementing the IShared interface ++ template ++ class Ptr { ++ public: ++ Ptr() : m_p( NULL ){} ++ Ptr( T* p ) : m_p( p ){ ++ if( m_p ) ++ m_p->addRef(); ++ } ++ Ptr( Ptr const& other ) : m_p( other.m_p ){ ++ if( m_p ) ++ m_p->addRef(); ++ } ++ ~Ptr(){ ++ if( m_p ) ++ m_p->release(); ++ } ++ void reset() { ++ if( m_p ) ++ m_p->release(); ++ m_p = NULL; ++ } ++ Ptr& operator = ( T* p ){ ++ Ptr temp( p ); ++ swap( temp ); ++ return *this; ++ } ++ Ptr& operator = ( Ptr const& other ){ ++ Ptr temp( other ); ++ swap( temp ); ++ return *this; ++ } ++ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } ++ T* get() { return m_p; } ++ const T* get() const{ return m_p; } ++ T& operator*() const { return *m_p; } ++ T* operator->() const { return m_p; } ++ bool operator !() const { return m_p == NULL; } ++ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } ++ ++ private: ++ T* m_p; ++ }; ++ ++ struct IShared : NonCopyable { ++ virtual ~IShared(); ++ virtual void addRef() const = 0; ++ virtual void release() const = 0; ++ }; ++ ++ template ++ struct SharedImpl : T { ++ ++ SharedImpl() : m_rc( 0 ){} ++ ++ virtual void addRef() const { ++ ++m_rc; ++ } ++ virtual void release() const { ++ if( --m_rc == 0 ) ++ delete this; ++ } ++ ++ mutable unsigned int m_rc; ++ }; ++ ++} // end namespace Catch ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class TestCase; ++ class Stream; ++ struct IResultCapture; ++ struct IRunner; ++ struct IGeneratorsForTest; ++ struct IConfig; ++ ++ struct IContext ++ { ++ virtual ~IContext(); ++ ++ virtual IResultCapture* getResultCapture() = 0; ++ virtual IRunner* getRunner() = 0; ++ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; ++ virtual bool advanceGeneratorsForCurrentTest() = 0; ++ virtual Ptr getConfig() const = 0; ++ }; ++ ++ struct IMutableContext : IContext ++ { ++ virtual ~IMutableContext(); ++ virtual void setResultCapture( IResultCapture* resultCapture ) = 0; ++ virtual void setRunner( IRunner* runner ) = 0; ++ virtual void setConfig( Ptr const& config ) = 0; ++ }; ++ ++ IContext& getCurrentContext(); ++ IMutableContext& getCurrentMutableContext(); ++ void cleanUpContext(); ++ Stream createStream( std::string const& streamName ); ++ ++} ++ ++// #included from: internal/catch_test_registry.hpp ++#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED ++ ++// #included from: catch_interfaces_testcase.h ++#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class TestSpec; ++ ++ struct ITestCase : IShared { ++ virtual void invoke () const = 0; ++ protected: ++ virtual ~ITestCase(); ++ }; ++ ++ class TestCase; ++ struct IConfig; ++ ++ struct ITestCaseRegistry { ++ virtual ~ITestCaseRegistry(); ++ virtual std::vector const& getAllTests() const = 0; ++ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const = 0; ++ ++ }; ++} ++ ++namespace Catch { ++ ++template ++class MethodTestCase : public SharedImpl { ++ ++public: ++ MethodTestCase( void (C::*method)() ) : m_method( method ) {} ++ ++ virtual void invoke() const { ++ C obj; ++ (obj.*m_method)(); ++ } ++ ++private: ++ virtual ~MethodTestCase() {} ++ ++ void (C::*m_method)(); ++}; ++ ++typedef void(*TestFunction)(); ++ ++struct NameAndDesc { ++ NameAndDesc( const char* _name = "", const char* _description= "" ) ++ : name( _name ), description( _description ) ++ {} ++ ++ const char* name; ++ const char* description; ++}; ++ ++struct AutoReg { ++ ++ AutoReg( TestFunction function, ++ SourceLineInfo const& lineInfo, ++ NameAndDesc const& nameAndDesc ); ++ ++ template ++ AutoReg( void (C::*method)(), ++ char const* className, ++ NameAndDesc const& nameAndDesc, ++ SourceLineInfo const& lineInfo ) { ++ registerTestCase( new MethodTestCase( method ), ++ className, ++ nameAndDesc, ++ lineInfo ); ++ } ++ ++ void registerTestCase( ITestCase* testCase, ++ char const* className, ++ NameAndDesc const& nameAndDesc, ++ SourceLineInfo const& lineInfo ); ++ ++ ~AutoReg(); ++ ++private: ++ AutoReg( AutoReg const& ); ++ void operator= ( AutoReg const& ); ++}; ++ ++} // end namespace Catch ++ ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TESTCASE( ... ) \ ++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ ++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ ++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ ++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ ++ namespace{ \ ++ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ ++ void test(); \ ++ }; \ ++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ ++ } \ ++ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() ++ ++#else ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ ++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ ++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ ++ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ ++ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } ++ ++ /////////////////////////////////////////////////////////////////////////////// ++ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ ++ namespace{ \ ++ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ ++ void test(); \ ++ }; \ ++ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ ++ } \ ++ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() ++ ++#endif ++ ++// #included from: internal/catch_capture.hpp ++#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED ++ ++// #included from: catch_result_builder.h ++#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED ++ ++// #included from: catch_result_type.h ++#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED ++ ++namespace Catch { ++ ++ // ResultWas::OfType enum ++ struct ResultWas { enum OfType { ++ Unknown = -1, ++ Ok = 0, ++ Info = 1, ++ Warning = 2, ++ ++ FailureBit = 0x10, ++ ++ ExpressionFailed = FailureBit | 1, ++ ExplicitFailure = FailureBit | 2, ++ ++ Exception = 0x100 | FailureBit, ++ ++ ThrewException = Exception | 1, ++ DidntThrowException = Exception | 2, ++ ++ FatalErrorCondition = 0x200 | FailureBit ++ ++ }; }; ++ ++ inline bool isOk( ResultWas::OfType resultType ) { ++ return ( resultType & ResultWas::FailureBit ) == 0; ++ } ++ inline bool isJustInfo( int flags ) { ++ return flags == ResultWas::Info; ++ } ++ ++ // ResultDisposition::Flags enum ++ struct ResultDisposition { enum Flags { ++ Normal = 0x00, ++ ++ ContinueOnFailure = 0x01, // Failures fail test, but execution continues ++ FalseTest = 0x02, // Prefix expression with ! ++ SuppressFail = 0x04 // Failures are reported but do not fail the test ++ }; }; ++ ++ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { ++ return static_cast( static_cast( lhs ) | static_cast( rhs ) ); ++ } ++ ++ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } ++ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } ++ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } ++ ++} // end namespace Catch ++ ++// #included from: catch_assertionresult.h ++#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct AssertionInfo ++ { ++ AssertionInfo() {} ++ AssertionInfo( std::string const& _macroName, ++ SourceLineInfo const& _lineInfo, ++ std::string const& _capturedExpression, ++ ResultDisposition::Flags _resultDisposition ); ++ ++ std::string macroName; ++ SourceLineInfo lineInfo; ++ std::string capturedExpression; ++ ResultDisposition::Flags resultDisposition; ++ }; ++ ++ struct AssertionResultData ++ { ++ AssertionResultData() : resultType( ResultWas::Unknown ) {} ++ ++ std::string reconstructedExpression; ++ std::string message; ++ ResultWas::OfType resultType; ++ }; ++ ++ class AssertionResult { ++ public: ++ AssertionResult(); ++ AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); ++ ~AssertionResult(); ++# ifdef CATCH_CPP11_OR_GREATER ++ AssertionResult( AssertionResult const& ) = default; ++ AssertionResult( AssertionResult && ) = default; ++ AssertionResult& operator = ( AssertionResult const& ) = default; ++ AssertionResult& operator = ( AssertionResult && ) = default; ++# endif ++ ++ bool isOk() const; ++ bool succeeded() const; ++ ResultWas::OfType getResultType() const; ++ bool hasExpression() const; ++ bool hasMessage() const; ++ std::string getExpression() const; ++ std::string getExpressionInMacro() const; ++ bool hasExpandedExpression() const; ++ std::string getExpandedExpression() const; ++ std::string getMessage() const; ++ SourceLineInfo getSourceInfo() const; ++ std::string getTestMacroName() const; ++ ++ protected: ++ AssertionInfo m_info; ++ AssertionResultData m_resultData; ++ }; ++ ++} // end namespace Catch ++ ++namespace Catch { ++ ++ struct TestFailureException{}; ++ ++ template class ExpressionLhs; ++ ++ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; ++ ++ struct CopyableStream { ++ CopyableStream() {} ++ CopyableStream( CopyableStream const& other ) { ++ oss << other.oss.str(); ++ } ++ CopyableStream& operator=( CopyableStream const& other ) { ++ oss.str(""); ++ oss << other.oss.str(); ++ return *this; ++ } ++ std::ostringstream oss; ++ }; ++ ++ class ResultBuilder { ++ public: ++ ResultBuilder( char const* macroName, ++ SourceLineInfo const& lineInfo, ++ char const* capturedExpression, ++ ResultDisposition::Flags resultDisposition ); ++ ++ template ++ ExpressionLhs operator->* ( T const& operand ); ++ ExpressionLhs operator->* ( bool value ); ++ ++ template ++ ResultBuilder& operator << ( T const& value ) { ++ m_stream.oss << value; ++ return *this; ++ } ++ ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); ++ ++ ResultBuilder& setResultType( ResultWas::OfType result ); ++ ResultBuilder& setResultType( bool result ); ++ ResultBuilder& setLhs( std::string const& lhs ); ++ ResultBuilder& setRhs( std::string const& rhs ); ++ ResultBuilder& setOp( std::string const& op ); ++ ++ void endExpression(); ++ ++ std::string reconstructExpression() const; ++ AssertionResult build() const; ++ ++ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); ++ void captureResult( ResultWas::OfType resultType ); ++ void captureExpression(); ++ void react(); ++ bool shouldDebugBreak() const; ++ bool allowThrows() const; ++ ++ private: ++ AssertionInfo m_assertionInfo; ++ AssertionResultData m_data; ++ struct ExprComponents { ++ ExprComponents() : testFalse( false ) {} ++ bool testFalse; ++ std::string lhs, rhs, op; ++ } m_exprComponents; ++ CopyableStream m_stream; ++ ++ bool m_shouldDebugBreak; ++ bool m_shouldThrow; ++ }; ++ ++} // namespace Catch ++ ++// Include after due to circular dependency: ++// #included from: catch_expression_lhs.hpp ++#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED ++ ++// #included from: catch_evaluate.hpp ++#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED ++ ++#ifdef _MSC_VER ++#pragma warning(push) ++#pragma warning(disable:4389) // '==' : signed/unsigned mismatch ++#endif ++ ++#include ++ ++namespace Catch { ++namespace Internal { ++ ++ enum Operator { ++ IsEqualTo, ++ IsNotEqualTo, ++ IsLessThan, ++ IsGreaterThan, ++ IsLessThanOrEqualTo, ++ IsGreaterThanOrEqualTo ++ }; ++ ++ template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; ++ template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; ++ template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; ++ template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; ++ template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; ++ template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; ++ template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; ++ ++ template ++ inline T& opCast(T const& t) { return const_cast(t); } ++ ++// nullptr_t support based on pull request #154 from Konstantin Baumann ++#ifdef CATCH_CONFIG_CPP11_NULLPTR ++ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } ++#endif // CATCH_CONFIG_CPP11_NULLPTR ++ ++ // So the compare overloads can be operator agnostic we convey the operator as a template ++ // enum, which is used to specialise an Evaluator for doing the comparison. ++ template ++ class Evaluator{}; ++ ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs) { ++ return opCast( lhs ) == opCast( rhs ); ++ } ++ }; ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs ) { ++ return opCast( lhs ) != opCast( rhs ); ++ } ++ }; ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs ) { ++ return opCast( lhs ) < opCast( rhs ); ++ } ++ }; ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs ) { ++ return opCast( lhs ) > opCast( rhs ); ++ } ++ }; ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs ) { ++ return opCast( lhs ) >= opCast( rhs ); ++ } ++ }; ++ template ++ struct Evaluator { ++ static bool evaluate( T1 const& lhs, T2 const& rhs ) { ++ return opCast( lhs ) <= opCast( rhs ); ++ } ++ }; ++ ++ template ++ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { ++ return Evaluator::evaluate( lhs, rhs ); ++ } ++ ++ // This level of indirection allows us to specialise for integer types ++ // to avoid signed/ unsigned warnings ++ ++ // "base" overload ++ template ++ bool compare( T1 const& lhs, T2 const& rhs ) { ++ return Evaluator::evaluate( lhs, rhs ); ++ } ++ ++ // unsigned X to int ++ template bool compare( unsigned int lhs, int rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ template bool compare( unsigned long lhs, int rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ template bool compare( unsigned char lhs, int rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ ++ // unsigned X to long ++ template bool compare( unsigned int lhs, long rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ template bool compare( unsigned long lhs, long rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ template bool compare( unsigned char lhs, long rhs ) { ++ return applyEvaluator( lhs, static_cast( rhs ) ); ++ } ++ ++ // int to unsigned X ++ template bool compare( int lhs, unsigned int rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ template bool compare( int lhs, unsigned long rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ template bool compare( int lhs, unsigned char rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ ++ // long to unsigned X ++ template bool compare( long lhs, unsigned int rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ template bool compare( long lhs, unsigned long rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ template bool compare( long lhs, unsigned char rhs ) { ++ return applyEvaluator( static_cast( lhs ), rhs ); ++ } ++ ++ // pointer to long (when comparing against NULL) ++ template bool compare( long lhs, T* rhs ) { ++ return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); ++ } ++ template bool compare( T* lhs, long rhs ) { ++ return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); ++ } ++ ++ // pointer to int (when comparing against NULL) ++ template bool compare( int lhs, T* rhs ) { ++ return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); ++ } ++ template bool compare( T* lhs, int rhs ) { ++ return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); ++ } ++ ++#ifdef CATCH_CONFIG_CPP11_NULLPTR ++ // pointer to nullptr_t (when comparing against nullptr) ++ template bool compare( std::nullptr_t, T* rhs ) { ++ return Evaluator::evaluate( NULL, rhs ); ++ } ++ template bool compare( T* lhs, std::nullptr_t ) { ++ return Evaluator::evaluate( lhs, NULL ); ++ } ++#endif // CATCH_CONFIG_CPP11_NULLPTR ++ ++} // end of namespace Internal ++} // end of namespace Catch ++ ++#ifdef _MSC_VER ++#pragma warning(pop) ++#endif ++ ++// #included from: catch_tostring.h ++#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED ++ ++// #included from: catch_sfinae.hpp ++#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED ++ ++// Try to detect if the current compiler supports SFINAE ++ ++namespace Catch { ++ ++ struct TrueType { ++ static const bool value = true; ++ typedef void Enable; ++ char sizer[1]; ++ }; ++ struct FalseType { ++ static const bool value = false; ++ typedef void Disable; ++ char sizer[2]; ++ }; ++ ++#ifdef CATCH_CONFIG_SFINAE ++ ++ template struct NotABooleanExpression; ++ ++ template struct If : NotABooleanExpression {}; ++ template<> struct If : TrueType {}; ++ template<> struct If : FalseType {}; ++ ++ template struct SizedIf; ++ template<> struct SizedIf : TrueType {}; ++ template<> struct SizedIf : FalseType {}; ++ ++#endif // CATCH_CONFIG_SFINAE ++ ++} // end namespace Catch ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef __OBJC__ ++// #included from: catch_objc_arc.hpp ++#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED ++ ++#import ++ ++#ifdef __has_feature ++#define CATCH_ARC_ENABLED __has_feature(objc_arc) ++#else ++#define CATCH_ARC_ENABLED 0 ++#endif ++ ++void arcSafeRelease( NSObject* obj ); ++id performOptionalSelector( id obj, SEL sel ); ++ ++#if !CATCH_ARC_ENABLED ++inline void arcSafeRelease( NSObject* obj ) { ++ [obj release]; ++} ++inline id performOptionalSelector( id obj, SEL sel ) { ++ if( [obj respondsToSelector: sel] ) ++ return [obj performSelector: sel]; ++ return nil; ++} ++#define CATCH_UNSAFE_UNRETAINED ++#define CATCH_ARC_STRONG ++#else ++inline void arcSafeRelease( NSObject* ){} ++inline id performOptionalSelector( id obj, SEL sel ) { ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Warc-performSelector-leaks" ++#endif ++ if( [obj respondsToSelector: sel] ) ++ return [obj performSelector: sel]; ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ return nil; ++} ++#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained ++#define CATCH_ARC_STRONG __strong ++#endif ++ ++#endif ++ ++#ifdef CATCH_CPP11_OR_GREATER ++#include ++#include ++#endif ++ ++namespace Catch { ++ ++// Why we're here. ++template ++std::string toString( T const& value ); ++ ++// Built in overloads ++ ++std::string toString( std::string const& value ); ++std::string toString( std::wstring const& value ); ++std::string toString( const char* const value ); ++std::string toString( char* const value ); ++std::string toString( const wchar_t* const value ); ++std::string toString( wchar_t* const value ); ++std::string toString( int value ); ++std::string toString( unsigned long value ); ++std::string toString( unsigned int value ); ++std::string toString( const double value ); ++std::string toString( const float value ); ++std::string toString( bool value ); ++std::string toString( char value ); ++std::string toString( signed char value ); ++std::string toString( unsigned char value ); ++ ++#ifdef CATCH_CONFIG_CPP11_NULLPTR ++std::string toString( std::nullptr_t ); ++#endif ++ ++#ifdef __OBJC__ ++ std::string toString( NSString const * const& nsstring ); ++ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); ++ std::string toString( NSObject* const& nsObject ); ++#endif ++ ++namespace Detail { ++ ++ extern std::string unprintableString; ++ ++// SFINAE is currently disabled by default for all compilers. ++// If the non SFINAE version of IsStreamInsertable is ambiguous for you ++// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE ++#ifdef CATCH_CONFIG_SFINAE ++ ++ template ++ class IsStreamInsertableHelper { ++ template struct TrueIfSizeable : TrueType {}; ++ ++ template ++ static TrueIfSizeable dummy(T2*); ++ static FalseType dummy(...); ++ ++ public: ++ typedef SizedIf type; ++ }; ++ ++ template ++ struct IsStreamInsertable : IsStreamInsertableHelper::type {}; ++ ++#else ++ ++ struct BorgType { ++ template BorgType( T const& ); ++ }; ++ ++ TrueType& testStreamable( std::ostream& ); ++ FalseType testStreamable( FalseType ); ++ ++ FalseType operator<<( std::ostream const&, BorgType const& ); ++ ++ template ++ struct IsStreamInsertable { ++ static std::ostream &s; ++ static T const&t; ++ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; ++ }; ++ ++#endif ++ ++#if defined(CATCH_CPP11_OR_GREATER) ++ template::value ++ > ++ struct EnumStringMaker ++ { ++ static std::string convert( T const& ) { return unprintableString; } ++ }; ++ ++ template ++ struct EnumStringMaker ++ { ++ static std::string convert( T const& v ) ++ { ++ return ::Catch::toString( ++ static_cast::type>(v) ++ ); ++ } ++ }; ++#endif ++ template ++ struct StringMakerBase { ++#if defined(CATCH_CPP11_OR_GREATER) ++ template ++ static std::string convert( T const& v ) ++ { ++ return EnumStringMaker::convert( v ); ++ } ++#else ++ template ++ static std::string convert( T const& ) { return unprintableString; } ++#endif ++ }; ++ ++ template<> ++ struct StringMakerBase { ++ template ++ static std::string convert( T const& _value ) { ++ std::ostringstream oss; ++ oss << _value; ++ return oss.str(); ++ } ++ }; ++ ++ std::string rawMemoryToString( const void *object, std::size_t size ); ++ ++ template ++ inline std::string rawMemoryToString( const T& object ) { ++ return rawMemoryToString( &object, sizeof(object) ); ++ } ++ ++} // end namespace Detail ++ ++template ++struct StringMaker : ++ Detail::StringMakerBase::value> {}; ++ ++template ++struct StringMaker { ++ template ++ static std::string convert( U* p ) { ++ if( !p ) ++ return INTERNAL_CATCH_STRINGIFY( NULL ); ++ else ++ return Detail::rawMemoryToString( p ); ++ } ++}; ++ ++template ++struct StringMaker { ++ static std::string convert( R C::* p ) { ++ if( !p ) ++ return INTERNAL_CATCH_STRINGIFY( NULL ); ++ else ++ return Detail::rawMemoryToString( p ); ++ } ++}; ++ ++namespace Detail { ++ template ++ std::string rangeToString( InputIterator first, InputIterator last ); ++} ++ ++//template ++//struct StringMaker > { ++// static std::string convert( std::vector const& v ) { ++// return Detail::rangeToString( v.begin(), v.end() ); ++// } ++//}; ++ ++template ++std::string toString( std::vector const& v ) { ++ return Detail::rangeToString( v.begin(), v.end() ); ++} ++ ++#ifdef CATCH_CPP11_OR_GREATER ++ ++// toString for tuples ++namespace TupleDetail { ++ template< ++ typename Tuple, ++ std::size_t N = 0, ++ bool = (N < std::tuple_size::value) ++ > ++ struct ElementPrinter { ++ static void print( const Tuple& tuple, std::ostream& os ) ++ { ++ os << ( N ? ", " : " " ) ++ << Catch::toString(std::get(tuple)); ++ ElementPrinter::print(tuple,os); ++ } ++ }; ++ ++ template< ++ typename Tuple, ++ std::size_t N ++ > ++ struct ElementPrinter { ++ static void print( const Tuple&, std::ostream& ) {} ++ }; ++ ++} ++ ++template ++struct StringMaker> { ++ ++ static std::string convert( const std::tuple& tuple ) ++ { ++ std::ostringstream os; ++ os << '{'; ++ TupleDetail::ElementPrinter>::print( tuple, os ); ++ os << " }"; ++ return os.str(); ++ } ++}; ++#endif ++ ++namespace Detail { ++ template ++ std::string makeString( T const& value ) { ++ return StringMaker::convert( value ); ++ } ++} // end namespace Detail ++ ++/// \brief converts any type to a string ++/// ++/// The default template forwards on to ostringstream - except when an ++/// ostringstream overload does not exist - in which case it attempts to detect ++/// that and writes {?}. ++/// Overload (not specialise) this template for custom typs that you don't want ++/// to provide an ostream overload for. ++template ++std::string toString( T const& value ) { ++ return StringMaker::convert( value ); ++} ++ ++ namespace Detail { ++ template ++ std::string rangeToString( InputIterator first, InputIterator last ) { ++ std::ostringstream oss; ++ oss << "{ "; ++ if( first != last ) { ++ oss << Catch::toString( *first ); ++ for( ++first ; first != last ; ++first ) ++ oss << ", " << Catch::toString( *first ); ++ } ++ oss << " }"; ++ return oss.str(); ++ } ++} ++ ++} // end namespace Catch ++ ++namespace Catch { ++ ++// Wraps the LHS of an expression and captures the operator and RHS (if any) - ++// wrapping them all in a ResultBuilder object ++template ++class ExpressionLhs { ++ ExpressionLhs& operator = ( ExpressionLhs const& ); ++# ifdef CATCH_CPP11_OR_GREATER ++ ExpressionLhs& operator = ( ExpressionLhs && ) = delete; ++# endif ++ ++public: ++ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} ++# ifdef CATCH_CPP11_OR_GREATER ++ ExpressionLhs( ExpressionLhs const& ) = default; ++ ExpressionLhs( ExpressionLhs && ) = default; ++# endif ++ ++ template ++ ResultBuilder& operator == ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ template ++ ResultBuilder& operator != ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ template ++ ResultBuilder& operator < ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ template ++ ResultBuilder& operator > ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ template ++ ResultBuilder& operator <= ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ template ++ ResultBuilder& operator >= ( RhsT const& rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ ResultBuilder& operator == ( bool rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ ResultBuilder& operator != ( bool rhs ) { ++ return captureExpression( rhs ); ++ } ++ ++ void endExpression() { ++ bool value = m_lhs ? true : false; ++ m_rb ++ .setLhs( Catch::toString( value ) ) ++ .setResultType( value ) ++ .endExpression(); ++ } ++ ++ // Only simple binary expressions are allowed on the LHS. ++ // If more complex compositions are required then place the sub expression in parentheses ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); ++ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); ++ ++private: ++ template ++ ResultBuilder& captureExpression( RhsT const& rhs ) { ++ return m_rb ++ .setResultType( Internal::compare( m_lhs, rhs ) ) ++ .setLhs( Catch::toString( m_lhs ) ) ++ .setRhs( Catch::toString( rhs ) ) ++ .setOp( Internal::OperatorTraits::getName() ); ++ } ++ ++private: ++ ResultBuilder& m_rb; ++ T m_lhs; ++}; ++ ++} // end namespace Catch ++ ++ ++namespace Catch { ++ ++ template ++ inline ExpressionLhs ResultBuilder::operator->* ( T const& operand ) { ++ return ExpressionLhs( *this, operand ); ++ } ++ ++ inline ExpressionLhs ResultBuilder::operator->* ( bool value ) { ++ return ExpressionLhs( *this, value ); ++ } ++ ++} // namespace Catch ++ ++// #included from: catch_message.h ++#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct MessageInfo { ++ MessageInfo( std::string const& _macroName, ++ SourceLineInfo const& _lineInfo, ++ ResultWas::OfType _type ); ++ ++ std::string macroName; ++ SourceLineInfo lineInfo; ++ ResultWas::OfType type; ++ std::string message; ++ unsigned int sequence; ++ ++ bool operator == ( MessageInfo const& other ) const { ++ return sequence == other.sequence; ++ } ++ bool operator < ( MessageInfo const& other ) const { ++ return sequence < other.sequence; ++ } ++ private: ++ static unsigned int globalCount; ++ }; ++ ++ struct MessageBuilder { ++ MessageBuilder( std::string const& macroName, ++ SourceLineInfo const& lineInfo, ++ ResultWas::OfType type ) ++ : m_info( macroName, lineInfo, type ) ++ {} ++ ++ template ++ MessageBuilder& operator << ( T const& value ) { ++ m_stream << value; ++ return *this; ++ } ++ ++ MessageInfo m_info; ++ std::ostringstream m_stream; ++ }; ++ ++ class ScopedMessage { ++ public: ++ ScopedMessage( MessageBuilder const& builder ); ++ ScopedMessage( ScopedMessage const& other ); ++ ~ScopedMessage(); ++ ++ MessageInfo m_info; ++ }; ++ ++} // end namespace Catch ++ ++// #included from: catch_interfaces_capture.h ++#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class TestCase; ++ class AssertionResult; ++ struct AssertionInfo; ++ struct SectionInfo; ++ struct MessageInfo; ++ class ScopedMessageBuilder; ++ struct Counts; ++ ++ struct IResultCapture { ++ ++ virtual ~IResultCapture(); ++ ++ virtual void assertionEnded( AssertionResult const& result ) = 0; ++ virtual bool sectionStarted( SectionInfo const& sectionInfo, ++ Counts& assertions ) = 0; ++ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; ++ virtual void pushScopedMessage( MessageInfo const& message ) = 0; ++ virtual void popScopedMessage( MessageInfo const& message ) = 0; ++ ++ virtual std::string getCurrentTestName() const = 0; ++ virtual const AssertionResult* getLastResult() const = 0; ++ ++ virtual void handleFatalErrorCondition( std::string const& message ) = 0; ++ }; ++ ++ IResultCapture& getResultCapture(); ++} ++ ++// #included from: catch_debugger.h ++#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED ++ ++// #included from: catch_platform.h ++#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED ++ ++#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) ++#define CATCH_PLATFORM_MAC ++#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) ++#define CATCH_PLATFORM_IPHONE ++#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ++#define CATCH_PLATFORM_WINDOWS ++#endif ++ ++#include ++ ++namespace Catch{ ++ ++ bool isDebuggerActive(); ++ void writeToDebugConsole( std::string const& text ); ++} ++ ++#ifdef CATCH_PLATFORM_MAC ++ ++ // The following code snippet based on: ++ // http://cocoawithlove.com/2008/03/break-into-debugger.html ++ #ifdef DEBUG ++ #if defined(__ppc64__) || defined(__ppc__) ++ #define CATCH_BREAK_INTO_DEBUGGER() \ ++ if( Catch::isDebuggerActive() ) { \ ++ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ ++ : : : "memory","r0","r3","r4" ); \ ++ } ++ #else ++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} ++ #endif ++ #endif ++ ++#elif defined(_MSC_VER) ++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } ++#elif defined(__MINGW32__) ++ extern "C" __declspec(dllimport) void __stdcall DebugBreak(); ++ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } ++#endif ++ ++#ifndef CATCH_BREAK_INTO_DEBUGGER ++#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); ++#endif ++ ++// #included from: catch_interfaces_runner.h ++#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED ++ ++namespace Catch { ++ class TestCase; ++ ++ struct IRunner { ++ virtual ~IRunner(); ++ virtual bool aborting() const = 0; ++ }; ++} ++ ++/////////////////////////////////////////////////////////////////////////////// ++// In the event of a failure works out if the debugger needs to be invoked ++// and/or an exception thrown and takes appropriate action. ++// This needs to be done as a macro so the debugger will stop in the user ++// source code rather than in Catch library code ++#define INTERNAL_CATCH_REACT( resultBuilder ) \ ++ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ ++ resultBuilder.react(); ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ ++ try { \ ++ ( __catchResult->*expr ).endExpression(); \ ++ } \ ++ catch( ... ) { \ ++ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ ++ } \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ ++ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ ++ if( Catch::getResultCapture().getLastResult()->succeeded() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ ++ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ ++ if( !Catch::getResultCapture().getLastResult()->succeeded() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ ++ try { \ ++ expr; \ ++ __catchResult.captureResult( Catch::ResultWas::Ok ); \ ++ } \ ++ catch( ... ) { \ ++ __catchResult.useActiveException( resultDisposition ); \ ++ } \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ ++ if( __catchResult.allowThrows() ) \ ++ try { \ ++ expr; \ ++ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ ++ } \ ++ catch( ... ) { \ ++ __catchResult.captureResult( Catch::ResultWas::Ok ); \ ++ } \ ++ else \ ++ __catchResult.captureResult( Catch::ResultWas::Ok ); \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ ++ if( __catchResult.allowThrows() ) \ ++ try { \ ++ expr; \ ++ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ ++ } \ ++ catch( exceptionType ) { \ ++ __catchResult.captureResult( Catch::ResultWas::Ok ); \ ++ } \ ++ catch( ... ) { \ ++ __catchResult.useActiveException( resultDisposition ); \ ++ } \ ++ else \ ++ __catchResult.captureResult( Catch::ResultWas::Ok ); \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++ ++/////////////////////////////////////////////////////////////////////////////// ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ ++ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ ++ __catchResult.captureResult( messageType ); \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++#else ++ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ ++ __catchResult << log + ::Catch::StreamEndStop(); \ ++ __catchResult.captureResult( messageType ); \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_INFO( log, macroName ) \ ++ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ ++ do { \ ++ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ ++ try { \ ++ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \ ++ __catchResult \ ++ .setLhs( Catch::toString( arg ) ) \ ++ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ ++ .setOp( "matches" ) \ ++ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \ ++ __catchResult.captureExpression(); \ ++ } catch( ... ) { \ ++ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ ++ } \ ++ INTERNAL_CATCH_REACT( __catchResult ) \ ++ } while( Catch::alwaysFalse() ) ++ ++// #included from: internal/catch_section.h ++#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED ++ ++// #included from: catch_section_info.h ++#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED ++ ++namespace Catch { ++ ++ struct SectionInfo { ++ SectionInfo ++ ( SourceLineInfo const& _lineInfo, ++ std::string const& _name, ++ std::string const& _description = std::string() ); ++ ++ std::string name; ++ std::string description; ++ SourceLineInfo lineInfo; ++ }; ++ ++} // end namespace Catch ++ ++// #included from: catch_totals.hpp ++#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct Counts { ++ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} ++ ++ Counts operator - ( Counts const& other ) const { ++ Counts diff; ++ diff.passed = passed - other.passed; ++ diff.failed = failed - other.failed; ++ diff.failedButOk = failedButOk - other.failedButOk; ++ return diff; ++ } ++ Counts& operator += ( Counts const& other ) { ++ passed += other.passed; ++ failed += other.failed; ++ failedButOk += other.failedButOk; ++ return *this; ++ } ++ ++ std::size_t total() const { ++ return passed + failed + failedButOk; ++ } ++ bool allPassed() const { ++ return failed == 0 && failedButOk == 0; ++ } ++ bool allOk() const { ++ return failed == 0; ++ } ++ ++ std::size_t passed; ++ std::size_t failed; ++ std::size_t failedButOk; ++ }; ++ ++ struct Totals { ++ ++ Totals operator - ( Totals const& other ) const { ++ Totals diff; ++ diff.assertions = assertions - other.assertions; ++ diff.testCases = testCases - other.testCases; ++ return diff; ++ } ++ ++ Totals delta( Totals const& prevTotals ) const { ++ Totals diff = *this - prevTotals; ++ if( diff.assertions.failed > 0 ) ++ ++diff.testCases.failed; ++ else if( diff.assertions.failedButOk > 0 ) ++ ++diff.testCases.failedButOk; ++ else ++ ++diff.testCases.passed; ++ return diff; ++ } ++ ++ Totals& operator += ( Totals const& other ) { ++ assertions += other.assertions; ++ testCases += other.testCases; ++ return *this; ++ } ++ ++ Counts assertions; ++ Counts testCases; ++ }; ++} ++ ++// #included from: catch_timer.h ++#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED ++ ++#ifdef CATCH_PLATFORM_WINDOWS ++typedef unsigned long long uint64_t; ++#else ++#include ++#endif ++ ++namespace Catch { ++ ++ class Timer { ++ public: ++ Timer() : m_ticks( 0 ) {} ++ void start(); ++ unsigned int getElapsedMicroseconds() const; ++ unsigned int getElapsedMilliseconds() const; ++ double getElapsedSeconds() const; ++ ++ private: ++ uint64_t m_ticks; ++ }; ++ ++} // namespace Catch ++ ++#include ++ ++namespace Catch { ++ ++ class Section : NonCopyable { ++ public: ++ Section( SectionInfo const& info ); ++ ~Section(); ++ ++ // This indicates whether the section should be executed or not ++ operator bool() const; ++ ++ private: ++ SectionInfo m_info; ++ ++ std::string m_name; ++ Counts m_assertions; ++ bool m_sectionIncluded; ++ Timer m_timer; ++ }; ++ ++} // end namespace Catch ++ ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++ #define INTERNAL_CATCH_SECTION( ... ) \ ++ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) ++#else ++ #define INTERNAL_CATCH_SECTION( name, desc ) \ ++ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) ++#endif ++ ++// #included from: internal/catch_generators.hpp ++#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ ++template ++struct IGenerator { ++ virtual ~IGenerator() {} ++ virtual T getValue( std::size_t index ) const = 0; ++ virtual std::size_t size () const = 0; ++}; ++ ++template ++class BetweenGenerator : public IGenerator { ++public: ++ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} ++ ++ virtual T getValue( std::size_t index ) const { ++ return m_from+static_cast( index ); ++ } ++ ++ virtual std::size_t size() const { ++ return static_cast( 1+m_to-m_from ); ++ } ++ ++private: ++ ++ T m_from; ++ T m_to; ++}; ++ ++template ++class ValuesGenerator : public IGenerator { ++public: ++ ValuesGenerator(){} ++ ++ void add( T value ) { ++ m_values.push_back( value ); ++ } ++ ++ virtual T getValue( std::size_t index ) const { ++ return m_values[index]; ++ } ++ ++ virtual std::size_t size() const { ++ return m_values.size(); ++ } ++ ++private: ++ std::vector m_values; ++}; ++ ++template ++class CompositeGenerator { ++public: ++ CompositeGenerator() : m_totalSize( 0 ) {} ++ ++ // *** Move semantics, similar to auto_ptr *** ++ CompositeGenerator( CompositeGenerator& other ) ++ : m_fileInfo( other.m_fileInfo ), ++ m_totalSize( 0 ) ++ { ++ move( other ); ++ } ++ ++ CompositeGenerator& setFileInfo( const char* fileInfo ) { ++ m_fileInfo = fileInfo; ++ return *this; ++ } ++ ++ ~CompositeGenerator() { ++ deleteAll( m_composed ); ++ } ++ ++ operator T () const { ++ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); ++ ++ typename std::vector*>::const_iterator it = m_composed.begin(); ++ typename std::vector*>::const_iterator itEnd = m_composed.end(); ++ for( size_t index = 0; it != itEnd; ++it ) ++ { ++ const IGenerator* generator = *it; ++ if( overallIndex >= index && overallIndex < index + generator->size() ) ++ { ++ return generator->getValue( overallIndex-index ); ++ } ++ index += generator->size(); ++ } ++ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); ++ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so ++ } ++ ++ void add( const IGenerator* generator ) { ++ m_totalSize += generator->size(); ++ m_composed.push_back( generator ); ++ } ++ ++ CompositeGenerator& then( CompositeGenerator& other ) { ++ move( other ); ++ return *this; ++ } ++ ++ CompositeGenerator& then( T value ) { ++ ValuesGenerator* valuesGen = new ValuesGenerator(); ++ valuesGen->add( value ); ++ add( valuesGen ); ++ return *this; ++ } ++ ++private: ++ ++ void move( CompositeGenerator& other ) { ++ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); ++ m_totalSize += other.m_totalSize; ++ other.m_composed.clear(); ++ } ++ ++ std::vector*> m_composed; ++ std::string m_fileInfo; ++ size_t m_totalSize; ++}; ++ ++namespace Generators ++{ ++ template ++ CompositeGenerator between( T from, T to ) { ++ CompositeGenerator generators; ++ generators.add( new BetweenGenerator( from, to ) ); ++ return generators; ++ } ++ ++ template ++ CompositeGenerator values( T val1, T val2 ) { ++ CompositeGenerator generators; ++ ValuesGenerator* valuesGen = new ValuesGenerator(); ++ valuesGen->add( val1 ); ++ valuesGen->add( val2 ); ++ generators.add( valuesGen ); ++ return generators; ++ } ++ ++ template ++ CompositeGenerator values( T val1, T val2, T val3 ){ ++ CompositeGenerator generators; ++ ValuesGenerator* valuesGen = new ValuesGenerator(); ++ valuesGen->add( val1 ); ++ valuesGen->add( val2 ); ++ valuesGen->add( val3 ); ++ generators.add( valuesGen ); ++ return generators; ++ } ++ ++ template ++ CompositeGenerator values( T val1, T val2, T val3, T val4 ) { ++ CompositeGenerator generators; ++ ValuesGenerator* valuesGen = new ValuesGenerator(); ++ valuesGen->add( val1 ); ++ valuesGen->add( val2 ); ++ valuesGen->add( val3 ); ++ valuesGen->add( val4 ); ++ generators.add( valuesGen ); ++ return generators; ++ } ++ ++} // end namespace Generators ++ ++using namespace Generators; ++ ++} // end namespace Catch ++ ++#define INTERNAL_CATCH_LINESTR2( line ) #line ++#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) ++ ++#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) ++ ++// #included from: internal/catch_interfaces_exception.h ++#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED ++ ++#include ++// #included from: catch_interfaces_registry_hub.h ++#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class TestCase; ++ struct ITestCaseRegistry; ++ struct IExceptionTranslatorRegistry; ++ struct IExceptionTranslator; ++ struct IReporterRegistry; ++ struct IReporterFactory; ++ ++ struct IRegistryHub { ++ virtual ~IRegistryHub(); ++ ++ virtual IReporterRegistry const& getReporterRegistry() const = 0; ++ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; ++ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; ++ }; ++ ++ struct IMutableRegistryHub { ++ virtual ~IMutableRegistryHub(); ++ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; ++ virtual void registerTest( TestCase const& testInfo ) = 0; ++ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; ++ }; ++ ++ IRegistryHub& getRegistryHub(); ++ IMutableRegistryHub& getMutableRegistryHub(); ++ void cleanUp(); ++ std::string translateActiveException(); ++ ++} ++ ++ ++namespace Catch { ++ ++ typedef std::string(*exceptionTranslateFunction)(); ++ ++ struct IExceptionTranslator { ++ virtual ~IExceptionTranslator(); ++ virtual std::string translate() const = 0; ++ }; ++ ++ struct IExceptionTranslatorRegistry { ++ virtual ~IExceptionTranslatorRegistry(); ++ ++ virtual std::string translateActiveException() const = 0; ++ }; ++ ++ class ExceptionTranslatorRegistrar { ++ template ++ class ExceptionTranslator : public IExceptionTranslator { ++ public: ++ ++ ExceptionTranslator( std::string(*translateFunction)( T& ) ) ++ : m_translateFunction( translateFunction ) ++ {} ++ ++ virtual std::string translate() const { ++ try { ++ throw; ++ } ++ catch( T& ex ) { ++ return m_translateFunction( ex ); ++ } ++ } ++ ++ protected: ++ std::string(*m_translateFunction)( T& ); ++ }; ++ ++ public: ++ template ++ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { ++ getMutableRegistryHub().registerTranslator ++ ( new ExceptionTranslator( translateFunction ) ); ++ } ++ }; ++} ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ ++ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ ++ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ ++ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) ++ ++// #included from: internal/catch_approx.hpp ++#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED ++ ++#include ++#include ++ ++namespace Catch { ++namespace Detail { ++ ++ class Approx { ++ public: ++ explicit Approx ( double value ) ++ : m_epsilon( std::numeric_limits::epsilon()*100 ), ++ m_scale( 1.0 ), ++ m_value( value ) ++ {} ++ ++ Approx( Approx const& other ) ++ : m_epsilon( other.m_epsilon ), ++ m_scale( other.m_scale ), ++ m_value( other.m_value ) ++ {} ++ ++ static Approx custom() { ++ return Approx( 0 ); ++ } ++ ++ Approx operator()( double value ) { ++ Approx approx( value ); ++ approx.epsilon( m_epsilon ); ++ approx.scale( m_scale ); ++ return approx; ++ } ++ ++ friend bool operator == ( double lhs, Approx const& rhs ) { ++ // Thanks to Richard Harris for his help refining this formula ++ return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); ++ } ++ ++ friend bool operator == ( Approx const& lhs, double rhs ) { ++ return operator==( rhs, lhs ); ++ } ++ ++ friend bool operator != ( double lhs, Approx const& rhs ) { ++ return !operator==( lhs, rhs ); ++ } ++ ++ friend bool operator != ( Approx const& lhs, double rhs ) { ++ return !operator==( rhs, lhs ); ++ } ++ ++ Approx& epsilon( double newEpsilon ) { ++ m_epsilon = newEpsilon; ++ return *this; ++ } ++ ++ Approx& scale( double newScale ) { ++ m_scale = newScale; ++ return *this; ++ } ++ ++ std::string toString() const { ++ std::ostringstream oss; ++ oss << "Approx( " << Catch::toString( m_value ) << " )"; ++ return oss.str(); ++ } ++ ++ private: ++ double m_epsilon; ++ double m_scale; ++ double m_value; ++ }; ++} ++ ++template<> ++inline std::string toString( Detail::Approx const& value ) { ++ return value.toString(); ++} ++ ++} // end namespace Catch ++ ++// #included from: internal/catch_matchers.hpp ++#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED ++ ++namespace Catch { ++namespace Matchers { ++ namespace Impl { ++ ++ template ++ struct Matcher : SharedImpl ++ { ++ typedef ExpressionT ExpressionType; ++ ++ virtual ~Matcher() {} ++ virtual Ptr clone() const = 0; ++ virtual bool match( ExpressionT const& expr ) const = 0; ++ virtual std::string toString() const = 0; ++ }; ++ ++ template ++ struct MatcherImpl : Matcher { ++ ++ virtual Ptr > clone() const { ++ return Ptr >( new DerivedT( static_cast( *this ) ) ); ++ } ++ }; ++ ++ namespace Generic { ++ ++ template ++ class AllOf : public MatcherImpl, ExpressionT> { ++ public: ++ ++ AllOf() {} ++ AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} ++ ++ AllOf& add( Matcher const& matcher ) { ++ m_matchers.push_back( matcher.clone() ); ++ return *this; ++ } ++ virtual bool match( ExpressionT const& expr ) const ++ { ++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) ++ if( !m_matchers[i]->match( expr ) ) ++ return false; ++ return true; ++ } ++ virtual std::string toString() const { ++ std::ostringstream oss; ++ oss << "( "; ++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) { ++ if( i != 0 ) ++ oss << " and "; ++ oss << m_matchers[i]->toString(); ++ } ++ oss << " )"; ++ return oss.str(); ++ } ++ ++ private: ++ std::vector > > m_matchers; ++ }; ++ ++ template ++ class AnyOf : public MatcherImpl, ExpressionT> { ++ public: ++ ++ AnyOf() {} ++ AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} ++ ++ AnyOf& add( Matcher const& matcher ) { ++ m_matchers.push_back( matcher.clone() ); ++ return *this; ++ } ++ virtual bool match( ExpressionT const& expr ) const ++ { ++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) ++ if( m_matchers[i]->match( expr ) ) ++ return true; ++ return false; ++ } ++ virtual std::string toString() const { ++ std::ostringstream oss; ++ oss << "( "; ++ for( std::size_t i = 0; i < m_matchers.size(); ++i ) { ++ if( i != 0 ) ++ oss << " or "; ++ oss << m_matchers[i]->toString(); ++ } ++ oss << " )"; ++ return oss.str(); ++ } ++ ++ private: ++ std::vector > > m_matchers; ++ }; ++ ++ } ++ ++ namespace StdString { ++ ++ inline std::string makeString( std::string const& str ) { return str; } ++ inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } ++ ++ struct Equals : MatcherImpl { ++ Equals( std::string const& str ) : m_str( str ){} ++ Equals( Equals const& other ) : m_str( other.m_str ){} ++ ++ virtual ~Equals(); ++ ++ virtual bool match( std::string const& expr ) const { ++ return m_str == expr; ++ } ++ virtual std::string toString() const { ++ return "equals: \"" + m_str + "\""; ++ } ++ ++ std::string m_str; ++ }; ++ ++ struct Contains : MatcherImpl { ++ Contains( std::string const& substr ) : m_substr( substr ){} ++ Contains( Contains const& other ) : m_substr( other.m_substr ){} ++ ++ virtual ~Contains(); ++ ++ virtual bool match( std::string const& expr ) const { ++ return expr.find( m_substr ) != std::string::npos; ++ } ++ virtual std::string toString() const { ++ return "contains: \"" + m_substr + "\""; ++ } ++ ++ std::string m_substr; ++ }; ++ ++ struct StartsWith : MatcherImpl { ++ StartsWith( std::string const& substr ) : m_substr( substr ){} ++ StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} ++ ++ virtual ~StartsWith(); ++ ++ virtual bool match( std::string const& expr ) const { ++ return expr.find( m_substr ) == 0; ++ } ++ virtual std::string toString() const { ++ return "starts with: \"" + m_substr + "\""; ++ } ++ ++ std::string m_substr; ++ }; ++ ++ struct EndsWith : MatcherImpl { ++ EndsWith( std::string const& substr ) : m_substr( substr ){} ++ EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} ++ ++ virtual ~EndsWith(); ++ ++ virtual bool match( std::string const& expr ) const { ++ return expr.find( m_substr ) == expr.size() - m_substr.size(); ++ } ++ virtual std::string toString() const { ++ return "ends with: \"" + m_substr + "\""; ++ } ++ ++ std::string m_substr; ++ }; ++ } // namespace StdString ++ } // namespace Impl ++ ++ // The following functions create the actual matcher objects. ++ // This allows the types to be inferred ++ template ++ inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, ++ Impl::Matcher const& m2 ) { ++ return Impl::Generic::AllOf().add( m1 ).add( m2 ); ++ } ++ template ++ inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, ++ Impl::Matcher const& m2, ++ Impl::Matcher const& m3 ) { ++ return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); ++ } ++ template ++ inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, ++ Impl::Matcher const& m2 ) { ++ return Impl::Generic::AnyOf().add( m1 ).add( m2 ); ++ } ++ template ++ inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, ++ Impl::Matcher const& m2, ++ Impl::Matcher const& m3 ) { ++ return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); ++ } ++ ++ inline Impl::StdString::Equals Equals( std::string const& str ) { ++ return Impl::StdString::Equals( str ); ++ } ++ inline Impl::StdString::Equals Equals( const char* str ) { ++ return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); ++ } ++ inline Impl::StdString::Contains Contains( std::string const& substr ) { ++ return Impl::StdString::Contains( substr ); ++ } ++ inline Impl::StdString::Contains Contains( const char* substr ) { ++ return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); ++ } ++ inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { ++ return Impl::StdString::StartsWith( substr ); ++ } ++ inline Impl::StdString::StartsWith StartsWith( const char* substr ) { ++ return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); ++ } ++ inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { ++ return Impl::StdString::EndsWith( substr ); ++ } ++ inline Impl::StdString::EndsWith EndsWith( const char* substr ) { ++ return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); ++ } ++ ++} // namespace Matchers ++ ++using namespace Matchers; ++ ++} // namespace Catch ++ ++// #included from: internal/catch_interfaces_tag_alias_registry.h ++#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED ++ ++// #included from: catch_tag_alias.h ++#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct TagAlias { ++ TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} ++ ++ std::string tag; ++ SourceLineInfo lineInfo; ++ }; ++ ++ struct RegistrarForTagAliases { ++ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); ++ }; ++ ++} // end namespace Catch ++ ++#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } ++// #included from: catch_option.hpp ++#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED ++ ++namespace Catch { ++ ++ // An optional type ++ template ++ class Option { ++ public: ++ Option() : nullableValue( NULL ) {} ++ Option( T const& _value ) ++ : nullableValue( new( storage ) T( _value ) ) ++ {} ++ Option( Option const& _other ) ++ : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) ++ {} ++ ++ ~Option() { ++ reset(); ++ } ++ ++ Option& operator= ( Option const& _other ) { ++ if( &_other != this ) { ++ reset(); ++ if( _other ) ++ nullableValue = new( storage ) T( *_other ); ++ } ++ return *this; ++ } ++ Option& operator = ( T const& _value ) { ++ reset(); ++ nullableValue = new( storage ) T( _value ); ++ return *this; ++ } ++ ++ void reset() { ++ if( nullableValue ) ++ nullableValue->~T(); ++ nullableValue = NULL; ++ } ++ ++ T& operator*() { return *nullableValue; } ++ T const& operator*() const { return *nullableValue; } ++ T* operator->() { return nullableValue; } ++ const T* operator->() const { return nullableValue; } ++ ++ T valueOr( T const& defaultValue ) const { ++ return nullableValue ? *nullableValue : defaultValue; ++ } ++ ++ bool some() const { return nullableValue != NULL; } ++ bool none() const { return nullableValue == NULL; } ++ ++ bool operator !() const { return nullableValue == NULL; } ++ operator SafeBool::type() const { ++ return SafeBool::makeSafe( some() ); ++ } ++ ++ private: ++ T* nullableValue; ++ char storage[sizeof(T)]; ++ }; ++ ++} // end namespace Catch ++ ++namespace Catch { ++ ++ struct ITagAliasRegistry { ++ virtual ~ITagAliasRegistry(); ++ virtual Option find( std::string const& alias ) const = 0; ++ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; ++ ++ static ITagAliasRegistry const& get(); ++ }; ++ ++} // end namespace Catch ++ ++// These files are included here so the single_include script doesn't put them ++// in the conditionally compiled sections ++// #included from: internal/catch_test_case_info.h ++#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED ++ ++#include ++#include ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++namespace Catch { ++ ++ struct ITestCase; ++ ++ struct TestCaseInfo { ++ enum SpecialProperties{ ++ None = 0, ++ IsHidden = 1 << 1, ++ ShouldFail = 1 << 2, ++ MayFail = 1 << 3, ++ Throws = 1 << 4 ++ }; ++ ++ TestCaseInfo( std::string const& _name, ++ std::string const& _className, ++ std::string const& _description, ++ std::set const& _tags, ++ SourceLineInfo const& _lineInfo ); ++ ++ TestCaseInfo( TestCaseInfo const& other ); ++ ++ bool isHidden() const; ++ bool throws() const; ++ bool okToFail() const; ++ bool expectedToFail() const; ++ ++ std::string name; ++ std::string className; ++ std::string description; ++ std::set tags; ++ std::set lcaseTags; ++ std::string tagsAsString; ++ SourceLineInfo lineInfo; ++ SpecialProperties properties; ++ }; ++ ++ class TestCase : public TestCaseInfo { ++ public: ++ ++ TestCase( ITestCase* testCase, TestCaseInfo const& info ); ++ TestCase( TestCase const& other ); ++ ++ TestCase withName( std::string const& _newName ) const; ++ ++ void invoke() const; ++ ++ TestCaseInfo const& getTestCaseInfo() const; ++ ++ void swap( TestCase& other ); ++ bool operator == ( TestCase const& other ) const; ++ bool operator < ( TestCase const& other ) const; ++ TestCase& operator = ( TestCase const& other ); ++ ++ private: ++ Ptr test; ++ }; ++ ++ TestCase makeTestCase( ITestCase* testCase, ++ std::string const& className, ++ std::string const& name, ++ std::string const& description, ++ SourceLineInfo const& lineInfo ); ++} ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++ ++#ifdef __OBJC__ ++// #included from: internal/catch_objc.hpp ++#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED ++ ++#import ++ ++#include ++ ++// NB. Any general catch headers included here must be included ++// in catch.hpp first to make sure they are included by the single ++// header for non obj-usage ++ ++/////////////////////////////////////////////////////////////////////////////// ++// This protocol is really only here for (self) documenting purposes, since ++// all its methods are optional. ++@protocol OcFixture ++ ++@optional ++ ++-(void) setUp; ++-(void) tearDown; ++ ++@end ++ ++namespace Catch { ++ ++ class OcMethod : public SharedImpl { ++ ++ public: ++ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} ++ ++ virtual void invoke() const { ++ id obj = [[m_cls alloc] init]; ++ ++ performOptionalSelector( obj, @selector(setUp) ); ++ performOptionalSelector( obj, m_sel ); ++ performOptionalSelector( obj, @selector(tearDown) ); ++ ++ arcSafeRelease( obj ); ++ } ++ private: ++ virtual ~OcMethod() {} ++ ++ Class m_cls; ++ SEL m_sel; ++ }; ++ ++ namespace Detail{ ++ ++ inline std::string getAnnotation( Class cls, ++ std::string const& annotationName, ++ std::string const& testCaseName ) { ++ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; ++ SEL sel = NSSelectorFromString( selStr ); ++ arcSafeRelease( selStr ); ++ id value = performOptionalSelector( cls, sel ); ++ if( value ) ++ return [(NSString*)value UTF8String]; ++ return ""; ++ } ++ } ++ ++ inline size_t registerTestMethods() { ++ size_t noTestMethods = 0; ++ int noClasses = objc_getClassList( NULL, 0 ); ++ ++ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); ++ objc_getClassList( classes, noClasses ); ++ ++ for( int c = 0; c < noClasses; c++ ) { ++ Class cls = classes[c]; ++ { ++ u_int count; ++ Method* methods = class_copyMethodList( cls, &count ); ++ for( u_int m = 0; m < count ; m++ ) { ++ SEL selector = method_getName(methods[m]); ++ std::string methodName = sel_getName(selector); ++ if( startsWith( methodName, "Catch_TestCase_" ) ) { ++ std::string testCaseName = methodName.substr( 15 ); ++ std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); ++ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); ++ const char* className = class_getName( cls ); ++ ++ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); ++ noTestMethods++; ++ } ++ } ++ free(methods); ++ } ++ } ++ return noTestMethods; ++ } ++ ++ namespace Matchers { ++ namespace Impl { ++ namespace NSStringMatchers { ++ ++ template ++ struct StringHolder : MatcherImpl{ ++ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} ++ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} ++ StringHolder() { ++ arcSafeRelease( m_substr ); ++ } ++ ++ NSString* m_substr; ++ }; ++ ++ struct Equals : StringHolder { ++ Equals( NSString* substr ) : StringHolder( substr ){} ++ ++ virtual bool match( ExpressionType const& str ) const { ++ return (str != nil || m_substr == nil ) && ++ [str isEqualToString:m_substr]; ++ } ++ ++ virtual std::string toString() const { ++ return "equals string: " + Catch::toString( m_substr ); ++ } ++ }; ++ ++ struct Contains : StringHolder { ++ Contains( NSString* substr ) : StringHolder( substr ){} ++ ++ virtual bool match( ExpressionType const& str ) const { ++ return (str != nil || m_substr == nil ) && ++ [str rangeOfString:m_substr].location != NSNotFound; ++ } ++ ++ virtual std::string toString() const { ++ return "contains string: " + Catch::toString( m_substr ); ++ } ++ }; ++ ++ struct StartsWith : StringHolder { ++ StartsWith( NSString* substr ) : StringHolder( substr ){} ++ ++ virtual bool match( ExpressionType const& str ) const { ++ return (str != nil || m_substr == nil ) && ++ [str rangeOfString:m_substr].location == 0; ++ } ++ ++ virtual std::string toString() const { ++ return "starts with: " + Catch::toString( m_substr ); ++ } ++ }; ++ struct EndsWith : StringHolder { ++ EndsWith( NSString* substr ) : StringHolder( substr ){} ++ ++ virtual bool match( ExpressionType const& str ) const { ++ return (str != nil || m_substr == nil ) && ++ [str rangeOfString:m_substr].location == [str length] - [m_substr length]; ++ } ++ ++ virtual std::string toString() const { ++ return "ends with: " + Catch::toString( m_substr ); ++ } ++ }; ++ ++ } // namespace NSStringMatchers ++ } // namespace Impl ++ ++ inline Impl::NSStringMatchers::Equals ++ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } ++ ++ inline Impl::NSStringMatchers::Contains ++ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } ++ ++ inline Impl::NSStringMatchers::StartsWith ++ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } ++ ++ inline Impl::NSStringMatchers::EndsWith ++ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } ++ ++ } // namespace Matchers ++ ++ using namespace Matchers; ++ ++} // namespace Catch ++ ++/////////////////////////////////////////////////////////////////////////////// ++#define OC_TEST_CASE( name, desc )\ +++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ ++{\ ++return @ name; \ ++}\ +++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ ++{ \ ++return @ desc; \ ++} \ ++-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) ++ ++#endif ++ ++#ifdef CATCH_IMPL ++// #included from: internal/catch_impl.hpp ++#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED ++ ++// Collect all the implementation files together here ++// These are the equivalent of what would usually be cpp files ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wweak-vtables" ++#endif ++ ++// #included from: ../catch_runner.hpp ++#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED ++ ++// #included from: internal/catch_commandline.hpp ++#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED ++ ++// #included from: catch_config.hpp ++#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED ++ ++// #included from: catch_test_spec_parser.hpp ++#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++// #included from: catch_test_spec.hpp ++#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++#include ++#include ++ ++namespace Catch { ++ ++ class TestSpec { ++ struct Pattern : SharedImpl<> { ++ virtual ~Pattern(); ++ virtual bool matches( TestCaseInfo const& testCase ) const = 0; ++ }; ++ class NamePattern : public Pattern { ++ enum WildcardPosition { ++ NoWildcard = 0, ++ WildcardAtStart = 1, ++ WildcardAtEnd = 2, ++ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd ++ }; ++ ++ public: ++ NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { ++ if( startsWith( m_name, "*" ) ) { ++ m_name = m_name.substr( 1 ); ++ m_wildcard = WildcardAtStart; ++ } ++ if( endsWith( m_name, "*" ) ) { ++ m_name = m_name.substr( 0, m_name.size()-1 ); ++ m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); ++ } ++ } ++ virtual ~NamePattern(); ++ virtual bool matches( TestCaseInfo const& testCase ) const { ++ switch( m_wildcard ) { ++ case NoWildcard: ++ return m_name == toLower( testCase.name ); ++ case WildcardAtStart: ++ return endsWith( toLower( testCase.name ), m_name ); ++ case WildcardAtEnd: ++ return startsWith( toLower( testCase.name ), m_name ); ++ case WildcardAtBothEnds: ++ return contains( toLower( testCase.name ), m_name ); ++ } ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wunreachable-code" ++#endif ++ throw std::logic_error( "Unknown enum" ); ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ } ++ private: ++ std::string m_name; ++ WildcardPosition m_wildcard; ++ }; ++ class TagPattern : public Pattern { ++ public: ++ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} ++ virtual ~TagPattern(); ++ virtual bool matches( TestCaseInfo const& testCase ) const { ++ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); ++ } ++ private: ++ std::string m_tag; ++ }; ++ class ExcludedPattern : public Pattern { ++ public: ++ ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} ++ virtual ~ExcludedPattern(); ++ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } ++ private: ++ Ptr m_underlyingPattern; ++ }; ++ ++ struct Filter { ++ std::vector > m_patterns; ++ ++ bool matches( TestCaseInfo const& testCase ) const { ++ // All patterns in a filter must match for the filter to be a match ++ for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) ++ if( !(*it)->matches( testCase ) ) ++ return false; ++ return true; ++ } ++ }; ++ ++ public: ++ bool hasFilters() const { ++ return !m_filters.empty(); ++ } ++ bool matches( TestCaseInfo const& testCase ) const { ++ // A TestSpec matches if any filter matches ++ for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) ++ if( it->matches( testCase ) ) ++ return true; ++ return false; ++ } ++ ++ private: ++ std::vector m_filters; ++ ++ friend class TestSpecParser; ++ }; ++} ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++namespace Catch { ++ ++ class TestSpecParser { ++ enum Mode{ None, Name, QuotedName, Tag }; ++ Mode m_mode; ++ bool m_exclusion; ++ std::size_t m_start, m_pos; ++ std::string m_arg; ++ TestSpec::Filter m_currentFilter; ++ TestSpec m_testSpec; ++ ITagAliasRegistry const* m_tagAliases; ++ ++ public: ++ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} ++ ++ TestSpecParser& parse( std::string const& arg ) { ++ m_mode = None; ++ m_exclusion = false; ++ m_start = std::string::npos; ++ m_arg = m_tagAliases->expandAliases( arg ); ++ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) ++ visitChar( m_arg[m_pos] ); ++ if( m_mode == Name ) ++ addPattern(); ++ return *this; ++ } ++ TestSpec testSpec() { ++ addFilter(); ++ return m_testSpec; ++ } ++ private: ++ void visitChar( char c ) { ++ if( m_mode == None ) { ++ switch( c ) { ++ case ' ': return; ++ case '~': m_exclusion = true; return; ++ case '[': return startNewMode( Tag, ++m_pos ); ++ case '"': return startNewMode( QuotedName, ++m_pos ); ++ default: startNewMode( Name, m_pos ); break; ++ } ++ } ++ if( m_mode == Name ) { ++ if( c == ',' ) { ++ addPattern(); ++ addFilter(); ++ } ++ else if( c == '[' ) { ++ if( subString() == "exclude:" ) ++ m_exclusion = true; ++ else ++ addPattern(); ++ startNewMode( Tag, ++m_pos ); ++ } ++ } ++ else if( m_mode == QuotedName && c == '"' ) ++ addPattern(); ++ else if( m_mode == Tag && c == ']' ) ++ addPattern(); ++ } ++ void startNewMode( Mode mode, std::size_t start ) { ++ m_mode = mode; ++ m_start = start; ++ } ++ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } ++ template ++ void addPattern() { ++ std::string token = subString(); ++ if( startsWith( token, "exclude:" ) ) { ++ m_exclusion = true; ++ token = token.substr( 8 ); ++ } ++ if( !token.empty() ) { ++ Ptr pattern = new T( token ); ++ if( m_exclusion ) ++ pattern = new TestSpec::ExcludedPattern( pattern ); ++ m_currentFilter.m_patterns.push_back( pattern ); ++ } ++ m_exclusion = false; ++ m_mode = None; ++ } ++ void addFilter() { ++ if( !m_currentFilter.m_patterns.empty() ) { ++ m_testSpec.m_filters.push_back( m_currentFilter ); ++ m_currentFilter = TestSpec::Filter(); ++ } ++ } ++ }; ++ inline TestSpec parseTestSpec( std::string const& arg ) { ++ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); ++ } ++ ++} // namespace Catch ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++// #included from: catch_interfaces_config.h ++#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ struct Verbosity { enum Level { ++ NoOutput = 0, ++ Quiet, ++ Normal ++ }; }; ++ ++ struct WarnAbout { enum What { ++ Nothing = 0x00, ++ NoAssertions = 0x01 ++ }; }; ++ ++ struct ShowDurations { enum OrNot { ++ DefaultForReporter, ++ Always, ++ Never ++ }; }; ++ struct RunTests { enum InWhatOrder { ++ InDeclarationOrder, ++ InLexicographicalOrder, ++ InRandomOrder ++ }; }; ++ ++ class TestSpec; ++ ++ struct IConfig : IShared { ++ ++ virtual ~IConfig(); ++ ++ virtual bool allowThrows() const = 0; ++ virtual std::ostream& stream() const = 0; ++ virtual std::string name() const = 0; ++ virtual bool includeSuccessfulResults() const = 0; ++ virtual bool shouldDebugBreak() const = 0; ++ virtual bool warnAboutMissingAssertions() const = 0; ++ virtual int abortAfter() const = 0; ++ virtual bool showInvisibles() const = 0; ++ virtual ShowDurations::OrNot showDurations() const = 0; ++ virtual TestSpec const& testSpec() const = 0; ++ virtual RunTests::InWhatOrder runOrder() const = 0; ++ virtual unsigned int rngSeed() const = 0; ++ virtual bool forceColour() const = 0; ++ }; ++} ++ ++// #included from: catch_stream.h ++#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED ++ ++#include ++ ++#ifdef __clang__ ++#pragma clang diagnostic ignored "-Wpadded" ++#endif ++ ++namespace Catch { ++ ++ class Stream { ++ public: ++ Stream(); ++ Stream( std::streambuf* _streamBuf, bool _isOwned ); ++ void release(); ++ ++ std::streambuf* streamBuf; ++ ++ private: ++ bool isOwned; ++ }; ++ ++ std::ostream& cout(); ++ std::ostream& cerr(); ++} ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef CATCH_CONFIG_CONSOLE_WIDTH ++#define CATCH_CONFIG_CONSOLE_WIDTH 80 ++#endif ++ ++namespace Catch { ++ ++ struct ConfigData { ++ ++ ConfigData() ++ : listTests( false ), ++ listTags( false ), ++ listReporters( false ), ++ listTestNamesOnly( false ), ++ showSuccessfulTests( false ), ++ shouldDebugBreak( false ), ++ noThrow( false ), ++ showHelp( false ), ++ showInvisibles( false ), ++ forceColour( false ), ++ abortAfter( -1 ), ++ rngSeed( 0 ), ++ verbosity( Verbosity::Normal ), ++ warnings( WarnAbout::Nothing ), ++ showDurations( ShowDurations::DefaultForReporter ), ++ runOrder( RunTests::InDeclarationOrder ) ++ {} ++ ++ bool listTests; ++ bool listTags; ++ bool listReporters; ++ bool listTestNamesOnly; ++ ++ bool showSuccessfulTests; ++ bool shouldDebugBreak; ++ bool noThrow; ++ bool showHelp; ++ bool showInvisibles; ++ bool forceColour; ++ ++ int abortAfter; ++ unsigned int rngSeed; ++ ++ Verbosity::Level verbosity; ++ WarnAbout::What warnings; ++ ShowDurations::OrNot showDurations; ++ RunTests::InWhatOrder runOrder; ++ ++ std::string reporterName; ++ std::string outputFilename; ++ std::string name; ++ std::string processName; ++ ++ std::vector testsOrTags; ++ }; ++ ++ class Config : public SharedImpl { ++ private: ++ Config( Config const& other ); ++ Config& operator = ( Config const& other ); ++ virtual void dummy(); ++ public: ++ ++ Config() ++ : m_os( Catch::cout().rdbuf() ) ++ {} ++ ++ Config( ConfigData const& data ) ++ : m_data( data ), ++ m_os( Catch::cout().rdbuf() ) ++ { ++ if( !data.testsOrTags.empty() ) { ++ TestSpecParser parser( ITagAliasRegistry::get() ); ++ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) ++ parser.parse( data.testsOrTags[i] ); ++ m_testSpec = parser.testSpec(); ++ } ++ } ++ ++ virtual ~Config() { ++ m_os.rdbuf( Catch::cout().rdbuf() ); ++ m_stream.release(); ++ } ++ ++ void setFilename( std::string const& filename ) { ++ m_data.outputFilename = filename; ++ } ++ ++ std::string const& getFilename() const { ++ return m_data.outputFilename ; ++ } ++ ++ bool listTests() const { return m_data.listTests; } ++ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } ++ bool listTags() const { return m_data.listTags; } ++ bool listReporters() const { return m_data.listReporters; } ++ ++ std::string getProcessName() const { return m_data.processName; } ++ ++ bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } ++ ++ void setStreamBuf( std::streambuf* buf ) { ++ m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() ); ++ } ++ ++ void useStream( std::string const& streamName ) { ++ Stream stream = createStream( streamName ); ++ setStreamBuf( stream.streamBuf ); ++ m_stream.release(); ++ m_stream = stream; ++ } ++ ++ std::string getReporterName() const { return m_data.reporterName; } ++ ++ int abortAfter() const { return m_data.abortAfter; } ++ ++ TestSpec const& testSpec() const { return m_testSpec; } ++ ++ bool showHelp() const { return m_data.showHelp; } ++ bool showInvisibles() const { return m_data.showInvisibles; } ++ ++ // IConfig interface ++ virtual bool allowThrows() const { return !m_data.noThrow; } ++ virtual std::ostream& stream() const { return m_os; } ++ virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } ++ virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } ++ virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } ++ virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } ++ virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } ++ virtual unsigned int rngSeed() const { return m_data.rngSeed; } ++ virtual bool forceColour() const { return m_data.forceColour; } ++ ++ private: ++ ConfigData m_data; ++ ++ Stream m_stream; ++ mutable std::ostream m_os; ++ TestSpec m_testSpec; ++ }; ++ ++} // end namespace Catch ++ ++// #included from: catch_clara.h ++#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED ++ ++// Use Catch's value for console width (store Clara's off to the side, if present) ++#ifdef CLARA_CONFIG_CONSOLE_WIDTH ++#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH ++#undef CLARA_CONFIG_CONSOLE_WIDTH ++#endif ++#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH ++ ++// Declare Clara inside the Catch namespace ++#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { ++// #included from: ../external/clara.h ++ ++// Only use header guard if we are not using an outer namespace ++#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) ++ ++#ifndef STITCH_CLARA_OPEN_NAMESPACE ++#define TWOBLUECUBES_CLARA_H_INCLUDED ++#define STITCH_CLARA_OPEN_NAMESPACE ++#define STITCH_CLARA_CLOSE_NAMESPACE ++#else ++#define STITCH_CLARA_CLOSE_NAMESPACE } ++#endif ++ ++#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE ++ ++// ----------- #included from tbc_text_format.h ----------- ++ ++// Only use header guard if we are not using an outer namespace ++#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) ++#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++#define TBC_TEXT_FORMAT_H_INCLUDED ++#endif ++ ++#include ++#include ++#include ++ ++// Use optional outer namespace ++#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { ++#endif ++ ++namespace Tbc { ++ ++#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH ++ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; ++#else ++ const unsigned int consoleWidth = 80; ++#endif ++ ++ struct TextAttributes { ++ TextAttributes() ++ : initialIndent( std::string::npos ), ++ indent( 0 ), ++ width( consoleWidth-1 ), ++ tabChar( '\t' ) ++ {} ++ ++ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } ++ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } ++ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } ++ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } ++ ++ std::size_t initialIndent; // indent of first line, or npos ++ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos ++ std::size_t width; // maximum width of text, including indent. Longer text will wrap ++ char tabChar; // If this char is seen the indent is changed to current pos ++ }; ++ ++ class Text { ++ public: ++ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) ++ : attr( _attr ) ++ { ++ std::string wrappableChars = " [({.,/|\\-"; ++ std::size_t indent = _attr.initialIndent != std::string::npos ++ ? _attr.initialIndent ++ : _attr.indent; ++ std::string remainder = _str; ++ ++ while( !remainder.empty() ) { ++ if( lines.size() >= 1000 ) { ++ lines.push_back( "... message truncated due to excessive size" ); ++ return; ++ } ++ std::size_t tabPos = std::string::npos; ++ std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); ++ std::size_t pos = remainder.find_first_of( '\n' ); ++ if( pos <= width ) { ++ width = pos; ++ } ++ pos = remainder.find_last_of( _attr.tabChar, width ); ++ if( pos != std::string::npos ) { ++ tabPos = pos; ++ if( remainder[width] == '\n' ) ++ width--; ++ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); ++ } ++ ++ if( width == remainder.size() ) { ++ spliceLine( indent, remainder, width ); ++ } ++ else if( remainder[width] == '\n' ) { ++ spliceLine( indent, remainder, width ); ++ if( width <= 1 || remainder.size() != 1 ) ++ remainder = remainder.substr( 1 ); ++ indent = _attr.indent; ++ } ++ else { ++ pos = remainder.find_last_of( wrappableChars, width ); ++ if( pos != std::string::npos && pos > 0 ) { ++ spliceLine( indent, remainder, pos ); ++ if( remainder[0] == ' ' ) ++ remainder = remainder.substr( 1 ); ++ } ++ else { ++ spliceLine( indent, remainder, width-1 ); ++ lines.back() += "-"; ++ } ++ if( lines.size() == 1 ) ++ indent = _attr.indent; ++ if( tabPos != std::string::npos ) ++ indent += tabPos; ++ } ++ } ++ } ++ ++ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { ++ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); ++ _remainder = _remainder.substr( _pos ); ++ } ++ ++ typedef std::vector::const_iterator const_iterator; ++ ++ const_iterator begin() const { return lines.begin(); } ++ const_iterator end() const { return lines.end(); } ++ std::string const& last() const { return lines.back(); } ++ std::size_t size() const { return lines.size(); } ++ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } ++ std::string toString() const { ++ std::ostringstream oss; ++ oss << *this; ++ return oss.str(); ++ } ++ ++ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { ++ for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); ++ it != itEnd; ++it ) { ++ if( it != _text.begin() ) ++ _stream << "\n"; ++ _stream << *it; ++ } ++ return _stream; ++ } ++ ++ private: ++ std::string str; ++ TextAttributes attr; ++ std::vector lines; ++ }; ++ ++} // end namespace Tbc ++ ++#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++} // end outer namespace ++#endif ++ ++#endif // TBC_TEXT_FORMAT_H_INCLUDED ++ ++// ----------- end of #include from tbc_text_format.h ----------- ++// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h ++ ++#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE ++ ++#include ++#include ++#include ++#include ++ ++// Use optional outer namespace ++#ifdef STITCH_CLARA_OPEN_NAMESPACE ++STITCH_CLARA_OPEN_NAMESPACE ++#endif ++ ++namespace Clara { ++ ++ struct UnpositionalTag {}; ++ ++ extern UnpositionalTag _; ++ ++#ifdef CLARA_CONFIG_MAIN ++ UnpositionalTag _; ++#endif ++ ++ namespace Detail { ++ ++#ifdef CLARA_CONSOLE_WIDTH ++ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; ++#else ++ const unsigned int consoleWidth = 80; ++#endif ++ ++ using namespace Tbc; ++ ++ inline bool startsWith( std::string const& str, std::string const& prefix ) { ++ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; ++ } ++ ++ template struct RemoveConstRef{ typedef T type; }; ++ template struct RemoveConstRef{ typedef T type; }; ++ template struct RemoveConstRef{ typedef T type; }; ++ template struct RemoveConstRef{ typedef T type; }; ++ ++ template struct IsBool { static const bool value = false; }; ++ template<> struct IsBool { static const bool value = true; }; ++ ++ template ++ void convertInto( std::string const& _source, T& _dest ) { ++ std::stringstream ss; ++ ss << _source; ++ ss >> _dest; ++ if( ss.fail() ) ++ throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); ++ } ++ inline void convertInto( std::string const& _source, std::string& _dest ) { ++ _dest = _source; ++ } ++ inline void convertInto( std::string const& _source, bool& _dest ) { ++ std::string sourceLC = _source; ++ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); ++ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) ++ _dest = true; ++ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) ++ _dest = false; ++ else ++ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); ++ } ++ inline void convertInto( bool _source, bool& _dest ) { ++ _dest = _source; ++ } ++ template ++ inline void convertInto( bool, T& ) { ++ throw std::runtime_error( "Invalid conversion" ); ++ } ++ ++ template ++ struct IArgFunction { ++ virtual ~IArgFunction() {} ++# ifdef CATCH_CPP11_OR_GREATER ++ IArgFunction() = default; ++ IArgFunction( IArgFunction const& ) = default; ++# endif ++ virtual void set( ConfigT& config, std::string const& value ) const = 0; ++ virtual void setFlag( ConfigT& config ) const = 0; ++ virtual bool takesArg() const = 0; ++ virtual IArgFunction* clone() const = 0; ++ }; ++ ++ template ++ class BoundArgFunction { ++ public: ++ BoundArgFunction() : functionObj( NULL ) {} ++ BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} ++ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} ++ BoundArgFunction& operator = ( BoundArgFunction const& other ) { ++ IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; ++ delete functionObj; ++ functionObj = newFunctionObj; ++ return *this; ++ } ++ ~BoundArgFunction() { delete functionObj; } ++ ++ void set( ConfigT& config, std::string const& value ) const { ++ functionObj->set( config, value ); ++ } ++ void setFlag( ConfigT& config ) const { ++ functionObj->setFlag( config ); ++ } ++ bool takesArg() const { return functionObj->takesArg(); } ++ ++ bool isSet() const { ++ return functionObj != NULL; ++ } ++ private: ++ IArgFunction* functionObj; ++ }; ++ ++ template ++ struct NullBinder : IArgFunction{ ++ virtual void set( C&, std::string const& ) const {} ++ virtual void setFlag( C& ) const {} ++ virtual bool takesArg() const { return true; } ++ virtual IArgFunction* clone() const { return new NullBinder( *this ); } ++ }; ++ ++ template ++ struct BoundDataMember : IArgFunction{ ++ BoundDataMember( M C::* _member ) : member( _member ) {} ++ virtual void set( C& p, std::string const& stringValue ) const { ++ convertInto( stringValue, p.*member ); ++ } ++ virtual void setFlag( C& p ) const { ++ convertInto( true, p.*member ); ++ } ++ virtual bool takesArg() const { return !IsBool::value; } ++ virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } ++ M C::* member; ++ }; ++ template ++ struct BoundUnaryMethod : IArgFunction{ ++ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} ++ virtual void set( C& p, std::string const& stringValue ) const { ++ typename RemoveConstRef::type value; ++ convertInto( stringValue, value ); ++ (p.*member)( value ); ++ } ++ virtual void setFlag( C& p ) const { ++ typename RemoveConstRef::type value; ++ convertInto( true, value ); ++ (p.*member)( value ); ++ } ++ virtual bool takesArg() const { return !IsBool::value; } ++ virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } ++ void (C::*member)( M ); ++ }; ++ template ++ struct BoundNullaryMethod : IArgFunction{ ++ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} ++ virtual void set( C& p, std::string const& stringValue ) const { ++ bool value; ++ convertInto( stringValue, value ); ++ if( value ) ++ (p.*member)(); ++ } ++ virtual void setFlag( C& p ) const { ++ (p.*member)(); ++ } ++ virtual bool takesArg() const { return false; } ++ virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } ++ void (C::*member)(); ++ }; ++ ++ template ++ struct BoundUnaryFunction : IArgFunction{ ++ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} ++ virtual void set( C& obj, std::string const& stringValue ) const { ++ bool value; ++ convertInto( stringValue, value ); ++ if( value ) ++ function( obj ); ++ } ++ virtual void setFlag( C& p ) const { ++ function( p ); ++ } ++ virtual bool takesArg() const { return false; } ++ virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } ++ void (*function)( C& ); ++ }; ++ ++ template ++ struct BoundBinaryFunction : IArgFunction{ ++ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} ++ virtual void set( C& obj, std::string const& stringValue ) const { ++ typename RemoveConstRef::type value; ++ convertInto( stringValue, value ); ++ function( obj, value ); ++ } ++ virtual void setFlag( C& obj ) const { ++ typename RemoveConstRef::type value; ++ convertInto( true, value ); ++ function( obj, value ); ++ } ++ virtual bool takesArg() const { return !IsBool::value; } ++ virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } ++ void (*function)( C&, T ); ++ }; ++ ++ } // namespace Detail ++ ++ struct Parser { ++ Parser() : separators( " \t=:" ) {} ++ ++ struct Token { ++ enum Type { Positional, ShortOpt, LongOpt }; ++ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} ++ Type type; ++ std::string data; ++ }; ++ ++ void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { ++ const std::string doubleDash = "--"; ++ for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) ++ parseIntoTokens( argv[i] , tokens); ++ } ++ void parseIntoTokens( std::string arg, std::vector& tokens ) const { ++ while( !arg.empty() ) { ++ Parser::Token token( Parser::Token::Positional, arg ); ++ arg = ""; ++ if( token.data[0] == '-' ) { ++ if( token.data.size() > 1 && token.data[1] == '-' ) { ++ token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); ++ } ++ else { ++ token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); ++ if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { ++ arg = "-" + token.data.substr( 1 ); ++ token.data = token.data.substr( 0, 1 ); ++ } ++ } ++ } ++ if( token.type != Parser::Token::Positional ) { ++ std::size_t pos = token.data.find_first_of( separators ); ++ if( pos != std::string::npos ) { ++ arg = token.data.substr( pos+1 ); ++ token.data = token.data.substr( 0, pos ); ++ } ++ } ++ tokens.push_back( token ); ++ } ++ } ++ std::string separators; ++ }; ++ ++ template ++ struct CommonArgProperties { ++ CommonArgProperties() {} ++ CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} ++ ++ Detail::BoundArgFunction boundField; ++ std::string description; ++ std::string detail; ++ std::string placeholder; // Only value if boundField takes an arg ++ ++ bool takesArg() const { ++ return !placeholder.empty(); ++ } ++ void validate() const { ++ if( !boundField.isSet() ) ++ throw std::logic_error( "option not bound" ); ++ } ++ }; ++ struct OptionArgProperties { ++ std::vector shortNames; ++ std::string longName; ++ ++ bool hasShortName( std::string const& shortName ) const { ++ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); ++ } ++ bool hasLongName( std::string const& _longName ) const { ++ return _longName == longName; ++ } ++ }; ++ struct PositionalArgProperties { ++ PositionalArgProperties() : position( -1 ) {} ++ int position; // -1 means non-positional (floating) ++ ++ bool isFixedPositional() const { ++ return position != -1; ++ } ++ }; ++ ++ template ++ class CommandLine { ++ ++ struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { ++ Arg() {} ++ Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} ++ ++ using CommonArgProperties::placeholder; // !TBD ++ ++ std::string dbgName() const { ++ if( !longName.empty() ) ++ return "--" + longName; ++ if( !shortNames.empty() ) ++ return "-" + shortNames[0]; ++ return "positional args"; ++ } ++ std::string commands() const { ++ std::ostringstream oss; ++ bool first = true; ++ std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); ++ for(; it != itEnd; ++it ) { ++ if( first ) ++ first = false; ++ else ++ oss << ", "; ++ oss << "-" << *it; ++ } ++ if( !longName.empty() ) { ++ if( !first ) ++ oss << ", "; ++ oss << "--" << longName; ++ } ++ if( !placeholder.empty() ) ++ oss << " <" << placeholder << ">"; ++ return oss.str(); ++ } ++ }; ++ ++ // NOTE: std::auto_ptr is deprecated in c++11/c++0x ++#if defined(__cplusplus) && __cplusplus > 199711L ++ typedef std::unique_ptr ArgAutoPtr; ++#else ++ typedef std::auto_ptr ArgAutoPtr; ++#endif ++ ++ friend void addOptName( Arg& arg, std::string const& optName ) ++ { ++ if( optName.empty() ) ++ return; ++ if( Detail::startsWith( optName, "--" ) ) { ++ if( !arg.longName.empty() ) ++ throw std::logic_error( "Only one long opt may be specified. '" ++ + arg.longName ++ + "' already specified, now attempting to add '" ++ + optName + "'" ); ++ arg.longName = optName.substr( 2 ); ++ } ++ else if( Detail::startsWith( optName, "-" ) ) ++ arg.shortNames.push_back( optName.substr( 1 ) ); ++ else ++ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); ++ } ++ friend void setPositionalArg( Arg& arg, int position ) ++ { ++ arg.position = position; ++ } ++ ++ class ArgBuilder { ++ public: ++ ArgBuilder( Arg* arg ) : m_arg( arg ) {} ++ ++ // Bind a non-boolean data member (requires placeholder string) ++ template ++ void bind( M C::* field, std::string const& placeholder ) { ++ m_arg->boundField = new Detail::BoundDataMember( field ); ++ m_arg->placeholder = placeholder; ++ } ++ // Bind a boolean data member (no placeholder required) ++ template ++ void bind( bool C::* field ) { ++ m_arg->boundField = new Detail::BoundDataMember( field ); ++ } ++ ++ // Bind a method taking a single, non-boolean argument (requires a placeholder string) ++ template ++ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { ++ m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); ++ m_arg->placeholder = placeholder; ++ } ++ ++ // Bind a method taking a single, boolean argument (no placeholder string required) ++ template ++ void bind( void (C::* unaryMethod)( bool ) ) { ++ m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); ++ } ++ ++ // Bind a method that takes no arguments (will be called if opt is present) ++ template ++ void bind( void (C::* nullaryMethod)() ) { ++ m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); ++ } ++ ++ // Bind a free function taking a single argument - the object to operate on (no placeholder string required) ++ template ++ void bind( void (* unaryFunction)( C& ) ) { ++ m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); ++ } ++ ++ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) ++ template ++ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { ++ m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); ++ m_arg->placeholder = placeholder; ++ } ++ ++ ArgBuilder& describe( std::string const& description ) { ++ m_arg->description = description; ++ return *this; ++ } ++ ArgBuilder& detail( std::string const& detail ) { ++ m_arg->detail = detail; ++ return *this; ++ } ++ ++ protected: ++ Arg* m_arg; ++ }; ++ ++ class OptBuilder : public ArgBuilder { ++ public: ++ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} ++ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} ++ ++ OptBuilder& operator[]( std::string const& optName ) { ++ addOptName( *ArgBuilder::m_arg, optName ); ++ return *this; ++ } ++ }; ++ ++ public: ++ ++ CommandLine() ++ : m_boundProcessName( new Detail::NullBinder() ), ++ m_highestSpecifiedArgPosition( 0 ), ++ m_throwOnUnrecognisedTokens( false ) ++ {} ++ CommandLine( CommandLine const& other ) ++ : m_boundProcessName( other.m_boundProcessName ), ++ m_options ( other.m_options ), ++ m_positionalArgs( other.m_positionalArgs ), ++ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), ++ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) ++ { ++ if( other.m_floatingArg.get() ) ++ m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); ++ } ++ ++ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { ++ m_throwOnUnrecognisedTokens = shouldThrow; ++ return *this; ++ } ++ ++ OptBuilder operator[]( std::string const& optName ) { ++ m_options.push_back( Arg() ); ++ addOptName( m_options.back(), optName ); ++ OptBuilder builder( &m_options.back() ); ++ return builder; ++ } ++ ++ ArgBuilder operator[]( int position ) { ++ m_positionalArgs.insert( std::make_pair( position, Arg() ) ); ++ if( position > m_highestSpecifiedArgPosition ) ++ m_highestSpecifiedArgPosition = position; ++ setPositionalArg( m_positionalArgs[position], position ); ++ ArgBuilder builder( &m_positionalArgs[position] ); ++ return builder; ++ } ++ ++ // Invoke this with the _ instance ++ ArgBuilder operator[]( UnpositionalTag ) { ++ if( m_floatingArg.get() ) ++ throw std::logic_error( "Only one unpositional argument can be added" ); ++ m_floatingArg.reset( new Arg() ); ++ ArgBuilder builder( m_floatingArg.get() ); ++ return builder; ++ } ++ ++ template ++ void bindProcessName( M C::* field ) { ++ m_boundProcessName = new Detail::BoundDataMember( field ); ++ } ++ template ++ void bindProcessName( void (C::*_unaryMethod)( M ) ) { ++ m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); ++ } ++ ++ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { ++ typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; ++ std::size_t maxWidth = 0; ++ for( it = itBegin; it != itEnd; ++it ) ++ maxWidth = (std::max)( maxWidth, it->commands().size() ); ++ ++ for( it = itBegin; it != itEnd; ++it ) { ++ Detail::Text usage( it->commands(), Detail::TextAttributes() ++ .setWidth( maxWidth+indent ) ++ .setIndent( indent ) ); ++ Detail::Text desc( it->description, Detail::TextAttributes() ++ .setWidth( width - maxWidth - 3 ) ); ++ ++ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { ++ std::string usageCol = i < usage.size() ? usage[i] : ""; ++ os << usageCol; ++ ++ if( i < desc.size() && !desc[i].empty() ) ++ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) ++ << desc[i]; ++ os << "\n"; ++ } ++ } ++ } ++ std::string optUsage() const { ++ std::ostringstream oss; ++ optUsage( oss ); ++ return oss.str(); ++ } ++ ++ void argSynopsis( std::ostream& os ) const { ++ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { ++ if( i > 1 ) ++ os << " "; ++ typename std::map::const_iterator it = m_positionalArgs.find( i ); ++ if( it != m_positionalArgs.end() ) ++ os << "<" << it->second.placeholder << ">"; ++ else if( m_floatingArg.get() ) ++ os << "<" << m_floatingArg->placeholder << ">"; ++ else ++ throw std::logic_error( "non consecutive positional arguments with no floating args" ); ++ } ++ // !TBD No indication of mandatory args ++ if( m_floatingArg.get() ) { ++ if( m_highestSpecifiedArgPosition > 1 ) ++ os << " "; ++ os << "[<" << m_floatingArg->placeholder << "> ...]"; ++ } ++ } ++ std::string argSynopsis() const { ++ std::ostringstream oss; ++ argSynopsis( oss ); ++ return oss.str(); ++ } ++ ++ void usage( std::ostream& os, std::string const& procName ) const { ++ validate(); ++ os << "usage:\n " << procName << " "; ++ argSynopsis( os ); ++ if( !m_options.empty() ) { ++ os << " [options]\n\nwhere options are: \n"; ++ optUsage( os, 2 ); ++ } ++ os << "\n"; ++ } ++ std::string usage( std::string const& procName ) const { ++ std::ostringstream oss; ++ usage( oss, procName ); ++ return oss.str(); ++ } ++ ++ ConfigT parse( int argc, char const * const * argv ) const { ++ ConfigT config; ++ parseInto( argc, argv, config ); ++ return config; ++ } ++ ++ std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { ++ std::string processName = argv[0]; ++ std::size_t lastSlash = processName.find_last_of( "/\\" ); ++ if( lastSlash != std::string::npos ) ++ processName = processName.substr( lastSlash+1 ); ++ m_boundProcessName.set( config, processName ); ++ std::vector tokens; ++ Parser parser; ++ parser.parseIntoTokens( argc, argv, tokens ); ++ return populate( tokens, config ); ++ } ++ ++ std::vector populate( std::vector const& tokens, ConfigT& config ) const { ++ validate(); ++ std::vector unusedTokens = populateOptions( tokens, config ); ++ unusedTokens = populateFixedArgs( unusedTokens, config ); ++ unusedTokens = populateFloatingArgs( unusedTokens, config ); ++ return unusedTokens; ++ } ++ ++ std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { ++ std::vector unusedTokens; ++ std::vector errors; ++ for( std::size_t i = 0; i < tokens.size(); ++i ) { ++ Parser::Token const& token = tokens[i]; ++ typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); ++ for(; it != itEnd; ++it ) { ++ Arg const& arg = *it; ++ ++ try { ++ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || ++ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { ++ if( arg.takesArg() ) { ++ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) ++ errors.push_back( "Expected argument to option: " + token.data ); ++ else ++ arg.boundField.set( config, tokens[++i].data ); ++ } ++ else { ++ arg.boundField.setFlag( config ); ++ } ++ break; ++ } ++ } ++ catch( std::exception& ex ) { ++ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); ++ } ++ } ++ if( it == itEnd ) { ++ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) ++ unusedTokens.push_back( token ); ++ else if( errors.empty() && m_throwOnUnrecognisedTokens ) ++ errors.push_back( "unrecognised option: " + token.data ); ++ } ++ } ++ if( !errors.empty() ) { ++ std::ostringstream oss; ++ for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); ++ it != itEnd; ++ ++it ) { ++ if( it != errors.begin() ) ++ oss << "\n"; ++ oss << *it; ++ } ++ throw std::runtime_error( oss.str() ); ++ } ++ return unusedTokens; ++ } ++ std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { ++ std::vector unusedTokens; ++ int position = 1; ++ for( std::size_t i = 0; i < tokens.size(); ++i ) { ++ Parser::Token const& token = tokens[i]; ++ typename std::map::const_iterator it = m_positionalArgs.find( position ); ++ if( it != m_positionalArgs.end() ) ++ it->second.boundField.set( config, token.data ); ++ else ++ unusedTokens.push_back( token ); ++ if( token.type == Parser::Token::Positional ) ++ position++; ++ } ++ return unusedTokens; ++ } ++ std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { ++ if( !m_floatingArg.get() ) ++ return tokens; ++ std::vector unusedTokens; ++ for( std::size_t i = 0; i < tokens.size(); ++i ) { ++ Parser::Token const& token = tokens[i]; ++ if( token.type == Parser::Token::Positional ) ++ m_floatingArg->boundField.set( config, token.data ); ++ else ++ unusedTokens.push_back( token ); ++ } ++ return unusedTokens; ++ } ++ ++ void validate() const ++ { ++ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) ++ throw std::logic_error( "No options or arguments specified" ); ++ ++ for( typename std::vector::const_iterator it = m_options.begin(), ++ itEnd = m_options.end(); ++ it != itEnd; ++it ) ++ it->validate(); ++ } ++ ++ private: ++ Detail::BoundArgFunction m_boundProcessName; ++ std::vector m_options; ++ std::map m_positionalArgs; ++ ArgAutoPtr m_floatingArg; ++ int m_highestSpecifiedArgPosition; ++ bool m_throwOnUnrecognisedTokens; ++ }; ++ ++} // end namespace Clara ++ ++STITCH_CLARA_CLOSE_NAMESPACE ++#undef STITCH_CLARA_OPEN_NAMESPACE ++#undef STITCH_CLARA_CLOSE_NAMESPACE ++ ++#endif // TWOBLUECUBES_CLARA_H_INCLUDED ++#undef STITCH_CLARA_OPEN_NAMESPACE ++ ++// Restore Clara's value for console width, if present ++#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH ++#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH ++#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH ++#endif ++ ++#include ++ ++namespace Catch { ++ ++ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } ++ inline void abortAfterX( ConfigData& config, int x ) { ++ if( x < 1 ) ++ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); ++ config.abortAfter = x; ++ } ++ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } ++ ++ inline void addWarning( ConfigData& config, std::string const& _warning ) { ++ if( _warning == "NoAssertions" ) ++ config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); ++ else ++ throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); ++ } ++ inline void setOrder( ConfigData& config, std::string const& order ) { ++ if( startsWith( "declared", order ) ) ++ config.runOrder = RunTests::InDeclarationOrder; ++ else if( startsWith( "lexical", order ) ) ++ config.runOrder = RunTests::InLexicographicalOrder; ++ else if( startsWith( "random", order ) ) ++ config.runOrder = RunTests::InRandomOrder; ++ else ++ throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); ++ } ++ inline void setRngSeed( ConfigData& config, std::string const& seed ) { ++ if( seed == "time" ) { ++ config.rngSeed = static_cast( std::time(0) ); ++ } ++ else { ++ std::stringstream ss; ++ ss << seed; ++ ss >> config.rngSeed; ++ if( ss.fail() ) ++ throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); ++ } ++ } ++ inline void setVerbosity( ConfigData& config, int level ) { ++ // !TBD: accept strings? ++ config.verbosity = static_cast( level ); ++ } ++ inline void setShowDurations( ConfigData& config, bool _showDurations ) { ++ config.showDurations = _showDurations ++ ? ShowDurations::Always ++ : ShowDurations::Never; ++ } ++ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { ++ std::ifstream f( _filename.c_str() ); ++ if( !f.is_open() ) ++ throw std::domain_error( "Unable to load input file: " + _filename ); ++ ++ std::string line; ++ while( std::getline( f, line ) ) { ++ line = trim(line); ++ if( !line.empty() && !startsWith( line, "#" ) ) ++ addTestOrTags( config, "\"" + line + "\"," ); ++ } ++ } ++ ++ inline Clara::CommandLine makeCommandLineParser() { ++ ++ using namespace Clara; ++ CommandLine cli; ++ ++ cli.bindProcessName( &ConfigData::processName ); ++ ++ cli["-?"]["-h"]["--help"] ++ .describe( "display usage information" ) ++ .bind( &ConfigData::showHelp ); ++ ++ cli["-l"]["--list-tests"] ++ .describe( "list all/matching test cases" ) ++ .bind( &ConfigData::listTests ); ++ ++ cli["-t"]["--list-tags"] ++ .describe( "list all/matching tags" ) ++ .bind( &ConfigData::listTags ); ++ ++ cli["-s"]["--success"] ++ .describe( "include successful tests in output" ) ++ .bind( &ConfigData::showSuccessfulTests ); ++ ++ cli["-b"]["--break"] ++ .describe( "break into debugger on failure" ) ++ .bind( &ConfigData::shouldDebugBreak ); ++ ++ cli["-e"]["--nothrow"] ++ .describe( "skip exception tests" ) ++ .bind( &ConfigData::noThrow ); ++ ++ cli["-i"]["--invisibles"] ++ .describe( "show invisibles (tabs, newlines)" ) ++ .bind( &ConfigData::showInvisibles ); ++ ++ cli["-o"]["--out"] ++ .describe( "output filename" ) ++ .bind( &ConfigData::outputFilename, "filename" ); ++ ++ cli["-r"]["--reporter"] ++// .placeholder( "name[:filename]" ) ++ .describe( "reporter to use (defaults to console)" ) ++ .bind( &ConfigData::reporterName, "name" ); ++ ++ cli["-n"]["--name"] ++ .describe( "suite name" ) ++ .bind( &ConfigData::name, "name" ); ++ ++ cli["-a"]["--abort"] ++ .describe( "abort at first failure" ) ++ .bind( &abortAfterFirst ); ++ ++ cli["-x"]["--abortx"] ++ .describe( "abort after x failures" ) ++ .bind( &abortAfterX, "no. failures" ); ++ ++ cli["-w"]["--warn"] ++ .describe( "enable warnings" ) ++ .bind( &addWarning, "warning name" ); ++ ++// - needs updating if reinstated ++// cli.into( &setVerbosity ) ++// .describe( "level of verbosity (0=no output)" ) ++// .shortOpt( "v") ++// .longOpt( "verbosity" ) ++// .placeholder( "level" ); ++ ++ cli[_] ++ .describe( "which test or tests to use" ) ++ .bind( &addTestOrTags, "test name, pattern or tags" ); ++ ++ cli["-d"]["--durations"] ++ .describe( "show test durations" ) ++ .bind( &setShowDurations, "yes/no" ); ++ ++ cli["-f"]["--input-file"] ++ .describe( "load test names to run from a file" ) ++ .bind( &loadTestNamesFromFile, "filename" ); ++ ++ // Less common commands which don't have a short form ++ cli["--list-test-names-only"] ++ .describe( "list all/matching test cases names only" ) ++ .bind( &ConfigData::listTestNamesOnly ); ++ ++ cli["--list-reporters"] ++ .describe( "list all reporters" ) ++ .bind( &ConfigData::listReporters ); ++ ++ cli["--order"] ++ .describe( "test case order (defaults to decl)" ) ++ .bind( &setOrder, "decl|lex|rand" ); ++ ++ cli["--rng-seed"] ++ .describe( "set a specific seed for random numbers" ) ++ .bind( &setRngSeed, "'time'|number" ); ++ ++ cli["--force-colour"] ++ .describe( "force colourised output" ) ++ .bind( &ConfigData::forceColour ); ++ ++ return cli; ++ } ++ ++} // end namespace Catch ++ ++// #included from: internal/catch_list.hpp ++#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED ++ ++// #included from: catch_text.h ++#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED ++ ++#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH ++ ++#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch ++// #included from: ../external/tbc_text_format.h ++// Only use header guard if we are not using an outer namespace ++#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED ++# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED ++# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED ++# endif ++# else ++# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED ++# endif ++#endif ++#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED ++#include ++#include ++#include ++ ++// Use optional outer namespace ++#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { ++#endif ++ ++namespace Tbc { ++ ++#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH ++ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; ++#else ++ const unsigned int consoleWidth = 80; ++#endif ++ ++ struct TextAttributes { ++ TextAttributes() ++ : initialIndent( std::string::npos ), ++ indent( 0 ), ++ width( consoleWidth-1 ), ++ tabChar( '\t' ) ++ {} ++ ++ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } ++ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } ++ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } ++ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } ++ ++ std::size_t initialIndent; // indent of first line, or npos ++ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos ++ std::size_t width; // maximum width of text, including indent. Longer text will wrap ++ char tabChar; // If this char is seen the indent is changed to current pos ++ }; ++ ++ class Text { ++ public: ++ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) ++ : attr( _attr ) ++ { ++ std::string wrappableChars = " [({.,/|\\-"; ++ std::size_t indent = _attr.initialIndent != std::string::npos ++ ? _attr.initialIndent ++ : _attr.indent; ++ std::string remainder = _str; ++ ++ while( !remainder.empty() ) { ++ if( lines.size() >= 1000 ) { ++ lines.push_back( "... message truncated due to excessive size" ); ++ return; ++ } ++ std::size_t tabPos = std::string::npos; ++ std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); ++ std::size_t pos = remainder.find_first_of( '\n' ); ++ if( pos <= width ) { ++ width = pos; ++ } ++ pos = remainder.find_last_of( _attr.tabChar, width ); ++ if( pos != std::string::npos ) { ++ tabPos = pos; ++ if( remainder[width] == '\n' ) ++ width--; ++ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); ++ } ++ ++ if( width == remainder.size() ) { ++ spliceLine( indent, remainder, width ); ++ } ++ else if( remainder[width] == '\n' ) { ++ spliceLine( indent, remainder, width ); ++ if( width <= 1 || remainder.size() != 1 ) ++ remainder = remainder.substr( 1 ); ++ indent = _attr.indent; ++ } ++ else { ++ pos = remainder.find_last_of( wrappableChars, width ); ++ if( pos != std::string::npos && pos > 0 ) { ++ spliceLine( indent, remainder, pos ); ++ if( remainder[0] == ' ' ) ++ remainder = remainder.substr( 1 ); ++ } ++ else { ++ spliceLine( indent, remainder, width-1 ); ++ lines.back() += "-"; ++ } ++ if( lines.size() == 1 ) ++ indent = _attr.indent; ++ if( tabPos != std::string::npos ) ++ indent += tabPos; ++ } ++ } ++ } ++ ++ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { ++ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); ++ _remainder = _remainder.substr( _pos ); ++ } ++ ++ typedef std::vector::const_iterator const_iterator; ++ ++ const_iterator begin() const { return lines.begin(); } ++ const_iterator end() const { return lines.end(); } ++ std::string const& last() const { return lines.back(); } ++ std::size_t size() const { return lines.size(); } ++ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } ++ std::string toString() const { ++ std::ostringstream oss; ++ oss << *this; ++ return oss.str(); ++ } ++ ++ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { ++ for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); ++ it != itEnd; ++it ) { ++ if( it != _text.begin() ) ++ _stream << "\n"; ++ _stream << *it; ++ } ++ return _stream; ++ } ++ ++ private: ++ std::string str; ++ TextAttributes attr; ++ std::vector lines; ++ }; ++ ++} // end namespace Tbc ++ ++#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++} // end outer namespace ++#endif ++ ++#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED ++#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE ++ ++namespace Catch { ++ using Tbc::Text; ++ using Tbc::TextAttributes; ++} ++ ++// #included from: catch_console_colour.hpp ++#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED ++ ++namespace Catch { ++ ++ struct Colour { ++ enum Code { ++ None = 0, ++ ++ White, ++ Red, ++ Green, ++ Blue, ++ Cyan, ++ Yellow, ++ Grey, ++ ++ Bright = 0x10, ++ ++ BrightRed = Bright | Red, ++ BrightGreen = Bright | Green, ++ LightGrey = Bright | Grey, ++ BrightWhite = Bright | White, ++ ++ // By intention ++ FileName = LightGrey, ++ Warning = Yellow, ++ ResultError = BrightRed, ++ ResultSuccess = BrightGreen, ++ ResultExpectedFailure = Warning, ++ ++ Error = BrightRed, ++ Success = Green, ++ ++ OriginalExpression = Cyan, ++ ReconstructedExpression = Yellow, ++ ++ SecondaryText = LightGrey, ++ Headers = White ++ }; ++ ++ // Use constructed object for RAII guard ++ Colour( Code _colourCode ); ++ Colour( Colour const& other ); ++ ~Colour(); ++ ++ // Use static method for one-shot changes ++ static void use( Code _colourCode ); ++ ++ private: ++ bool m_moved; ++ }; ++ ++ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } ++ ++} // end namespace Catch ++ ++// #included from: catch_interfaces_reporter.h ++#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED ++ ++#include ++#include ++#include ++#include ++ ++namespace Catch ++{ ++ struct ReporterConfig { ++ explicit ReporterConfig( Ptr const& _fullConfig ) ++ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ++ ++ ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) ++ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} ++ ++ std::ostream& stream() const { return *m_stream; } ++ Ptr fullConfig() const { return m_fullConfig; } ++ ++ private: ++ std::ostream* m_stream; ++ Ptr m_fullConfig; ++ }; ++ ++ struct ReporterPreferences { ++ ReporterPreferences() ++ : shouldRedirectStdOut( false ) ++ {} ++ ++ bool shouldRedirectStdOut; ++ }; ++ ++ template ++ struct LazyStat : Option { ++ LazyStat() : used( false ) {} ++ LazyStat& operator=( T const& _value ) { ++ Option::operator=( _value ); ++ used = false; ++ return *this; ++ } ++ void reset() { ++ Option::reset(); ++ used = false; ++ } ++ bool used; ++ }; ++ ++ struct TestRunInfo { ++ TestRunInfo( std::string const& _name ) : name( _name ) {} ++ std::string name; ++ }; ++ struct GroupInfo { ++ GroupInfo( std::string const& _name, ++ std::size_t _groupIndex, ++ std::size_t _groupsCount ) ++ : name( _name ), ++ groupIndex( _groupIndex ), ++ groupsCounts( _groupsCount ) ++ {} ++ ++ std::string name; ++ std::size_t groupIndex; ++ std::size_t groupsCounts; ++ }; ++ ++ struct AssertionStats { ++ AssertionStats( AssertionResult const& _assertionResult, ++ std::vector const& _infoMessages, ++ Totals const& _totals ) ++ : assertionResult( _assertionResult ), ++ infoMessages( _infoMessages ), ++ totals( _totals ) ++ { ++ if( assertionResult.hasMessage() ) { ++ // Copy message into messages list. ++ // !TBD This should have been done earlier, somewhere ++ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); ++ builder << assertionResult.getMessage(); ++ builder.m_info.message = builder.m_stream.str(); ++ ++ infoMessages.push_back( builder.m_info ); ++ } ++ } ++ virtual ~AssertionStats(); ++ ++# ifdef CATCH_CPP11_OR_GREATER ++ AssertionStats( AssertionStats const& ) = default; ++ AssertionStats( AssertionStats && ) = default; ++ AssertionStats& operator = ( AssertionStats const& ) = default; ++ AssertionStats& operator = ( AssertionStats && ) = default; ++# endif ++ ++ AssertionResult assertionResult; ++ std::vector infoMessages; ++ Totals totals; ++ }; ++ ++ struct SectionStats { ++ SectionStats( SectionInfo const& _sectionInfo, ++ Counts const& _assertions, ++ double _durationInSeconds, ++ bool _missingAssertions ) ++ : sectionInfo( _sectionInfo ), ++ assertions( _assertions ), ++ durationInSeconds( _durationInSeconds ), ++ missingAssertions( _missingAssertions ) ++ {} ++ virtual ~SectionStats(); ++# ifdef CATCH_CPP11_OR_GREATER ++ SectionStats( SectionStats const& ) = default; ++ SectionStats( SectionStats && ) = default; ++ SectionStats& operator = ( SectionStats const& ) = default; ++ SectionStats& operator = ( SectionStats && ) = default; ++# endif ++ ++ SectionInfo sectionInfo; ++ Counts assertions; ++ double durationInSeconds; ++ bool missingAssertions; ++ }; ++ ++ struct TestCaseStats { ++ TestCaseStats( TestCaseInfo const& _testInfo, ++ Totals const& _totals, ++ std::string const& _stdOut, ++ std::string const& _stdErr, ++ bool _aborting ) ++ : testInfo( _testInfo ), ++ totals( _totals ), ++ stdOut( _stdOut ), ++ stdErr( _stdErr ), ++ aborting( _aborting ) ++ {} ++ virtual ~TestCaseStats(); ++ ++# ifdef CATCH_CPP11_OR_GREATER ++ TestCaseStats( TestCaseStats const& ) = default; ++ TestCaseStats( TestCaseStats && ) = default; ++ TestCaseStats& operator = ( TestCaseStats const& ) = default; ++ TestCaseStats& operator = ( TestCaseStats && ) = default; ++# endif ++ ++ TestCaseInfo testInfo; ++ Totals totals; ++ std::string stdOut; ++ std::string stdErr; ++ bool aborting; ++ }; ++ ++ struct TestGroupStats { ++ TestGroupStats( GroupInfo const& _groupInfo, ++ Totals const& _totals, ++ bool _aborting ) ++ : groupInfo( _groupInfo ), ++ totals( _totals ), ++ aborting( _aborting ) ++ {} ++ TestGroupStats( GroupInfo const& _groupInfo ) ++ : groupInfo( _groupInfo ), ++ aborting( false ) ++ {} ++ virtual ~TestGroupStats(); ++ ++# ifdef CATCH_CPP11_OR_GREATER ++ TestGroupStats( TestGroupStats const& ) = default; ++ TestGroupStats( TestGroupStats && ) = default; ++ TestGroupStats& operator = ( TestGroupStats const& ) = default; ++ TestGroupStats& operator = ( TestGroupStats && ) = default; ++# endif ++ ++ GroupInfo groupInfo; ++ Totals totals; ++ bool aborting; ++ }; ++ ++ struct TestRunStats { ++ TestRunStats( TestRunInfo const& _runInfo, ++ Totals const& _totals, ++ bool _aborting ) ++ : runInfo( _runInfo ), ++ totals( _totals ), ++ aborting( _aborting ) ++ {} ++ virtual ~TestRunStats(); ++ ++# ifndef CATCH_CPP11_OR_GREATER ++ TestRunStats( TestRunStats const& _other ) ++ : runInfo( _other.runInfo ), ++ totals( _other.totals ), ++ aborting( _other.aborting ) ++ {} ++# else ++ TestRunStats( TestRunStats const& ) = default; ++ TestRunStats( TestRunStats && ) = default; ++ TestRunStats& operator = ( TestRunStats const& ) = default; ++ TestRunStats& operator = ( TestRunStats && ) = default; ++# endif ++ ++ TestRunInfo runInfo; ++ Totals totals; ++ bool aborting; ++ }; ++ ++ struct IStreamingReporter : IShared { ++ virtual ~IStreamingReporter(); ++ ++ // Implementing class must also provide the following static method: ++ // static std::string getDescription(); ++ ++ virtual ReporterPreferences getPreferences() const = 0; ++ ++ virtual void noMatchingTestCases( std::string const& spec ) = 0; ++ ++ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; ++ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; ++ ++ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; ++ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; ++ ++ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; ++ ++ // The return value indicates if the messages buffer should be cleared: ++ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; ++ virtual void sectionEnded( SectionStats const& sectionStats ) = 0; ++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; ++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; ++ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; ++ ++ virtual void skipTest( TestCaseInfo const& testInfo ) = 0; ++ }; ++ ++ struct IReporterFactory { ++ virtual ~IReporterFactory(); ++ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; ++ virtual std::string getDescription() const = 0; ++ }; ++ ++ struct IReporterRegistry { ++ typedef std::map FactoryMap; ++ ++ virtual ~IReporterRegistry(); ++ virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; ++ virtual FactoryMap const& getFactories() const = 0; ++ }; ++ ++} ++ ++#include ++#include ++ ++namespace Catch { ++ ++ inline std::size_t listTests( Config const& config ) { ++ ++ TestSpec testSpec = config.testSpec(); ++ if( config.testSpec().hasFilters() ) ++ Catch::cout() << "Matching test cases:\n"; ++ else { ++ Catch::cout() << "All available test cases:\n"; ++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); ++ } ++ ++ std::size_t matchedTests = 0; ++ TextAttributes nameAttr, tagsAttr; ++ nameAttr.setInitialIndent( 2 ).setIndent( 4 ); ++ tagsAttr.setIndent( 6 ); ++ ++ std::vector matchedTestCases; ++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); ++ for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); ++ it != itEnd; ++ ++it ) { ++ matchedTests++; ++ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); ++ Colour::Code colour = testCaseInfo.isHidden() ++ ? Colour::SecondaryText ++ : Colour::None; ++ Colour colourGuard( colour ); ++ ++ Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; ++ if( !testCaseInfo.tags.empty() ) ++ Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; ++ } ++ ++ if( !config.testSpec().hasFilters() ) ++ Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; ++ else ++ Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; ++ return matchedTests; ++ } ++ ++ inline std::size_t listTestsNamesOnly( Config const& config ) { ++ TestSpec testSpec = config.testSpec(); ++ if( !config.testSpec().hasFilters() ) ++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); ++ std::size_t matchedTests = 0; ++ std::vector matchedTestCases; ++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); ++ for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); ++ it != itEnd; ++ ++it ) { ++ matchedTests++; ++ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); ++ Catch::cout() << testCaseInfo.name << std::endl; ++ } ++ return matchedTests; ++ } ++ ++ struct TagInfo { ++ TagInfo() : count ( 0 ) {} ++ void add( std::string const& spelling ) { ++ ++count; ++ spellings.insert( spelling ); ++ } ++ std::string all() const { ++ std::string out; ++ for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); ++ it != itEnd; ++ ++it ) ++ out += "[" + *it + "]"; ++ return out; ++ } ++ std::set spellings; ++ std::size_t count; ++ }; ++ ++ inline std::size_t listTags( Config const& config ) { ++ TestSpec testSpec = config.testSpec(); ++ if( config.testSpec().hasFilters() ) ++ Catch::cout() << "Tags for matching test cases:\n"; ++ else { ++ Catch::cout() << "All available tags:\n"; ++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); ++ } ++ ++ std::map tagCounts; ++ ++ std::vector matchedTestCases; ++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); ++ for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); ++ it != itEnd; ++ ++it ) { ++ for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), ++ tagItEnd = it->getTestCaseInfo().tags.end(); ++ tagIt != tagItEnd; ++ ++tagIt ) { ++ std::string tagName = *tagIt; ++ std::string lcaseTagName = toLower( tagName ); ++ std::map::iterator countIt = tagCounts.find( lcaseTagName ); ++ if( countIt == tagCounts.end() ) ++ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; ++ countIt->second.add( tagName ); ++ } ++ } ++ ++ for( std::map::const_iterator countIt = tagCounts.begin(), ++ countItEnd = tagCounts.end(); ++ countIt != countItEnd; ++ ++countIt ) { ++ std::ostringstream oss; ++ oss << " " << std::setw(2) << countIt->second.count << " "; ++ Text wrapper( countIt->second.all(), TextAttributes() ++ .setInitialIndent( 0 ) ++ .setIndent( oss.str().size() ) ++ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); ++ Catch::cout() << oss.str() << wrapper << "\n"; ++ } ++ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; ++ return tagCounts.size(); ++ } ++ ++ inline std::size_t listReporters( Config const& /*config*/ ) { ++ Catch::cout() << "Available reporters:\n"; ++ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); ++ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; ++ std::size_t maxNameLen = 0; ++ for(it = itBegin; it != itEnd; ++it ) ++ maxNameLen = (std::max)( maxNameLen, it->first.size() ); ++ ++ for(it = itBegin; it != itEnd; ++it ) { ++ Text wrapper( it->second->getDescription(), TextAttributes() ++ .setInitialIndent( 0 ) ++ .setIndent( 7+maxNameLen ) ++ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); ++ Catch::cout() << " " ++ << it->first ++ << ":" ++ << std::string( maxNameLen - it->first.size() + 2, ' ' ) ++ << wrapper << "\n"; ++ } ++ Catch::cout() << std::endl; ++ return factories.size(); ++ } ++ ++ inline Option list( Config const& config ) { ++ Option listedCount; ++ if( config.listTests() ) ++ listedCount = listedCount.valueOr(0) + listTests( config ); ++ if( config.listTestNamesOnly() ) ++ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); ++ if( config.listTags() ) ++ listedCount = listedCount.valueOr(0) + listTags( config ); ++ if( config.listReporters() ) ++ listedCount = listedCount.valueOr(0) + listReporters( config ); ++ return listedCount; ++ } ++ ++} // end namespace Catch ++ ++// #included from: internal/catch_runner_impl.hpp ++#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED ++ ++// #included from: catch_test_case_tracker.hpp ++#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++namespace SectionTracking { ++ ++ class TrackedSection { ++ ++ typedef std::map TrackedSections; ++ ++ public: ++ enum RunState { ++ NotStarted, ++ Executing, ++ ExecutingChildren, ++ Completed ++ }; ++ ++ TrackedSection( std::string const& name, TrackedSection* parent ) ++ : m_name( name ), m_runState( NotStarted ), m_parent( parent ) ++ {} ++ ++ RunState runState() const { return m_runState; } ++ ++ TrackedSection* findChild( std::string const& childName ) { ++ TrackedSections::iterator it = m_children.find( childName ); ++ return it != m_children.end() ++ ? &it->second ++ : NULL; ++ } ++ TrackedSection* acquireChild( std::string const& childName ) { ++ if( TrackedSection* child = findChild( childName ) ) ++ return child; ++ m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); ++ return findChild( childName ); ++ } ++ void enter() { ++ if( m_runState == NotStarted ) ++ m_runState = Executing; ++ } ++ void leave() { ++ for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); ++ it != itEnd; ++ ++it ) ++ if( it->second.runState() != Completed ) { ++ m_runState = ExecutingChildren; ++ return; ++ } ++ m_runState = Completed; ++ } ++ TrackedSection* getParent() { ++ return m_parent; ++ } ++ bool hasChildren() const { ++ return !m_children.empty(); ++ } ++ ++ private: ++ std::string m_name; ++ RunState m_runState; ++ TrackedSections m_children; ++ TrackedSection* m_parent; ++ ++ }; ++ ++ class TestCaseTracker { ++ public: ++ TestCaseTracker( std::string const& testCaseName ) ++ : m_testCase( testCaseName, NULL ), ++ m_currentSection( &m_testCase ), ++ m_completedASectionThisRun( false ) ++ {} ++ ++ bool enterSection( std::string const& name ) { ++ TrackedSection* child = m_currentSection->acquireChild( name ); ++ if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) ++ return false; ++ ++ m_currentSection = child; ++ m_currentSection->enter(); ++ return true; ++ } ++ void leaveSection() { ++ m_currentSection->leave(); ++ m_currentSection = m_currentSection->getParent(); ++ assert( m_currentSection != NULL ); ++ m_completedASectionThisRun = true; ++ } ++ ++ bool currentSectionHasChildren() const { ++ return m_currentSection->hasChildren(); ++ } ++ bool isCompleted() const { ++ return m_testCase.runState() == TrackedSection::Completed; ++ } ++ ++ class Guard { ++ public: ++ Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { ++ m_tracker.enterTestCase(); ++ } ++ ~Guard() { ++ m_tracker.leaveTestCase(); ++ } ++ private: ++ Guard( Guard const& ); ++ void operator = ( Guard const& ); ++ TestCaseTracker& m_tracker; ++ }; ++ ++ private: ++ void enterTestCase() { ++ m_currentSection = &m_testCase; ++ m_completedASectionThisRun = false; ++ m_testCase.enter(); ++ } ++ void leaveTestCase() { ++ m_testCase.leave(); ++ } ++ ++ TrackedSection m_testCase; ++ TrackedSection* m_currentSection; ++ bool m_completedASectionThisRun; ++ }; ++ ++} // namespace SectionTracking ++ ++using SectionTracking::TestCaseTracker; ++ ++} // namespace Catch ++ ++// #included from: catch_fatal_condition.hpp ++#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED ++ ++namespace Catch { ++ ++ // Report the error condition then exit the process ++ inline void fatal( std::string const& message, int exitCode ) { ++ IContext& context = Catch::getCurrentContext(); ++ IResultCapture* resultCapture = context.getResultCapture(); ++ resultCapture->handleFatalErrorCondition( message ); ++ ++ if( Catch::alwaysTrue() ) // avoids "no return" warnings ++ exit( exitCode ); ++ } ++ ++} // namespace Catch ++ ++#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// ++ ++namespace Catch { ++ ++ struct FatalConditionHandler { ++ void reset() {} ++ }; ++ ++} // namespace Catch ++ ++#else // Not Windows - assumed to be POSIX compatible ////////////////////////// ++ ++#include ++ ++namespace Catch { ++ ++ struct SignalDefs { int id; const char* name; }; ++ extern SignalDefs signalDefs[]; ++ SignalDefs signalDefs[] = { ++ { SIGINT, "SIGINT - Terminal interrupt signal" }, ++ { SIGILL, "SIGILL - Illegal instruction signal" }, ++ { SIGFPE, "SIGFPE - Floating point error signal" }, ++ { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, ++ { SIGTERM, "SIGTERM - Termination request signal" }, ++ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } ++ }; ++ ++ struct FatalConditionHandler { ++ ++ static void handleSignal( int sig ) { ++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) ++ if( sig == signalDefs[i].id ) ++ fatal( signalDefs[i].name, -sig ); ++ fatal( "", -sig ); ++ } ++ ++ FatalConditionHandler() : m_isSet( true ) { ++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) ++ signal( signalDefs[i].id, handleSignal ); ++ } ++ ~FatalConditionHandler() { ++ reset(); ++ } ++ void reset() { ++ if( m_isSet ) { ++ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) ++ signal( signalDefs[i].id, SIG_DFL ); ++ m_isSet = false; ++ } ++ } ++ ++ bool m_isSet; ++ }; ++ ++} // namespace Catch ++ ++#endif // not Windows ++ ++#include ++#include ++ ++namespace Catch { ++ ++ class StreamRedirect { ++ ++ public: ++ StreamRedirect( std::ostream& stream, std::string& targetString ) ++ : m_stream( stream ), ++ m_prevBuf( stream.rdbuf() ), ++ m_targetString( targetString ) ++ { ++ stream.rdbuf( m_oss.rdbuf() ); ++ } ++ ++ ~StreamRedirect() { ++ m_targetString += m_oss.str(); ++ m_stream.rdbuf( m_prevBuf ); ++ } ++ ++ private: ++ std::ostream& m_stream; ++ std::streambuf* m_prevBuf; ++ std::ostringstream m_oss; ++ std::string& m_targetString; ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ ++ class RunContext : public IResultCapture, public IRunner { ++ ++ RunContext( RunContext const& ); ++ void operator =( RunContext const& ); ++ ++ public: ++ ++ explicit RunContext( Ptr const& config, Ptr const& reporter ) ++ : m_runInfo( config->name() ), ++ m_context( getCurrentMutableContext() ), ++ m_activeTestCase( NULL ), ++ m_config( config ), ++ m_reporter( reporter ), ++ m_prevRunner( m_context.getRunner() ), ++ m_prevResultCapture( m_context.getResultCapture() ), ++ m_prevConfig( m_context.getConfig() ) ++ { ++ m_context.setRunner( this ); ++ m_context.setConfig( m_config ); ++ m_context.setResultCapture( this ); ++ m_reporter->testRunStarting( m_runInfo ); ++ } ++ ++ virtual ~RunContext() { ++ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); ++ m_context.setRunner( m_prevRunner ); ++ m_context.setConfig( NULL ); ++ m_context.setResultCapture( m_prevResultCapture ); ++ m_context.setConfig( m_prevConfig ); ++ } ++ ++ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { ++ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); ++ } ++ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { ++ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); ++ } ++ ++ Totals runTest( TestCase const& testCase ) { ++ Totals prevTotals = m_totals; ++ ++ std::string redirectedCout; ++ std::string redirectedCerr; ++ ++ TestCaseInfo testInfo = testCase.getTestCaseInfo(); ++ ++ m_reporter->testCaseStarting( testInfo ); ++ ++ m_activeTestCase = &testCase; ++ m_testCaseTracker = TestCaseTracker( testInfo.name ); ++ ++ do { ++ do { ++ runCurrentTest( redirectedCout, redirectedCerr ); ++ } ++ while( !m_testCaseTracker->isCompleted() && !aborting() ); ++ } ++ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); ++ ++ Totals deltaTotals = m_totals.delta( prevTotals ); ++ m_totals.testCases += deltaTotals.testCases; ++ m_reporter->testCaseEnded( TestCaseStats( testInfo, ++ deltaTotals, ++ redirectedCout, ++ redirectedCerr, ++ aborting() ) ); ++ ++ m_activeTestCase = NULL; ++ m_testCaseTracker.reset(); ++ ++ return deltaTotals; ++ } ++ ++ Ptr config() const { ++ return m_config; ++ } ++ ++ private: // IResultCapture ++ ++ virtual void assertionEnded( AssertionResult const& result ) { ++ if( result.getResultType() == ResultWas::Ok ) { ++ m_totals.assertions.passed++; ++ } ++ else if( !result.isOk() ) { ++ m_totals.assertions.failed++; ++ } ++ ++ if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) ++ m_messages.clear(); ++ ++ // Reset working state ++ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); ++ m_lastResult = result; ++ } ++ ++ virtual bool sectionStarted ( ++ SectionInfo const& sectionInfo, ++ Counts& assertions ++ ) ++ { ++ std::ostringstream oss; ++ oss << sectionInfo.name << "@" << sectionInfo.lineInfo; ++ ++ if( !m_testCaseTracker->enterSection( oss.str() ) ) ++ return false; ++ ++ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; ++ ++ m_reporter->sectionStarting( sectionInfo ); ++ ++ assertions = m_totals.assertions; ++ ++ return true; ++ } ++ bool testForMissingAssertions( Counts& assertions ) { ++ if( assertions.total() != 0 || ++ !m_config->warnAboutMissingAssertions() || ++ m_testCaseTracker->currentSectionHasChildren() ) ++ return false; ++ m_totals.assertions.failed++; ++ assertions.failed++; ++ return true; ++ } ++ ++ virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { ++ if( std::uncaught_exception() ) { ++ m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); ++ return; ++ } ++ ++ Counts assertions = m_totals.assertions - prevAssertions; ++ bool missingAssertions = testForMissingAssertions( assertions ); ++ ++ m_testCaseTracker->leaveSection(); ++ ++ m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); ++ m_messages.clear(); ++ } ++ ++ virtual void pushScopedMessage( MessageInfo const& message ) { ++ m_messages.push_back( message ); ++ } ++ ++ virtual void popScopedMessage( MessageInfo const& message ) { ++ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); ++ } ++ ++ virtual std::string getCurrentTestName() const { ++ return m_activeTestCase ++ ? m_activeTestCase->getTestCaseInfo().name ++ : ""; ++ } ++ ++ virtual const AssertionResult* getLastResult() const { ++ return &m_lastResult; ++ } ++ ++ virtual void handleFatalErrorCondition( std::string const& message ) { ++ ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); ++ resultBuilder.setResultType( ResultWas::FatalErrorCondition ); ++ resultBuilder << message; ++ resultBuilder.captureExpression(); ++ ++ handleUnfinishedSections(); ++ ++ // Recreate section for test case (as we will lose the one that was in scope) ++ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); ++ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); ++ ++ Counts assertions; ++ assertions.failed = 1; ++ SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); ++ m_reporter->sectionEnded( testCaseSectionStats ); ++ ++ TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); ++ ++ Totals deltaTotals; ++ deltaTotals.testCases.failed = 1; ++ m_reporter->testCaseEnded( TestCaseStats( testInfo, ++ deltaTotals, ++ "", ++ "", ++ false ) ); ++ m_totals.testCases.failed++; ++ testGroupEnded( "", m_totals, 1, 1 ); ++ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); ++ } ++ ++ public: ++ // !TBD We need to do this another way! ++ bool aborting() const { ++ return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); ++ } ++ ++ private: ++ ++ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { ++ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); ++ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); ++ m_reporter->sectionStarting( testCaseSection ); ++ Counts prevAssertions = m_totals.assertions; ++ double duration = 0; ++ try { ++ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); ++ TestCaseTracker::Guard guard( *m_testCaseTracker ); ++ ++ Timer timer; ++ timer.start(); ++ if( m_reporter->getPreferences().shouldRedirectStdOut ) { ++ StreamRedirect coutRedir( Catch::cout(), redirectedCout ); ++ StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); ++ invokeActiveTestCase(); ++ } ++ else { ++ invokeActiveTestCase(); ++ } ++ duration = timer.getElapsedSeconds(); ++ } ++ catch( TestFailureException& ) { ++ // This just means the test was aborted due to failure ++ } ++ catch(...) { ++ makeUnexpectedResultBuilder().useActiveException(); ++ } ++ handleUnfinishedSections(); ++ m_messages.clear(); ++ ++ Counts assertions = m_totals.assertions - prevAssertions; ++ bool missingAssertions = testForMissingAssertions( assertions ); ++ ++ if( testCaseInfo.okToFail() ) { ++ std::swap( assertions.failedButOk, assertions.failed ); ++ m_totals.assertions.failed -= assertions.failedButOk; ++ m_totals.assertions.failedButOk += assertions.failedButOk; ++ } ++ ++ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); ++ m_reporter->sectionEnded( testCaseSectionStats ); ++ } ++ ++ void invokeActiveTestCase() { ++ FatalConditionHandler fatalConditionHandler; // Handle signals ++ m_activeTestCase->invoke(); ++ fatalConditionHandler.reset(); ++ } ++ ++ private: ++ ++ ResultBuilder makeUnexpectedResultBuilder() const { ++ return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), ++ m_lastAssertionInfo.lineInfo, ++ m_lastAssertionInfo.capturedExpression.c_str(), ++ m_lastAssertionInfo.resultDisposition ); ++ } ++ ++ void handleUnfinishedSections() { ++ // If sections ended prematurely due to an exception we stored their ++ // infos here so we can tear them down outside the unwind process. ++ for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), ++ itEnd = m_unfinishedSections.rend(); ++ it != itEnd; ++ ++it ) ++ sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); ++ m_unfinishedSections.clear(); ++ } ++ ++ struct UnfinishedSections { ++ UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) ++ : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) ++ {} ++ ++ SectionInfo info; ++ Counts prevAssertions; ++ double durationInSeconds; ++ }; ++ ++ TestRunInfo m_runInfo; ++ IMutableContext& m_context; ++ TestCase const* m_activeTestCase; ++ Option m_testCaseTracker; ++ AssertionResult m_lastResult; ++ ++ Ptr m_config; ++ Totals m_totals; ++ Ptr m_reporter; ++ std::vector m_messages; ++ IRunner* m_prevRunner; ++ IResultCapture* m_prevResultCapture; ++ Ptr m_prevConfig; ++ AssertionInfo m_lastAssertionInfo; ++ std::vector m_unfinishedSections; ++ }; ++ ++ IResultCapture& getResultCapture() { ++ if( IResultCapture* capture = getCurrentContext().getResultCapture() ) ++ return *capture; ++ else ++ throw std::logic_error( "No result capture instance" ); ++ } ++ ++} // end namespace Catch ++ ++// #included from: internal/catch_version.h ++#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED ++ ++namespace Catch { ++ ++ // Versioning information ++ struct Version { ++ Version( unsigned int _majorVersion, ++ unsigned int _minorVersion, ++ unsigned int _buildNumber, ++ char const* const _branchName ) ++ : majorVersion( _majorVersion ), ++ minorVersion( _minorVersion ), ++ buildNumber( _buildNumber ), ++ branchName( _branchName ) ++ {} ++ ++ unsigned int const majorVersion; ++ unsigned int const minorVersion; ++ unsigned int const buildNumber; ++ char const* const branchName; ++ ++ private: ++ void operator=( Version const& ); ++ }; ++ ++ extern Version libraryVersion; ++} ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class Runner { ++ ++ public: ++ Runner( Ptr const& config ) ++ : m_config( config ) ++ { ++ openStream(); ++ makeReporter(); ++ } ++ ++ Totals runTests() { ++ ++ RunContext context( m_config.get(), m_reporter ); ++ ++ Totals totals; ++ ++ context.testGroupStarting( "all tests", 1, 1 ); // deprecated? ++ ++ TestSpec testSpec = m_config->testSpec(); ++ if( !testSpec.hasFilters() ) ++ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests ++ ++ std::vector testCases; ++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); ++ ++ int testsRunForGroup = 0; ++ for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); ++ it != itEnd; ++ ++it ) { ++ testsRunForGroup++; ++ if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { ++ ++ if( context.aborting() ) ++ break; ++ ++ totals += context.runTest( *it ); ++ m_testsAlreadyRun.insert( *it ); ++ } ++ } ++ std::vector skippedTestCases; ++ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true ); ++ ++ for( std::vector::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end(); ++ it != itEnd; ++ ++it ) ++ m_reporter->skipTest( *it ); ++ ++ context.testGroupEnded( "all tests", totals, 1, 1 ); ++ return totals; ++ } ++ ++ private: ++ void openStream() { ++ // Open output file, if specified ++ if( !m_config->getFilename().empty() ) { ++ m_ofs.open( m_config->getFilename().c_str() ); ++ if( m_ofs.fail() ) { ++ std::ostringstream oss; ++ oss << "Unable to open file: '" << m_config->getFilename() << "'"; ++ throw std::domain_error( oss.str() ); ++ } ++ m_config->setStreamBuf( m_ofs.rdbuf() ); ++ } ++ } ++ void makeReporter() { ++ std::string reporterName = m_config->getReporterName().empty() ++ ? "console" ++ : m_config->getReporterName(); ++ ++ m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); ++ if( !m_reporter ) { ++ std::ostringstream oss; ++ oss << "No reporter registered with name: '" << reporterName << "'"; ++ throw std::domain_error( oss.str() ); ++ } ++ } ++ ++ private: ++ Ptr m_config; ++ std::ofstream m_ofs; ++ Ptr m_reporter; ++ std::set m_testsAlreadyRun; ++ }; ++ ++ class Session : NonCopyable { ++ static bool alreadyInstantiated; ++ ++ public: ++ ++ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; ++ ++ Session() ++ : m_cli( makeCommandLineParser() ) { ++ if( alreadyInstantiated ) { ++ std::string msg = "Only one instance of Catch::Session can ever be used"; ++ Catch::cerr() << msg << std::endl; ++ throw std::logic_error( msg ); ++ } ++ alreadyInstantiated = true; ++ } ++ ~Session() { ++ Catch::cleanUp(); ++ } ++ ++ void showHelp( std::string const& processName ) { ++ Catch::cout() << "\nCatch v" << libraryVersion.majorVersion << "." ++ << libraryVersion.minorVersion << " build " ++ << libraryVersion.buildNumber; ++ if( libraryVersion.branchName != std::string( "master" ) ) ++ Catch::cout() << " (" << libraryVersion.branchName << " branch)"; ++ Catch::cout() << "\n"; ++ ++ m_cli.usage( Catch::cout(), processName ); ++ Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; ++ } ++ ++ int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { ++ try { ++ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); ++ m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); ++ if( m_configData.showHelp ) ++ showHelp( m_configData.processName ); ++ m_config.reset(); ++ } ++ catch( std::exception& ex ) { ++ { ++ Colour colourGuard( Colour::Red ); ++ Catch::cerr() << "\nError(s) in input:\n" ++ << Text( ex.what(), TextAttributes().setIndent(2) ) ++ << "\n\n"; ++ } ++ m_cli.usage( Catch::cout(), m_configData.processName ); ++ return (std::numeric_limits::max)(); ++ } ++ return 0; ++ } ++ ++ void useConfigData( ConfigData const& _configData ) { ++ m_configData = _configData; ++ m_config.reset(); ++ } ++ ++ int run( int argc, char* const argv[] ) { ++ ++ int returnCode = applyCommandLine( argc, argv ); ++ if( returnCode == 0 ) ++ returnCode = run(); ++ return returnCode; ++ } ++ ++ int run() { ++ if( m_configData.showHelp ) ++ return 0; ++ ++ try ++ { ++ config(); // Force config to be constructed ++ ++ std::srand( m_configData.rngSeed ); ++ ++ Runner runner( m_config ); ++ ++ // Handle list request ++ if( Option listed = list( config() ) ) ++ return static_cast( *listed ); ++ ++ return static_cast( runner.runTests().assertions.failed ); ++ } ++ catch( std::exception& ex ) { ++ Catch::cerr() << ex.what() << std::endl; ++ return (std::numeric_limits::max)(); ++ } ++ } ++ ++ Clara::CommandLine const& cli() const { ++ return m_cli; ++ } ++ std::vector const& unusedTokens() const { ++ return m_unusedTokens; ++ } ++ ConfigData& configData() { ++ return m_configData; ++ } ++ Config& config() { ++ if( !m_config ) ++ m_config = new Config( m_configData ); ++ return *m_config; ++ } ++ ++ private: ++ Clara::CommandLine m_cli; ++ std::vector m_unusedTokens; ++ ConfigData m_configData; ++ Ptr m_config; ++ }; ++ ++ bool Session::alreadyInstantiated = false; ++ ++} // end namespace Catch ++ ++// #included from: catch_registry_hub.hpp ++#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED ++ ++// #included from: catch_test_case_registry_impl.hpp ++#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED ++ ++#include ++#include ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class TestRegistry : public ITestCaseRegistry { ++ struct LexSort { ++ bool operator() (TestCase i,TestCase j) const { return (i const& getAllTests() const { ++ return m_functionsInOrder; ++ } ++ ++ virtual std::vector const& getAllNonHiddenTests() const { ++ return m_nonHiddenFunctions; ++ } ++ ++ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const { ++ ++ for( std::vector::const_iterator it = m_functionsInOrder.begin(), ++ itEnd = m_functionsInOrder.end(); ++ it != itEnd; ++ ++it ) { ++ bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ); ++ if( includeTest != negated ) ++ matchingTestCases.push_back( *it ); ++ } ++ sortTests( config, matchingTestCases ); ++ } ++ ++ private: ++ ++ static void sortTests( IConfig const& config, std::vector& matchingTestCases ) { ++ ++ switch( config.runOrder() ) { ++ case RunTests::InLexicographicalOrder: ++ std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() ); ++ break; ++ case RunTests::InRandomOrder: ++ { ++ RandomNumberGenerator rng; ++ std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng ); ++ } ++ break; ++ case RunTests::InDeclarationOrder: ++ // already in declaration order ++ break; ++ } ++ } ++ std::set m_functions; ++ std::vector m_functionsInOrder; ++ std::vector m_nonHiddenFunctions; ++ size_t m_unnamedCount; ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ ++ class FreeFunctionTestCase : public SharedImpl { ++ public: ++ ++ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} ++ ++ virtual void invoke() const { ++ m_fun(); ++ } ++ ++ private: ++ virtual ~FreeFunctionTestCase(); ++ ++ TestFunction m_fun; ++ }; ++ ++ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { ++ std::string className = classOrQualifiedMethodName; ++ if( startsWith( className, "&" ) ) ++ { ++ std::size_t lastColons = className.rfind( "::" ); ++ std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); ++ if( penultimateColons == std::string::npos ) ++ penultimateColons = 1; ++ className = className.substr( penultimateColons, lastColons-penultimateColons ); ++ } ++ return className; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ ++ AutoReg::AutoReg( TestFunction function, ++ SourceLineInfo const& lineInfo, ++ NameAndDesc const& nameAndDesc ) { ++ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); ++ } ++ ++ AutoReg::~AutoReg() {} ++ ++ void AutoReg::registerTestCase( ITestCase* testCase, ++ char const* classOrQualifiedMethodName, ++ NameAndDesc const& nameAndDesc, ++ SourceLineInfo const& lineInfo ) { ++ ++ getMutableRegistryHub().registerTest ++ ( makeTestCase( testCase, ++ extractClassName( classOrQualifiedMethodName ), ++ nameAndDesc.name, ++ nameAndDesc.description, ++ lineInfo ) ); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_reporter_registry.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class ReporterRegistry : public IReporterRegistry { ++ ++ public: ++ ++ virtual ~ReporterRegistry() { ++ deleteAllValues( m_factories ); ++ } ++ ++ virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const { ++ FactoryMap::const_iterator it = m_factories.find( name ); ++ if( it == m_factories.end() ) ++ return NULL; ++ return it->second->create( ReporterConfig( config ) ); ++ } ++ ++ void registerReporter( std::string const& name, IReporterFactory* factory ) { ++ m_factories.insert( std::make_pair( name, factory ) ); ++ } ++ ++ FactoryMap const& getFactories() const { ++ return m_factories; ++ } ++ ++ private: ++ FactoryMap m_factories; ++ }; ++} ++ ++// #included from: catch_exception_translator_registry.hpp ++#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED ++ ++#ifdef __OBJC__ ++#import "Foundation/Foundation.h" ++#endif ++ ++namespace Catch { ++ ++ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { ++ public: ++ ~ExceptionTranslatorRegistry() { ++ deleteAll( m_translators ); ++ } ++ ++ virtual void registerTranslator( const IExceptionTranslator* translator ) { ++ m_translators.push_back( translator ); ++ } ++ ++ virtual std::string translateActiveException() const { ++ try { ++#ifdef __OBJC__ ++ // In Objective-C try objective-c exceptions first ++ @try { ++ throw; ++ } ++ @catch (NSException *exception) { ++ return Catch::toString( [exception description] ); ++ } ++#else ++ throw; ++#endif ++ } ++ catch( TestFailureException& ) { ++ throw; ++ } ++ catch( std::exception& ex ) { ++ return ex.what(); ++ } ++ catch( std::string& msg ) { ++ return msg; ++ } ++ catch( const char* msg ) { ++ return msg; ++ } ++ catch(...) { ++ return tryTranslators( m_translators.begin() ); ++ } ++ } ++ ++ std::string tryTranslators( std::vector::const_iterator it ) const { ++ if( it == m_translators.end() ) ++ return "Unknown exception"; ++ ++ try { ++ return (*it)->translate(); ++ } ++ catch(...) { ++ return tryTranslators( it+1 ); ++ } ++ } ++ ++ private: ++ std::vector m_translators; ++ }; ++} ++ ++namespace Catch { ++ ++ namespace { ++ ++ class RegistryHub : public IRegistryHub, public IMutableRegistryHub { ++ ++ RegistryHub( RegistryHub const& ); ++ void operator=( RegistryHub const& ); ++ ++ public: // IRegistryHub ++ RegistryHub() { ++ } ++ virtual IReporterRegistry const& getReporterRegistry() const { ++ return m_reporterRegistry; ++ } ++ virtual ITestCaseRegistry const& getTestCaseRegistry() const { ++ return m_testCaseRegistry; ++ } ++ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { ++ return m_exceptionTranslatorRegistry; ++ } ++ ++ public: // IMutableRegistryHub ++ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { ++ m_reporterRegistry.registerReporter( name, factory ); ++ } ++ virtual void registerTest( TestCase const& testInfo ) { ++ m_testCaseRegistry.registerTest( testInfo ); ++ } ++ virtual void registerTranslator( const IExceptionTranslator* translator ) { ++ m_exceptionTranslatorRegistry.registerTranslator( translator ); ++ } ++ ++ private: ++ TestRegistry m_testCaseRegistry; ++ ReporterRegistry m_reporterRegistry; ++ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; ++ }; ++ ++ // Single, global, instance ++ inline RegistryHub*& getTheRegistryHub() { ++ static RegistryHub* theRegistryHub = NULL; ++ if( !theRegistryHub ) ++ theRegistryHub = new RegistryHub(); ++ return theRegistryHub; ++ } ++ } ++ ++ IRegistryHub& getRegistryHub() { ++ return *getTheRegistryHub(); ++ } ++ IMutableRegistryHub& getMutableRegistryHub() { ++ return *getTheRegistryHub(); ++ } ++ void cleanUp() { ++ delete getTheRegistryHub(); ++ getTheRegistryHub() = NULL; ++ cleanUpContext(); ++ } ++ std::string translateActiveException() { ++ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_notimplemented_exception.hpp ++#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) ++ : m_lineInfo( lineInfo ) { ++ std::ostringstream oss; ++ oss << lineInfo << ": function "; ++ oss << "not implemented"; ++ m_what = oss.str(); ++ } ++ ++ const char* NotImplementedException::what() const CATCH_NOEXCEPT { ++ return m_what.c_str(); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_context_impl.hpp ++#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED ++ ++// #included from: catch_stream.hpp ++#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED ++ ++// #included from: catch_streambuf.h ++#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class StreamBufBase : public std::streambuf { ++ public: ++ virtual ~StreamBufBase() CATCH_NOEXCEPT; ++ }; ++} ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ template ++ class StreamBufImpl : public StreamBufBase { ++ char data[bufferSize]; ++ WriterF m_writer; ++ ++ public: ++ StreamBufImpl() { ++ setp( data, data + sizeof(data) ); ++ } ++ ++ ~StreamBufImpl() CATCH_NOEXCEPT { ++ sync(); ++ } ++ ++ private: ++ int overflow( int c ) { ++ sync(); ++ ++ if( c != EOF ) { ++ if( pbase() == epptr() ) ++ m_writer( std::string( 1, static_cast( c ) ) ); ++ else ++ sputc( static_cast( c ) ); ++ } ++ return 0; ++ } ++ ++ int sync() { ++ if( pbase() != pptr() ) { ++ m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); ++ setp( pbase(), epptr() ); ++ } ++ return 0; ++ } ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ ++ struct OutputDebugWriter { ++ ++ void operator()( std::string const&str ) { ++ writeToDebugConsole( str ); ++ } ++ }; ++ ++ Stream::Stream() ++ : streamBuf( NULL ), isOwned( false ) ++ {} ++ ++ Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) ++ : streamBuf( _streamBuf ), isOwned( _isOwned ) ++ {} ++ ++ void Stream::release() { ++ if( isOwned ) { ++ delete streamBuf; ++ streamBuf = NULL; ++ isOwned = false; ++ } ++ } ++ ++#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions ++ std::ostream& cout() { ++ return std::cout; ++ } ++ std::ostream& cerr() { ++ return std::cerr; ++ } ++#endif ++} ++ ++namespace Catch { ++ ++ class Context : public IMutableContext { ++ ++ Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} ++ Context( Context const& ); ++ void operator=( Context const& ); ++ ++ public: // IContext ++ virtual IResultCapture* getResultCapture() { ++ return m_resultCapture; ++ } ++ virtual IRunner* getRunner() { ++ return m_runner; ++ } ++ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { ++ return getGeneratorsForCurrentTest() ++ .getGeneratorInfo( fileInfo, totalSize ) ++ .getCurrentIndex(); ++ } ++ virtual bool advanceGeneratorsForCurrentTest() { ++ IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); ++ return generators && generators->moveNext(); ++ } ++ ++ virtual Ptr getConfig() const { ++ return m_config; ++ } ++ ++ public: // IMutableContext ++ virtual void setResultCapture( IResultCapture* resultCapture ) { ++ m_resultCapture = resultCapture; ++ } ++ virtual void setRunner( IRunner* runner ) { ++ m_runner = runner; ++ } ++ virtual void setConfig( Ptr const& config ) { ++ m_config = config; ++ } ++ ++ friend IMutableContext& getCurrentMutableContext(); ++ ++ private: ++ IGeneratorsForTest* findGeneratorsForCurrentTest() { ++ std::string testName = getResultCapture()->getCurrentTestName(); ++ ++ std::map::const_iterator it = ++ m_generatorsByTestName.find( testName ); ++ return it != m_generatorsByTestName.end() ++ ? it->second ++ : NULL; ++ } ++ ++ IGeneratorsForTest& getGeneratorsForCurrentTest() { ++ IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); ++ if( !generators ) { ++ std::string testName = getResultCapture()->getCurrentTestName(); ++ generators = createGeneratorsForTest(); ++ m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); ++ } ++ return *generators; ++ } ++ ++ private: ++ Ptr m_config; ++ IRunner* m_runner; ++ IResultCapture* m_resultCapture; ++ std::map m_generatorsByTestName; ++ }; ++ ++ namespace { ++ Context* currentContext = NULL; ++ } ++ IMutableContext& getCurrentMutableContext() { ++ if( !currentContext ) ++ currentContext = new Context(); ++ return *currentContext; ++ } ++ IContext& getCurrentContext() { ++ return getCurrentMutableContext(); ++ } ++ ++ Stream createStream( std::string const& streamName ) { ++ if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false ); ++ if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false ); ++ if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); ++ ++ throw std::domain_error( "Unknown stream: " + streamName ); ++ } ++ ++ void cleanUpContext() { ++ delete currentContext; ++ currentContext = NULL; ++ } ++} ++ ++// #included from: catch_console_colour_impl.hpp ++#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED ++ ++namespace Catch { ++ namespace { ++ ++ struct IColourImpl { ++ virtual ~IColourImpl() {} ++ virtual void use( Colour::Code _colourCode ) = 0; ++ }; ++ ++ struct NoColourImpl : IColourImpl { ++ void use( Colour::Code ) {} ++ ++ static IColourImpl* instance() { ++ static NoColourImpl s_instance; ++ return &s_instance; ++ } ++ }; ++ ++ } // anon namespace ++} // namespace Catch ++ ++#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) ++# ifdef CATCH_PLATFORM_WINDOWS ++# define CATCH_CONFIG_COLOUR_WINDOWS ++# else ++# define CATCH_CONFIG_COLOUR_ANSI ++# endif ++#endif ++ ++#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// ++ ++#ifndef NOMINMAX ++#define NOMINMAX ++#endif ++ ++#ifdef __AFXDLL ++#include ++#else ++#include ++#endif ++ ++namespace Catch { ++namespace { ++ ++ class Win32ColourImpl : public IColourImpl { ++ public: ++ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) ++ { ++ CONSOLE_SCREEN_BUFFER_INFO csbiInfo; ++ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); ++ originalAttributes = csbiInfo.wAttributes; ++ } ++ ++ virtual void use( Colour::Code _colourCode ) { ++ switch( _colourCode ) { ++ case Colour::None: return setTextAttribute( originalAttributes ); ++ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); ++ case Colour::Red: return setTextAttribute( FOREGROUND_RED ); ++ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); ++ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); ++ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); ++ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); ++ case Colour::Grey: return setTextAttribute( 0 ); ++ ++ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); ++ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); ++ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); ++ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); ++ ++ case Colour::Bright: throw std::logic_error( "not a colour" ); ++ } ++ } ++ ++ private: ++ void setTextAttribute( WORD _textAttribute ) { ++ SetConsoleTextAttribute( stdoutHandle, _textAttribute ); ++ } ++ HANDLE stdoutHandle; ++ WORD originalAttributes; ++ }; ++ ++ IColourImpl* platformColourInstance() { ++ static Win32ColourImpl s_instance; ++ return &s_instance; ++ } ++ ++} // end anon namespace ++} // end namespace Catch ++ ++#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// ++ ++#include ++ ++namespace Catch { ++namespace { ++ ++ // use POSIX/ ANSI console terminal codes ++ // Thanks to Adam Strzelecki for original contribution ++ // (http://github.com/nanoant) ++ // https://github.com/philsquared/Catch/pull/131 ++ class PosixColourImpl : public IColourImpl { ++ public: ++ virtual void use( Colour::Code _colourCode ) { ++ switch( _colourCode ) { ++ case Colour::None: ++ case Colour::White: return setColour( "[0m" ); ++ case Colour::Red: return setColour( "[0;31m" ); ++ case Colour::Green: return setColour( "[0;32m" ); ++ case Colour::Blue: return setColour( "[0:34m" ); ++ case Colour::Cyan: return setColour( "[0;36m" ); ++ case Colour::Yellow: return setColour( "[0;33m" ); ++ case Colour::Grey: return setColour( "[1;30m" ); ++ ++ case Colour::LightGrey: return setColour( "[0;37m" ); ++ case Colour::BrightRed: return setColour( "[1;31m" ); ++ case Colour::BrightGreen: return setColour( "[1;32m" ); ++ case Colour::BrightWhite: return setColour( "[1;37m" ); ++ ++ case Colour::Bright: throw std::logic_error( "not a colour" ); ++ } ++ } ++ static IColourImpl* instance() { ++ static PosixColourImpl s_instance; ++ return &s_instance; ++ } ++ ++ private: ++ void setColour( const char* _escapeCode ) { ++ Catch::cout() << '\033' << _escapeCode; ++ } ++ }; ++ ++ IColourImpl* platformColourInstance() { ++ Ptr config = getCurrentContext().getConfig(); ++ return (config && config->forceColour()) || isatty(STDOUT_FILENO) ++ ? PosixColourImpl::instance() ++ : NoColourImpl::instance(); ++ } ++ ++} // end anon namespace ++} // end namespace Catch ++ ++#else // not Windows or ANSI /////////////////////////////////////////////// ++ ++namespace Catch { ++ ++ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } ++ ++} // end namespace Catch ++ ++#endif // Windows/ ANSI/ None ++ ++namespace Catch { ++ ++ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } ++ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } ++ Colour::~Colour(){ if( !m_moved ) use( None ); } ++ ++ void Colour::use( Code _colourCode ) { ++ static IColourImpl* impl = isDebuggerActive() ++ ? NoColourImpl::instance() ++ : platformColourInstance(); ++ impl->use( _colourCode ); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_generators_impl.hpp ++#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ struct GeneratorInfo : IGeneratorInfo { ++ ++ GeneratorInfo( std::size_t size ) ++ : m_size( size ), ++ m_currentIndex( 0 ) ++ {} ++ ++ bool moveNext() { ++ if( ++m_currentIndex == m_size ) { ++ m_currentIndex = 0; ++ return false; ++ } ++ return true; ++ } ++ ++ std::size_t getCurrentIndex() const { ++ return m_currentIndex; ++ } ++ ++ std::size_t m_size; ++ std::size_t m_currentIndex; ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ ++ class GeneratorsForTest : public IGeneratorsForTest { ++ ++ public: ++ ~GeneratorsForTest() { ++ deleteAll( m_generatorsInOrder ); ++ } ++ ++ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { ++ std::map::const_iterator it = m_generatorsByName.find( fileInfo ); ++ if( it == m_generatorsByName.end() ) { ++ IGeneratorInfo* info = new GeneratorInfo( size ); ++ m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); ++ m_generatorsInOrder.push_back( info ); ++ return *info; ++ } ++ return *it->second; ++ } ++ ++ bool moveNext() { ++ std::vector::const_iterator it = m_generatorsInOrder.begin(); ++ std::vector::const_iterator itEnd = m_generatorsInOrder.end(); ++ for(; it != itEnd; ++it ) { ++ if( (*it)->moveNext() ) ++ return true; ++ } ++ return false; ++ } ++ ++ private: ++ std::map m_generatorsByName; ++ std::vector m_generatorsInOrder; ++ }; ++ ++ IGeneratorsForTest* createGeneratorsForTest() ++ { ++ return new GeneratorsForTest(); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_assertionresult.hpp ++#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED ++ ++namespace Catch { ++ ++ AssertionInfo::AssertionInfo( std::string const& _macroName, ++ SourceLineInfo const& _lineInfo, ++ std::string const& _capturedExpression, ++ ResultDisposition::Flags _resultDisposition ) ++ : macroName( _macroName ), ++ lineInfo( _lineInfo ), ++ capturedExpression( _capturedExpression ), ++ resultDisposition( _resultDisposition ) ++ {} ++ ++ AssertionResult::AssertionResult() {} ++ ++ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) ++ : m_info( info ), ++ m_resultData( data ) ++ {} ++ ++ AssertionResult::~AssertionResult() {} ++ ++ // Result was a success ++ bool AssertionResult::succeeded() const { ++ return Catch::isOk( m_resultData.resultType ); ++ } ++ ++ // Result was a success, or failure is suppressed ++ bool AssertionResult::isOk() const { ++ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); ++ } ++ ++ ResultWas::OfType AssertionResult::getResultType() const { ++ return m_resultData.resultType; ++ } ++ ++ bool AssertionResult::hasExpression() const { ++ return !m_info.capturedExpression.empty(); ++ } ++ ++ bool AssertionResult::hasMessage() const { ++ return !m_resultData.message.empty(); ++ } ++ ++ std::string AssertionResult::getExpression() const { ++ if( isFalseTest( m_info.resultDisposition ) ) ++ return "!" + m_info.capturedExpression; ++ else ++ return m_info.capturedExpression; ++ } ++ std::string AssertionResult::getExpressionInMacro() const { ++ if( m_info.macroName.empty() ) ++ return m_info.capturedExpression; ++ else ++ return m_info.macroName + "( " + m_info.capturedExpression + " )"; ++ } ++ ++ bool AssertionResult::hasExpandedExpression() const { ++ return hasExpression() && getExpandedExpression() != getExpression(); ++ } ++ ++ std::string AssertionResult::getExpandedExpression() const { ++ return m_resultData.reconstructedExpression; ++ } ++ ++ std::string AssertionResult::getMessage() const { ++ return m_resultData.message; ++ } ++ SourceLineInfo AssertionResult::getSourceInfo() const { ++ return m_info.lineInfo; ++ } ++ ++ std::string AssertionResult::getTestMacroName() const { ++ return m_info.macroName; ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_test_case_info.hpp ++#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED ++ ++namespace Catch { ++ ++ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { ++ if( startsWith( tag, "." ) || ++ tag == "hide" || ++ tag == "!hide" ) ++ return TestCaseInfo::IsHidden; ++ else if( tag == "!throws" ) ++ return TestCaseInfo::Throws; ++ else if( tag == "!shouldfail" ) ++ return TestCaseInfo::ShouldFail; ++ else if( tag == "!mayfail" ) ++ return TestCaseInfo::MayFail; ++ else ++ return TestCaseInfo::None; ++ } ++ inline bool isReservedTag( std::string const& tag ) { ++ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); ++ } ++ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { ++ if( isReservedTag( tag ) ) { ++ { ++ Colour colourGuard( Colour::Red ); ++ Catch::cerr() ++ << "Tag name [" << tag << "] not allowed.\n" ++ << "Tag names starting with non alpha-numeric characters are reserved\n"; ++ } ++ { ++ Colour colourGuard( Colour::FileName ); ++ Catch::cerr() << _lineInfo << std::endl; ++ } ++ exit(1); ++ } ++ } ++ ++ TestCase makeTestCase( ITestCase* _testCase, ++ std::string const& _className, ++ std::string const& _name, ++ std::string const& _descOrTags, ++ SourceLineInfo const& _lineInfo ) ++ { ++ bool isHidden( startsWith( _name, "./" ) ); // Legacy support ++ ++ // Parse out tags ++ std::set tags; ++ std::string desc, tag; ++ bool inTag = false; ++ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { ++ char c = _descOrTags[i]; ++ if( !inTag ) { ++ if( c == '[' ) ++ inTag = true; ++ else ++ desc += c; ++ } ++ else { ++ if( c == ']' ) { ++ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); ++ if( prop == TestCaseInfo::IsHidden ) ++ isHidden = true; ++ else if( prop == TestCaseInfo::None ) ++ enforceNotReservedTag( tag, _lineInfo ); ++ ++ tags.insert( tag ); ++ tag.clear(); ++ inTag = false; ++ } ++ else ++ tag += c; ++ } ++ } ++ if( isHidden ) { ++ tags.insert( "hide" ); ++ tags.insert( "." ); ++ } ++ ++ TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); ++ return TestCase( _testCase, info ); ++ } ++ ++ TestCaseInfo::TestCaseInfo( std::string const& _name, ++ std::string const& _className, ++ std::string const& _description, ++ std::set const& _tags, ++ SourceLineInfo const& _lineInfo ) ++ : name( _name ), ++ className( _className ), ++ description( _description ), ++ tags( _tags ), ++ lineInfo( _lineInfo ), ++ properties( None ) ++ { ++ std::ostringstream oss; ++ for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { ++ oss << "[" << *it << "]"; ++ std::string lcaseTag = toLower( *it ); ++ properties = static_cast( properties | parseSpecialTag( lcaseTag ) ); ++ lcaseTags.insert( lcaseTag ); ++ } ++ tagsAsString = oss.str(); ++ } ++ ++ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) ++ : name( other.name ), ++ className( other.className ), ++ description( other.description ), ++ tags( other.tags ), ++ lcaseTags( other.lcaseTags ), ++ tagsAsString( other.tagsAsString ), ++ lineInfo( other.lineInfo ), ++ properties( other.properties ) ++ {} ++ ++ bool TestCaseInfo::isHidden() const { ++ return ( properties & IsHidden ) != 0; ++ } ++ bool TestCaseInfo::throws() const { ++ return ( properties & Throws ) != 0; ++ } ++ bool TestCaseInfo::okToFail() const { ++ return ( properties & (ShouldFail | MayFail ) ) != 0; ++ } ++ bool TestCaseInfo::expectedToFail() const { ++ return ( properties & (ShouldFail ) ) != 0; ++ } ++ ++ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} ++ ++ TestCase::TestCase( TestCase const& other ) ++ : TestCaseInfo( other ), ++ test( other.test ) ++ {} ++ ++ TestCase TestCase::withName( std::string const& _newName ) const { ++ TestCase other( *this ); ++ other.name = _newName; ++ return other; ++ } ++ ++ void TestCase::swap( TestCase& other ) { ++ test.swap( other.test ); ++ name.swap( other.name ); ++ className.swap( other.className ); ++ description.swap( other.description ); ++ tags.swap( other.tags ); ++ lcaseTags.swap( other.lcaseTags ); ++ tagsAsString.swap( other.tagsAsString ); ++ std::swap( TestCaseInfo::properties, static_cast( other ).properties ); ++ std::swap( lineInfo, other.lineInfo ); ++ } ++ ++ void TestCase::invoke() const { ++ test->invoke(); ++ } ++ ++ bool TestCase::operator == ( TestCase const& other ) const { ++ return test.get() == other.test.get() && ++ name == other.name && ++ className == other.className; ++ } ++ ++ bool TestCase::operator < ( TestCase const& other ) const { ++ return name < other.name; ++ } ++ TestCase& TestCase::operator = ( TestCase const& other ) { ++ TestCase temp( other ); ++ swap( temp ); ++ return *this; ++ } ++ ++ TestCaseInfo const& TestCase::getTestCaseInfo() const ++ { ++ return *this; ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_version.hpp ++#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED ++ ++namespace Catch { ++ ++ // These numbers are maintained by a script ++ Version libraryVersion( 1, 1, 1, "master" ); ++} ++ ++// #included from: catch_message.hpp ++#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED ++ ++namespace Catch { ++ ++ MessageInfo::MessageInfo( std::string const& _macroName, ++ SourceLineInfo const& _lineInfo, ++ ResultWas::OfType _type ) ++ : macroName( _macroName ), ++ lineInfo( _lineInfo ), ++ type( _type ), ++ sequence( ++globalCount ) ++ {} ++ ++ // This may need protecting if threading support is added ++ unsigned int MessageInfo::globalCount = 0; ++ ++ //////////////////////////////////////////////////////////////////////////// ++ ++ ScopedMessage::ScopedMessage( MessageBuilder const& builder ) ++ : m_info( builder.m_info ) ++ { ++ m_info.message = builder.m_stream.str(); ++ getResultCapture().pushScopedMessage( m_info ); ++ } ++ ScopedMessage::ScopedMessage( ScopedMessage const& other ) ++ : m_info( other.m_info ) ++ {} ++ ++ ScopedMessage::~ScopedMessage() { ++ getResultCapture().popScopedMessage( m_info ); ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_legacy_reporter_adapter.hpp ++#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED ++ ++// #included from: catch_legacy_reporter_adapter.h ++#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED ++ ++namespace Catch ++{ ++ // Deprecated ++ struct IReporter : IShared { ++ virtual ~IReporter(); ++ ++ virtual bool shouldRedirectStdout() const = 0; ++ ++ virtual void StartTesting() = 0; ++ virtual void EndTesting( Totals const& totals ) = 0; ++ virtual void StartGroup( std::string const& groupName ) = 0; ++ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; ++ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; ++ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; ++ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; ++ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; ++ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; ++ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; ++ virtual void Aborted() = 0; ++ virtual void Result( AssertionResult const& result ) = 0; ++ }; ++ ++ class LegacyReporterAdapter : public SharedImpl ++ { ++ public: ++ LegacyReporterAdapter( Ptr const& legacyReporter ); ++ virtual ~LegacyReporterAdapter(); ++ ++ virtual ReporterPreferences getPreferences() const; ++ virtual void noMatchingTestCases( std::string const& ); ++ virtual void testRunStarting( TestRunInfo const& ); ++ virtual void testGroupStarting( GroupInfo const& groupInfo ); ++ virtual void testCaseStarting( TestCaseInfo const& testInfo ); ++ virtual void sectionStarting( SectionInfo const& sectionInfo ); ++ virtual void assertionStarting( AssertionInfo const& ); ++ virtual bool assertionEnded( AssertionStats const& assertionStats ); ++ virtual void sectionEnded( SectionStats const& sectionStats ); ++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ); ++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ); ++ virtual void testRunEnded( TestRunStats const& testRunStats ); ++ virtual void skipTest( TestCaseInfo const& ); ++ ++ private: ++ Ptr m_legacyReporter; ++ }; ++} ++ ++namespace Catch ++{ ++ LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) ++ : m_legacyReporter( legacyReporter ) ++ {} ++ LegacyReporterAdapter::~LegacyReporterAdapter() {} ++ ++ ReporterPreferences LegacyReporterAdapter::getPreferences() const { ++ ReporterPreferences prefs; ++ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); ++ return prefs; ++ } ++ ++ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} ++ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { ++ m_legacyReporter->StartTesting(); ++ } ++ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { ++ m_legacyReporter->StartGroup( groupInfo.name ); ++ } ++ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { ++ m_legacyReporter->StartTestCase( testInfo ); ++ } ++ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { ++ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); ++ } ++ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { ++ // Not on legacy interface ++ } ++ ++ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { ++ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { ++ for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); ++ it != itEnd; ++ ++it ) { ++ if( it->type == ResultWas::Info ) { ++ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); ++ rb << it->message; ++ rb.setResultType( ResultWas::Info ); ++ AssertionResult result = rb.build(); ++ m_legacyReporter->Result( result ); ++ } ++ } ++ } ++ m_legacyReporter->Result( assertionStats.assertionResult ); ++ return true; ++ } ++ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { ++ if( sectionStats.missingAssertions ) ++ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); ++ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); ++ } ++ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { ++ m_legacyReporter->EndTestCase ++ ( testCaseStats.testInfo, ++ testCaseStats.totals, ++ testCaseStats.stdOut, ++ testCaseStats.stdErr ); ++ } ++ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { ++ if( testGroupStats.aborting ) ++ m_legacyReporter->Aborted(); ++ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); ++ } ++ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { ++ m_legacyReporter->EndTesting( testRunStats.totals ); ++ } ++ void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { ++ } ++} ++ ++// #included from: catch_timer.hpp ++ ++#ifdef __clang__ ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wc++11-long-long" ++#endif ++ ++#ifdef CATCH_PLATFORM_WINDOWS ++#include ++#else ++#include ++#endif ++ ++namespace Catch { ++ ++ namespace { ++#ifdef CATCH_PLATFORM_WINDOWS ++ uint64_t getCurrentTicks() { ++ static uint64_t hz=0, hzo=0; ++ if (!hz) { ++ QueryPerformanceFrequency( reinterpret_cast( &hz ) ); ++ QueryPerformanceCounter( reinterpret_cast( &hzo ) ); ++ } ++ uint64_t t; ++ QueryPerformanceCounter( reinterpret_cast( &t ) ); ++ return ((t-hzo)*1000000)/hz; ++ } ++#else ++ uint64_t getCurrentTicks() { ++ timeval t; ++ gettimeofday(&t,NULL); ++ return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); ++ } ++#endif ++ } ++ ++ void Timer::start() { ++ m_ticks = getCurrentTicks(); ++ } ++ unsigned int Timer::getElapsedMicroseconds() const { ++ return static_cast(getCurrentTicks() - m_ticks); ++ } ++ unsigned int Timer::getElapsedMilliseconds() const { ++ return static_cast(getElapsedMicroseconds()/1000); ++ } ++ double Timer::getElapsedSeconds() const { ++ return getElapsedMicroseconds()/1000000.0; ++ } ++ ++} // namespace Catch ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++// #included from: catch_common.hpp ++#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED ++ ++namespace Catch { ++ ++ bool startsWith( std::string const& s, std::string const& prefix ) { ++ return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; ++ } ++ bool endsWith( std::string const& s, std::string const& suffix ) { ++ return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; ++ } ++ bool contains( std::string const& s, std::string const& infix ) { ++ return s.find( infix ) != std::string::npos; ++ } ++ void toLowerInPlace( std::string& s ) { ++ std::transform( s.begin(), s.end(), s.begin(), ::tolower ); ++ } ++ std::string toLower( std::string const& s ) { ++ std::string lc = s; ++ toLowerInPlace( lc ); ++ return lc; ++ } ++ std::string trim( std::string const& str ) { ++ static char const* whitespaceChars = "\n\r\t "; ++ std::string::size_type start = str.find_first_not_of( whitespaceChars ); ++ std::string::size_type end = str.find_last_not_of( whitespaceChars ); ++ ++ return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; ++ } ++ ++ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { ++ bool replaced = false; ++ std::size_t i = str.find( replaceThis ); ++ while( i != std::string::npos ) { ++ replaced = true; ++ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); ++ if( i < str.size()-withThis.size() ) ++ i = str.find( replaceThis, i+withThis.size() ); ++ else ++ i = std::string::npos; ++ } ++ return replaced; ++ } ++ ++ pluralise::pluralise( std::size_t count, std::string const& label ) ++ : m_count( count ), ++ m_label( label ) ++ {} ++ ++ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { ++ os << pluraliser.m_count << " " << pluraliser.m_label; ++ if( pluraliser.m_count != 1 ) ++ os << "s"; ++ return os; ++ } ++ ++ SourceLineInfo::SourceLineInfo() : line( 0 ){} ++ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) ++ : file( _file ), ++ line( _line ) ++ {} ++ SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) ++ : file( other.file ), ++ line( other.line ) ++ {} ++ bool SourceLineInfo::empty() const { ++ return file.empty(); ++ } ++ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { ++ return line == other.line && file == other.file; ++ } ++ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { ++ return line < other.line || ( line == other.line && file < other.file ); ++ } ++ ++ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { ++#ifndef __GNUG__ ++ os << info.file << "(" << info.line << ")"; ++#else ++ os << info.file << ":" << info.line; ++#endif ++ return os; ++ } ++ ++ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { ++ std::ostringstream oss; ++ oss << locationInfo << ": Internal Catch error: '" << message << "'"; ++ if( alwaysTrue() ) ++ throw std::logic_error( oss.str() ); ++ } ++} ++ ++// #included from: catch_section.hpp ++#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED ++ ++namespace Catch { ++ ++ SectionInfo::SectionInfo ++ ( SourceLineInfo const& _lineInfo, ++ std::string const& _name, ++ std::string const& _description ) ++ : name( _name ), ++ description( _description ), ++ lineInfo( _lineInfo ) ++ {} ++ ++ Section::Section( SectionInfo const& info ) ++ : m_info( info ), ++ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) ++ { ++ m_timer.start(); ++ } ++ ++ Section::~Section() { ++ if( m_sectionIncluded ) ++ getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); ++ } ++ ++ // This indicates whether the section should be executed or not ++ Section::operator bool() const { ++ return m_sectionIncluded; ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_debugger.hpp ++#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED ++ ++#include ++ ++#ifdef CATCH_PLATFORM_MAC ++ ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ namespace Catch{ ++ ++ // The following function is taken directly from the following technical note: ++ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html ++ ++ // Returns true if the current process is being debugged (either ++ // running under the debugger or has a debugger attached post facto). ++ bool isDebuggerActive(){ ++ ++ int mib[4]; ++ struct kinfo_proc info; ++ size_t size; ++ ++ // Initialize the flags so that, if sysctl fails for some bizarre ++ // reason, we get a predictable result. ++ ++ info.kp_proc.p_flag = 0; ++ ++ // Initialize mib, which tells sysctl the info we want, in this case ++ // we're looking for information about a specific process ID. ++ ++ mib[0] = CTL_KERN; ++ mib[1] = KERN_PROC; ++ mib[2] = KERN_PROC_PID; ++ mib[3] = getpid(); ++ ++ // Call sysctl. ++ ++ size = sizeof(info); ++ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { ++ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; ++ return false; ++ } ++ ++ // We're being debugged if the P_TRACED flag is set. ++ ++ return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); ++ } ++ } // namespace Catch ++ ++#elif defined(_MSC_VER) ++ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); ++ namespace Catch { ++ bool isDebuggerActive() { ++ return IsDebuggerPresent() != 0; ++ } ++ } ++#elif defined(__MINGW32__) ++ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); ++ namespace Catch { ++ bool isDebuggerActive() { ++ return IsDebuggerPresent() != 0; ++ } ++ } ++#else ++ namespace Catch { ++ inline bool isDebuggerActive() { return false; } ++ } ++#endif // Platform ++ ++#ifdef CATCH_PLATFORM_WINDOWS ++ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); ++ namespace Catch { ++ void writeToDebugConsole( std::string const& text ) { ++ ::OutputDebugStringA( text.c_str() ); ++ } ++ } ++#else ++ namespace Catch { ++ void writeToDebugConsole( std::string const& text ) { ++ // !TBD: Need a version for Mac/ XCode and other IDEs ++ Catch::cout() << text; ++ } ++ } ++#endif // Platform ++ ++// #included from: catch_tostring.hpp ++#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED ++ ++namespace Catch { ++ ++namespace Detail { ++ ++ std::string unprintableString = "{?}"; ++ ++ namespace { ++ struct Endianness { ++ enum Arch { Big, Little }; ++ ++ static Arch which() { ++ union _{ ++ int asInt; ++ char asChar[sizeof (int)]; ++ } u; ++ ++ u.asInt = 1; ++ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; ++ } ++ }; ++ } ++ ++ std::string rawMemoryToString( const void *object, std::size_t size ) ++ { ++ // Reverse order for little endian architectures ++ int i = 0, end = static_cast( size ), inc = 1; ++ if( Endianness::which() == Endianness::Little ) { ++ i = end-1; ++ end = inc = -1; ++ } ++ ++ unsigned char const *bytes = static_cast(object); ++ std::ostringstream os; ++ os << "0x" << std::setfill('0') << std::hex; ++ for( ; i != end; i += inc ) ++ os << std::setw(2) << static_cast(bytes[i]); ++ return os.str(); ++ } ++} ++ ++std::string toString( std::string const& value ) { ++ std::string s = value; ++ if( getCurrentContext().getConfig()->showInvisibles() ) { ++ for(size_t i = 0; i < s.size(); ++i ) { ++ std::string subs; ++ switch( s[i] ) { ++ case '\n': subs = "\\n"; break; ++ case '\t': subs = "\\t"; break; ++ default: break; ++ } ++ if( !subs.empty() ) { ++ s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++ ++i; ++ } ++ } ++ } ++ return "\"" + s + "\""; ++} ++std::string toString( std::wstring const& value ) { ++ ++ std::string s; ++ s.reserve( value.size() ); ++ for(size_t i = 0; i < value.size(); ++i ) ++ s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; ++ return Catch::toString( s ); ++} ++ ++std::string toString( const char* const value ) { ++ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); ++} ++ ++std::string toString( char* const value ) { ++ return Catch::toString( static_cast( value ) ); ++} ++ ++std::string toString( const wchar_t* const value ) ++{ ++ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); ++} ++ ++std::string toString( wchar_t* const value ) ++{ ++ return Catch::toString( static_cast( value ) ); ++} ++ ++std::string toString( int value ) { ++ std::ostringstream oss; ++ if( value > 8192 ) ++ oss << "0x" << std::hex << value; ++ else ++ oss << value; ++ return oss.str(); ++} ++ ++std::string toString( unsigned long value ) { ++ std::ostringstream oss; ++ if( value > 8192 ) ++ oss << "0x" << std::hex << value; ++ else ++ oss << value; ++ return oss.str(); ++} ++ ++std::string toString( unsigned int value ) { ++ return Catch::toString( static_cast( value ) ); ++} ++ ++template ++std::string fpToString( T value, int precision ) { ++ std::ostringstream oss; ++ oss << std::setprecision( precision ) ++ << std::fixed ++ << value; ++ std::string d = oss.str(); ++ std::size_t i = d.find_last_not_of( '0' ); ++ if( i != std::string::npos && i != d.size()-1 ) { ++ if( d[i] == '.' ) ++ i++; ++ d = d.substr( 0, i+1 ); ++ } ++ return d; ++} ++ ++std::string toString( const double value ) { ++ return fpToString( value, 10 ); ++} ++std::string toString( const float value ) { ++ return fpToString( value, 5 ) + "f"; ++} ++ ++std::string toString( bool value ) { ++ return value ? "true" : "false"; ++} ++ ++std::string toString( char value ) { ++ return value < ' ' ++ ? toString( static_cast( value ) ) ++ : Detail::makeString( value ); ++} ++ ++std::string toString( signed char value ) { ++ return toString( static_cast( value ) ); ++} ++ ++std::string toString( unsigned char value ) { ++ return toString( static_cast( value ) ); ++} ++ ++#ifdef CATCH_CONFIG_CPP11_NULLPTR ++std::string toString( std::nullptr_t ) { ++ return "nullptr"; ++} ++#endif ++ ++#ifdef __OBJC__ ++ std::string toString( NSString const * const& nsstring ) { ++ if( !nsstring ) ++ return "nil"; ++ return "@" + toString([nsstring UTF8String]); ++ } ++ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { ++ if( !nsstring ) ++ return "nil"; ++ return "@" + toString([nsstring UTF8String]); ++ } ++ std::string toString( NSObject* const& nsObject ) { ++ return toString( [nsObject description] ); ++ } ++#endif ++ ++} // end namespace Catch ++ ++// #included from: catch_result_builder.hpp ++#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED ++ ++namespace Catch { ++ ++ ResultBuilder::ResultBuilder( char const* macroName, ++ SourceLineInfo const& lineInfo, ++ char const* capturedExpression, ++ ResultDisposition::Flags resultDisposition ) ++ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ), ++ m_shouldDebugBreak( false ), ++ m_shouldThrow( false ) ++ {} ++ ++ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { ++ m_data.resultType = result; ++ return *this; ++ } ++ ResultBuilder& ResultBuilder::setResultType( bool result ) { ++ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; ++ return *this; ++ } ++ ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { ++ m_exprComponents.lhs = lhs; ++ return *this; ++ } ++ ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { ++ m_exprComponents.rhs = rhs; ++ return *this; ++ } ++ ResultBuilder& ResultBuilder::setOp( std::string const& op ) { ++ m_exprComponents.op = op; ++ return *this; ++ } ++ ++ void ResultBuilder::endExpression() { ++ m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); ++ captureExpression(); ++ } ++ ++ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { ++ m_assertionInfo.resultDisposition = resultDisposition; ++ m_stream.oss << Catch::translateActiveException(); ++ captureResult( ResultWas::ThrewException ); ++ } ++ ++ void ResultBuilder::captureResult( ResultWas::OfType resultType ) { ++ setResultType( resultType ); ++ captureExpression(); ++ } ++ ++ void ResultBuilder::captureExpression() { ++ AssertionResult result = build(); ++ getResultCapture().assertionEnded( result ); ++ ++ if( !result.isOk() ) { ++ if( getCurrentContext().getConfig()->shouldDebugBreak() ) ++ m_shouldDebugBreak = true; ++ if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal ) ++ m_shouldThrow = true; ++ } ++ } ++ void ResultBuilder::react() { ++ if( m_shouldThrow ) ++ throw Catch::TestFailureException(); ++ } ++ ++ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } ++ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } ++ ++ AssertionResult ResultBuilder::build() const ++ { ++ assert( m_data.resultType != ResultWas::Unknown ); ++ ++ AssertionResultData data = m_data; ++ ++ // Flip bool results if testFalse is set ++ if( m_exprComponents.testFalse ) { ++ if( data.resultType == ResultWas::Ok ) ++ data.resultType = ResultWas::ExpressionFailed; ++ else if( data.resultType == ResultWas::ExpressionFailed ) ++ data.resultType = ResultWas::Ok; ++ } ++ ++ data.message = m_stream.oss.str(); ++ data.reconstructedExpression = reconstructExpression(); ++ if( m_exprComponents.testFalse ) { ++ if( m_exprComponents.op == "" ) ++ data.reconstructedExpression = "!" + data.reconstructedExpression; ++ else ++ data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; ++ } ++ return AssertionResult( m_assertionInfo, data ); ++ } ++ std::string ResultBuilder::reconstructExpression() const { ++ if( m_exprComponents.op == "" ) ++ return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; ++ else if( m_exprComponents.op == "matches" ) ++ return m_exprComponents.lhs + " " + m_exprComponents.rhs; ++ else if( m_exprComponents.op != "!" ) { ++ if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && ++ m_exprComponents.lhs.find("\n") == std::string::npos && ++ m_exprComponents.rhs.find("\n") == std::string::npos ) ++ return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; ++ else ++ return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; ++ } ++ else ++ return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; ++ } ++ ++} // end namespace Catch ++ ++// #included from: catch_tag_alias_registry.hpp ++#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED ++ ++// #included from: catch_tag_alias_registry.h ++#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class TagAliasRegistry : public ITagAliasRegistry { ++ public: ++ virtual ~TagAliasRegistry(); ++ virtual Option find( std::string const& alias ) const; ++ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; ++ void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); ++ static TagAliasRegistry& get(); ++ ++ private: ++ std::map m_registry; ++ }; ++ ++} // end namespace Catch ++ ++#include ++#include ++ ++namespace Catch { ++ ++ TagAliasRegistry::~TagAliasRegistry() {} ++ ++ Option TagAliasRegistry::find( std::string const& alias ) const { ++ std::map::const_iterator it = m_registry.find( alias ); ++ if( it != m_registry.end() ) ++ return it->second; ++ else ++ return Option(); ++ } ++ ++ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { ++ std::string expandedTestSpec = unexpandedTestSpec; ++ for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); ++ it != itEnd; ++ ++it ) { ++ std::size_t pos = expandedTestSpec.find( it->first ); ++ if( pos != std::string::npos ) { ++ expandedTestSpec = expandedTestSpec.substr( 0, pos ) + ++ it->second.tag + ++ expandedTestSpec.substr( pos + it->first.size() ); ++ } ++ } ++ return expandedTestSpec; ++ } ++ ++ void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { ++ ++ if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { ++ std::ostringstream oss; ++ oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; ++ throw std::domain_error( oss.str().c_str() ); ++ } ++ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { ++ std::ostringstream oss; ++ oss << "error: tag alias, \"" << alias << "\" already registered.\n" ++ << "\tFirst seen at " << find(alias)->lineInfo << "\n" ++ << "\tRedefined at " << lineInfo; ++ throw std::domain_error( oss.str().c_str() ); ++ } ++ } ++ ++ TagAliasRegistry& TagAliasRegistry::get() { ++ static TagAliasRegistry instance; ++ return instance; ++ ++ } ++ ++ ITagAliasRegistry::~ITagAliasRegistry() {} ++ ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } ++ ++ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { ++ try { ++ TagAliasRegistry::get().add( alias, tag, lineInfo ); ++ } ++ catch( std::exception& ex ) { ++ Colour colourGuard( Colour::Red ); ++ Catch::cerr() << ex.what() << std::endl; ++ exit(1); ++ } ++ } ++ ++} // end namespace Catch ++ ++// #included from: ../reporters/catch_reporter_xml.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED ++ ++// #included from: catch_reporter_bases.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ struct StreamingReporterBase : SharedImpl { ++ ++ StreamingReporterBase( ReporterConfig const& _config ) ++ : m_config( _config.fullConfig() ), ++ stream( _config.stream() ) ++ {} ++ ++ virtual ~StreamingReporterBase(); ++ ++ virtual void noMatchingTestCases( std::string const& ) {} ++ ++ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { ++ currentTestRunInfo = _testRunInfo; ++ } ++ virtual void testGroupStarting( GroupInfo const& _groupInfo ) { ++ currentGroupInfo = _groupInfo; ++ } ++ ++ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { ++ currentTestCaseInfo = _testInfo; ++ } ++ virtual void sectionStarting( SectionInfo const& _sectionInfo ) { ++ m_sectionStack.push_back( _sectionInfo ); ++ } ++ ++ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { ++ m_sectionStack.pop_back(); ++ } ++ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { ++ currentTestCaseInfo.reset(); ++ } ++ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { ++ currentGroupInfo.reset(); ++ } ++ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { ++ currentTestCaseInfo.reset(); ++ currentGroupInfo.reset(); ++ currentTestRunInfo.reset(); ++ } ++ ++ virtual void skipTest( TestCaseInfo const& ) { ++ // Don't do anything with this by default. ++ // It can optionally be overridden in the derived class. ++ } ++ ++ Ptr m_config; ++ std::ostream& stream; ++ ++ LazyStat currentTestRunInfo; ++ LazyStat currentGroupInfo; ++ LazyStat currentTestCaseInfo; ++ ++ std::vector m_sectionStack; ++ }; ++ ++ struct CumulativeReporterBase : SharedImpl { ++ template ++ struct Node : SharedImpl<> { ++ explicit Node( T const& _value ) : value( _value ) {} ++ virtual ~Node() {} ++ ++ typedef std::vector > ChildNodes; ++ T value; ++ ChildNodes children; ++ }; ++ struct SectionNode : SharedImpl<> { ++ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} ++ virtual ~SectionNode(); ++ ++ bool operator == ( SectionNode const& other ) const { ++ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; ++ } ++ bool operator == ( Ptr const& other ) const { ++ return operator==( *other ); ++ } ++ ++ SectionStats stats; ++ typedef std::vector > ChildSections; ++ typedef std::vector Assertions; ++ ChildSections childSections; ++ Assertions assertions; ++ std::string stdOut; ++ std::string stdErr; ++ }; ++ ++ struct BySectionInfo { ++ BySectionInfo( SectionInfo const& other ) : m_other( other ) {} ++ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} ++ bool operator() ( Ptr const& node ) const { ++ return node->stats.sectionInfo.lineInfo == m_other.lineInfo; ++ } ++ private: ++ void operator=( BySectionInfo const& ); ++ SectionInfo const& m_other; ++ }; ++ ++ typedef Node TestCaseNode; ++ typedef Node TestGroupNode; ++ typedef Node TestRunNode; ++ ++ CumulativeReporterBase( ReporterConfig const& _config ) ++ : m_config( _config.fullConfig() ), ++ stream( _config.stream() ) ++ {} ++ ~CumulativeReporterBase(); ++ ++ virtual void testRunStarting( TestRunInfo const& ) {} ++ virtual void testGroupStarting( GroupInfo const& ) {} ++ ++ virtual void testCaseStarting( TestCaseInfo const& ) {} ++ ++ virtual void sectionStarting( SectionInfo const& sectionInfo ) { ++ SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); ++ Ptr node; ++ if( m_sectionStack.empty() ) { ++ if( !m_rootSection ) ++ m_rootSection = new SectionNode( incompleteStats ); ++ node = m_rootSection; ++ } ++ else { ++ SectionNode& parentNode = *m_sectionStack.back(); ++ SectionNode::ChildSections::const_iterator it = ++ std::find_if( parentNode.childSections.begin(), ++ parentNode.childSections.end(), ++ BySectionInfo( sectionInfo ) ); ++ if( it == parentNode.childSections.end() ) { ++ node = new SectionNode( incompleteStats ); ++ parentNode.childSections.push_back( node ); ++ } ++ else ++ node = *it; ++ } ++ m_sectionStack.push_back( node ); ++ m_deepestSection = node; ++ } ++ ++ virtual void assertionStarting( AssertionInfo const& ) {} ++ ++ virtual bool assertionEnded( AssertionStats const& assertionStats ) { ++ assert( !m_sectionStack.empty() ); ++ SectionNode& sectionNode = *m_sectionStack.back(); ++ sectionNode.assertions.push_back( assertionStats ); ++ return true; ++ } ++ virtual void sectionEnded( SectionStats const& sectionStats ) { ++ assert( !m_sectionStack.empty() ); ++ SectionNode& node = *m_sectionStack.back(); ++ node.stats = sectionStats; ++ m_sectionStack.pop_back(); ++ } ++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { ++ Ptr node = new TestCaseNode( testCaseStats ); ++ assert( m_sectionStack.size() == 0 ); ++ node->children.push_back( m_rootSection ); ++ m_testCases.push_back( node ); ++ m_rootSection.reset(); ++ ++ assert( m_deepestSection ); ++ m_deepestSection->stdOut = testCaseStats.stdOut; ++ m_deepestSection->stdErr = testCaseStats.stdErr; ++ } ++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { ++ Ptr node = new TestGroupNode( testGroupStats ); ++ node->children.swap( m_testCases ); ++ m_testGroups.push_back( node ); ++ } ++ virtual void testRunEnded( TestRunStats const& testRunStats ) { ++ Ptr node = new TestRunNode( testRunStats ); ++ node->children.swap( m_testGroups ); ++ m_testRuns.push_back( node ); ++ testRunEndedCumulative(); ++ } ++ virtual void testRunEndedCumulative() = 0; ++ ++ virtual void skipTest( TestCaseInfo const& ) {} ++ ++ Ptr m_config; ++ std::ostream& stream; ++ std::vector m_assertions; ++ std::vector > > m_sections; ++ std::vector > m_testCases; ++ std::vector > m_testGroups; ++ ++ std::vector > m_testRuns; ++ ++ Ptr m_rootSection; ++ Ptr m_deepestSection; ++ std::vector > m_sectionStack; ++ ++ }; ++ ++ template ++ char const* getLineOfChars() { ++ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; ++ if( !*line ) { ++ memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); ++ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; ++ } ++ return line; ++ } ++ ++} // end namespace Catch ++ ++// #included from: ../internal/catch_reporter_registrars.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED ++ ++namespace Catch { ++ ++ template ++ class LegacyReporterRegistrar { ++ ++ class ReporterFactory : public IReporterFactory { ++ virtual IStreamingReporter* create( ReporterConfig const& config ) const { ++ return new LegacyReporterAdapter( new T( config ) ); ++ } ++ ++ virtual std::string getDescription() const { ++ return T::getDescription(); ++ } ++ }; ++ ++ public: ++ ++ LegacyReporterRegistrar( std::string const& name ) { ++ getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); ++ } ++ }; ++ ++ template ++ class ReporterRegistrar { ++ ++ class ReporterFactory : public IReporterFactory { ++ ++ // *** Please Note ***: ++ // - If you end up here looking at a compiler error because it's trying to register ++ // your custom reporter class be aware that the native reporter interface has changed ++ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via ++ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. ++ // However please consider updating to the new interface as the old one is now ++ // deprecated and will probably be removed quite soon! ++ // Please contact me via github if you have any questions at all about this. ++ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have ++ // no idea who is actually using custom reporters at all (possibly no-one!). ++ // The new interface is designed to minimise exposure to interface changes in the future. ++ virtual IStreamingReporter* create( ReporterConfig const& config ) const { ++ return new T( config ); ++ } ++ ++ virtual std::string getDescription() const { ++ return T::getDescription(); ++ } ++ }; ++ ++ public: ++ ++ ReporterRegistrar( std::string const& name ) { ++ getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); ++ } ++ }; ++} ++ ++#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ ++ namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } ++#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ ++ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } ++ ++// #included from: ../internal/catch_xmlwriter.hpp ++#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED ++ ++#include ++#include ++#include ++ ++namespace Catch { ++ ++ class XmlWriter { ++ public: ++ ++ class ScopedElement { ++ public: ++ ScopedElement( XmlWriter* writer ) ++ : m_writer( writer ) ++ {} ++ ++ ScopedElement( ScopedElement const& other ) ++ : m_writer( other.m_writer ){ ++ other.m_writer = NULL; ++ } ++ ++ ~ScopedElement() { ++ if( m_writer ) ++ m_writer->endElement(); ++ } ++ ++ ScopedElement& writeText( std::string const& text, bool indent = true ) { ++ m_writer->writeText( text, indent ); ++ return *this; ++ } ++ ++ template ++ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { ++ m_writer->writeAttribute( name, attribute ); ++ return *this; ++ } ++ ++ private: ++ mutable XmlWriter* m_writer; ++ }; ++ ++ XmlWriter() ++ : m_tagIsOpen( false ), ++ m_needsNewline( false ), ++ m_os( &Catch::cout() ) ++ {} ++ ++ XmlWriter( std::ostream& os ) ++ : m_tagIsOpen( false ), ++ m_needsNewline( false ), ++ m_os( &os ) ++ {} ++ ++ ~XmlWriter() { ++ while( !m_tags.empty() ) ++ endElement(); ++ } ++ ++//# ifndef CATCH_CPP11_OR_GREATER ++// XmlWriter& operator = ( XmlWriter const& other ) { ++// XmlWriter temp( other ); ++// swap( temp ); ++// return *this; ++// } ++//# else ++// XmlWriter( XmlWriter const& ) = default; ++// XmlWriter( XmlWriter && ) = default; ++// XmlWriter& operator = ( XmlWriter const& ) = default; ++// XmlWriter& operator = ( XmlWriter && ) = default; ++//# endif ++// ++// void swap( XmlWriter& other ) { ++// std::swap( m_tagIsOpen, other.m_tagIsOpen ); ++// std::swap( m_needsNewline, other.m_needsNewline ); ++// std::swap( m_tags, other.m_tags ); ++// std::swap( m_indent, other.m_indent ); ++// std::swap( m_os, other.m_os ); ++// } ++ ++ XmlWriter& startElement( std::string const& name ) { ++ ensureTagClosed(); ++ newlineIfNecessary(); ++ stream() << m_indent << "<" << name; ++ m_tags.push_back( name ); ++ m_indent += " "; ++ m_tagIsOpen = true; ++ return *this; ++ } ++ ++ ScopedElement scopedElement( std::string const& name ) { ++ ScopedElement scoped( this ); ++ startElement( name ); ++ return scoped; ++ } ++ ++ XmlWriter& endElement() { ++ newlineIfNecessary(); ++ m_indent = m_indent.substr( 0, m_indent.size()-2 ); ++ if( m_tagIsOpen ) { ++ stream() << "/>\n"; ++ m_tagIsOpen = false; ++ } ++ else { ++ stream() << m_indent << "\n"; ++ } ++ m_tags.pop_back(); ++ return *this; ++ } ++ ++ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { ++ if( !name.empty() && !attribute.empty() ) { ++ stream() << " " << name << "=\""; ++ writeEncodedText( attribute ); ++ stream() << "\""; ++ } ++ return *this; ++ } ++ ++ XmlWriter& writeAttribute( std::string const& name, bool attribute ) { ++ stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; ++ return *this; ++ } ++ ++ template ++ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { ++ if( !name.empty() ) ++ stream() << " " << name << "=\"" << attribute << "\""; ++ return *this; ++ } ++ ++ XmlWriter& writeText( std::string const& text, bool indent = true ) { ++ if( !text.empty() ){ ++ bool tagWasOpen = m_tagIsOpen; ++ ensureTagClosed(); ++ if( tagWasOpen && indent ) ++ stream() << m_indent; ++ writeEncodedText( text ); ++ m_needsNewline = true; ++ } ++ return *this; ++ } ++ ++ XmlWriter& writeComment( std::string const& text ) { ++ ensureTagClosed(); ++ stream() << m_indent << ""; ++ m_needsNewline = true; ++ return *this; ++ } ++ ++ XmlWriter& writeBlankLine() { ++ ensureTagClosed(); ++ stream() << "\n"; ++ return *this; ++ } ++ ++ void setStream( std::ostream& os ) { ++ m_os = &os; ++ } ++ ++ private: ++ XmlWriter( XmlWriter const& ); ++ void operator=( XmlWriter const& ); ++ ++ std::ostream& stream() { ++ return *m_os; ++ } ++ ++ void ensureTagClosed() { ++ if( m_tagIsOpen ) { ++ stream() << ">\n"; ++ m_tagIsOpen = false; ++ } ++ } ++ ++ void newlineIfNecessary() { ++ if( m_needsNewline ) { ++ stream() << "\n"; ++ m_needsNewline = false; ++ } ++ } ++ ++ void writeEncodedText( std::string const& text ) { ++ static const char* charsToEncode = "<&\""; ++ std::string mtext = text; ++ std::string::size_type pos = mtext.find_first_of( charsToEncode ); ++ while( pos != std::string::npos ) { ++ stream() << mtext.substr( 0, pos ); ++ ++ switch( mtext[pos] ) { ++ case '<': ++ stream() << "<"; ++ break; ++ case '&': ++ stream() << "&"; ++ break; ++ case '\"': ++ stream() << """; ++ break; ++ } ++ mtext = mtext.substr( pos+1 ); ++ pos = mtext.find_first_of( charsToEncode ); ++ } ++ stream() << mtext; ++ } ++ ++ bool m_tagIsOpen; ++ bool m_needsNewline; ++ std::vector m_tags; ++ std::string m_indent; ++ std::ostream* m_os; ++ }; ++ ++} ++namespace Catch { ++ class XmlReporter : public StreamingReporterBase { ++ public: ++ XmlReporter( ReporterConfig const& _config ) ++ : StreamingReporterBase( _config ), ++ m_sectionDepth( 0 ) ++ {} ++ ++ virtual ~XmlReporter(); ++ ++ static std::string getDescription() { ++ return "Reports test results as an XML document"; ++ } ++ ++ public: // StreamingReporterBase ++ virtual ReporterPreferences getPreferences() const { ++ ReporterPreferences prefs; ++ prefs.shouldRedirectStdOut = true; ++ return prefs; ++ } ++ ++ virtual void noMatchingTestCases( std::string const& s ) { ++ StreamingReporterBase::noMatchingTestCases( s ); ++ } ++ ++ virtual void testRunStarting( TestRunInfo const& testInfo ) { ++ StreamingReporterBase::testRunStarting( testInfo ); ++ m_xml.setStream( stream ); ++ m_xml.startElement( "Catch" ); ++ if( !m_config->name().empty() ) ++ m_xml.writeAttribute( "name", m_config->name() ); ++ } ++ ++ virtual void testGroupStarting( GroupInfo const& groupInfo ) { ++ StreamingReporterBase::testGroupStarting( groupInfo ); ++ m_xml.startElement( "Group" ) ++ .writeAttribute( "name", groupInfo.name ); ++ } ++ ++ virtual void testCaseStarting( TestCaseInfo const& testInfo ) { ++ StreamingReporterBase::testCaseStarting(testInfo); ++ m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); ++ ++ if ( m_config->showDurations() == ShowDurations::Always ) ++ m_testCaseTimer.start(); ++ } ++ ++ virtual void sectionStarting( SectionInfo const& sectionInfo ) { ++ StreamingReporterBase::sectionStarting( sectionInfo ); ++ if( m_sectionDepth++ > 0 ) { ++ m_xml.startElement( "Section" ) ++ .writeAttribute( "name", trim( sectionInfo.name ) ) ++ .writeAttribute( "description", sectionInfo.description ); ++ } ++ } ++ ++ virtual void assertionStarting( AssertionInfo const& ) { } ++ ++ virtual bool assertionEnded( AssertionStats const& assertionStats ) { ++ const AssertionResult& assertionResult = assertionStats.assertionResult; ++ ++ // Print any info messages in tags. ++ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { ++ for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); ++ it != itEnd; ++ ++it ) { ++ if( it->type == ResultWas::Info ) { ++ m_xml.scopedElement( "Info" ) ++ .writeText( it->message ); ++ } else if ( it->type == ResultWas::Warning ) { ++ m_xml.scopedElement( "Warning" ) ++ .writeText( it->message ); ++ } ++ } ++ } ++ ++ // Drop out if result was successful but we're not printing them. ++ if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) ++ return true; ++ ++ // Print the expression if there is one. ++ if( assertionResult.hasExpression() ) { ++ m_xml.startElement( "Expression" ) ++ .writeAttribute( "success", assertionResult.succeeded() ) ++ .writeAttribute( "type", assertionResult.getTestMacroName() ) ++ .writeAttribute( "filename", assertionResult.getSourceInfo().file ) ++ .writeAttribute( "line", assertionResult.getSourceInfo().line ); ++ ++ m_xml.scopedElement( "Original" ) ++ .writeText( assertionResult.getExpression() ); ++ m_xml.scopedElement( "Expanded" ) ++ .writeText( assertionResult.getExpandedExpression() ); ++ } ++ ++ // And... Print a result applicable to each result type. ++ switch( assertionResult.getResultType() ) { ++ case ResultWas::ThrewException: ++ m_xml.scopedElement( "Exception" ) ++ .writeAttribute( "filename", assertionResult.getSourceInfo().file ) ++ .writeAttribute( "line", assertionResult.getSourceInfo().line ) ++ .writeText( assertionResult.getMessage() ); ++ break; ++ case ResultWas::FatalErrorCondition: ++ m_xml.scopedElement( "Fatal Error Condition" ) ++ .writeAttribute( "filename", assertionResult.getSourceInfo().file ) ++ .writeAttribute( "line", assertionResult.getSourceInfo().line ) ++ .writeText( assertionResult.getMessage() ); ++ break; ++ case ResultWas::Info: ++ m_xml.scopedElement( "Info" ) ++ .writeText( assertionResult.getMessage() ); ++ break; ++ case ResultWas::Warning: ++ // Warning will already have been written ++ break; ++ case ResultWas::ExplicitFailure: ++ m_xml.scopedElement( "Failure" ) ++ .writeText( assertionResult.getMessage() ); ++ break; ++ default: ++ break; ++ } ++ ++ if( assertionResult.hasExpression() ) ++ m_xml.endElement(); ++ ++ return true; ++ } ++ ++ virtual void sectionEnded( SectionStats const& sectionStats ) { ++ StreamingReporterBase::sectionEnded( sectionStats ); ++ if( --m_sectionDepth > 0 ) { ++ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); ++ e.writeAttribute( "successes", sectionStats.assertions.passed ); ++ e.writeAttribute( "failures", sectionStats.assertions.failed ); ++ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); ++ ++ if ( m_config->showDurations() == ShowDurations::Always ) ++ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); ++ ++ m_xml.endElement(); ++ } ++ } ++ ++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { ++ StreamingReporterBase::testCaseEnded( testCaseStats ); ++ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); ++ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); ++ ++ if ( m_config->showDurations() == ShowDurations::Always ) ++ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); ++ ++ m_xml.endElement(); ++ } ++ ++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { ++ StreamingReporterBase::testGroupEnded( testGroupStats ); ++ // TODO: Check testGroupStats.aborting and act accordingly. ++ m_xml.scopedElement( "OverallResults" ) ++ .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) ++ .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) ++ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); ++ m_xml.endElement(); ++ } ++ ++ virtual void testRunEnded( TestRunStats const& testRunStats ) { ++ StreamingReporterBase::testRunEnded( testRunStats ); ++ m_xml.scopedElement( "OverallResults" ) ++ .writeAttribute( "successes", testRunStats.totals.assertions.passed ) ++ .writeAttribute( "failures", testRunStats.totals.assertions.failed ) ++ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); ++ m_xml.endElement(); ++ } ++ ++ private: ++ Timer m_testCaseTimer; ++ XmlWriter m_xml; ++ int m_sectionDepth; ++ }; ++ ++ INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) ++ ++} // end namespace Catch ++ ++// #included from: ../reporters/catch_reporter_junit.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED ++ ++#include ++ ++namespace Catch { ++ ++ class JunitReporter : public CumulativeReporterBase { ++ public: ++ JunitReporter( ReporterConfig const& _config ) ++ : CumulativeReporterBase( _config ), ++ xml( _config.stream() ) ++ {} ++ ++ ~JunitReporter(); ++ ++ static std::string getDescription() { ++ return "Reports test results in an XML format that looks like Ant's junitreport target"; ++ } ++ ++ virtual void noMatchingTestCases( std::string const& /*spec*/ ) {} ++ ++ virtual ReporterPreferences getPreferences() const { ++ ReporterPreferences prefs; ++ prefs.shouldRedirectStdOut = true; ++ return prefs; ++ } ++ ++ virtual void testRunStarting( TestRunInfo const& runInfo ) { ++ CumulativeReporterBase::testRunStarting( runInfo ); ++ xml.startElement( "testsuites" ); ++ } ++ ++ virtual void testGroupStarting( GroupInfo const& groupInfo ) { ++ suiteTimer.start(); ++ stdOutForSuite.str(""); ++ stdErrForSuite.str(""); ++ unexpectedExceptions = 0; ++ CumulativeReporterBase::testGroupStarting( groupInfo ); ++ } ++ ++ virtual bool assertionEnded( AssertionStats const& assertionStats ) { ++ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) ++ unexpectedExceptions++; ++ return CumulativeReporterBase::assertionEnded( assertionStats ); ++ } ++ ++ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { ++ stdOutForSuite << testCaseStats.stdOut; ++ stdErrForSuite << testCaseStats.stdErr; ++ CumulativeReporterBase::testCaseEnded( testCaseStats ); ++ } ++ ++ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { ++ double suiteTime = suiteTimer.getElapsedSeconds(); ++ CumulativeReporterBase::testGroupEnded( testGroupStats ); ++ writeGroup( *m_testGroups.back(), suiteTime ); ++ } ++ ++ virtual void testRunEndedCumulative() { ++ xml.endElement(); ++ } ++ ++ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { ++ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); ++ TestGroupStats const& stats = groupNode.value; ++ xml.writeAttribute( "name", stats.groupInfo.name ); ++ xml.writeAttribute( "errors", unexpectedExceptions ); ++ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); ++ xml.writeAttribute( "tests", stats.totals.assertions.total() ); ++ xml.writeAttribute( "hostname", "tbd" ); // !TBD ++ if( m_config->showDurations() == ShowDurations::Never ) ++ xml.writeAttribute( "time", "" ); ++ else ++ xml.writeAttribute( "time", suiteTime ); ++ xml.writeAttribute( "timestamp", "tbd" ); // !TBD ++ ++ // Write test cases ++ for( TestGroupNode::ChildNodes::const_iterator ++ it = groupNode.children.begin(), itEnd = groupNode.children.end(); ++ it != itEnd; ++ ++it ) ++ writeTestCase( **it ); ++ ++ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); ++ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); ++ } ++ ++ void writeTestCase( TestCaseNode const& testCaseNode ) { ++ TestCaseStats const& stats = testCaseNode.value; ++ ++ // All test cases have exactly one section - which represents the ++ // test case itself. That section may have 0-n nested sections ++ assert( testCaseNode.children.size() == 1 ); ++ SectionNode const& rootSection = *testCaseNode.children.front(); ++ ++ std::string className = stats.testInfo.className; ++ ++ if( className.empty() ) { ++ if( rootSection.childSections.empty() ) ++ className = "global"; ++ } ++ writeSection( className, "", rootSection ); ++ } ++ ++ void writeSection( std::string const& className, ++ std::string const& rootName, ++ SectionNode const& sectionNode ) { ++ std::string name = trim( sectionNode.stats.sectionInfo.name ); ++ if( !rootName.empty() ) ++ name = rootName + "/" + name; ++ ++ if( !sectionNode.assertions.empty() || ++ !sectionNode.stdOut.empty() || ++ !sectionNode.stdErr.empty() ) { ++ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); ++ if( className.empty() ) { ++ xml.writeAttribute( "classname", name ); ++ xml.writeAttribute( "name", "root" ); ++ } ++ else { ++ xml.writeAttribute( "classname", className ); ++ xml.writeAttribute( "name", name ); ++ } ++ xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); ++ ++ writeAssertions( sectionNode ); ++ ++ if( !sectionNode.stdOut.empty() ) ++ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); ++ if( !sectionNode.stdErr.empty() ) ++ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); ++ } ++ for( SectionNode::ChildSections::const_iterator ++ it = sectionNode.childSections.begin(), ++ itEnd = sectionNode.childSections.end(); ++ it != itEnd; ++ ++it ) ++ if( className.empty() ) ++ writeSection( name, "", **it ); ++ else ++ writeSection( className, name, **it ); ++ } ++ ++ void writeAssertions( SectionNode const& sectionNode ) { ++ for( SectionNode::Assertions::const_iterator ++ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); ++ it != itEnd; ++ ++it ) ++ writeAssertion( *it ); ++ } ++ void writeAssertion( AssertionStats const& stats ) { ++ AssertionResult const& result = stats.assertionResult; ++ if( !result.isOk() ) { ++ std::string elementName; ++ switch( result.getResultType() ) { ++ case ResultWas::ThrewException: ++ case ResultWas::FatalErrorCondition: ++ elementName = "error"; ++ break; ++ case ResultWas::ExplicitFailure: ++ elementName = "failure"; ++ break; ++ case ResultWas::ExpressionFailed: ++ elementName = "failure"; ++ break; ++ case ResultWas::DidntThrowException: ++ elementName = "failure"; ++ break; ++ ++ // We should never see these here: ++ case ResultWas::Info: ++ case ResultWas::Warning: ++ case ResultWas::Ok: ++ case ResultWas::Unknown: ++ case ResultWas::FailureBit: ++ case ResultWas::Exception: ++ elementName = "internalError"; ++ break; ++ } ++ ++ XmlWriter::ScopedElement e = xml.scopedElement( elementName ); ++ ++ xml.writeAttribute( "message", result.getExpandedExpression() ); ++ xml.writeAttribute( "type", result.getTestMacroName() ); ++ ++ std::ostringstream oss; ++ if( !result.getMessage().empty() ) ++ oss << result.getMessage() << "\n"; ++ for( std::vector::const_iterator ++ it = stats.infoMessages.begin(), ++ itEnd = stats.infoMessages.end(); ++ it != itEnd; ++ ++it ) ++ if( it->type == ResultWas::Info ) ++ oss << it->message << "\n"; ++ ++ oss << "at " << result.getSourceInfo(); ++ xml.writeText( oss.str(), false ); ++ } ++ } ++ ++ XmlWriter xml; ++ Timer suiteTimer; ++ std::ostringstream stdOutForSuite; ++ std::ostringstream stdErrForSuite; ++ unsigned int unexpectedExceptions; ++ }; ++ ++ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) ++ ++} // end namespace Catch ++ ++// #included from: ../reporters/catch_reporter_console.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED ++ ++namespace Catch { ++ ++ struct ConsoleReporter : StreamingReporterBase { ++ ConsoleReporter( ReporterConfig const& _config ) ++ : StreamingReporterBase( _config ), ++ m_headerPrinted( false ) ++ {} ++ ++ virtual ~ConsoleReporter(); ++ static std::string getDescription() { ++ return "Reports test results as plain lines of text"; ++ } ++ virtual ReporterPreferences getPreferences() const { ++ ReporterPreferences prefs; ++ prefs.shouldRedirectStdOut = false; ++ return prefs; ++ } ++ ++ virtual void noMatchingTestCases( std::string const& spec ) { ++ stream << "No test cases matched '" << spec << "'" << std::endl; ++ } ++ ++ virtual void assertionStarting( AssertionInfo const& ) { ++ } ++ ++ virtual bool assertionEnded( AssertionStats const& _assertionStats ) { ++ AssertionResult const& result = _assertionStats.assertionResult; ++ ++ bool printInfoMessages = true; ++ ++ // Drop out if result was successful and we're not printing those ++ if( !m_config->includeSuccessfulResults() && result.isOk() ) { ++ if( result.getResultType() != ResultWas::Warning ) ++ return false; ++ printInfoMessages = false; ++ } ++ ++ lazyPrint(); ++ ++ AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); ++ printer.print(); ++ stream << std::endl; ++ return true; ++ } ++ ++ virtual void sectionStarting( SectionInfo const& _sectionInfo ) { ++ m_headerPrinted = false; ++ StreamingReporterBase::sectionStarting( _sectionInfo ); ++ } ++ virtual void sectionEnded( SectionStats const& _sectionStats ) { ++ if( _sectionStats.missingAssertions ) { ++ lazyPrint(); ++ Colour colour( Colour::ResultError ); ++ if( m_sectionStack.size() > 1 ) ++ stream << "\nNo assertions in section"; ++ else ++ stream << "\nNo assertions in test case"; ++ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; ++ } ++ if( m_headerPrinted ) { ++ if( m_config->showDurations() == ShowDurations::Always ) ++ stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; ++ m_headerPrinted = false; ++ } ++ else { ++ if( m_config->showDurations() == ShowDurations::Always ) ++ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; ++ } ++ StreamingReporterBase::sectionEnded( _sectionStats ); ++ } ++ ++ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { ++ StreamingReporterBase::testCaseEnded( _testCaseStats ); ++ m_headerPrinted = false; ++ } ++ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { ++ if( currentGroupInfo.used ) { ++ printSummaryDivider(); ++ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; ++ printTotals( _testGroupStats.totals ); ++ stream << "\n" << std::endl; ++ } ++ StreamingReporterBase::testGroupEnded( _testGroupStats ); ++ } ++ virtual void testRunEnded( TestRunStats const& _testRunStats ) { ++ printTotalsDivider( _testRunStats.totals ); ++ printTotals( _testRunStats.totals ); ++ stream << std::endl; ++ StreamingReporterBase::testRunEnded( _testRunStats ); ++ } ++ ++ private: ++ ++ class AssertionPrinter { ++ void operator= ( AssertionPrinter const& ); ++ public: ++ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) ++ : stream( _stream ), ++ stats( _stats ), ++ result( _stats.assertionResult ), ++ colour( Colour::None ), ++ message( result.getMessage() ), ++ messages( _stats.infoMessages ), ++ printInfoMessages( _printInfoMessages ) ++ { ++ switch( result.getResultType() ) { ++ case ResultWas::Ok: ++ colour = Colour::Success; ++ passOrFail = "PASSED"; ++ //if( result.hasMessage() ) ++ if( _stats.infoMessages.size() == 1 ) ++ messageLabel = "with message"; ++ if( _stats.infoMessages.size() > 1 ) ++ messageLabel = "with messages"; ++ break; ++ case ResultWas::ExpressionFailed: ++ if( result.isOk() ) { ++ colour = Colour::Success; ++ passOrFail = "FAILED - but was ok"; ++ } ++ else { ++ colour = Colour::Error; ++ passOrFail = "FAILED"; ++ } ++ if( _stats.infoMessages.size() == 1 ) ++ messageLabel = "with message"; ++ if( _stats.infoMessages.size() > 1 ) ++ messageLabel = "with messages"; ++ break; ++ case ResultWas::ThrewException: ++ colour = Colour::Error; ++ passOrFail = "FAILED"; ++ messageLabel = "due to unexpected exception with message"; ++ break; ++ case ResultWas::FatalErrorCondition: ++ colour = Colour::Error; ++ passOrFail = "FAILED"; ++ messageLabel = "due to a fatal error condition"; ++ break; ++ case ResultWas::DidntThrowException: ++ colour = Colour::Error; ++ passOrFail = "FAILED"; ++ messageLabel = "because no exception was thrown where one was expected"; ++ break; ++ case ResultWas::Info: ++ messageLabel = "info"; ++ break; ++ case ResultWas::Warning: ++ messageLabel = "warning"; ++ break; ++ case ResultWas::ExplicitFailure: ++ passOrFail = "FAILED"; ++ colour = Colour::Error; ++ if( _stats.infoMessages.size() == 1 ) ++ messageLabel = "explicitly with message"; ++ if( _stats.infoMessages.size() > 1 ) ++ messageLabel = "explicitly with messages"; ++ break; ++ // These cases are here to prevent compiler warnings ++ case ResultWas::Unknown: ++ case ResultWas::FailureBit: ++ case ResultWas::Exception: ++ passOrFail = "** internal error **"; ++ colour = Colour::Error; ++ break; ++ } ++ } ++ ++ void print() const { ++ printSourceInfo(); ++ if( stats.totals.assertions.total() > 0 ) { ++ if( result.isOk() ) ++ stream << "\n"; ++ printResultType(); ++ printOriginalExpression(); ++ printReconstructedExpression(); ++ } ++ else { ++ stream << "\n"; ++ } ++ printMessage(); ++ } ++ ++ private: ++ void printResultType() const { ++ if( !passOrFail.empty() ) { ++ Colour colourGuard( colour ); ++ stream << passOrFail << ":\n"; ++ } ++ } ++ void printOriginalExpression() const { ++ if( result.hasExpression() ) { ++ Colour colourGuard( Colour::OriginalExpression ); ++ stream << " "; ++ stream << result.getExpressionInMacro(); ++ stream << "\n"; ++ } ++ } ++ void printReconstructedExpression() const { ++ if( result.hasExpandedExpression() ) { ++ stream << "with expansion:\n"; ++ Colour colourGuard( Colour::ReconstructedExpression ); ++ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; ++ } ++ } ++ void printMessage() const { ++ if( !messageLabel.empty() ) ++ stream << messageLabel << ":" << "\n"; ++ for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); ++ it != itEnd; ++ ++it ) { ++ // If this assertion is a warning ignore any INFO messages ++ if( printInfoMessages || it->type != ResultWas::Info ) ++ stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; ++ } ++ } ++ void printSourceInfo() const { ++ Colour colourGuard( Colour::FileName ); ++ stream << result.getSourceInfo() << ": "; ++ } ++ ++ std::ostream& stream; ++ AssertionStats const& stats; ++ AssertionResult const& result; ++ Colour::Code colour; ++ std::string passOrFail; ++ std::string messageLabel; ++ std::string message; ++ std::vector messages; ++ bool printInfoMessages; ++ }; ++ ++ void lazyPrint() { ++ ++ if( !currentTestRunInfo.used ) ++ lazyPrintRunInfo(); ++ if( !currentGroupInfo.used ) ++ lazyPrintGroupInfo(); ++ ++ if( !m_headerPrinted ) { ++ printTestCaseAndSectionHeader(); ++ m_headerPrinted = true; ++ } ++ } ++ void lazyPrintRunInfo() { ++ stream << "\n" << getLineOfChars<'~'>() << "\n"; ++ Colour colour( Colour::SecondaryText ); ++ stream << currentTestRunInfo->name ++ << " is a Catch v" << libraryVersion.majorVersion << "." ++ << libraryVersion.minorVersion << " b" ++ << libraryVersion.buildNumber; ++ if( libraryVersion.branchName != std::string( "master" ) ) ++ stream << " (" << libraryVersion.branchName << ")"; ++ stream << " host application.\n" ++ << "Run with -? for options\n\n"; ++ ++ if( m_config->rngSeed() != 0 ) ++ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; ++ ++ currentTestRunInfo.used = true; ++ } ++ void lazyPrintGroupInfo() { ++ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { ++ printClosedHeader( "Group: " + currentGroupInfo->name ); ++ currentGroupInfo.used = true; ++ } ++ } ++ void printTestCaseAndSectionHeader() { ++ assert( !m_sectionStack.empty() ); ++ printOpenHeader( currentTestCaseInfo->name ); ++ ++ if( m_sectionStack.size() > 1 ) { ++ Colour colourGuard( Colour::Headers ); ++ ++ std::vector::const_iterator ++ it = m_sectionStack.begin()+1, // Skip first section (test case) ++ itEnd = m_sectionStack.end(); ++ for( ; it != itEnd; ++it ) ++ printHeaderString( it->name, 2 ); ++ } ++ ++ SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; ++ ++ if( !lineInfo.empty() ){ ++ stream << getLineOfChars<'-'>() << "\n"; ++ Colour colourGuard( Colour::FileName ); ++ stream << lineInfo << "\n"; ++ } ++ stream << getLineOfChars<'.'>() << "\n" << std::endl; ++ } ++ ++ void printClosedHeader( std::string const& _name ) { ++ printOpenHeader( _name ); ++ stream << getLineOfChars<'.'>() << "\n"; ++ } ++ void printOpenHeader( std::string const& _name ) { ++ stream << getLineOfChars<'-'>() << "\n"; ++ { ++ Colour colourGuard( Colour::Headers ); ++ printHeaderString( _name ); ++ } ++ } ++ ++ // if string has a : in first line will set indent to follow it on ++ // subsequent lines ++ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { ++ std::size_t i = _string.find( ": " ); ++ if( i != std::string::npos ) ++ i+=2; ++ else ++ i = 0; ++ stream << Text( _string, TextAttributes() ++ .setIndent( indent+i) ++ .setInitialIndent( indent ) ) << "\n"; ++ } ++ ++ struct SummaryColumn { ++ ++ SummaryColumn( std::string const& _label, Colour::Code _colour ) ++ : label( _label ), ++ colour( _colour ) ++ {} ++ SummaryColumn addRow( std::size_t count ) { ++ std::ostringstream oss; ++ oss << count; ++ std::string row = oss.str(); ++ for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { ++ while( it->size() < row.size() ) ++ *it = " " + *it; ++ while( it->size() > row.size() ) ++ row = " " + row; ++ } ++ rows.push_back( row ); ++ return *this; ++ } ++ ++ std::string label; ++ Colour::Code colour; ++ std::vector rows; ++ ++ }; ++ ++ void printTotals( Totals const& totals ) { ++ if( totals.testCases.total() == 0 ) { ++ stream << Colour( Colour::Warning ) << "No tests ran\n"; ++ } ++ else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { ++ stream << Colour( Colour::ResultSuccess ) << "All tests passed"; ++ stream << " (" ++ << pluralise( totals.assertions.passed, "assertion" ) << " in " ++ << pluralise( totals.testCases.passed, "test case" ) << ")" ++ << "\n"; ++ } ++ else { ++ ++ std::vector columns; ++ columns.push_back( SummaryColumn( "", Colour::None ) ++ .addRow( totals.testCases.total() ) ++ .addRow( totals.assertions.total() ) ); ++ columns.push_back( SummaryColumn( "passed", Colour::Success ) ++ .addRow( totals.testCases.passed ) ++ .addRow( totals.assertions.passed ) ); ++ columns.push_back( SummaryColumn( "failed", Colour::ResultError ) ++ .addRow( totals.testCases.failed ) ++ .addRow( totals.assertions.failed ) ); ++ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) ++ .addRow( totals.testCases.failedButOk ) ++ .addRow( totals.assertions.failedButOk ) ); ++ ++ printSummaryRow( "test cases", columns, 0 ); ++ printSummaryRow( "assertions", columns, 1 ); ++ } ++ } ++ void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { ++ for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { ++ std::string value = it->rows[row]; ++ if( it->label.empty() ) { ++ stream << label << ": "; ++ if( value != "0" ) ++ stream << value; ++ else ++ stream << Colour( Colour::Warning ) << "- none -"; ++ } ++ else if( value != "0" ) { ++ stream << Colour( Colour::LightGrey ) << " | "; ++ stream << Colour( it->colour ) ++ << value << " " << it->label; ++ } ++ } ++ stream << "\n"; ++ } ++ ++ static std::size_t makeRatio( std::size_t number, std::size_t total ) { ++ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; ++ return ( ratio == 0 && number > 0 ) ? 1 : ratio; ++ } ++ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { ++ if( i > j && i > k ) ++ return i; ++ else if( j > k ) ++ return j; ++ else ++ return k; ++ } ++ ++ void printTotalsDivider( Totals const& totals ) { ++ if( totals.testCases.total() > 0 ) { ++ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); ++ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); ++ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); ++ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) ++ findMax( failedRatio, failedButOkRatio, passedRatio )++; ++ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) ++ findMax( failedRatio, failedButOkRatio, passedRatio )--; ++ ++ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); ++ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); ++ if( totals.testCases.allPassed() ) ++ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); ++ else ++ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); ++ } ++ else { ++ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); ++ } ++ stream << "\n"; ++ } ++ void printSummaryDivider() { ++ stream << getLineOfChars<'-'>() << "\n"; ++ } ++ ++ private: ++ bool m_headerPrinted; ++ }; ++ ++ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) ++ ++} // end namespace Catch ++ ++// #included from: ../reporters/catch_reporter_compact.hpp ++#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED ++ ++namespace Catch { ++ ++ struct CompactReporter : StreamingReporterBase { ++ ++ CompactReporter( ReporterConfig const& _config ) ++ : StreamingReporterBase( _config ) ++ {} ++ ++ virtual ~CompactReporter(); ++ ++ static std::string getDescription() { ++ return "Reports test results on a single line, suitable for IDEs"; ++ } ++ ++ virtual ReporterPreferences getPreferences() const { ++ ReporterPreferences prefs; ++ prefs.shouldRedirectStdOut = false; ++ return prefs; ++ } ++ ++ virtual void noMatchingTestCases( std::string const& spec ) { ++ stream << "No test cases matched '" << spec << "'" << std::endl; ++ } ++ ++ virtual void assertionStarting( AssertionInfo const& ) { ++ } ++ ++ virtual bool assertionEnded( AssertionStats const& _assertionStats ) { ++ AssertionResult const& result = _assertionStats.assertionResult; ++ ++ bool printInfoMessages = true; ++ ++ // Drop out if result was successful and we're not printing those ++ if( !m_config->includeSuccessfulResults() && result.isOk() ) { ++ if( result.getResultType() != ResultWas::Warning ) ++ return false; ++ printInfoMessages = false; ++ } ++ ++ AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); ++ printer.print(); ++ ++ stream << std::endl; ++ return true; ++ } ++ ++ virtual void testRunEnded( TestRunStats const& _testRunStats ) { ++ printTotals( _testRunStats.totals ); ++ stream << "\n" << std::endl; ++ StreamingReporterBase::testRunEnded( _testRunStats ); ++ } ++ ++ private: ++ class AssertionPrinter { ++ void operator= ( AssertionPrinter const& ); ++ public: ++ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) ++ : stream( _stream ) ++ , stats( _stats ) ++ , result( _stats.assertionResult ) ++ , messages( _stats.infoMessages ) ++ , itMessage( _stats.infoMessages.begin() ) ++ , printInfoMessages( _printInfoMessages ) ++ {} ++ ++ void print() { ++ printSourceInfo(); ++ ++ itMessage = messages.begin(); ++ ++ switch( result.getResultType() ) { ++ case ResultWas::Ok: ++ printResultType( Colour::ResultSuccess, passedString() ); ++ printOriginalExpression(); ++ printReconstructedExpression(); ++ if ( ! result.hasExpression() ) ++ printRemainingMessages( Colour::None ); ++ else ++ printRemainingMessages(); ++ break; ++ case ResultWas::ExpressionFailed: ++ if( result.isOk() ) ++ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); ++ else ++ printResultType( Colour::Error, failedString() ); ++ printOriginalExpression(); ++ printReconstructedExpression(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::ThrewException: ++ printResultType( Colour::Error, failedString() ); ++ printIssue( "unexpected exception with message:" ); ++ printMessage(); ++ printExpressionWas(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::FatalErrorCondition: ++ printResultType( Colour::Error, failedString() ); ++ printIssue( "fatal error condition with message:" ); ++ printMessage(); ++ printExpressionWas(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::DidntThrowException: ++ printResultType( Colour::Error, failedString() ); ++ printIssue( "expected exception, got none" ); ++ printExpressionWas(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::Info: ++ printResultType( Colour::None, "info" ); ++ printMessage(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::Warning: ++ printResultType( Colour::None, "warning" ); ++ printMessage(); ++ printRemainingMessages(); ++ break; ++ case ResultWas::ExplicitFailure: ++ printResultType( Colour::Error, failedString() ); ++ printIssue( "explicitly" ); ++ printRemainingMessages( Colour::None ); ++ break; ++ // These cases are here to prevent compiler warnings ++ case ResultWas::Unknown: ++ case ResultWas::FailureBit: ++ case ResultWas::Exception: ++ printResultType( Colour::Error, "** internal error **" ); ++ break; ++ } ++ } ++ ++ private: ++ // Colour::LightGrey ++ ++ static Colour::Code dimColour() { return Colour::FileName; } ++ ++#ifdef CATCH_PLATFORM_MAC ++ static const char* failedString() { return "FAILED"; } ++ static const char* passedString() { return "PASSED"; } ++#else ++ static const char* failedString() { return "failed"; } ++ static const char* passedString() { return "passed"; } ++#endif ++ ++ void printSourceInfo() const { ++ Colour colourGuard( Colour::FileName ); ++ stream << result.getSourceInfo() << ":"; ++ } ++ ++ void printResultType( Colour::Code colour, std::string passOrFail ) const { ++ if( !passOrFail.empty() ) { ++ { ++ Colour colourGuard( colour ); ++ stream << " " << passOrFail; ++ } ++ stream << ":"; ++ } ++ } ++ ++ void printIssue( std::string issue ) const { ++ stream << " " << issue; ++ } ++ ++ void printExpressionWas() { ++ if( result.hasExpression() ) { ++ stream << ";"; ++ { ++ Colour colour( dimColour() ); ++ stream << " expression was:"; ++ } ++ printOriginalExpression(); ++ } ++ } ++ ++ void printOriginalExpression() const { ++ if( result.hasExpression() ) { ++ stream << " " << result.getExpression(); ++ } ++ } ++ ++ void printReconstructedExpression() const { ++ if( result.hasExpandedExpression() ) { ++ { ++ Colour colour( dimColour() ); ++ stream << " for: "; ++ } ++ stream << result.getExpandedExpression(); ++ } ++ } ++ ++ void printMessage() { ++ if ( itMessage != messages.end() ) { ++ stream << " '" << itMessage->message << "'"; ++ ++itMessage; ++ } ++ } ++ ++ void printRemainingMessages( Colour::Code colour = dimColour() ) { ++ if ( itMessage == messages.end() ) ++ return; ++ ++ // using messages.end() directly yields compilation error: ++ std::vector::const_iterator itEnd = messages.end(); ++ const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); ++ ++ { ++ Colour colourGuard( colour ); ++ stream << " with " << pluralise( N, "message" ) << ":"; ++ } ++ ++ for(; itMessage != itEnd; ) { ++ // If this assertion is a warning ignore any INFO messages ++ if( printInfoMessages || itMessage->type != ResultWas::Info ) { ++ stream << " '" << itMessage->message << "'"; ++ if ( ++itMessage != itEnd ) { ++ Colour colourGuard( dimColour() ); ++ stream << " and"; ++ } ++ } ++ } ++ } ++ ++ private: ++ std::ostream& stream; ++ AssertionStats const& stats; ++ AssertionResult const& result; ++ std::vector messages; ++ std::vector::const_iterator itMessage; ++ bool printInfoMessages; ++ }; ++ ++ // Colour, message variants: ++ // - white: No tests ran. ++ // - red: Failed [both/all] N test cases, failed [both/all] M assertions. ++ // - white: Passed [both/all] N test cases (no assertions). ++ // - red: Failed N tests cases, failed M assertions. ++ // - green: Passed [both/all] N tests cases with M assertions. ++ ++ std::string bothOrAll( std::size_t count ) const { ++ return count == 1 ? "" : count == 2 ? "both " : "all " ; ++ } ++ ++ void printTotals( const Totals& totals ) const { ++ if( totals.testCases.total() == 0 ) { ++ stream << "No tests ran."; ++ } ++ else if( totals.testCases.failed == totals.testCases.total() ) { ++ Colour colour( Colour::ResultError ); ++ const std::string qualify_assertions_failed = ++ totals.assertions.failed == totals.assertions.total() ? ++ bothOrAll( totals.assertions.failed ) : ""; ++ stream << ++ "Failed " << bothOrAll( totals.testCases.failed ) ++ << pluralise( totals.testCases.failed, "test case" ) << ", " ++ "failed " << qualify_assertions_failed << ++ pluralise( totals.assertions.failed, "assertion" ) << "."; ++ } ++ else if( totals.assertions.total() == 0 ) { ++ stream << ++ "Passed " << bothOrAll( totals.testCases.total() ) ++ << pluralise( totals.testCases.total(), "test case" ) ++ << " (no assertions)."; ++ } ++ else if( totals.assertions.failed ) { ++ Colour colour( Colour::ResultError ); ++ stream << ++ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " ++ "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; ++ } ++ else { ++ Colour colour( Colour::ResultSuccess ); ++ stream << ++ "Passed " << bothOrAll( totals.testCases.passed ) ++ << pluralise( totals.testCases.passed, "test case" ) << ++ " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; ++ } ++ } ++ }; ++ ++ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) ++ ++} // end namespace Catch ++ ++namespace Catch { ++ NonCopyable::~NonCopyable() {} ++ IShared::~IShared() {} ++ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} ++ IContext::~IContext() {} ++ IResultCapture::~IResultCapture() {} ++ ITestCase::~ITestCase() {} ++ ITestCaseRegistry::~ITestCaseRegistry() {} ++ IRegistryHub::~IRegistryHub() {} ++ IMutableRegistryHub::~IMutableRegistryHub() {} ++ IExceptionTranslator::~IExceptionTranslator() {} ++ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} ++ IReporter::~IReporter() {} ++ IReporterFactory::~IReporterFactory() {} ++ IReporterRegistry::~IReporterRegistry() {} ++ IStreamingReporter::~IStreamingReporter() {} ++ AssertionStats::~AssertionStats() {} ++ SectionStats::~SectionStats() {} ++ TestCaseStats::~TestCaseStats() {} ++ TestGroupStats::~TestGroupStats() {} ++ TestRunStats::~TestRunStats() {} ++ CumulativeReporterBase::SectionNode::~SectionNode() {} ++ CumulativeReporterBase::~CumulativeReporterBase() {} ++ ++ StreamingReporterBase::~StreamingReporterBase() {} ++ ConsoleReporter::~ConsoleReporter() {} ++ CompactReporter::~CompactReporter() {} ++ IRunner::~IRunner() {} ++ IMutableContext::~IMutableContext() {} ++ IConfig::~IConfig() {} ++ XmlReporter::~XmlReporter() {} ++ JunitReporter::~JunitReporter() {} ++ TestRegistry::~TestRegistry() {} ++ FreeFunctionTestCase::~FreeFunctionTestCase() {} ++ IGeneratorInfo::~IGeneratorInfo() {} ++ IGeneratorsForTest::~IGeneratorsForTest() {} ++ TestSpec::Pattern::~Pattern() {} ++ TestSpec::NamePattern::~NamePattern() {} ++ TestSpec::TagPattern::~TagPattern() {} ++ TestSpec::ExcludedPattern::~ExcludedPattern() {} ++ ++ Matchers::Impl::StdString::Equals::~Equals() {} ++ Matchers::Impl::StdString::Contains::~Contains() {} ++ Matchers::Impl::StdString::StartsWith::~StartsWith() {} ++ Matchers::Impl::StdString::EndsWith::~EndsWith() {} ++ ++ void Config::dummy() {} ++} ++ ++#ifdef __clang__ ++#pragma clang diagnostic pop ++#endif ++ ++#endif ++ ++#ifdef CATCH_CONFIG_MAIN ++// #included from: internal/catch_default_main.hpp ++#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED ++ ++#ifndef __OBJC__ ++ ++// Standard C/C++ main entry point ++int main (int argc, char * const argv[]) { ++ return Catch::Session().run( argc, argv ); ++} ++ ++#else // __OBJC__ ++ ++// Objective-C entry point ++int main (int argc, char * const argv[]) { ++#if !CATCH_ARC_ENABLED ++ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ++#endif ++ ++ Catch::registerTestMethods(); ++ int result = Catch::Session().run( argc, (char* const*)argv ); ++ ++#if !CATCH_ARC_ENABLED ++ [pool drain]; ++#endif ++ ++ return result; ++} ++ ++#endif // __OBJC__ ++ ++#endif ++ ++#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED ++# undef CLARA_CONFIG_MAIN ++#endif ++ ++////// ++ ++// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ ++#ifdef CATCH_CONFIG_PREFIX_ALL ++ ++#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) ++#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) ++ ++#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" ) ++#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) ++#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) ++ ++#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) ++#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) ++#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) ++#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) ++#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) ++ ++#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) ++#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) ++#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) ++ ++#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) ++#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) ++ ++#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) ++#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) ++#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) ++#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) ++#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) ++ ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) ++ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) ++ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) ++ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) ++ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) ++#else ++ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) ++ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) ++ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) ++ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) ++ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) ++ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) ++#endif ++#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) ++ ++#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) ++#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) ++ ++#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) ++ ++// "BDD-style" convenience wrappers ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) ++#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) ++#else ++#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) ++#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) ++#endif ++#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) ++#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) ++#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) ++#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) ++#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) ++ ++// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required ++#else ++ ++#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) ++#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) ++ ++#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" ) ++#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) ++#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) ++ ++#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) ++#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) ++#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) ++#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) ++#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) ++ ++#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" ) ++#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) ++#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) ++ ++#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) ++#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) ++ ++#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) ++#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) ++#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) ++#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) ++#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) ++ ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) ++ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) ++ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) ++ #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) ++ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) ++#else ++ #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) ++ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) ++ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) ++ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) ++ #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) ++ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) ++#endif ++#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) ++ ++#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) ++#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) ++ ++#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) ++ ++#endif ++ ++#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) ++ ++// "BDD-style" convenience wrappers ++#ifdef CATCH_CONFIG_VARIADIC_MACROS ++#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) ++#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) ++#else ++#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) ++#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) ++#endif ++#define GIVEN( desc ) SECTION( " Given: " desc, "" ) ++#define WHEN( desc ) SECTION( " When: " desc, "" ) ++#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) ++#define THEN( desc ) SECTION( " Then: " desc, "" ) ++#define AND_THEN( desc ) SECTION( " And: " desc, "" ) ++ ++using Catch::Detail::Approx; ++ ++// #included from: internal/catch_reenable_warnings.h ++ ++#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED ++ ++#ifdef __clang__ ++# ifdef __ICC // icpc defines the __clang__ macro ++# pragma warning(pop) ++# else ++# pragma clang diagnostic pop ++# endif ++#elif defined __GNUC__ ++# pragma GCC diagnostic pop ++#endif ++ ++#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED ++ +diff --git a/external/spdlog-0.14.0/tests/cond_logging.cpp b/external/spdlog-0.14.0/tests/cond_logging.cpp +new file mode 100644 +index 00000000..dd5a6ced +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/cond_logging.cpp +@@ -0,0 +1,154 @@ ++ ++#include "includes.h" ++ ++template ++std::string conditional_log(const bool flag, const T& what, spdlog::level::level_enum logger_level) ++{ ++ std::ostringstream oss; ++ auto oss_sink = std::make_shared(oss); ++ ++ spdlog::logger oss_logger("oss", oss_sink); ++ oss_logger.set_level(logger_level); ++ oss_logger.set_pattern("%v"); ++ ++ switch (logger_level) ++ { ++ case spdlog::level::trace: ++ oss_logger.trace_if(flag, what); ++ break; ++ case spdlog::level::debug: ++ oss_logger.debug_if(flag, what); ++ break; ++ case spdlog::level::info: ++ oss_logger.info_if(flag, what); ++ break; ++ case spdlog::level::warn: ++ oss_logger.warn_if(flag, what); ++ break; ++ case spdlog::level::err: ++ oss_logger.error_if(flag, what); ++ break; ++ case spdlog::level::critical: ++ oss_logger.critical_if(flag, what); ++ break; ++ default: ++ break; ++ } ++ ++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size); ++} ++ ++template ++std::string conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const char* fmt, const Arg1& arg1, const Args&... args) ++{ ++ std::ostringstream oss; ++ auto oss_sink = std::make_shared(oss); ++ ++ spdlog::logger oss_logger("oss", oss_sink); ++ oss_logger.set_level(logger_level); ++ oss_logger.set_pattern("%v"); ++ ++ switch (logger_level) ++ { ++ case spdlog::level::trace: ++ oss_logger.trace_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::debug: ++ oss_logger.debug_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::info: ++ oss_logger.info_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::warn: ++ oss_logger.warn_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::err: ++ oss_logger.error_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::critical: ++ oss_logger.critical_if(flag, fmt, arg1, args...); ++ break; ++ default: ++ break; ++ } ++ ++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size); ++} ++ ++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ ++template ++std::wstring conditional_log_varags(spdlog::level::level_enum logger_level, const bool flag, const wchar_t* fmt, const Arg1& arg1, const Args&... args) ++{ ++ std::wstringstream oss; ++ auto oss_sink = std::make_shared(oss); ++ ++ spdlog::logger oss_logger("oss", oss_sink); ++ oss_logger.set_level(logger_level); ++ oss_logger.set_pattern("%v"); ++ ++ switch (logger_level) ++ { ++ case spdlog::level::trace: ++ oss_logger.trace_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::debug: ++ oss_logger.debug_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::info: ++ oss_logger.info_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::warn: ++ oss_logger.warn_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::err: ++ oss_logger.error_if(flag, fmt, arg1, args...); ++ break; ++ case spdlog::level::critical: ++ oss_logger.critical_if(flag, fmt, arg1, args...); ++ break; ++ default: ++ break; ++ } ++ ++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size); ++} ++ ++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ ++TEST_CASE("conditional_trace_simple", "[conditional_trace_simple]") ++{ ++ //const char ++ for (auto i = 0; i < 2; i++) ++ { ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::trace) == ( i % 2 == 0 ? "Hello" : "")); ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::debug) == (i % 2 == 0 ? "Hello" : "")); ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::info) == (i % 2 == 0 ? "Hello" : "")); ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::warn) == (i % 2 == 0 ? "Hello" : "")); ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::err) == (i % 2 == 0 ? "Hello" : "")); ++ REQUIRE(conditional_log((i % 2 == 0), "Hello", spdlog::level::critical) == (i % 2 == 0 ? "Hello" : "")); ++ } ++} ++ ++TEST_CASE("conditional_trace_varargs", "[conditional_trace_varargs]") ++{ ++ //const char ++ for (auto i = 0; i < 2; i++) ++ { ++ REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), "Hello {}", i) == (i % 2 == 0 ? "Hello " + std::to_string(i) : "")); ++ ++#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ REQUIRE(conditional_log_varags(spdlog::level::trace, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++ REQUIRE(conditional_log_varags(spdlog::level::debug, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++ REQUIRE(conditional_log_varags(spdlog::level::info, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++ REQUIRE(conditional_log_varags(spdlog::level::warn, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++ REQUIRE(conditional_log_varags(spdlog::level::err, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++ REQUIRE(conditional_log_varags(spdlog::level::critical, (i % 2 == 0), L"Hello {}", i) == (i % 2 == 0 ? L"Hello " + std::to_wstring(i) : L"")); ++#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT ++ } ++} +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/tests/errors.cpp b/external/spdlog-0.14.0/tests/errors.cpp +new file mode 100644 +index 00000000..75de900a +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/errors.cpp +@@ -0,0 +1,113 @@ ++/* ++* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE ++*/ ++#include "includes.h" ++ ++#include ++ ++ ++ ++ ++class failing_sink: public spdlog::sinks::sink ++{ ++ void log(const spdlog::details::log_msg& msg) override ++ { ++ throw std::runtime_error("some error happened during log"); ++ } ++ ++ void flush() ++ {} ++}; ++ ++TEST_CASE("default_error_handler", "[errors]]") ++{ ++ prepare_logdir(); ++ std::string filename = "logs/simple_log.txt"; ++ ++ auto logger = spdlog::create("logger", filename, true); ++ logger->set_pattern("%v"); ++ logger->info("Test message {} {}", 1); ++ logger->info("Test message {}", 2); ++ logger->flush(); ++ ++ REQUIRE(file_contents(filename) == std::string("Test message 2\n")); ++ REQUIRE(count_lines(filename) == 1); ++} ++ ++ ++ ++ ++struct custom_ex ++{}; ++TEST_CASE("custom_error_handler", "[errors]]") ++{ ++ prepare_logdir(); ++ std::string filename = "logs/simple_log.txt"; ++ auto logger = spdlog::create("logger", filename, true); ++ logger->flush_on(spdlog::level::info); ++ logger->set_error_handler([=](const std::string& msg) ++ { ++ throw custom_ex(); ++ }); ++ logger->info("Good message #1"); ++ REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex); ++ logger->info("Good message #2"); ++ REQUIRE(count_lines(filename) == 2); ++} ++ ++TEST_CASE("default_error_handler2", "[errors]]") ++{ ++ ++ auto logger = spdlog::create("failed_logger"); ++ logger->set_error_handler([=](const std::string& msg) ++ { ++ throw custom_ex(); ++ }); ++ REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); ++} ++ ++TEST_CASE("async_error_handler", "[errors]]") ++{ ++ prepare_logdir(); ++ std::string err_msg("log failed with some msg"); ++ spdlog::set_async_mode(128); ++ std::string filename = "logs/simple_async_log.txt"; ++ { ++ auto logger = spdlog::create("logger", filename, true); ++ logger->set_error_handler([=](const std::string& msg) ++ { ++ std::ofstream ofs("logs/custom_err.txt"); ++ if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt"); ++ ofs << err_msg; ++ }); ++ logger->info("Good message #1"); ++ logger->info("Bad format msg {} {}", "xxx"); ++ logger->info("Good message #2"); ++ spdlog::drop("logger"); //force logger to drain the queue and shutdown ++ spdlog::set_sync_mode(); ++ } ++ REQUIRE(count_lines(filename) == 2); ++ REQUIRE(file_contents("logs/custom_err.txt") == err_msg); ++} ++ ++// Make sure async error handler is executed ++TEST_CASE("async_error_handler2", "[errors]]") ++{ ++ prepare_logdir(); ++ std::string err_msg("This is async handler error message"); ++ spdlog::set_async_mode(128); ++ { ++ auto logger = spdlog::create("failed_logger"); ++ logger->set_error_handler([=](const std::string& msg) ++ { ++ std::ofstream ofs("logs/custom_err2.txt"); ++ if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt"); ++ ofs << err_msg; ++ }); ++ logger->info("Hello failure"); ++ spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown ++ spdlog::set_sync_mode(); ++ } ++ ++ REQUIRE(file_contents("logs/custom_err2.txt") == err_msg); ++} +diff --git a/external/spdlog-0.14.0/tests/file_helper.cpp b/external/spdlog-0.14.0/tests/file_helper.cpp +new file mode 100644 +index 00000000..9a4ad603 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/file_helper.cpp +@@ -0,0 +1,78 @@ ++/* ++* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE ++*/ ++#include "includes.h" ++ ++using namespace spdlog::details; ++ ++static const std::string target_filename = "logs/file_helper_test.txt"; ++ ++static void write_with_helper(file_helper &helper, size_t howmany) ++{ ++ log_msg msg; ++ msg.formatted << std::string(howmany, '1'); ++ helper.write(msg); ++ helper.flush(); ++} ++ ++ ++TEST_CASE("file_helper_filename", "[file_helper::filename()]]") ++{ ++ prepare_logdir(); ++ ++ file_helper helper; ++ helper.open(target_filename); ++ REQUIRE(helper.filename() == target_filename); ++} ++ ++ ++ ++TEST_CASE("file_helper_size", "[file_helper::size()]]") ++{ ++ prepare_logdir(); ++ size_t expected_size = 123; ++ { ++ file_helper helper; ++ helper.open(target_filename); ++ write_with_helper(helper, expected_size); ++ REQUIRE(static_cast(helper.size()) == expected_size); ++ } ++ REQUIRE(get_filesize(target_filename) == expected_size); ++} ++ ++ ++TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]") ++{ ++ prepare_logdir(); ++ REQUIRE(!file_helper::file_exists(target_filename)); ++ file_helper helper; ++ helper.open(target_filename); ++ REQUIRE(file_helper::file_exists(target_filename)); ++} ++ ++TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]") ++{ ++ prepare_logdir(); ++ file_helper helper; ++ helper.open(target_filename); ++ write_with_helper(helper, 12); ++ REQUIRE(helper.size() == 12); ++ helper.reopen(true); ++ REQUIRE(helper.size() == 0); ++} ++ ++TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]") ++{ ++ prepare_logdir(); ++ size_t expected_size = 14; ++ file_helper helper; ++ helper.open(target_filename); ++ write_with_helper(helper, expected_size); ++ REQUIRE(helper.size() == expected_size); ++ helper.reopen(false); ++ REQUIRE(helper.size() == expected_size); ++} ++ ++ ++ ++ +diff --git a/external/spdlog-0.14.0/tests/file_log.cpp b/external/spdlog-0.14.0/tests/file_log.cpp +new file mode 100644 +index 00000000..45f6e8c1 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/file_log.cpp +@@ -0,0 +1,151 @@ ++/* ++ * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE ++ */ ++#include "includes.h" ++ ++ ++TEST_CASE("simple_file_logger", "[simple_logger]]") ++{ ++ prepare_logdir(); ++ std::string filename = "logs/simple_log"; ++ ++ auto logger = spdlog::create("logger", filename); ++ logger->set_pattern("%v"); ++ ++ ++ logger->info("Test message {}", 1); ++ logger->info("Test message {}", 2); ++ logger->flush(); ++ REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n")); ++ REQUIRE(count_lines(filename) == 2); ++} ++ ++ ++TEST_CASE("flush_on", "[flush_on]]") ++{ ++ prepare_logdir(); ++ std::string filename = "logs/simple_log"; ++ ++ auto logger = spdlog::create("logger", filename); ++ logger->set_pattern("%v"); ++ logger->set_level(spdlog::level::trace); ++ logger->flush_on(spdlog::level::info); ++ logger->trace("Should not be flushed"); ++ REQUIRE(count_lines(filename) == 0); ++ ++ logger->info("Test message {}", 1); ++ logger->info("Test message {}", 2); ++ logger->flush(); ++ REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n")); ++ REQUIRE(count_lines(filename) == 3); ++} ++ ++TEST_CASE("rotating_file_logger1", "[rotating_logger]]") ++{ ++ prepare_logdir(); ++ std::string basename = "logs/rotating_log"; ++ auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0); ++ ++ for (int i = 0; i < 10; ++i) ++ logger->info("Test message {}", i); ++ ++ logger->flush(); ++ auto filename = basename; ++ REQUIRE(count_lines(filename) == 10); ++} ++ ++ ++TEST_CASE("rotating_file_logger2", "[rotating_logger]]") ++{ ++ prepare_logdir(); ++ std::string basename = "logs/rotating_log"; ++ auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1); ++ for (int i = 0; i < 10; ++i) ++ logger->info("Test message {}", i); ++ ++ logger->flush(); ++ auto filename = basename; ++ REQUIRE(count_lines(filename) == 10); ++ for (int i = 0; i < 1000; i++) ++ logger->info("Test message {}", i); ++ ++ logger->flush(); ++ REQUIRE(get_filesize(filename) <= 1024); ++ auto filename1 = basename + ".1"; ++ REQUIRE(get_filesize(filename1) <= 1024); ++} ++ ++ ++TEST_CASE("daily_logger", "[daily_logger]]") ++{ ++ prepare_logdir(); ++ //calculate filename (time based) ++ std::string basename = "logs/daily_log"; ++ std::tm tm = spdlog::details::os::localtime(); ++ fmt::MemoryWriter w; ++ w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min); ++ ++ auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0); ++ logger->flush_on(spdlog::level::info); ++ for (int i = 0; i < 10; ++i) ++ logger->info("Test message {}", i); ++ ++ auto filename = w.str(); ++ REQUIRE(count_lines(filename) == 10); ++} ++ ++ ++TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]") ++{ ++ using sink_type = spdlog::sinks::daily_file_sink< ++ std::mutex, ++ spdlog::sinks::dateonly_daily_file_name_calculator>; ++ ++ prepare_logdir(); ++ //calculate filename (time based) ++ std::string basename = "logs/daily_dateonly"; ++ std::tm tm = spdlog::details::os::localtime(); ++ fmt::MemoryWriter w; ++ w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ ++ auto logger = spdlog::create("logger", basename, 0, 0); ++ for (int i = 0; i < 10; ++i) ++ logger->info("Test message {}", i); ++ logger->flush(); ++ auto filename = w.str(); ++ REQUIRE(count_lines(filename) == 10); ++} ++ ++struct custom_daily_file_name_calculator ++{ ++ static spdlog::filename_t calc_filename(const spdlog::filename_t& basename) ++ { ++ std::tm tm = spdlog::details::os::localtime(); ++ fmt::MemoryWriter w; ++ w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ return w.str(); ++ } ++}; ++ ++TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]") ++{ ++ using sink_type = spdlog::sinks::daily_file_sink< ++ std::mutex, ++ custom_daily_file_name_calculator>; ++ ++ prepare_logdir(); ++ //calculate filename (time based) ++ std::string basename = "logs/daily_dateonly"; ++ std::tm tm = spdlog::details::os::localtime(); ++ fmt::MemoryWriter w; ++ w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ ++ auto logger = spdlog::create("logger", basename, 0, 0); ++ for (int i = 0; i < 10; ++i) ++ logger->info("Test message {}", i); ++ ++ logger->flush(); ++ auto filename = w.str(); ++ REQUIRE(count_lines(filename) == 10); ++} ++ +diff --git a/external/spdlog-0.14.0/tests/format.cpp b/external/spdlog-0.14.0/tests/format.cpp +new file mode 100644 +index 00000000..adb8ba8b +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/format.cpp +@@ -0,0 +1,56 @@ ++ ++#include "includes.h" ++ ++template ++std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info) ++{ ++ ++ std::ostringstream oss; ++ auto oss_sink = std::make_shared(oss); ++ ++ spdlog::logger oss_logger("oss", oss_sink); ++ oss_logger.set_level(logger_level); ++ oss_logger.set_pattern("%v"); ++ oss_logger.info(what); ++ ++ return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size); ++} ++ ++ ++ ++ ++ ++ ++TEST_CASE("basic_logging ", "[basic_logging]") ++{ ++ //const char ++ REQUIRE(log_info("Hello") == "Hello"); ++ REQUIRE(log_info("") == ""); ++ ++ //std::string ++ REQUIRE(log_info(std::string("Hello")) == "Hello"); ++ REQUIRE(log_info(std::string()) == std::string()); ++ ++ //Numbers ++ REQUIRE(log_info(5) == "5"); ++ REQUIRE(log_info(5.6) == "5.6"); ++ ++ //User defined class ++ //REQUIRE(log_info(some_logged_class("some_val")) == "some_val"); ++} ++ ++ ++TEST_CASE("log_levels", "[log_levels]") ++{ ++ REQUIRE(log_info("Hello", spdlog::level::err) == ""); ++ REQUIRE(log_info("Hello", spdlog::level::critical) == ""); ++ REQUIRE(log_info("Hello", spdlog::level::info) == "Hello"); ++ REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello"); ++ REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello"); ++} ++ ++ ++ ++ ++ ++ +diff --git a/external/spdlog-0.14.0/tests/includes.h b/external/spdlog-0.14.0/tests/includes.h +new file mode 100644 +index 00000000..0590fc60 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/includes.h +@@ -0,0 +1,16 @@ ++#pragma once ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "catch.hpp" ++#include "utils.h" ++ ++#include "../include/spdlog/spdlog.h" ++#include "../include/spdlog/sinks/null_sink.h" ++#include "../include/spdlog/sinks/ostream_sink.h" ++ +diff --git a/external/spdlog-0.14.0/tests/install_libcxx.sh b/external/spdlog-0.14.0/tests/install_libcxx.sh +new file mode 100755 +index 00000000..cee97692 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/install_libcxx.sh +@@ -0,0 +1,12 @@ ++#!/bin/bash ++# ++# Install libc++ under travis ++ ++svn --quiet co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx ++mkdir libcxx/build ++(cd libcxx/build && cmake .. -DLIBCXX_CXX_ABI=libstdc++ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/4.6;/usr/include/c++/4.6/x86_64-linux-gnu") ++make -C libcxx/build cxx -j2 ++sudo cp libcxx/build/lib/libc++.so.1.0 /usr/lib/ ++sudo cp -r libcxx/build/include/c++/v1 /usr/include/c++/v1/ ++sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so ++sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so.1 +diff --git a/external/spdlog-0.14.0/tests/main.cpp b/external/spdlog-0.14.0/tests/main.cpp +new file mode 100644 +index 00000000..063e8787 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/main.cpp +@@ -0,0 +1,2 @@ ++#define CATCH_CONFIG_MAIN ++#include "catch.hpp" +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/tests/registry.cpp b/external/spdlog-0.14.0/tests/registry.cpp +new file mode 100644 +index 00000000..1936932a +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/registry.cpp +@@ -0,0 +1,84 @@ ++#include "includes.h" ++ ++static const char *tested_logger_name = "null_logger"; ++static const char *tested_logger_name2 = "null_logger2"; ++ ++TEST_CASE("register_drop", "[registry]") ++{ ++ spdlog::drop_all(); ++ spdlog::create(tested_logger_name); ++ REQUIRE(spdlog::get(tested_logger_name)!=nullptr); ++ //Throw if registring existing name ++ REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), spdlog::spdlog_ex); ++} ++ ++ ++TEST_CASE("explicit register" "[registry]") ++{ ++ spdlog::drop_all(); ++ auto logger = std::make_shared(tested_logger_name, std::make_shared()); ++ spdlog::register_logger(logger); ++ REQUIRE(spdlog::get(tested_logger_name) != nullptr); ++ //Throw if registring existing name ++ REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), spdlog::spdlog_ex); ++} ++ ++TEST_CASE("apply_all" "[registry]") ++{ ++ spdlog::drop_all(); ++ auto logger = std::make_shared(tested_logger_name, std::make_shared()); ++ spdlog::register_logger(logger); ++ auto logger2 = std::make_shared(tested_logger_name2, std::make_shared()); ++ spdlog::register_logger(logger2); ++ ++ int counter = 0; ++ spdlog::apply_all([&counter](std::shared_ptr l) ++ { ++ counter++; ++ }); ++ REQUIRE(counter == 2); ++ ++ counter = 0; ++ spdlog::drop(tested_logger_name2); ++ spdlog::apply_all([&counter](std::shared_ptr l) ++ { ++ REQUIRE(l->name() == tested_logger_name); ++ counter++; ++ } ++ ); ++ REQUIRE(counter == 1); ++} ++ ++ ++ ++TEST_CASE("drop" "[registry]") ++{ ++ spdlog::drop_all(); ++ spdlog::create(tested_logger_name); ++ spdlog::drop(tested_logger_name); ++ REQUIRE_FALSE(spdlog::get(tested_logger_name)); ++} ++ ++TEST_CASE("drop_all" "[registry]") ++{ ++ spdlog::drop_all(); ++ spdlog::create(tested_logger_name); ++ spdlog::create(tested_logger_name2); ++ spdlog::drop_all(); ++ REQUIRE_FALSE(spdlog::get(tested_logger_name)); ++ REQUIRE_FALSE(spdlog::get(tested_logger_name)); ++} ++ ++ ++TEST_CASE("drop non existing" "[registry]") ++{ ++ spdlog::drop_all(); ++ spdlog::create(tested_logger_name); ++ spdlog::drop("some_name"); ++ REQUIRE_FALSE(spdlog::get("some_name")); ++ REQUIRE(spdlog::get(tested_logger_name)); ++ spdlog::drop_all(); ++} ++ ++ ++ +diff --git a/external/spdlog-0.14.0/tests/tests.sln b/external/spdlog-0.14.0/tests/tests.sln +new file mode 100644 +index 00000000..d224d204 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/tests.sln +@@ -0,0 +1,28 @@ ++ ++Microsoft Visual Studio Solution File, Format Version 12.00 ++# Visual Studio 2015 ++VisualStudioVersion = 14.0 ++MinimumVisualStudioVersion = 10.0.40219.1 ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}" ++EndProject ++Global ++ GlobalSection(SolutionConfigurationPlatforms) = preSolution ++ Debug|Win32 = Debug|Win32 ++ Debug|x64 = Debug|x64 ++ Release|Win32 = Release|Win32 ++ Release|x64 = Release|x64 ++ EndGlobalSection ++ GlobalSection(ProjectConfigurationPlatforms) = postSolution ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.Build.0 = Debug|Win32 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.ActiveCfg = Debug|x64 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.Build.0 = Debug|x64 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.ActiveCfg = Release|Win32 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.Build.0 = Release|Win32 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.ActiveCfg = Release|x64 ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.Build.0 = Release|x64 ++ EndGlobalSection ++ GlobalSection(SolutionProperties) = preSolution ++ HideSolutionNode = FALSE ++ EndGlobalSection ++EndGlobal +diff --git a/external/spdlog-0.14.0/tests/tests.vcxproj b/external/spdlog-0.14.0/tests/tests.vcxproj +new file mode 100644 +index 00000000..f5c854db +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/tests.vcxproj +@@ -0,0 +1,145 @@ ++ ++ ++ ++ ++ Debug ++ Win32 ++ ++ ++ Debug ++ x64 ++ ++ ++ Release ++ Win32 ++ ++ ++ Release ++ x64 ++ ++ ++ ++ {59A07559-5F38-4DD6-A7FA-DB4153690B42} ++ tests ++ ++ ++ ++ Application ++ true ++ v140 ++ MultiByte ++ ++ ++ Application ++ true ++ v140 ++ MultiByte ++ ++ ++ Application ++ false ++ v140 ++ true ++ MultiByte ++ ++ ++ Application ++ false ++ v140 ++ true ++ MultiByte ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Level3 ++ Disabled ++ true ++ $(SolutionDir)\..\include;%(AdditionalIncludeDirectories) ++ ++ ++ true ++ Console ++ ++ ++ ++ ++ Level3 ++ Disabled ++ true ++ _MBCS;%(PreprocessorDefinitions) ++ $(SolutionDir)..\include;%(AdditionalIncludeDirectories) ++ ++ ++ true ++ Console ++ ++ ++ ++ ++ Level4 ++ MaxSpeed ++ true ++ true ++ true ++ $(SolutionDir)\..\include;%(AdditionalIncludeDirectories) ++ ++ ++ true ++ true ++ true ++ Console ++ ++ ++ ++ ++ Level4 ++ MaxSpeed ++ true ++ true ++ true ++ _MBCS;%(PreprocessorDefinitions) ++ $(SolutionDir)..\include;%(AdditionalIncludeDirectories) ++ ++ ++ true ++ true ++ true ++ Console ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/tests/tests.vcxproj.filters b/external/spdlog-0.14.0/tests/tests.vcxproj.filters +new file mode 100644 +index 00000000..b5612d0c +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/tests.vcxproj.filters +@@ -0,0 +1,54 @@ ++ ++ ++ ++ ++ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} ++ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx ++ ++ ++ {93995380-89BD-4b04-88EB-625FBE52EBFB} ++ h;hh;hpp;hxx;hm;inl;inc;xsd ++ ++ ++ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} ++ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms ++ ++ ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ ++ ++ Header Files ++ ++ ++ Header Files ++ ++ ++ Header Files ++ ++ ++ +\ No newline at end of file +diff --git a/external/spdlog-0.14.0/tests/utils.cpp b/external/spdlog-0.14.0/tests/utils.cpp +new file mode 100644 +index 00000000..e0785352 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/utils.cpp +@@ -0,0 +1,48 @@ ++#include "includes.h" ++ ++ ++void prepare_logdir() ++{ ++ spdlog::drop_all(); ++#ifdef _WIN32 ++ system("if not exist logs mkdir logs"); ++ system("del /F /Q logs\\*"); ++#else ++ auto rv = system("mkdir -p logs"); ++ rv = system("rm -f logs/*"); ++ (void)rv; ++#endif ++} ++ ++ ++std::string file_contents(const std::string& filename) ++{ ++ std::ifstream ifs(filename); ++ if (!ifs) ++ throw std::runtime_error("Failed open file "); ++ return std::string((std::istreambuf_iterator(ifs)), ++ (std::istreambuf_iterator())); ++ ++} ++ ++std::size_t count_lines(const std::string& filename) ++{ ++ std::ifstream ifs(filename); ++ if (!ifs) ++ throw std::runtime_error("Failed open file "); ++ ++ std::string line; ++ size_t counter = 0; ++ while(std::getline(ifs, line)) ++ counter++; ++ return counter; ++} ++ ++std::size_t get_filesize(const std::string& filename) ++{ ++ std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary); ++ if (!ifs) ++ throw std::runtime_error("Failed open file "); ++ ++ return static_cast(ifs.tellg()); ++} +diff --git a/external/spdlog-0.14.0/tests/utils.h b/external/spdlog-0.14.0/tests/utils.h +new file mode 100644 +index 00000000..1d9b6213 +--- /dev/null ++++ b/external/spdlog-0.14.0/tests/utils.h +@@ -0,0 +1,15 @@ ++#pragma once ++ ++#include ++#include ++ ++std::size_t count_lines(const std::string& filename); ++ ++void prepare_logdir(); ++ ++std::string file_contents(const std::string& filename); ++ ++std::size_t count_lines(const std::string& filename); ++ ++std::size_t get_filesize(const std::string& filename); ++ +-- +2.14.3 + diff --git a/0001-main-Remove-supplementary-groups-when-dropping-privi.patch b/0001-main-Remove-supplementary-groups-when-dropping-privi.patch deleted file mode 100644 index b0bd905..0000000 --- a/0001-main-Remove-supplementary-groups-when-dropping-privi.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 9d00681b24b02e0a143506c0f60c83d50136e87d Mon Sep 17 00:00:00 2001 -From: Jonathan Dieter -Date: Tue, 11 Apr 2017 11:20:32 +0300 -Subject: [PATCH] [main] Remove supplementary groups when dropping privileges - -When dropping privileges, remove supplementary groups which give -unnecessary access. - -This will fail if we're not root, at which point the next statement will -also fail, so don't bother checking return value. - -Signed-off-by: Jonathan Dieter ---- - src/main/main.cc | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/main/main.cc b/src/main/main.cc -index 36cf9cb..a974881 100644 ---- a/src/main/main.cc -+++ b/src/main/main.cc -@@ -374,6 +374,7 @@ void changeugid(RunMode runmode) { - free(wuser); - free(wgroup); - -+ setgroups(0, NULL); - if (setgid(wrk_gid)<0) { - lzfs_pretty_errlog(LOG_ERR,"can't set gid to %d",(int)wrk_gid); - exit(LIZARDFS_EXIT_STATUS_ERROR); --- -2.9.3 - diff --git a/0001-master-Fix-issues-with-reporting-defective-files.patch b/0001-master-Fix-issues-with-reporting-defective-files.patch deleted file mode 100644 index 8cc8b4e..0000000 --- a/0001-master-Fix-issues-with-reporting-defective-files.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 0ff659db5d0847db2aa0d59be4955c16012d6fc2 Mon Sep 17 00:00:00 2001 -From: Piotr Sarna -Date: Mon, 12 Jun 2017 12:41:56 +0200 -Subject: [PATCH 1/3] master: Fix issues with reporting defective files - -This commit fixes printing paths for trash/reserved files -in defective files report and adds missing counter increments -in fs_test_getdata loop. - -Fixes #565 - -Change-Id: Ifb3932800b1d7998ff55cecb09fcc28a5dbc4717 ---- - src/master/filesystem_periodic.cc | 59 ++++++++++++++++++++------------------- - 1 file changed, 31 insertions(+), 28 deletions(-) - -diff --git a/src/master/filesystem_periodic.cc b/src/master/filesystem_periodic.cc -index d9fab6f9..8b7913fb 100644 ---- a/src/master/filesystem_periodic.cc -+++ b/src/master/filesystem_periodic.cc -@@ -92,36 +92,11 @@ void fs_background_task_manager_work() { - } - } - --std::vector fs_get_defective_nodes_info(uint8_t requested_flags, uint64_t max_entries, -- uint64_t &entry_index) { -- FSNode *node; -- FSNodeDirectory *parent; -- std::string file_path; -- std::vector defective_nodes_info; -- ActiveLoopWatchdog watchdog; -- defective_nodes_info.reserve(max_entries); -- auto it = gDefectiveNodes.find_nth(entry_index); -- watchdog.start(); -- for (uint64_t i = 0; i < max_entries && it != gDefectiveNodes.end(); ++it) { -- if (((*it).second & requested_flags) != 0) { -- node = fsnodes_id_to_node((*it).first); -- parent = fsnodes_get_first_parent(node); -- fsnodes_getpath(parent, node, file_path); -- file_path = "/" + file_path; -- defective_nodes_info.emplace_back(file_path, (*it).second); -- ++i; -- } -- ++entry_index; -- if (watchdog.expired()) { -- return defective_nodes_info; -- } -- } -- entry_index = 0; -- return defective_nodes_info; --} -- - static std::string get_node_info(FSNode *node) { - std::string name; -+ if (node == nullptr) { -+ return name; -+ } - if (node->type == FSNode::kTrash) { - name = "file in trash " + std::to_string(node->id) + ": " + - (std::string)gMetadata->trash.at(TrashPathKey(node)); -@@ -157,6 +132,30 @@ static std::string get_node_info(FSNode *node) { - return fsnodes_escape_name(name); - } - -+std::vector fs_get_defective_nodes_info(uint8_t requested_flags, uint64_t max_entries, -+ uint64_t &entry_index) { -+ FSNode *node; -+ std::vector defective_nodes_info; -+ ActiveLoopWatchdog watchdog; -+ defective_nodes_info.reserve(max_entries); -+ auto it = gDefectiveNodes.find_nth(entry_index); -+ watchdog.start(); -+ for (uint64_t i = 0; i < max_entries && it != gDefectiveNodes.end(); ++it) { -+ if (((*it).second & requested_flags) != 0) { -+ node = fsnodes_id_to_node((*it).first); -+ std::string info = get_node_info(node); -+ defective_nodes_info.emplace_back(std::move(info), (*it).second); -+ ++i; -+ } -+ ++entry_index; -+ if (watchdog.expired()) { -+ return defective_nodes_info; -+ } -+ } -+ entry_index = 0; -+ return defective_nodes_info; -+} -+ - void fs_test_getdata(uint32_t &loopstart, uint32_t &loopend, uint32_t &files, uint32_t &ugfiles, - uint32_t &mfiles, uint32_t &chunks, uint32_t &ugchunks, uint32_t &mchunks, - std::string &result) { -@@ -170,6 +169,8 @@ void fs_test_getdata(uint32_t &loopstart, uint32_t &loopend, uint32_t &files, ui - - FSNode *node = fsnodes_id_to_node(entry.first); - if (!node) { -+ report << "Structure error in defective list, entry " << std::to_string(entry.first) << "\n"; -+ errors++; - continue; - } - -@@ -213,6 +214,7 @@ void fs_test_getdata(uint32_t &loopstart, uint32_t &loopend, uint32_t &files, ui - report << "*"; - } - report << " currently unavailable " << name << "\n"; -+ errors++; - } - - if (errors >= ERRORS_LOG_MAX) { -@@ -222,6 +224,7 @@ void fs_test_getdata(uint32_t &loopstart, uint32_t &loopend, uint32_t &files, ui - if (entry.second & kStructureError) { - std::string name = get_node_info(node); - report << "Structure error in " << name << "\n"; -+ errors++; - } - - if (errors >= ERRORS_LOG_MAX) { --- -2.13.0 - diff --git a/0002-mount-Fix-request-size-in-read-cache-for-empty-resul.patch b/0002-mount-Fix-request-size-in-read-cache-for-empty-resul.patch deleted file mode 100644 index 85cc8dd..0000000 --- a/0002-mount-Fix-request-size-in-read-cache-for-empty-resul.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 48004d85280a8d8483c0b1a85e3fe2db28967750 Mon Sep 17 00:00:00 2001 -From: Piotr Sarna -Date: Mon, 12 Jun 2017 17:24:34 +0200 -Subject: [PATCH 2/3] mount: Fix request size in read cache for empty results - -This commit makes empty cache results return correct '0' value -when checking its size. - -Change-Id: I9d2fa823bff46133bc471aae32155b5c8b21e11c ---- - src/mount/readdata_cache.h | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/mount/readdata_cache.h b/src/mount/readdata_cache.h -index 9043dc32..37f56e50 100644 ---- a/src/mount/readdata_cache.h -+++ b/src/mount/readdata_cache.h -@@ -186,6 +186,9 @@ public: - } - - Size requestSize(Offset real_offset, Size real_size) const { -+ if (entries.empty()) { -+ return 0; -+ } - assert(real_offset >= frontOffset()); - assert(real_offset <= endOffset()); - return std::min(endOffset() - real_offset, real_size); --- -2.13.0 - diff --git a/0003-mount-Fix-read-request-size-with-disabled-readahead.patch b/0003-mount-Fix-read-request-size-with-disabled-readahead.patch deleted file mode 100644 index 2d91f15..0000000 --- a/0003-mount-Fix-read-request-size-with-disabled-readahead.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0b970d4e0cad10a70c920cad0437bf8b278df00d Mon Sep 17 00:00:00 2001 -From: Piotr Sarna -Date: Mon, 19 Jun 2017 14:38:05 +0200 -Subject: [PATCH 3/3] mount: Fix read request size with disabled readahead - -This commit makes read requests ask for proper size when readahead -feature is disabled (cacheexpirationtime set to 0). - -Change-Id: Ia5d8cfb746d689b1f4750e79721419d02ae7db70 ---- - src/mount/readahead_adviser.h | 4 ++++ - src/mount/readdata_cache.h | 2 +- - 2 files changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/mount/readahead_adviser.h b/src/mount/readahead_adviser.h -index 1dc89eae..832be05b 100644 ---- a/src/mount/readahead_adviser.h -+++ b/src/mount/readahead_adviser.h -@@ -54,6 +54,10 @@ public: - * \param size size of read operation - */ - void feed(uint64_t offset, uint32_t size) { -+ if (timeout_ms_ == 0) { -+ window_ = 0; -+ return; -+ } - addToHistory(size); - if (offset == current_offset_) { - random_candidates_ = 0; -diff --git a/src/mount/readdata_cache.h b/src/mount/readdata_cache.h -index 37f56e50..717826b6 100644 ---- a/src/mount/readdata_cache.h -+++ b/src/mount/readdata_cache.h -@@ -61,7 +61,7 @@ public: - } - - bool expired(uint32_t expiration_time) const { -- return timer.elapsed_ms() > expiration_time; -+ return timer.elapsed_ms() >= expiration_time; - } - - Offset endOffset() const { --- -2.13.0 - diff --git a/lizardfs.spec b/lizardfs.spec index 0b0620b..b0d3df3 100644 --- a/lizardfs.spec +++ b/lizardfs.spec @@ -1,6 +1,6 @@ Name: lizardfs Summary: Distributed, fault tolerant file system -Version: 3.11.3 +Version: 3.12.0 Release: 1%{?dist} # LizardFS is under GPLv3 while crcutil is under ASL 2.0 and there's one header, # src/common/coroutine.h, under the Boost license @@ -10,9 +10,8 @@ URL: http://www.lizardfs.org/ Source: https://github.com/lizardfs/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz Source1: pam-lizardfs Source2: 95-lizardfs.conf -# Make sure we drop supplementary groups when running setgid -# Pull request at https://github.com/lizardfs/lizardfs/pull/533 -Patch1: 0001-main-Remove-supplementary-groups-when-dropping-privi.patch +# Use spdlog system library if available +Patch0: 0001-Put-customized-spdlog-in-source-so-we-don-t-download.patch BuildRequires: fuse-devel BuildRequires: cmake BuildRequires: pkgconfig @@ -27,6 +26,8 @@ BuildRequires: systemd # libcrcutil is basically a copylib with a dead upstream # https://code.google.com/archive/p/crcutil/ Provides: bundled(libcrcutil) = 1.0 +# spdlog is a copylib that lizardfs changes, so we can't use the system version +Provides: bundled(spdlib) = 0.14.0 %global liz_project mfs %global liz_group %{liz_project} @@ -196,7 +197,7 @@ exit 0 %autosetup -p1 # Remove /usr/bin/env from bash scripts -for i in src/tools/mfstools.sh src/mount/mfssnapshot src/master/mfsrestoremaster.in \ +for i in src/tools/mfstools.sh src/master/mfsrestoremaster.in \ src/common/serialization_macros_generate.sh src/data/postinst.in \ utils/coverage.sh utils/cpp-interpreter.sh utils/wireshark/plugins/lizardfs/generate.sh; do sed -i 's@#!/usr/bin/env bash@#!/bin/bash@' $i @@ -258,7 +259,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files master -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_sbindir}/mfsmaster %{_sbindir}/mfsrestoremaster @@ -291,7 +292,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files metalogger -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_sbindir}/mfsmetalogger %{_mandir}/man5/mfsmetalogger.cfg.5* @@ -303,7 +304,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files chunkserver -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_sbindir}/mfschunkserver %{_mandir}/man5/mfschunkserver.cfg.5* @@ -319,12 +320,11 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files client -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_bindir}/lizardfs %{_bindir}/mfstools.sh %{_bindir}/mfsmount -%{_bindir}/mfssnapshot %{_bindir}/mfsappendchunks %{_bindir}/mfscheckfile %{_bindir}/mfsdeleattr @@ -378,7 +378,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files cgi -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %dir %{_datadir}/mfscgi %{_datadir}/mfscgi/err.gif @@ -391,7 +391,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files cgiserv -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_sbindir}/lizardfs-cgiserver %{_sbindir}/mfscgiserv @@ -401,7 +401,7 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %files adm -%doc NEWS README UPGRADE +%doc NEWS README.md UPGRADE %license COPYING %{_bindir}/lizardfs-admin %{_mandir}/man8/lizardfs-admin.8* @@ -410,6 +410,9 @@ install -m644 %{SOURCE2} %{buildroot}%{_sysconfdir}/security/limits.d/95-lizardf %changelog +* Tue Dec 26 2017 Jonathan Dieter - 3.12.0-1 +- Update to 3.12.0 + * Sat Aug 26 2017 Jonathan Dieter - 3.11.3-1 - Update to 3.11.3, removing upstreamed patches diff --git a/sources b/sources index f6c8e27..0821f1c 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (lizardfs-3.11.3.tar.gz) = daf90174a28ff9413cda57bb97e79c1f19a1af5ad2a673a5348e7f697ab8429824bc0153030b2fbd1045e96cf801c27504fceecaeb1dfa7bbf66896739dc9d86 +SHA512 (lizardfs-3.12.0.tar.gz) = 0136114266dfadcf8e2205bfd19f50ee201566958fba1dc97d4a238ed63ca91dc2cd6352f25d911f4410b0fbd59846f54206da773789d5b959c8c02bde5adf20