diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..4ae554ea --- /dev/null +++ b/.clang-format @@ -0,0 +1,68 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +Language: Cpp +Standard: c++20 +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignOperands: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: ForContinuationAndIndentation diff --git a/.dockerignore b/.dockerignore index 7caf5d04..ee7b04fb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,166 @@ +## project specific +/src/lib/tentris/tentris_version.hpp # log files tentris_* -# cmake build folders -./cmake* \ No newline at end of file + +# Created by https://www.toptal.com/developers/gitignore/api/c++,conan,jetbrains+all,cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=c++,conan,jetbrains+all,cmake + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + +### CMake Patch ### +# External projects +*-prefix/ + +### Conan ### +# Conan build information +conan.lock +conanbuildinfo.* +conaninfo.txt +graph_info.json + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +# End of https://www.toptal.com/developers/gitignore/api/c++,conan,jetbrains+all,cmake + +# docu folder +/docu/ \ No newline at end of file diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml new file mode 100644 index 00000000..7d93dded --- /dev/null +++ b/.github/workflows/push.yaml @@ -0,0 +1,29 @@ +name: push + +on: + push: + branches: + - '*' + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Build image + uses: docker/build-push-action@v2 + with: + push: false + tags: | + tentris:latest + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..2f900422 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,85 @@ +name: release + +on: + push: + tags: + - '*' + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Get the Ref + id: get-ref + uses: ankitvgupta/ref-to-tag-action@master + with: + ref: ${{ github.ref }} + head_ref: ${{ github.head_ref }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Login to Docker registry + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_REGISTRY_USER }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + - name: Build and load image + id: docker_build + uses: docker/build-push-action@v2 + with: + load: true # We can't load and push at the same time... + tags: | + dicegroup/tentris_server:current_build + dicegroup/tentris_server:${{ steps.get-ref.outputs.tag }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + - name: Push image + run: | + docker push dicegroup/tentris_server:${{ steps.get-ref.outputs.tag }} + - name: Extract binaries + run: | + container_id=$(docker create dicegroup/tentris_server:current_build) + docker cp ${container_id}:/tentris_server ./tentris_server + docker cp ${container_id}:/tentris_terminal ./tentris_terminal + docker cp ${container_id}:/rdf2ids ./rdf2ids + docker cp ${container_id}:/ids2hypertrie ./ids2hypertrie + zip benchmarktools_clang11_libstdcxx10.zip rdf2ids ids2hypertrie + zip tentris_clang11_libstdcxx10.zip tentris_server tentris_terminal + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.get-ref.outputs.tag }} + release_name: ${{ steps.get-ref.outputs.tag }} + draft: true + prerelease: false + - name: Upload tentris + id: upload-tentris-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: tentris_clang11_libstdcxx10.zip + asset_name: tentris_${{ steps.get-ref.outputs.tag }}_clang11_libstdcxx10.zip + asset_content_type: application/zip + - name: Upload benchmarktools + id: upload-benchmarktools-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: benchmarktools_clang11_libstdcxx10.zip + asset_name: benchmarktools_${{ steps.get-ref.outputs.tag }}_clang11_libstdcxx10.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore index 1a6d06cc..ee7b04fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,166 @@ -build/ -cmake-build-debug/ +## project specific +/src/lib/tentris/tentris_version.hpp +# log files +tentris_* + +# Created by https://www.toptal.com/developers/gitignore/api/c++,conan,jetbrains+all,cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=c++,conan,jetbrains+all,cmake + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + +### CMake Patch ### +# External projects +*-prefix/ + +### Conan ### +# Conan build information +conan.lock +conanbuildinfo.* +conaninfo.txt +graph_info.json + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + .idea/ -tests/googletest \ No newline at end of file + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +# End of https://www.toptal.com/developers/gitignore/api/c++,conan,jetbrains+all,cmake + +# docu folder +/docu/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6be96611..00000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -os: linux -dist: bionic -language: cpp - -services: - - docker - -cache: - directories: - - docker_images - -before_install: - - docker load -i docker_images/images.tar || true - -before_cache: - - docker save -o docker_images/images.tar $(docker images -a -q) - -script: - - travis_wait 30 docker build -t tentris_docker_image . - -before_deploy: - - container_id=$(docker create tentris_docker_image) - - docker cp ${container_id}:/tentris_server ./tentris_server - - docker cp ${container_id}:/tentris_terminal ./tentris_terminal - - docker cp ${container_id}:/rdf2ids ./rdf2ids - - docker cp ${container_id}:/ids2hypertrie ./ids2hypertrie - - zip benchmarktools_${TRAVIS_TAG}_clang9_libstdcxx9.zip rdf2ids ids2hypertrie - - zip tentris_${TRAVIS_TAG}_clang9_libstdcxx9.zip tentris_server tentris_terminal - -deploy: - provider: releases - file: - - "benchmarktools_${TRAVIS_TAG}_clang9_libstdcxx9.zip" - - "tentris_${TRAVIS_TAG}_clang9_libstdcxx9.zip" - token: $github_token - cleanup: false - skip_cleanup: true - overwrite: true - draft: true - on: - tags: true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a4a03fb8..f6f05a13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,10 @@ cmake_minimum_required(VERSION 3.13) -project(tentris CXX) -set(CMAKE_CXX_STANDARD 17) -set(tentris_VERSION_MAJOR 1) -set(tentris_VERSION_MINOR 0) -set(tentris_VERSION_PATCH 5) +project(tentris + LANGUAGES CXX + VERSION 1.0.7) +set(CMAKE_CXX_STANDARD 20) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/tentris/tentris_version.hpp) include(${CMAKE_BINARY_DIR}/conan_paths.cmake) @@ -13,54 +14,103 @@ if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt) endif () endif () +if(DEFINED ${TENTRIS_MARCH}) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=${TENTRIS_MARCH}") +endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fomit-frame-pointer -momit-leaf-frame-pointer") else () set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fomit-frame-pointer") endif () -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -g -O0") +if (TENTRIS_STATIC) + SET(CMAKE_FIND_LIBRARY_SUFFIXES .a) +endif () +if (TENTRIS_BUILD_WITH_TCMALLOC) + find_library(TCMALLOCMINIMAL tcmalloc_minimal) + if (NOT TCMALLOCMINIMAL) + find_library(TCMALLOCMINIMAL tcmalloc-minimal) + endif() + if (NOT TCMALLOCMINIMAL) + message(FATAL_ERROR "Neither tcmalloc-minimal nor tcmalloc_minimal was found") + endif() + message("tcmalloc minimal ${TCMALLOCMINIMAL}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TCMALLOCMINIMAL}") + if(TENTRIS_STATIC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--eh-frame-hdr") + endif() +endif() +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -g -O0") # Lightweight C++ command line option parser https://github.com/jarro2783/cxxopts -add_library(cxxopts INTERFACE) -target_include_directories(cxxopts INTERFACE - thirdparty/cxxopts/include +include(FetchContent) +FetchContent_Declare( + cxxopts + GIT_REPOSITORY https://github.com/jarro2783/cxxopts.git + GIT_TAG v2.2.1 + GIT_SHALLOW TRUE +) +set(CXXOPTS_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +set(CXXOPTS_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(CXXOPTS_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) +set(CXXOPTS_ENABLE_WARNINGS OFF CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(cxxopts) + +add_library(csv-parser INTERFACE) +target_include_directories(csv-parser INTERFACE + thirdparty/csv-parser ) + add_library(rapidjson INTERFACE) target_include_directories(rapidjson INTERFACE thirdparty/RapidJSON/include ) -find_package(absl REQUIRED) find_package(tsl-hopscotch-map REQUIRED) find_package(fmt REQUIRED) find_package(hypertrie REQUIRED) find_package(sparql-parser-base REQUIRED) find_package(rdf-parser REQUIRED) -SET(Boost_USE_STATIC_LIBS ON) +if (TENTRIS_STATIC) + SET(Boost_USE_STATIC_LIBS ON) +endif () + find_package(Boost REQUIRED COMPONENTS system log_setup log thread) -SET(RESTINIO_USE_BOOST_ASIO=static) -find_package(restinio CONFIG REQUIRED) -find_package(http-parser REQUIRED) +if (TENTRIS_STATIC) + SET(RESTINIO_USE_BOOST_ASIO=static) +endif () +find_package(restinio REQUIRED) +find_package(string-view-lite REQUIRED) +find_package(optional-lite REQUIRED) # make a library of the code add_library(tentris INTERFACE) +include(FetchContent) +FetchContent_Declare( + cppitertools + GIT_REPOSITORY https://github.com/ryanhaining/cppitertools.git + GIT_TAG v2.1 + GIT_SHALLOW TRUE) + +FetchContent_MakeAvailable(cppitertools) + target_link_libraries(tentris INTERFACE stdc++fs # for #include cxxopts rapidjson sparql-parser-base::sparql-parser-base - absl::absl - tsl-hopscotch-map::tsl-hopscotch-map + tsl::hopscotch_map fmt::fmt hypertrie::hypertrie rdf-parser::rdf-parser Boost::Boost serd-0 + cppitertools::cppitertools ) target_include_directories(tentris INTERFACE @@ -69,17 +119,29 @@ target_include_directories(tentris INTERFACE ${Boost_INCLUDE_DIRS} ) +# for rt and pthread linkage see: +# * https://stackoverflow.com/questions/58848694/gcc-whole-archive-recipe-for-static-linking-to-pthread-stopped-working-in-rec +# * https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why +if (TENTRIS_STATIC) + set(TENTRIS_STATIC_DEF -static) + set(TENTRIS_PTHREAD_DEF -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive) +else() + set(TENTRIS_PTHREAD_DEF -lpthread) +endif () + # main executable targets add_executable(tentris_server src/exec/TentrisServer.cpp src/exec/config/ServerConfig.hpp src/exec/config/TerminalConfig.hpp) target_link_libraries(tentris_server PRIVATE - -static - -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive + ${TENTRIS_STATIC_DEF} + ${TENTRIS_PTHREAD_DEF} tentris restinio::restinio - http-parser::http-parser + nonstd::string-view-lite + nonstd::optional-lite + nonstd::variant-lite ) set_target_properties(tentris_server PROPERTIES LINK_FLAGS_RELEASE -s) @@ -88,9 +150,9 @@ add_dependencies(tentris_server tentris) add_executable(tentris_terminal src/exec/TentrisTerminal.cpp src/exec/config/ServerConfig.hpp src/exec/config/TerminalConfig.hpp) target_link_libraries(tentris_terminal - -static + ${TENTRIS_STATIC_DEF} + ${TENTRIS_PTHREAD_DEF} tentris - -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive ) set_target_properties(tentris_terminal PROPERTIES LINK_FLAGS_RELEASE -s) @@ -99,23 +161,22 @@ add_dependencies(tentris_terminal tentris) add_executable(rdf2ids src/exec/tools/RDF2IDs.cpp) target_link_libraries(rdf2ids - -static + ${TENTRIS_STATIC_DEF} + ${TENTRIS_PTHREAD_DEF} tentris - -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive ) set_target_properties(rdf2ids PROPERTIES LINK_FLAGS_RELEASE -s) add_dependencies(rdf2ids tentris) -# for rt and pthread linkage see: -# * https://stackoverflow.com/questions/58848694/gcc-whole-archive-recipe-for-static-linking-to-pthread-stopped-working-in-rec -# * https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why + add_executable(ids2hypertrie src/exec/tools/IDs2Hypertrie.cpp) target_link_libraries(ids2hypertrie - -static + ${TENTRIS_STATIC_DEF} + ${TENTRIS_PTHREAD_DEF} tentris - -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive + csv-parser ) add_dependencies(ids2hypertrie tentris) diff --git a/Dockerfile b/Dockerfile index dba78f2c..81040e11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,61 +1,81 @@ -FROM ubuntu:focal AS builder +FROM ubuntu:groovy AS builder ARG DEBIAN_FRONTEND=noninteractive +ARG TENTRIS_MARCH="x86-64" RUN apt-get -qq update && \ - apt-get -qq install -y make cmake uuid-dev git openjdk-11-jdk python3-pip python3-setuptools python3-wheel libstdc++-9-dev clang-9 gcc-9 pkg-config + apt-get -qq install -y make cmake uuid-dev git openjdk-11-jdk python3-pip python3-setuptools python3-wheel libstdc++-10-dev clang-11 g++-10 pkg-config lld autoconf libtool +RUN rm /usr/bin/ld && ln -s /usr/bin/lld-11 /usr/bin/ld +ARG CXX="clang++-11" +ARG CC="clang-11" +ENV CXXFLAGS="${CXXFLAGS} -march=${TENTRIS_MARCH}" +ENV CMAKE_EXE_LINKER_FLAGS="-L/usr/local/lib/x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib" + +# Compile more recent tcmalloc-minimal with clang-11 + -march +RUN git clone --quiet --branch gperftools-2.8.1 https://github.com/gperftools/gperftools +WORKDIR /gperftools +RUN ./autogen.sh +RUN export LDFLAGS="${CMAKE_EXE_LINKER_FLAGS}" && ./configure \ + --enable-minimal \ + --disable-debugalloc \ + --enable-sized-delete \ + --enable-dynamic-sized-delete-support && \ + make -j && \ + make install +WORKDIR / + # we need serd as static library. Not available from ubuntu repos -RUN ln -s /usr/bin/python3 /usr/bin/python && \ - git clone --quiet --branch v0.30.2 https://gitlab.com/drobilla/serd.git && \ - cd serd && \ - git submodule update --quiet --init --recursive && \ +RUN ln -s /usr/bin/python3 /usr/bin/python +RUN git clone --quiet --branch v0.30.2 https://gitlab.com/drobilla/serd.git +WORKDIR serd +RUN git submodule update --quiet --init --recursive && \ ./waf configure --static && \ - ./waf install &&\ - rm /usr/bin/python + ./waf install +RUN rm /usr/bin/python +WORKDIR / + # install and configure conan RUN pip3 install conan && \ conan user && \ conan profile new --detect default && \ - conan profile update settings.sparql-parser-base:compiler.version=9 default && \ - conan profile update settings.sparql-parser-base:compiler.libcxx=libstdc++11 default && \ - conan profile update settings.sparql-parser-base:compiler=gcc default &&\ - conan profile update env.sparql-parser-base:CXX=/usr/bin/g++ default && \ - conan profile update env.sparql-parser-base:CC=/usr/bin/gcc default && \ - conan profile update settings.compiler=clang default &&\ - conan profile update settings.compiler.version=9 default && \ conan profile update settings.compiler.libcxx=libstdc++11 default && \ - conan profile update env.CXX=/usr/bin/clang++-9 default && \ - conan profile update env.CC=/usr/bin/clang-9 default + conan profile update env.CXXFLAGS="${CXXFLAGS}" default && \ + conan profile update env.CMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" default && \ + conan profile update env.CXX="${CXX}" default && \ + conan profile update env.CC="${CC}" default && \ + conan profile update options.boost:extra_b2_flags="cxxflags=\\\"${CXXFLAGS}\\\"" default # add conan repositories -RUN conan remote add tsl https://api.bintray.com/conan/tessil/tsl -RUN conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan -RUN conan remote add stiffstream https://api.bintray.com/conan/stiffstream/public -RUN conan remote add dice-group https://api.bintray.com/conan/dice-group/tentris +RUN conan remote add dice-group https://conan.dice-research.org/artifactory/api/conan/tentris # build and cache dependencies via conan -COPY conanfile.txt /conan_cache/conanfile.txt -RUN cd /conan_cache && conan install . --build=missing --profile default > conan_build.log +WORKDIR /conan_cache +COPY conanfile.txt conanfile.txt +RUN conan install . --build=missing --profile default > conan_build.log # import project files -COPY thirdparty /tentris/thirdparty/ -COPY src /tentris/src/ -COPY CMakeLists.txt /tentris/CMakeLists.txt -COPY conanfile.txt /tentris/conanfile.txt +WORKDIR /tentris +COPY thirdparty thirdparty +COPY src src +COPY cmake cmake +COPY CMakeLists.txt CMakeLists.txt +COPY conanfile.txt conanfile.txt ##build -# import and build depenedencies via conan -RUN mkdir /tentris/build && cd /tentris/build && \ - conan install .. --build=missing -# build tentris_server with clang++-9 again -RUN cd /tentris/build && \ - export CXX="clang++-9" && export CC="clang-9" && \ - cmake -DCMAKE_BUILD_TYPE=Release .. && \ - make -j $(nproc) - +WORKDIR /tentris/build +RUN conan install .. --build=missing +# todo: should be replaced with toolchain file like https://github.com/ruslo/polly/blob/master/clang-libcxx17-static.cmake +RUN cmake \ + -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DTENTRIS_BUILD_WITH_TCMALLOC=true \ + -DTENTRIS_STATIC=true \ + .. +RUN make -j $(nproc) FROM scratch -WORKDIR /tentris COPY --from=builder /tentris/build/tentris_server /tentris_server COPY --from=builder /tentris/build/tentris_terminal /tentris_terminal COPY --from=builder /tentris/build/ids2hypertrie /ids2hypertrie COPY --from=builder /tentris/build/rdf2ids /rdf2ids +COPY LICENSE LICENSE +COPY README.MD README.MD ENTRYPOINT ["/tentris_server"] diff --git a/README.MD b/README.MD index 80e07601..094ea039 100644 --- a/README.MD +++ b/README.MD @@ -63,7 +63,7 @@ The endpoint may now be queried locally at: `127.0.0.1:9080/sparql?query=*your q *Notice*: the query string `*your query*` must be URL encoded. You can use any online URL encoder like . -*currently deactivated*: ~~An additional endpoint is provided at `127.0.0.1:9080/stream` using chunk encoded HTTP response. This endpoint should be used for very large responses (>1mio results).~~ +An additional endpoint is provided at `127.0.0.1:9080/stream` using chunk encoded HTTP response. This endpoint should be used for very large responses (>1mio results). #### Usage Example @@ -166,10 +166,7 @@ pip3 install --user conan ### Dependencies Most required dependencies are installed via conan. Therefore, Add the respective remotes: ```shell script -conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan -conan remote add tsl https://api.bintray.com/conan/tessil/tsl -conan remote add stiffstream https://api.bintray.com/conan/stiffstream/public -conan remote add dice-group https://api.bintray.com/conan/dice-group/tentris +conan remote add dice-group https://conan.dice-research.org/artifactory/api/conan/tentris ``` Additionally, a statically linked version of the [Serd](https://drobilla.net/software/serd) library is required. As the packages in the deb/rpm repositories include only a dynamic library, we need to compile it manually: ```shell script diff --git a/cmake/version.hpp.in b/cmake/version.hpp.in new file mode 100644 index 00000000..d10471eb --- /dev/null +++ b/cmake/version.hpp.in @@ -0,0 +1,12 @@ +#ifndef TENTRIS_VERSION_HPP +#define TENTRIS_VERSION_HPP + +#include + +namespace Dice::tentris { + inline constexpr const char name[] = "@PROJECT_NAME@"; + inline constexpr const char version[] = "@tentris_VERSION@"; + inline constexpr std::array version_tuple = {@tentris_VERSION_MAJOR@, @tentris_VERSION_MINOR@, @tentris_VERSION_PATCH@}; +}// namespace Dice::tentris + +#endif//TENTRIS_VERSION_HPP diff --git a/conanfile.txt b/conanfile.txt index c17e883f..81d0ec9c 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,19 +1,17 @@ [requires] -boost/1.72.0 -fmt/6.1.2 -gtest/1.8.1 -abseil/20200205 -http-parser/2.8.1@bincrafters/stable # for restinio -restinio/0.6.5@stiffstream/stable -tsl-hopscotch-map/2.2.1@tessil/stable -hypertrie/0.5.2@dice-group/stable -rdf-parser/0.10@dice-group/stable -sparql-parser-base/0.1.0@dice-group/stable +boost/1.75.0 +fmt/7.1.3 +restinio/0.6.12 +hypertrie/0.5.3@dice-group/stable +rdf-parser/0.13.0@dice-group/stable +sparql-parser-base/0.2.2@dice-group/stable [options] -restinio:boost_libs=static +restinio:asio=boost +restinio:with_zlib=True boost:shared=False *:shared=False +sparql-parser-base:sparql_version=1.0 [generators] diff --git a/src/exec/TentrisServer.cpp b/src/exec/TentrisServer.cpp index 318bd535..2950a8ea 100644 --- a/src/exec/TentrisServer.cpp +++ b/src/exec/TentrisServer.cpp @@ -2,7 +2,6 @@ #include #include -#include "config/ServerConfig.hpp" #include #include #include @@ -10,48 +9,66 @@ #include +#include "config/ServerConfig.hpp" +#include "VersionStrings.hpp" -namespace { - using namespace tentris::http; - using namespace tentris::store::config; + +void bulkload(const std::string &triple_file, size_t bulksize) { namespace fs = std::filesystem; using namespace fmt::literals; -} - -void bulkload(std::string triple_file) { + using namespace tentris::logging; // log the starting time and print resource usage information auto loading_start_time = log_health_data(); if (fs::is_regular_file(triple_file)) { log("nt-file: {} loading ..."_format(triple_file)); - AtomicTripleStore::getInstance().loadRDF(triple_file); + ::tentris::store::AtomicTripleStore::getInstance().bulkloadRDF(triple_file, bulksize); } else { log("nt-file {} was not found."_format(triple_file)); log("Exiting ..."); std::exit(EXIT_FAILURE); } - log("Loaded {} triples.\n"_format(AtomicTripleStore::getInstance().size())); - // log the end time and print resource usage information auto loading_end_time = log_health_data(); // log the time it tool to load the file log_duration(loading_start_time, loading_end_time); } +struct tentris_restinio_traits : public restinio::traits_t< + restinio::null_timer_manager_t, +#ifdef DEBUG + restinio::shared_ostream_logger_t, +#else + restinio::null_logger_t, +#endif + restinio::router::express_router_t<> +>{ + static constexpr bool use_connection_count_limiter = true; +}; + + int main(int argc, char *argv[]) { + using namespace tentris::http; + using namespace tentris::store::config; + using namespace fmt::literals; + using namespace tentris::logging; + ServerConfig cfg{argc, argv}; init_logging(cfg.logstdout, cfg.logfile, cfg.logfiledir, cfg.loglevel); + log("Running {} with {}"_format(tentris_version_string, hypertrie_version_string)); + auto &store_cfg = AtomicTripleStoreConfig::getInstance(); store_cfg.rdf_file = cfg.rdf_file; store_cfg.timeout = cfg.timeout; store_cfg.cache_size = cfg.cache_size; + store_cfg.threads = cfg.threads; // bulkload file if (not cfg.rdf_file.empty()) { - bulkload(cfg.rdf_file); + bulkload(cfg.rdf_file, cfg.bulksize); } else { log("No file loaded."); } @@ -61,7 +78,10 @@ int main(int argc, char *argv[]) { auto router = std::make_unique>(); router->http_get( R"(/sparql)", - tentris::http::sparql_endpoint::sparql_endpoint); + tentris::http::sparql_endpoint::SparqlEndpoint{}); + router->http_get( + R"(/stream)", + tentris::http::sparql_endpoint::SparqlEndpoint{}); router->non_matched_request_handler( [](auto req) -> restinio::request_handling_status_t { @@ -70,21 +90,11 @@ int main(int argc, char *argv[]) { // Launching a server with custom traits. - using traits_t = - restinio::traits_t< - restinio::null_timer_manager_t, -#ifdef DEBUG - restinio::shared_ostream_logger_t, -#else - null_logger_t, -#endif - restinio::router::express_router_t<> - >; - log("SPARQL endpoint serving sparkling linked data treasures on {} threads at http://0.0.0.0:{}/sparql?query="_format(cfg.threads, cfg.port)); restinio::run( - restinio::on_thread_pool(cfg.threads) + restinio::on_thread_pool(cfg.threads) + .max_parallel_connections(cfg.threads) .address("0.0.0.0") .port(cfg.port) .request_handler(std::move(router)) diff --git a/src/exec/TentrisTerminal.cpp b/src/exec/TentrisTerminal.cpp index 841b994e..1c17c61a 100644 --- a/src/exec/TentrisTerminal.cpp +++ b/src/exec/TentrisTerminal.cpp @@ -2,32 +2,34 @@ #include #include #include -#include #include #include -#include "config/TerminalConfig.hpp" - #include #include #include #include #include +#include #include #include -#include #include -#include "tentris/http/QueryResultState.hpp" - -namespace { - using namespace tentris::store; - using namespace tentris::store::cache; - using namespace std::filesystem; - using namespace iter; - using namespace tentris::tensor; - using namespace std::chrono; -} + + +#include "config/TerminalConfig.hpp" +#include "VersionStrings.hpp" + +using namespace tentris::store; +using namespace tentris::logging; +using namespace tentris::store::cache; +using namespace tentris::store::sparql; +using namespace std::filesystem; +using namespace iter; +using namespace tentris::tensor; +using namespace std::chrono; + +using Variable = Dice::sparql::Variable; TerminalConfig cfg; @@ -237,9 +239,11 @@ void commandlineInterface(QueryExecutionPackage_cache &querypackage_cache) { int main(int argc, char *argv[]) { - cfg = {argc, argv}; + cfg = TerminalConfig{argc, argv}; tentris::logging::init_logging(cfg.logstdout, cfg.logfile, cfg.logfiledir, cfg.loglevel); + logsink() << fmt::format("Running {} with {}", tentris_version_string, hypertrie_version_string) << std::endl; + TripleStore triplestore{}; QueryExecutionPackage_cache executionpackage_cache{cfg.cache_size}; @@ -250,7 +254,7 @@ int main(int argc, char *argv[]) { if (not cfg.rdf_file.empty()) { logsink() << "Loading file " << cfg.rdf_file << " ..." << std::endl; auto start_time = steady_clock::now(); - AtomicTripleStore::getInstance().loadRDF(cfg.rdf_file); + AtomicTripleStore::getInstance().bulkloadRDF(cfg.rdf_file, cfg.bulksize); auto duration = steady_clock::now() - start_time; logsink() << fmt::format("... loading finished. {} triples loaded.", AtomicTripleStore::getInstance().size()) << std::endl; diff --git a/src/exec/VersionStrings.hpp b/src/exec/VersionStrings.hpp new file mode 100644 index 00000000..9b8fcd23 --- /dev/null +++ b/src/exec/VersionStrings.hpp @@ -0,0 +1,11 @@ +#ifndef TENTRIS_VERSIONSTRINGS_HPP +#define TENTRIS_VERSIONSTRINGS_HPP + +#include +#include + +inline const std::string tentris_version_string = std::string{} + Dice::tentris::name + " " + Dice::tentris::version; + +inline const std::string hypertrie_version_string = std::string{} + Dice::hypertrie::name + " " + Dice::hypertrie::version; + +#endif //TENTRIS_VERSIONSTRINGS_HPP diff --git a/src/exec/config/ExecutableConfig.hpp b/src/exec/config/ExecutableConfig.hpp index 1afa2eb2..0168ce52 100644 --- a/src/exec/config/ExecutableConfig.hpp +++ b/src/exec/config/ExecutableConfig.hpp @@ -12,6 +12,9 @@ #include +#include +#include +#include "../VersionStrings.hpp" namespace { using namespace fmt::literals; @@ -42,6 +45,9 @@ struct ExecutableConfig { mutable logging::trivial::severity_level loglevel; mutable bool logfile; + + mutable size_t bulksize; + mutable bool logstdout; mutable std::string logfiledir; @@ -66,6 +72,9 @@ struct ExecutableConfig { ("logfile", "If log is written to file.", cxxopts::value()->default_value("true")) + ("b,bulksize", + "Number of triples that are inserted at once. A larger value results in a higher memory consumption during loading RDF data but may result in shorter loading times.", + cxxopts::value()->default_value("1000000")) ("logstdout", "If log is written to stdout.", cxxopts::value()->default_value("false")) @@ -76,7 +85,7 @@ struct ExecutableConfig { public: - ExecutableConfig(int argc, char **argv) : ExecutableConfig{} { + ExecutableConfig(int argc, char ** &argv) : ExecutableConfig{} { initConfig(argc, argv); } @@ -86,7 +95,7 @@ struct ExecutableConfig { * @param argv array of char arrays with arguments */ - void initConfig(int argc, char **argv) { + void initConfig(int argc, char **&argv) { try { cxxopts::ParseResult arguments = options.parse(argc, argv); parseArguments(arguments); @@ -142,6 +151,8 @@ struct ExecutableConfig { logstdout = arguments["logstdout"].as(); + bulksize = arguments["bulksize"].as(); + logfiledir = arguments["logfiledir"].as(); if (not fs::exists(logfiledir)) { diff --git a/src/exec/config/ServerConfig.hpp b/src/exec/config/ServerConfig.hpp index dc893bf1..e4caeddd 100644 --- a/src/exec/config/ServerConfig.hpp +++ b/src/exec/config/ServerConfig.hpp @@ -19,7 +19,10 @@ struct ServerConfig : public ExecutableConfig { mutable uint threads; ServerConfig() { - options = {"tentris_server", "Tentris SPARQL endpoint queryable via HTTP. "}; + options = cxxopts::Options{ + "tentris_server", + "Tentris HTTP SPARQL endpoint" + "\n {}\n {}\n"_format(tentris_version_string, hypertrie_version_string)}; addOptions(); options.add_options() @@ -28,7 +31,7 @@ struct ServerConfig : public ExecutableConfig { cxxopts::value()->default_value("{}"_format(std::thread::hardware_concurrency()))); } - ServerConfig(int argc, char **argv) : ServerConfig{} { + ServerConfig(int argc, char **&argv) : ServerConfig{} { initConfig(argc, argv); } diff --git a/src/exec/config/TerminalConfig.hpp b/src/exec/config/TerminalConfig.hpp index 5f96092a..c8ab115d 100644 --- a/src/exec/config/TerminalConfig.hpp +++ b/src/exec/config/TerminalConfig.hpp @@ -9,8 +9,11 @@ struct TerminalConfig : public ExecutableConfig { mutable bool onlystdout; TerminalConfig() { - options = {"tentris_terminal", "Tentris SPARQL endpoint with terminal interface. " - "Just type your query and get the result."}; + options = cxxopts::Options{ + "tentris_terminal", + "Tentris SPARQL endpoint with terminal interface. " + "\n {}\n {}\n\n"_format( + tentris_version_string, hypertrie_version_string)}; addOptions(); options.add_options() @@ -19,7 +22,7 @@ struct TerminalConfig : public ExecutableConfig { } - TerminalConfig(int argc, char **argv) : TerminalConfig{} { + TerminalConfig(int argc, char **&argv) : TerminalConfig{} { initConfig(argc, argv); } diff --git a/src/exec/tools/IDs2Hypertrie.cpp b/src/exec/tools/IDs2Hypertrie.cpp index 7aa266b0..36a75dac 100644 --- a/src/exec/tools/IDs2Hypertrie.cpp +++ b/src/exec/tools/IDs2Hypertrie.cpp @@ -3,64 +3,94 @@ #include #include +#include + #include -#include #include #include #include #include +namespace tentris::IDs2Hypertrie { + void loadIDsAndWriteOutStats(const std::string &csv_file_path); +} int main(int argc, char *argv[]) { - using namespace rdf_parser::Turtle; - using namespace tentris::store; using namespace fmt::literals; - using namespace std::chrono; if (argc != 2) { std::cerr << "Please provide exactly one CSV file with triple IDS only and no headings." << std::endl; exit(EXIT_FAILURE); } - std::string rdf_file{argv[1]}; - if (not std::filesystem::is_regular_file(rdf_file)) { - std::cerr << "{} is not a file."_format(rdf_file) << std::endl; + std::string csv_file_path{argv[1]}; + if (not std::filesystem::is_regular_file(csv_file_path)) { + std::cerr << "{} is not a file."_format(csv_file_path) << std::endl; exit(EXIT_FAILURE); } - tentris::tensor::BoolHypertrie hypertrie(3); - - std::ifstream file(rdf_file); - - std::string line = ""; - // Iterate through each line and split the content using delimeter - unsigned int total = 0; - unsigned int count = 0; - unsigned int _1mios = 0; - auto start = steady_clock::now(); - while (getline(file, line)) { - ++count; - ++total; - using boost::lexical_cast; - using key_part_type = const rdf_parser::store::rdf::Term *; - std::vector id_triple; - boost::algorithm::split(id_triple, line, boost::algorithm::is_any_of(",")); - - hypertrie.set(tentris::tensor::Key{(key_part_type) lexical_cast(id_triple[0]), - (key_part_type) lexical_cast(id_triple[1]), - (key_part_type) lexical_cast(id_triple[2]) - }, true); - if (count == 1'000'000) { - count = 0; - ++_1mios; - std::cerr << "{:d} mio triples processed."_format(_1mios) << std::endl; + tentris::IDs2Hypertrie::loadIDsAndWriteOutStats(csv_file_path); +} + +namespace tentris::IDs2Hypertrie { + using namespace tentris::store; + using namespace fmt::literals; + using namespace std::chrono; + + using key_part_type = size_t; + using tr = hypertrie::Hypertrie_t; + + + void loadIDsAndWriteOutStats(const std::string &csv_file_path) { + hypertrie::Hypertrie trie(3); + + csv::CSVFormat format; + format.delimiter('\t').quote(false); + + + csv::CSVReader tsv_reader(csv_file_path, format); + + // Iterate through each line and split the content using delimiter + unsigned long count = 0; + auto start = steady_clock::now(); + + try { + hypertrie::BulkInserter bulk_inserter{trie, 0}; + + for (csv::CSVRow &row: tsv_reader) { // Input iterator + row[0].get(); + bulk_inserter.add({row[0].get(), + row[1].get(), + row[2].get()}); + ++count; + + if (bulk_inserter.size() == 1'000'000) { + bulk_inserter.flush(); + std::cerr << "{:>10.3} mio triples processed.\n"_format(double(count) / 1'000'000); + std::cerr << "{:>10.3} mio triples loaded.\n"_format(double(trie.size()) / 1'000'000); + } + } + + bulk_inserter.flush(true); + + } catch (...) { + throw std::invalid_argument{"A parsing error occurred while parsing {}"_format(csv_file_path)}; } + auto end = steady_clock::now(); + auto duration = end - start; + + std::cerr << "## total ## \n" + << "triples processed: {}\n"_format(count) + << "triples loaded: {}\n"_format(trie.size()) + << "hypertrie size estimation: {:d} kB\n"_format(tentris::logging::get_memory_usage()) + << "duration: {} h {} min {}.{:03d} s = {} ms\n"_format( + (std::chrono::duration_cast(duration)).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 1000).count(), + std::chrono::duration_cast(duration).count()); } - auto end = steady_clock::now(); - file.close(); - std::cerr << "{:d} mio triples processed."_format(total) << std::endl; - std::cerr << "hypertrie entries: {:d}."_format(total) << std::endl; - std::cerr << "hypertrie size estimation: {:d} kB."_format(tentris::logging::get_memory_usage()) << std::endl; - auto duration = end - start; - - std::cerr << "duration: {:d}.{:04d} s."_format(duration_cast(duration).count(), - (duration_cast(duration) % 1000).count()) << std::endl; -} + +} \ No newline at end of file diff --git a/src/exec/tools/RDF2IDs.cpp b/src/exec/tools/RDF2IDs.cpp index 152cd2fe..3a8867c2 100644 --- a/src/exec/tools/RDF2IDs.cpp +++ b/src/exec/tools/RDF2IDs.cpp @@ -1,14 +1,16 @@ #include #include +#include #include #include -#include int main(int argc, char *argv[]) { - using namespace rdf_parser::Turtle; using namespace tentris::store; using namespace fmt::literals; + using namespace std::chrono; + using Triple = Dice::rdf::Triple; + if (argc != 2) { std::cerr << "Please provide exactly one triple file as commandline argument." << std::endl; exit(EXIT_FAILURE); @@ -20,28 +22,37 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + fmt::print(stderr, "To store the result pipe stdout (not stderr!) to a file. Output format is TSV (tab-separated file, extension: .tsv).\n"); + rdf::TermStore ts{}; - unsigned int total = 0; + unsigned long count = 0; + try { - unsigned int count = 0; - unsigned int _1mios = 0; + auto start = steady_clock::now(); + fmt::print("S\tP\tO\n"); for (const Triple &triple : rdf::SerdParser{rdf_file}) { std::array id_triple{ (uintptr_t) ts[triple.subject()], (uintptr_t) ts[triple.predicate()], (uintptr_t) ts[triple.object()]}; - fmt::print("{},{},{}\n", id_triple[0], id_triple[1], id_triple[2]); + fmt::print("{}\t{}\t{}\n", id_triple[0], id_triple[1], id_triple[2]); ++count; - ++total; - if (count == 1'000'000) { - count = 0; - ++_1mios; - std::cerr << "{:d} mio triples processed."_format(_1mios) << std::endl; + if (count % 1'000'000 == 0) { + fmt::print(stderr, "triples processed: {:>10.3}\n", double(count) / 1'000'000); } } - std::cerr << "{:d} triples processed."_format(total) << std::endl; + auto end = steady_clock::now(); + auto duration = end - start; + + fmt::print(stderr, "total triples processed: {}\n", count); + fmt::print(stderr, "duration: {} h {} min {}.{:03d} s = {} ms\n", + (std::chrono::duration_cast(duration)).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 1000).count(), + std::chrono::duration_cast(duration).count()); } catch (...) { throw std::invalid_argument{ - "A parsing error occurred while parsing {}. Error occured at {}th triple."_format(rdf_file, total)}; + "A parsing error occurred while parsing {}. Error occurred at {}th triple."_format(rdf_file, count)}; } } diff --git a/src/lib/tentris/http/AtomicCleanupTaskGroup.hpp b/src/lib/tentris/http/AtomicCleanupTaskGroup.hpp deleted file mode 100644 index 182a6b28..00000000 --- a/src/lib/tentris/http/AtomicCleanupTaskGroup.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TENTRIS_ATOMIC_CLEANUP_TASKGROUP_HPP -#define TENTRIS_ATOMIC_CLEANUP_TASKGROUP_HPP -#include "tentris/util/SingletonFactory.hpp" -#include - -namespace { -} - -namespace tentris::http { - - /** - * A SingletonFactory that allows to share a single TripleStore instance between multiple threads. - */ - class AtomicCleanupTaskGroup : public util::sync::SingletonFactory { - public: - AtomicCleanupTaskGroup() : util::sync::SingletonFactory{} {} - }; - -}; -#endif //TENTRIS_ATOMIC_CLEANUP_TASKGROUP_HPP diff --git a/src/lib/tentris/http/SparqlEndpoint.hpp b/src/lib/tentris/http/SparqlEndpoint.hpp index 651e6277..f16d8b7c 100644 --- a/src/lib/tentris/http/SparqlEndpoint.hpp +++ b/src/lib/tentris/http/SparqlEndpoint.hpp @@ -14,179 +14,236 @@ #include "tentris/http/QueryResultState.hpp" #include "tentris/store/SPARQL/ParsedSPARQL.hpp" #include "tentris/store/AtomicQueryExecutionPackageCache.hpp" -#include "tentris/store/JsonQueryResult.hpp" +#include "tentris/store/SparqlJsonResultSAXWriter.hpp" +#include "tentris/store/AtomicTripleStore.hpp" #include "tentris/util/LogHelper.hpp" - namespace tentris::http { - namespace { - using namespace ::tentris::store::sparql; - using namespace ::tentris::store::cache; - using AtomicQueryExecutionCache = ::tentris::store::AtomicQueryExecutionCache; - using namespace ::std::chrono; - using namespace ::tentris::logging; - using namespace std::string_literals; - using Status = ResultState; - } // namespace - namespace sparql_endpoint { - Status - runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, - const time_point_t timeout); + using AtomicTripleStoreConfig = ::tentris::store::config::AtomicTripleStoreConfig; + using AtomicQueryExecutionCache = ::tentris::store::AtomicQueryExecutionCache; + using QueryExecutionPackage = ::tentris::store::cache::QueryExecutionPackage; + using Status = ResultState; - template - Status runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, - const time_point_t timeout); + using namespace ::tentris::logging; + using namespace ::tentris::store; + using namespace ::tentris::store::sparql; + using namespace ::tentris::tensor; + using namespace std::string_literals; + using namespace ::std::chrono; /** * Main SPARQL endpoint. Parses HTTP queries and returns SPARQL JSON Results. */ - auto sparql_endpoint = [](restinio::request_handle_t req, - [[maybe_unused]] auto params) -> restinio::request_handling_status_t { - using namespace std::string_literals; - auto start_time = steady_clock::now(); - log("request started."); - auto start_memory = get_memory_usage(); - logDebug("ram: {:d} kB"_format(start_memory)); - auto timeout = start_time + AtomicTripleStoreConfig::getInstance().timeout; - restinio::request_handling_status_t handled = restinio::request_rejected(); - Status status = Status::OK; - std::string error_message{}; - std::shared_ptr query_package; - std::string query_string{}; - try { - const auto query_params = restinio::parse_query( - req->header().query()); - if (query_params.has("query")) { - query_string = std::string(query_params["query"]); - log("query: {}"_format(query_string)); - // check if there is actually an query - try { - query_package = AtomicQueryExecutionCache::getInstance()[query_string]; - } catch (const std::invalid_argument &exc) { + + template requires std::is_same_v or + std::is_same_v + struct SparqlEndpoint { + private: + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; + using Triple = Dice::rdf::Triple; + using TriplePattern = Dice::sparql::TriplePattern; + using Variable = Dice::sparql::Variable; + public: + + constexpr static bool chunked_output = std::is_same_v; + constexpr static size_t chunk_size = 100'000'000UL; + + auto operator()(restinio::request_handle_t req, + [[maybe_unused]] auto params) -> restinio::request_handling_status_t { + using namespace std::string_literals; + auto start_time = steady_clock::now(); + log("request started."); + auto start_memory = get_memory_usage(); + logDebug("ram: {:d} kB"_format(start_memory)); + auto timeout = start_time + AtomicTripleStoreConfig::getInstance().timeout; + restinio::request_handling_status_t handled = restinio::request_rejected(); + Status status = Status::OK; + std::string error_message{}; + std::shared_ptr query_package; + std::string query_string{}; + try { + const auto query_params = restinio::parse_query( + req->header().query()); + if (query_params.has("query")) { + query_string = std::string(query_params["query"]); + log("query: {}"_format(query_string)); + // check if there is actually an query + try { + query_package = AtomicQueryExecutionCache::getInstance()[query_string]; + } catch (const std::invalid_argument &exc) { + status = Status::UNPARSABLE; + error_message = exc.what(); + } + if (status == Status::OK) { + status = runQuery(req, query_package, timeout); + } + } else { status = Status::UNPARSABLE; - error_message = exc.what(); } - if (status == Status::OK) { - status = runQuery(req, query_package, timeout); + } catch (const std::exception &exc) { + // if the execution of the query should fail return an internal server error + status = Status::UNEXPECTED; + error_message = exc.what(); + } + catch (...) { + // if the execution of the query should fail return an internal server error + status = Status::SEVERE_UNEXPECTED; + } + + switch (status) { + case OK: + handled = restinio::request_accepted(); + break; + case UNPARSABLE: + logError(" ## unparsable query\n" + " query_string: {}"_format(query_string) + ); + handled = req->create_response(restinio::http_status_line_t{restinio::status_code::bad_request, + "Could not parse the requested query."s}).done(); + break; + case UNKNOWN_REQUEST: + logError("unknown HTTP command. Only HTTP GET and POST are supported."); + handled = req->create_response(restinio::status_not_implemented()).done(); + break; + case PROCESSING_TIMEOUT: + logError("timeout during request processing"); + handled = req->create_response(restinio::status_request_time_out()).done(); + break; + case SERIALIZATION_TIMEOUT: + // no REQUEST TIMEOUT response can be sent here because we stream results directly to the client. + // Thus, the code was already written to the header. + logError("timeout during writing the result"); + handled = restinio::request_accepted(); + break; + case UNEXPECTED: + logError(" ## unexpected internal error, exception_message: {}"_format(error_message) + ); + handled = req->create_response( + restinio::status_internal_server_error()).connection_close().done(); + break; + case SEVERE_UNEXPECTED: + logError(" ## severe unexpected internal error, exception_message: {}"_format(error_message) + ); + handled = req->create_response( + restinio::status_internal_server_error()).connection_close().done(); + break; + } + if (handled == restinio::request_rejected()) + logError(fmt::format("Handling the request was rejected.")); + auto end_memory = get_memory_usage(); + logDebug("ram: {:d} kB"_format(end_memory)); + logDebug("ram diff: {:+3d} kB"_format(long(end_memory) - long(start_memory))); + logDebug("request duration: {}"_format(toDurationStr(start_time, steady_clock::now()))); + log("request ended."); + return handled; + }; + + Status + static runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, + const time_point_t timeout) { + + switch (query_package->getSelectModifier()) { + case SelectModifier::NONE: { + return runQuery < COUNTED_t > (req, query_package, timeout); } - } else { - status = Status::UNPARSABLE; + case SelectModifier::DISTINCT: { + return runQuery < DISTINCT_t > (req, query_package, timeout); + } + default: + break; } - } catch (const std::exception &exc) { - // if the execution of the query should fail return an internal server error - status = Status::UNEXPECTED; - error_message = exc.what(); - } - catch (...) { - // if the execution of the query should fail return an internal server error - status = Status::SEVERE_UNEXPECTED; + logTrace("Query type is not yet supported."); + return Status::UNPARSABLE; + }; + + template + static void async_cleanup(std::shared_ptr raw_results) { + std::thread([raw_results{move(raw_results)}]() { + auto &results = *static_cast *>(raw_results.get()); + results.clear(); + }).detach(); } - switch (status) { - case OK: - handled = restinio::request_accepted(); - break; - case UNPARSABLE: - logError(" ## unparsable query\n" - " query_string: {}"_format(query_string) - ); - handled = req->create_response(restinio::http_status_line_t{restinio::status_code::bad_request, - "Could not parse the requested query."s}).connection_close().done(); - break; - case UNKNOWN_REQUEST: - logError("unknown HTTP command. Only HTTP GET and POST are supported."); - handled = req->create_response(restinio::status_not_implemented()).connection_close().done(); - break; - case PROCESSING_TIMEOUT: - logError("timeout during request processing"); - handled = req->create_response(restinio::status_request_time_out()).connection_close().done(); - break; - case SERIALIZATION_TIMEOUT: - // no REQUEST TIMEOUT response can be sent here because we stream results directly to the client. - // Thus, the code was already written to the header. - logError("timeout during writing the result"); - handled = restinio::request_accepted(); - break; - case UNEXPECTED: - logError(" ## unexpected internal error, exception_message: {}"_format(error_message) - ); - handled = req->create_response( - restinio::status_internal_server_error()).connection_close().done(); - break; - case SEVERE_UNEXPECTED: - logError(" ## severe unexpected internal error, exception_message: {}"_format(error_message) - ); - handled = req->create_response( - restinio::status_internal_server_error()).connection_close().done(); - break; - } - if (handled == restinio::request_rejected()) - logError(fmt::format("Handling the request was rejected.")); - auto end_memory = get_memory_usage(); - logDebug("ram: {:d} kB"_format(end_memory)); - logDebug("ram diff: {:+3d} kB"_format(long(end_memory) - long(start_memory))); - logDebug("request duration: {}"_format(toDurationStr(start_time, steady_clock::now()))); - log("request ended."); - return handled; - }; + template + static Status + runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, + const time_point_t timeout) { + if (steady_clock::now() >= timeout) { + return Status::PROCESSING_TIMEOUT; + } - Status - runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, - const time_point_t timeout) { - switch (query_package->getSelectModifier()) { - case SelectModifier::NONE: { - return runQuery(req, query_package, timeout); - } - case SelectModifier::DISTINCT: { - return runQuery(req, query_package, timeout); - } - default: - break; - } - logTrace("Query type is not yet supported."); - return Status::UNPARSABLE; - }; + // check if it timed out + const std::vector &vars = query_package->getQueryVariables(); - template - Status runQuery(restinio::request_handle_t &req, std::shared_ptr &query_package, - const time_point_t timeout) { - // check if it timed out - if (steady_clock::now() >= timeout) { - return Status::PROCESSING_TIMEOUT; - } - const std::vector &vars = query_package->getQueryVariables(); - JsonQueryResult json_result{vars}; - if (not query_package->is_trivial_empty) { - std::shared_ptr raw_results = query_package->getEinsum(timeout); - auto &results = *static_cast *>(raw_results.get()); - - - auto timout_check = 0; - for (const EinsumEntry &result : results) { - json_result.add(result); - if (++timout_check == 100) { - if (steady_clock::now() >= timeout) - return Status::PROCESSING_TIMEOUT; - timout_check = 0; + + + if (query_package->is_trivial_empty) { + // create HTTP response object + auto resp = req->create_response(); + resp.append_header(restinio::http_field::content_type, "application/sparql-results+json"); + + SparqlJsonResultSAXWriter json_result(vars, 1'000UL); + json_result.close(); + resp.set_body(std::string{json_result.string_view()}); + resp.done(); + return Status::OK; + } else { + // create HTTP response object + restinio::response_builder_t resp = req->create_response(); + resp.append_header(restinio::http_field::content_type, "application/sparql-results+json"); + + std::shared_ptr raw_results = query_package->getEinsum(timeout); + auto &results = *static_cast *>(raw_results.get()); + + SparqlJsonResultSAXWriter json_result(vars, chunk_size); + + auto timout_check = 0; + for (const EinsumEntry &result : results) { + json_result.add(result); + if (++timout_check == 100) { + if (steady_clock::now() >= timeout) { + async_cleanup(std::move(raw_results)); + return Status::PROCESSING_TIMEOUT; + } + timout_check = 0; + } + if constexpr(chunked_output) { + if (json_result.full()) { + resp.append_chunk(std::string{json_result.string_view()}); + resp.flush(); + json_result.clear(); + } + } + } + + if (steady_clock::now() >= timeout) { + async_cleanup(std::move(raw_results)); + return Status::PROCESSING_TIMEOUT; } - } - } - if (steady_clock::now() >= timeout) { - return Status::PROCESSING_TIMEOUT; + json_result.close(); + + if constexpr(chunked_output) { + resp.append_chunk(std::string{json_result.string_view()}); + } else { + resp.set_body(std::string{json_result.string_view()}); + } + resp.done(); + + async_cleanup(std::move(raw_results)); + return Status::OK; + } } + }; - auto resp = req->create_response(); - resp.append_header(restinio::http_field::content_type, "application/sparql-results+json"); - resp.connection_close(); - resp.set_body(json_result.string()).done(); - return Status::OK; - } }; diff --git a/src/lib/tentris/store/AtomicQueryExecutionPackageCache.hpp b/src/lib/tentris/store/AtomicQueryExecutionPackageCache.hpp index 54a951d0..2d103118 100644 --- a/src/lib/tentris/store/AtomicQueryExecutionPackageCache.hpp +++ b/src/lib/tentris/store/AtomicQueryExecutionPackageCache.hpp @@ -6,18 +6,11 @@ #include "tentris/store/QueryExecutionPackageCache.hpp" #include "tentris/store/config/AtomicTripleStoreConfig.cpp" - -namespace { - using namespace tentris::store; - using namespace tentris::util::sync; - using namespace tentris::store::config; -} - namespace tentris::util::sync { template<> inline ::tentris::store::cache::QueryExecutionPackage_cache * SingletonFactory<::tentris::store::cache::QueryExecutionPackage_cache>::make_instance() { - const auto &config = AtomicTripleStoreConfig::getInstance(); + const auto &config = ::tentris::store::config::AtomicTripleStoreConfig::getInstance(); return new ::tentris::store::cache::QueryExecutionPackage_cache{config.cache_size}; } }; @@ -27,8 +20,6 @@ namespace tentris::store { /** * A SingletonFactory that allows to share a single TripleStore instance between multiple threads. */ - - - using AtomicQueryExecutionCache = SingletonFactory<::tentris::store::cache::QueryExecutionPackage_cache>; + using AtomicQueryExecutionCache = ::tentris::util::sync::SingletonFactory<::tentris::store::cache::QueryExecutionPackage_cache>; }; #endif //TENTRIS_ATOMIC_QUERY_EXECUTION_CACHE_STORE diff --git a/src/lib/tentris/store/AtomicTripleStore.hpp b/src/lib/tentris/store/AtomicTripleStore.hpp index f7d107e4..2d4b69ca 100644 --- a/src/lib/tentris/store/AtomicTripleStore.hpp +++ b/src/lib/tentris/store/AtomicTripleStore.hpp @@ -6,16 +6,11 @@ #include "tentris/store/TripleStore.hpp" #include "tentris/store/config/AtomicTripleStoreConfig.cpp" -namespace { - using namespace tentris::store; - using namespace tentris::util::sync; - using namespace tentris::store::config; -} namespace tentris::util::sync { template<> - inline TripleStore *SingletonFactory::make_instance() { - return new TripleStore{}; + inline ::tentris::store::TripleStore *SingletonFactory<::tentris::store::TripleStore>::make_instance() { + return new ::tentris::store::TripleStore{}; } }; @@ -24,8 +19,6 @@ namespace tentris::store { /** * A SingletonFactory that allows to share a single TripleStore instance between multiple threads. */ - - - using AtomicTripleStore = SingletonFactory; + using AtomicTripleStore = ::tentris::util::sync::SingletonFactory; }; #endif //TENTRIS_ATOMIC_TRIPLE_STORE diff --git a/src/lib/tentris/store/JsonQueryResult.hpp b/src/lib/tentris/store/JsonQueryResult.hpp deleted file mode 100644 index f1a7a158..00000000 --- a/src/lib/tentris/store/JsonQueryResult.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef TENTRIS_JSONQUERYRESULT_HPP -#define TENTRIS_JSONQUERYRESULT_HPP - -#include -#include - -#include "tentris/store/RDF/TermStore.hpp" -#include "tentris/store/SPARQL/Variable.hpp" -#include "tentris/util/LogHelper.hpp" -#include "tentris/util/HTTPUtils.hpp" -#define RAPIDJSON_HAS_STDSTRING 1 -#include -#include -#include -#include -#include - -#include "tentris/tensor/BoolHypertrie.hpp" - -namespace tentris::store { - namespace { - using namespace tentris::tensor; - using namespace ::tentris::logging; - using namespace tentris::http; - } - - template - class JsonQueryResult { - using Term = rdf_parser::store::rdf::Term; - using Variable = sparql::Variable; - using Entry = EinsumEntry; - using Key = typename Entry::key_type; - using Value = typename Entry::value_type; - - std::size_t result_count = 0; - - std::vector variables{}; - - rapidjson::Document json_doc; - rapidjson::Value * bindings; - - public: - explicit JsonQueryResult(std::vector variables) : variables(std::move(variables)) { - json_doc.SetObject(); - rapidjson::Value &vars = rapidjson::Pointer("/head/vars").Create(json_doc); - vars.SetArray(); - for (const auto &var : this->variables) - vars.PushBack(rapidjson::StringRef(var.name), json_doc.GetAllocator()); - rapidjson::Value &bindings = rapidjson::Pointer("/results/bindings").Create(json_doc); - bindings.SetArray(); - this->bindings = &bindings; - } - - void add(const Entry &entry) { - rapidjson::Document::AllocatorType& allocator = json_doc.GetAllocator(); - - rapidjson::Value entry_obj(rapidjson::kObjectType); - entry_obj.SetObject(); - for(const auto &[term, var]: iter::zip(entry.key, variables)){ - if (term == nullptr) - continue; - rapidjson::Value term_obj(rapidjson::kObjectType); - term_obj.SetObject(); - - switch (term->type()) { - case Term::NodeType::URIRef_: - term_obj.AddMember("type", "uri", allocator); - break; - case Term::NodeType::BNode_: - term_obj.AddMember("type", "bnode", allocator); - break; - case Term::NodeType::Literal_: - term_obj.AddMember("type", "literal", allocator); - break; - default: - log("Incomplete term with no type (Literal, BNode, URI) detected."); - assert(false); - } - auto value = term->value(); - term_obj.AddMember("value", rapidjson::StringRef(value.data(), value.size()), allocator); - - if (term->isLiteral()) { - const Literal & literal_term = term->castLiteral(); - if (literal_term.hasDataType()){ - auto data_type = literal_term.dataType(); - term_obj.AddMember("datatype", rapidjson::StringRef(data_type.data(), data_type.size()), allocator); - } - else if (literal_term.hasLang()){ - auto lang = literal_term.lang(); - term_obj.AddMember("datatype", rapidjson::StringRef(lang.data(), lang.size()), allocator); - } - } - entry_obj.AddMember(rapidjson::StringRef(var.name.data(), var.name.size()), term_obj, allocator); - } - - - for (auto i = 0; i < entry.value - 1; ++i) { - rapidjson::Value entry_obj_copy(entry_obj, allocator); - bindings->PushBack(entry_obj_copy,allocator); - } - bindings->PushBack(entry_obj,allocator); - result_count+=entry.value; - } - - [[nodiscard]] std::string string() const { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - json_doc.Accept(writer); - return std::string(buffer.GetString(), buffer.GetLength()); - } - - [[nodiscard]] std::size_t size() const { - return result_count; - } - }; -} - -#endif //TENTRIS_JSONQUERYRESULT_HPP diff --git a/src/lib/tentris/store/QueryExecutionPackage.hpp b/src/lib/tentris/store/QueryExecutionPackage.hpp index 86d90540..cfbc2e93 100644 --- a/src/lib/tentris/store/QueryExecutionPackage.hpp +++ b/src/lib/tentris/store/QueryExecutionPackage.hpp @@ -16,16 +16,18 @@ namespace tentris::store { namespace tentris::store::cache { - namespace { - using namespace tentris::store::sparql; - using namespace tentris::tensor; - }; // namespace - /** * A QueryExecutionPackage contains everything that is necessary to execute a given sparql query for a state of the * RDF graph. */ struct QueryExecutionPackage { + using const_BoolHypertrie = ::tentris::tensor::const_BoolHypertrie; + using time_point_t = logging::time_point_t; + using SelectModifier = sparql::SelectModifier; + using Variable = Dice::sparql::Variable; + using ParsedSPARQL = sparql::ParsedSPARQL; + using Subscript = ::tentris::tensor::Subscript; + private: std::string sparql_string; std::shared_ptr subscript; @@ -55,27 +57,39 @@ namespace tentris::store::cache { * @throw std::invalid_argument the sparql query was not parsable */ explicit QueryExecutionPackage(const std::string &sparql_string) : sparql_string{sparql_string} { + using namespace logging; + logDebug(fmt::format("Parsing query: {}", sparql_string)); ParsedSPARQL parsed_sparql{sparql_string}; subscript = parsed_sparql.getSubscript(); select_modifier = parsed_sparql.getSelectModifier(); + logDebug(fmt::format("Parsed subscript: {} [distinct = {}]", + subscript, + select_modifier == SelectModifier::DISTINCT)); query_variables = parsed_sparql.getQueryVariables(); auto &triple_store = AtomicTripleStore::getInstance(); - for (const auto &tp: parsed_sparql.getBgps()) { - std::variant, bool> op = triple_store.resolveTriplePattern(tp); + logDebug(fmt::format("Slicing TPs")); + for ([[maybe_unused]] const auto &[op_pos, tp]: iter::enumerate(parsed_sparql.getBgps())) { + logDebug(fmt::format("Slice key {}: ⟨{}⟩", op_pos, fmt::join(tp, ", "))); + std::variant op = triple_store.resolveTriplePattern(tp); if (std::holds_alternative(op)) { is_trivial_empty = not std::get(op); + logTrace(fmt::format("Operand {} is {}", op_pos, is_trivial_empty)); } else { - auto opt_bht = std::get>(op); - if (opt_bht) { - operands.emplace_back(*opt_bht); + auto bht = std::get(op); + if (not bht.empty()) { + logTrace(fmt::format("Operand {} size {}", op_pos, bht.size())); + operands.emplace_back(bht); } else { is_trivial_empty = true; operands.clear(); } } - if (is_trivial_empty) break; + if (is_trivial_empty) { + logDebug(fmt::format("Query is trivially empty, i.e. the lastly sliced operand {} is emtpy.", op_pos)); + break; + } } } @@ -94,11 +108,13 @@ namespace tentris::store::cache { static std::shared_ptr generateEinsum(const std::shared_ptr &subscript, const std::vector &hypertries, const time_point_t &timeout) { + using namespace tensor; return std::make_shared>(subscript, hypertries, timeout); } public: std::shared_ptr getEinsum(const time_point_t &timeout = time_point_t::max()) const { + using namespace tensor; if (select_modifier == SelectModifier::NONE) return generateEinsum(subscript, operands, timeout); else @@ -132,6 +148,7 @@ struct fmt::formatter { template auto format(const tentris::store::cache::QueryExecutionPackage &p, FormatContext &ctx) { + using SelectModifier = tentris::store::sparql::SelectModifier; return format_to(ctx.begin(), " SPARQL: {}\n" " subscript: {}\n" diff --git a/src/lib/tentris/store/RDF/SerdParser.hpp b/src/lib/tentris/store/RDF/SerdParser.hpp index 2d55eb70..d84c09e1 100644 --- a/src/lib/tentris/store/RDF/SerdParser.hpp +++ b/src/lib/tentris/store/RDF/SerdParser.hpp @@ -3,8 +3,9 @@ #include #include -#include -#include +#include +#include +#include #include #include #include @@ -12,17 +13,22 @@ #include #include #include +#include namespace tentris::store::rdf { class BulkLoad { - using Triple = rdf_parser::store::rdf::Triple; - using prefixes_map_type = tsl::hopscotch_map>; + using Triple = Dice::rdf::Triple; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; + using prefixes_map_type = tsl::hopscotch_map>; prefixes_map_type prefixes{}; public: boost::lockfree::spsc_queue result_queue{100000}; - bool parsing_done; + bool parsing_done = false; public: static std::shared_ptr parse(const std::string &file_path) { @@ -35,7 +41,7 @@ namespace tentris::store::rdf { nullptr); std::thread t([=]() { - SerdStatus status = serd_reader_read_file(sr, (uint8_t *) (file_path.data())); + [[maybe_unused]] SerdStatus status = serd_reader_read_file(sr, (uint8_t *) (file_path.data())); bulk_load->parsing_done = true; serd_reader_free(sr); }); @@ -48,11 +54,11 @@ namespace tentris::store::rdf { private: - auto getBNode(const SerdNode *node) const -> Term { + static auto getBNode(const SerdNode *node) -> Term { return BNode(std::string(std::string_view{(char *) (node->buf), size_t(node->n_bytes)})); } - auto getURI(const SerdNode *node) const -> Term { + static auto getURI(const SerdNode *node) -> Term { return URIRef(std::string(std::string_view{(char *) (node->buf), size_t(node->n_bytes)})); } @@ -67,7 +73,7 @@ namespace tentris::store::rdf { return URIRef(fmt::format("{}{}", prefixes.find(prefix_and_suffix[0])->second, prefix_and_suffix[1])); } - auto getLiteral(const SerdNode *literal, const SerdNode *type_node, const SerdNode *lang_node) const -> Term { + static auto getLiteral(const SerdNode *literal, const SerdNode *type_node, const SerdNode *lang_node) -> Term { std::string literal_value = std::string{(char *) (literal->buf), size_t(literal->n_bytes)}; if (type_node != nullptr) return Literal(literal_value, std::nullopt, @@ -107,10 +113,10 @@ namespace tentris::store::rdf { subject_term = bulk_load.getPrefixedUri(subject); break; case SERD_URI: - subject_term = bulk_load.getURI(subject); + subject_term = getURI(subject); break; case SERD_BLANK: { - subject_term = bulk_load.getBNode(subject); + subject_term = getBNode(subject); } break; default: @@ -122,7 +128,7 @@ namespace tentris::store::rdf { predicate_term = bulk_load.getPrefixedUri(predicate); break; case SERD_URI: - predicate_term = bulk_load.getURI(predicate); + predicate_term = getURI(predicate); break; default: return SERD_ERR_BAD_SYNTAX; @@ -133,13 +139,13 @@ namespace tentris::store::rdf { object_term = bulk_load.getPrefixedUri(object); break; case SERD_LITERAL: - object_term = bulk_load.getLiteral(object, object_datatype, object_lang); + object_term = getLiteral(object, object_datatype, object_lang); break; case SERD_BLANK: - object_term = bulk_load.getBNode(object); + object_term = getBNode(object); break; case SERD_URI: - object_term = bulk_load.getURI(object); + object_term = getURI(object); break; default: return SERD_ERR_BAD_SYNTAX; @@ -155,12 +161,16 @@ namespace tentris::store::rdf { }; class SerdParser { + using Triple = Dice::rdf::Triple; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; - private: std::string file_name_; public: - SerdParser(const std::string &file_name) : file_name_(file_name) {} + explicit SerdParser(std::string file_name) : file_name_(std::move(file_name)) {} public: @@ -195,7 +205,7 @@ namespace tentris::store::rdf { void operator++(int) { operator++(); } - operator bool() { return not done_; } + operator bool() const { return not done_; } const Triple &operator*() { return result; } }; diff --git a/src/lib/tentris/store/RDF/TermStore.hpp b/src/lib/tentris/store/RDF/TermStore.hpp index 9388c5bf..5d6f6519 100644 --- a/src/lib/tentris/store/RDF/TermStore.hpp +++ b/src/lib/tentris/store/RDF/TermStore.hpp @@ -5,17 +5,39 @@ #include #include -#include "tentris/util/All.hpp" -#include +#include + +#include namespace tentris::store::rdf { + /** + * A hash for Terms that returns for Term* the hash hash(Term) instead of hash(Term*). + */ + struct TermHash { + private: + using Term = Dice::rdf::Term; + public: + size_t operator()(Term const &term) const { + return ::Dice::hash::dice_hash(term); + } + + size_t operator()(std::unique_ptr const &term_ptr) const { + return ::Dice::hash::dice_hash(*term_ptr); + } + + size_t operator()(Term const *const term_ptr) const { + return ::Dice::hash::dice_hash(*term_ptr); + } + }; + + class TermStore { - using Term = rdf_parser::store::rdf::Term; - using BNode = rdf_parser::store::rdf::BNode; - using Literal = rdf_parser::store::rdf::Literal; - using URIRef = rdf_parser::store::rdf::URIRef; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; public: using set_type = tsl::sparse_set, TermHash, @@ -31,8 +53,8 @@ namespace tentris::store::rdf { public: using ptr_type = Term const *; - bool contains(const Term &term) const { - auto term_hash = std::hash()(term); + [[nodiscard]] bool contains(const Term &term) const { + auto term_hash = TermHash()(term); return contains(term, term_hash); } @@ -41,14 +63,14 @@ namespace tentris::store::rdf { return found != terms.end(); } - [[nodiscard]] bool valid(ptr_type term) const { - auto term_hash = std::hash()(term); - auto found = terms.find(term, term_hash); + [[nodiscard]] bool valid(ptr_type term_ptr) const { + auto term_hash = TermHash()(term_ptr); + auto found = terms.find(*term_ptr, term_hash); return found != terms.end(); } [[nodiscard]] ptr_type get(const Term &term) const { - auto term_hash = std::hash()(term); + auto term_hash = TermHash()(term); return get(term, term_hash); } @@ -70,12 +92,12 @@ namespace tentris::store::rdf { } [[nodiscard]] ptr_type find(const Term &term) const { - auto term_hash = std::hash()(term); + auto term_hash = TermHash()(term); return find(term, term_hash); } ptr_type operator[](const Term &term) { - auto term_hash = std::hash()(term); + auto term_hash = TermHash()(term); auto found = terms.find(term, term_hash); if (found != terms.end()) return (*found).get(); @@ -88,7 +110,7 @@ namespace tentris::store::rdf { friend class fmt::formatter; - std::size_t size() const { + [[nodiscard]] std::size_t size() const { return terms.size(); } diff --git a/src/lib/tentris/store/SPARQL/ParsedSPARQL.hpp b/src/lib/tentris/store/SPARQL/ParsedSPARQL.hpp index f137369c..01be32d7 100644 --- a/src/lib/tentris/store/SPARQL/ParsedSPARQL.hpp +++ b/src/lib/tentris/store/SPARQL/ParsedSPARQL.hpp @@ -15,22 +15,25 @@ #include #include +#include + #include #include #include -#include "tentris/util/All.hpp" -#include -#include "tentris/store/SPARQL/Variable.hpp" -#include "tentris/store/SPARQL/TriplePattern.hpp" +#include +#include +#include +#include namespace tentris::store::sparql { namespace { using Subscript = einsum::internal::Subscript; - using SparqlParser =Dice::tentris::sparql::parser::SparqlParser; + namespace parser = Dice::sparql_parser::base; + using SparqlParser = parser::SparqlParser; using namespace fmt::literals; } @@ -42,10 +45,7 @@ namespace tentris::store::sparql { }; class LexerErrorListener : public antlr4::BaseErrorListener { - using Term = rdf_parser::store::rdf::Term; - using BNode = rdf_parser::store::rdf::BNode; - using Literal = rdf_parser::store::rdf::Literal; - using URIRef = rdf_parser::store::rdf::URIRef; + using Variable = Dice::sparql::Variable; public: LexerErrorListener() = default; @@ -71,7 +71,15 @@ namespace tentris::store::sparql { class ParsedSPARQL { - using SparqlLexer = Dice::tentris::sparql::parser::SparqlLexer; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; + using TriplePattern = Dice::sparql::TriplePattern; + using VarOrTerm = Dice::sparql::VarOrTerm; + using Variable = Dice::sparql::Variable; + + using SparqlLexer = parser::SparqlLexer; using ANTLRInputStream =antlr4::ANTLRInputStream; using CommonTokenStream = antlr4::CommonTokenStream; using QueryContext = SparqlParser::QueryContext; @@ -79,11 +87,11 @@ namespace tentris::store::sparql { SelectModifier select_modifier = NONE; - std::map prefixes{}; + robin_hood::unordered_map prefixes{}; std::vector query_variables{}; - std::set variables{}; - std::set anonym_variables{}; - std::set bgps; + robin_hood::unordered_set variables{}; + robin_hood::unordered_set anonym_variables{}; + std::vector bgps; uint next_anon_var_id = 0; std::shared_ptr subscript; @@ -94,6 +102,8 @@ namespace tentris::store::sparql { explicit ParsedSPARQL(std::string sparqlstr) : sparql_str{std::move(sparqlstr)} { + namespace ranges = std::ranges; + std::istringstream str_stream{sparql_str}; ANTLRInputStream input{str_stream}; SparqlLexer lexer{&input}; @@ -148,8 +158,8 @@ namespace tentris::store::sparql { for (auto &obj_node : obj_nodes->object()) { VarOrTerm obj = parseObject(obj_node); registerVariable(obj); - - bgps.insert(TriplePattern{subj, pred, obj}); + if(ranges::find(bgps, TriplePattern{subj, pred, obj}) == bgps.end()) + bgps.push_back(TriplePattern{subj, pred, obj}); } } if (auto *next_block = block->triplesBlock(); next_block) @@ -159,16 +169,14 @@ namespace tentris::store::sparql { variables.insert(variable); if (all_vars) for (const auto &variable : variables) - query_variables.push_back(variable); - - if (query_variables.empty()) - throw std::invalid_argument{"Empty query variables is not allowed."}; + if (not anonym_variables.contains(variable)) + query_variables.push_back(variable); using Label = Subscript::Label; // generate subscript - std::map var_to_label{}; + robin_hood::unordered_map var_to_label{}; Label next_label = 'a'; for (const auto &var : variables) { var_to_label[var] = next_label++; @@ -195,31 +203,31 @@ namespace tentris::store::sparql { } } - SelectModifier getSelectModifier() const { + [[nodiscard]] SelectModifier getSelectModifier() const { return select_modifier; } - const std::vector &getQueryVariables() const { + [[nodiscard]] const std::vector &getQueryVariables() const { return query_variables; } - const std::set &getVariables() const { + [[nodiscard]] const robin_hood::unordered_set &getVariables() const { return variables; } - const std::set &getAnonymVariables() const { + [[nodiscard]] const robin_hood::unordered_set &getAnonymVariables() const { return anonym_variables; } - const std::string &getSparqlStr() const { + [[nodiscard]] const std::string &getSparqlStr() const { return sparql_str; } - const std::shared_ptr &getSubscript() const { + [[nodiscard]] const std::shared_ptr &getSubscript() const { return subscript; } - const std::set &getBgps() const { + [[nodiscard]] const std::vector &getBgps() const { return bgps; } @@ -228,9 +236,8 @@ namespace tentris::store::sparql { void registerVariable(VarOrTerm &variant) { if (std::holds_alternative(variant)) { auto &var = std::get(variant); - if (not var.is_anonym) - variables.insert(var); - else + variables.insert(var); + if (var.isAnon()) anonym_variables.insert(var); } } @@ -302,11 +309,11 @@ namespace tentris::store::sparql { return Literal{termContext->getText(), std::nullopt, "http://www.w3.org/2001/XMLSchema#boolean"}; } else if (SparqlParser::BlankNodeContext *blankNode = termContext->blankNode();blankNode) { if (blankNode->BLANK_NODE_LABEL()) - return Variable{termContext->getText()}; + return Variable{termContext->getText(), true}; else - return Variable{"__:" + std::to_string(next_anon_var_id++)}; + return Variable{"__:" + std::to_string(next_anon_var_id++), true}; } else if (not termContext->NIL()) { - return Literal::make_term(termContext->getText()); + return Dice::rdf::parse_term(termContext->getText()); } else { throw std::logic_error{"Handling NIL not yet implemented."}; // TODO: handle NIL value "( )" @@ -413,7 +420,13 @@ struct fmt::formatter { " variables: {}\n" " anonym_variables: {}\n" " bgps: {}\n", - p.prefixes, p.select_modifier, p.query_variables, p.variables, p.anonym_variables, p.bgps); + fmt::join(p.prefixes, ","), + p.select_modifier, + fmt::join(p.query_variables, ","), + fmt::join(p.variables, ","), + fmt::join(p.anonym_variables, ","), + fmt::join(p.bgps, ",") + ); } }; diff --git a/src/lib/tentris/store/SPARQL/TriplePattern.hpp b/src/lib/tentris/store/SPARQL/TriplePattern.hpp deleted file mode 100644 index a78b69aa..00000000 --- a/src/lib/tentris/store/SPARQL/TriplePattern.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TENTRIS_TRIPLEPATTERN_HPP -#define TENTRIS_TRIPLEPATTERN_HPP - -#include -#include -#include -#include "tentris/store/SPARQL/Variable.hpp" - -namespace tentris::store::sparql { - using VarOrTerm = std::variant; - using TriplePattern = std::array; -} - -#endif //TENTRIS_TRIPLEPATTERN_HPP diff --git a/src/lib/tentris/store/SPARQL/Variable.hpp b/src/lib/tentris/store/SPARQL/Variable.hpp deleted file mode 100644 index c55f6694..00000000 --- a/src/lib/tentris/store/SPARQL/Variable.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef TENTRIS_VARIABLE_HPP -#define TENTRIS_VARIABLE_HPP - -#include -#include -#include - -namespace tentris::store::sparql { - - class Variable { - public: - mutable std::string name; - mutable bool is_anonym; - - explicit Variable(std::string var_name, bool anonym = false) : name{std::move(var_name)}, is_anonym{anonym} {} - - inline bool operator==(const Variable &rhs) const { - return name == rhs.name; - } - - inline bool operator!=(const Variable &rhs) const { - return name != rhs.name; - } - - inline bool operator<(const Variable &rhs) const { - return name < rhs.name; - } - - inline bool operator>(const Variable &rhs) const { - return name > rhs.name; - } - - }; - -} - -template<> -struct fmt::formatter { - template - constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } - - template - auto format(const tentris::store::sparql::Variable &p, FormatContext &ctx) { - return format_to(ctx.out(), "{}", p.name); - } -}; - -#endif //TENTRIS_VARIABLE_HPP diff --git a/src/lib/tentris/store/SparqlJsonResultSAXWriter.hpp b/src/lib/tentris/store/SparqlJsonResultSAXWriter.hpp new file mode 100644 index 00000000..47411b97 --- /dev/null +++ b/src/lib/tentris/store/SparqlJsonResultSAXWriter.hpp @@ -0,0 +1,143 @@ +#ifndef TENTRIS_SPARQLJSONRESULTSAXWRITER_HPP +#define TENTRIS_SPARQLJSONRESULTSAXWRITER_HPP + +#include +#include + +#include + +#include "tentris/store/RDF/TermStore.hpp" +#include "tentris/util/LogHelper.hpp" + +#define RAPIDJSON_HAS_STDSTRING 1 + +#include +#include +#include +#include +#include + +#include "tentris/tensor/BoolHypertrie.hpp" + +namespace tentris::store { + + template + class SparqlJsonResultSAXWriter { + using Term = Dice::rdf::Term; + using Literal = Dice::rdf::Literal; + using Variable = Dice::sparql::Variable; + using Entry = ::tentris::tensor::EinsumEntry; + using Key = typename Entry::Key; + using Value = typename Entry::value_type; + + std::size_t result_count = 0; + std::size_t term_count_ = 0; + + std::vector variables{}; + + size_t buffer_size; + rapidjson::StringBuffer buffer; + rapidjson::Writer writer; + + + public: + explicit SparqlJsonResultSAXWriter(std::vector variables, size_t buffer_size) + : variables(std::move(variables)), + buffer_size(buffer_size), + buffer(nullptr, size_t(buffer_size * 1.3)), + writer(buffer) { + writer.StartObject(); + writer.Key("head"); + { + writer.StartObject(); + writer.Key("vars"); + { + writer.StartArray(); + for (const auto &var : this->variables) + writer.String(var.getName()); + writer.EndArray(); + } + writer.EndObject(); + } + writer.Key("results"); + writer.StartObject(); + writer.Key("bindings"); + writer.StartArray(); + } + + void close() { + writer.EndArray(); + writer.EndObject(); + writer.EndObject(); + } + + void add(const Entry &entry) { + + for (long i = 0; i < long(entry.value); ++i) { + writer.StartObject(); + for (const auto &[term, var]: iter::zip(entry.key, variables)) { + if (term == nullptr) + continue; + writer.Key(var.getName()); + writer.StartObject(); + writer.Key("type"); + switch (term->type()) { + case Term::NodeType::URIRef_: + writer.String("uri"); + break; + case Term::NodeType::BNode_: + writer.String("bnode"); + break; + case Term::NodeType::Literal_: + writer.String("literal"); + break; + default: + logging::log("Incomplete term with no type (Literal, BNode, URI) detected."); + assert(false); + } + writer.Key("value"); + + auto value = term->value(); + writer.String(value.data(), value.size()); + + if (term->isLiteral()) { + const Literal &literal_term = term->castLiteral(); + if (literal_term.hasDataType()) { + auto data_type = literal_term.dataType(); + writer.Key("datatype"); + writer.String(data_type.data(), data_type.size()); + } else if (literal_term.hasLang()) { + auto lang = literal_term.lang(); + writer.Key("xml:lang"); + writer.String(lang.data(), lang.size()); + } + } + writer.EndObject(); + term_count_++; + } + writer.EndObject(); + } + + result_count += entry.value; + } + + [[nodiscard]] std::size_t size() const { + return buffer.GetSize(); + } + + [[nodiscard]] bool full() const { + return buffer.GetSize() > this->buffer_size; + }; + + std::string_view string_view() { + writer.Flush(); + return std::string_view(buffer.GetString(), buffer.GetSize()); + } + + void clear() { + this->buffer.Clear(); + } + }; +} + +#endif //TENTRIS_SPARQLJSONRESULTSAXWRITER_HPP diff --git a/src/lib/tentris/store/TripleStore.hpp b/src/lib/tentris/store/TripleStore.hpp index 0b0e7e2f..10410617 100644 --- a/src/lib/tentris/store/TripleStore.hpp +++ b/src/lib/tentris/store/TripleStore.hpp @@ -8,24 +8,30 @@ #include +#include +#include +#include + #include "tentris/store/RDF/TermStore.hpp" #include "tentris/store/RDF/SerdParser.hpp" #include "tentris/store/SPARQL/ParsedSPARQL.hpp" #include "tentris/util/LogHelper.hpp" #include "tentris/tensor/BoolHypertrie.hpp" -#include "tentris/store/SPARQL/TriplePattern.hpp" -#include - -namespace { - using namespace tentris::store::sparql; - using namespace ::tentris::logging; - using namespace tentris::tensor; -} namespace tentris::store { class TripleStore { + using BoolHypertrie = ::tentris::tensor::BoolHypertrie; + using const_BoolHypertrie = ::tentris::tensor::const_BoolHypertrie; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; + using Triple = Dice::rdf::Triple; + using TriplePattern = Dice::sparql::TriplePattern; + using Variable = Dice::sparql::Variable; + using TermStore = tentris::store::rdf::TermStore; TermStore termIndex{}; @@ -45,34 +51,49 @@ namespace tentris::store { return trie; } - void loadRDF(const std::string &file_path) { - using namespace rdf_parser::Turtle; + void bulkloadRDF(const std::string &file_path, size_t bulk_size = 1'000'000) { + using namespace ::fmt::literals; + using namespace ::tentris::tensor; + using namespace ::tentris::logging; try { + hypertrie::BulkInserter bulk_inserter{trie, 0}; // TurtleParser parser{file_path}; - unsigned int count = 0; - unsigned int _1mios = 0; + unsigned long count = 0; for (const Triple &triple : rdf::SerdParser{file_path}) { - add(triple.subject(), triple.predicate(), triple.object()); + if (not triple.subject().isLiteral() and triple.predicate().isURIRef()) { + auto subject_id = termIndex[triple.subject()]; + auto predicate_id = termIndex[triple.predicate()]; + auto object_id = termIndex[triple.object()]; + bulk_inserter.add({subject_id, predicate_id, object_id}); + } else + throw std::invalid_argument{ + "Subject or predicate of the triple have a term type that is not allowed there."}; ++count; - if (count == 1000000) { - count = 0; - ++_1mios; - logDebug("{:d} mio triples loaded."_format(_1mios)); + + if (trie.size() % bulk_size == 0) { + bulk_inserter.flush(); + logDebug("{:>10.3} mio triples processed."_format(double(count)/1'000'000)); + logDebug("{:>10.3} mio triples loaded."_format(double(trie.size())/1'000'000)); } } + bulk_inserter.flush(true); + log("{:>10.3} mio triples processed."_format(double(count)/1'000'000)); + log("{:>10.3} mio triples loaded."_format(double(trie.size())/1'000'000)); } catch (...) { throw std::invalid_argument{"A parsing error occurred while parsing {}"_format(file_path)}; } } void add(const std::tuple &triple) { - add(Term::make_term(std::get<0>(triple)), - Term::make_term(std::get<1>(triple)), - Term::make_term(std::get<2>(triple))); + add(Dice::rdf::parse_term(std::get<0>(triple)), + Dice::rdf::parse_term(std::get<1>(triple)), + Dice::rdf::parse_term(std::get<2>(triple))); } - std::variant, bool> resolveTriplePattern(TriplePattern tp) { + std::variant resolveTriplePattern(TriplePattern tp) { + using namespace ::tentris::tensor; + auto slice_count = 0; for (const auto &entry: tp) if (std::holds_alternative(entry)) @@ -87,9 +108,9 @@ namespace tentris::store { } catch ([[maybe_unused]] std::out_of_range &exc) { // a keypart was not in the index so the result is zero anyways. return (slice_count > 0) - ? std::variant, bool>{ - std::optional()} - : std::variant, bool>{false}; + ? std::variant{ + const_BoolHypertrie()} + : std::variant{false}; } } return trie[slice_key]; @@ -108,9 +129,10 @@ namespace tentris::store { } bool contains(std::tuple triple) { - auto subject = termIndex.find(Term::make_term(std::get<0>(triple))); - auto predicate = termIndex.find(Term::make_term(std::get<1>(triple))); - auto object = termIndex.find(Term::make_term(std::get<2>(triple))); + using namespace ::tentris::tensor; + auto subject = termIndex.find(Dice::rdf::parse_term(std::get<0>(triple))); + auto predicate = termIndex.find(Dice::rdf::parse_term(std::get<1>(triple))); + auto object = termIndex.find(Dice::rdf::parse_term(std::get<2>(triple))); if (subject and predicate and object) { Key key{subject, predicate, object}; return trie[key]; @@ -118,7 +140,7 @@ namespace tentris::store { return false; } - size_t size() const { + [[nodiscard]] size_t size() const { return trie.size(); } diff --git a/src/lib/tentris/store/config/AtomicTripleStoreConfig.cpp b/src/lib/tentris/store/config/AtomicTripleStoreConfig.cpp index 1ef4edad..01eedc20 100644 --- a/src/lib/tentris/store/config/AtomicTripleStoreConfig.cpp +++ b/src/lib/tentris/store/config/AtomicTripleStoreConfig.cpp @@ -35,6 +35,10 @@ namespace tentris::store::config { * Max number queries that may be cached. */ size_t cache_size = 500; + /** + * number of concurrently available threads. + */ + size_t threads = 1; }; diff --git a/src/lib/tentris/tensor/BoolHypertrie.hpp b/src/lib/tentris/tensor/BoolHypertrie.hpp index fba5530b..5ce994fa 100644 --- a/src/lib/tentris/tensor/BoolHypertrie.hpp +++ b/src/lib/tentris/tensor/BoolHypertrie.hpp @@ -1,26 +1,30 @@ #ifndef TENTRIS_BOOLHYPERTRIE_HPP #define TENTRIS_BOOLHYPERTRIE_HPP -#include +#include #include "tentris/store/RDF/TermStore.hpp" namespace tentris::tensor { using key_part_type = store::rdf::TermStore::ptr_type; - using ht = typename hypertrie::template boolhypertrie; + using tr = hypertrie::Hypertrie_t; - using SliceKey = ht::const_BoolHypertrie::SliceKey; - using Key = ht::const_BoolHypertrie::Key; + using SliceKey = typename tr::SliceKey; + using Key = typename tr::Key ; - using BoolHypertrie = ht::BoolHypertrie; - using const_BoolHypertrie = ht::const_BoolHypertrie; + using BoolHypertrie = hypertrie::Hypertrie; + using const_BoolHypertrie = hypertrie::const_Hypertrie; template - using Einsum = ht::Einsum; + using Einsum = hypertrie::Einsum; template - using EinsumEntry = ht::EinsumEntry; + using EinsumEntry = hypertrie::EinsumEntry; using DISTINCT_t = bool; using COUNTED_t = std::size_t; + using Subscript = hypertrie::Subscript ; } #endif //TENTRIS_BOOLHYPERTRIE_HPP diff --git a/src/lib/tentris/util/All.hpp b/src/lib/tentris/util/All.hpp deleted file mode 100644 index fd6c28ee..00000000 --- a/src/lib/tentris/util/All.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SPARSETENSOR_UTIL_ALL_HPP -#define SPARSETENSOR_UTIL_ALL_HPP - -#include "tentris/util/container/ExtendHash.hpp" -#include "tentris/util/iterables/KeysValues.hpp" -#include "tentris/util/FmtHelper.hpp" -#include - - - - -#endif //SPARSETENSOR_UTIL_ALL_HPP diff --git a/src/lib/tentris/util/ArrayHelper.hpp b/src/lib/tentris/util/ArrayHelper.hpp deleted file mode 100644 index 72dd00a9..00000000 --- a/src/lib/tentris/util/ArrayHelper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ARRAYHELPER_HPP -#define ARRAYHELPER_HPP - -#include -#include - -class ArrayHelper { -public: - template - static std::string ArrayToString(T array[], int len, const std::string &separator = ",") { - std::ostringstream oss; - for (int i = 0; i < len; ++i) - oss << (i == 0 ? "" : separator) << array[i]; - std::string ret = oss.str(); - return ret; - } -}; - -#endif \ No newline at end of file diff --git a/src/lib/tentris/util/FmtHelper.hpp b/src/lib/tentris/util/FmtHelper.hpp index e621c214..6895e2f2 100644 --- a/src/lib/tentris/util/FmtHelper.hpp +++ b/src/lib/tentris/util/FmtHelper.hpp @@ -67,18 +67,4 @@ struct fmt::formatter> { } }; -//template -//struct fmt::formatter>> { -// template -// constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } -// -// template -// auto format(const std::shared_ptr> &p, FormatContext &ctx) { -// if (p) -// return format_to(ctx.begin(), "<{}>_shd", *p); -// else -// return format_to(ctx.begin(), "<>_shd"); -// } -//}; - #endif //TENTRIS_FMTHELPER_HPP diff --git a/src/lib/tentris/util/HTTPUtils.hpp b/src/lib/tentris/util/HTTPUtils.hpp deleted file mode 100644 index 993ec25c..00000000 --- a/src/lib/tentris/util/HTTPUtils.hpp +++ /dev/null @@ -1,181 +0,0 @@ -// -// Created by usr on 03.08.18. -// - -#ifndef TENTRIS_HTTPUTILS_HPP -#define TENTRIS_HTTPUTILS_HPP - -#include -#include -#include -#include -#include -#include - -namespace tentris::http { - -/** - * Decode Query parameter values from HTTP requests. - * @param urlEncoded url encoded string - * @return decoded string - */ - auto urlDecode(const std::string &urlEncoded) -> std::string { - std::stringstream out; - size_t i = 0; - const char *data = urlEncoded.data(); - while (i < urlEncoded.size()) { - switch (data[i]) { - case '+': - out << ' '; - ++i; - break; - case '%': - assert(i < urlEncoded.size() - 2); - char c; - std::from_chars(data + i + 1, data + i + 3, c, 16); - out << c; - i += 3; - break; - default: - out << data[i]; - ++i; - } - } - return out.str(); - } - - inline char hexchar2char(const char &input) { - if (input >= '0' && input <= '9') - return input - '0'; - if (input >= 'A' && input <= 'F') - return input - 'A' + char(10); - if (input >= 'a' && input <= 'f') - return input - 'a' + char(10); - throw std::invalid_argument("Invalid input string"); - } - -// This function assumes src to be a zero terminated sanitized string with -// an even number of [0-9a-f] characters, and target to be sufficiently large - inline char two_hexchars2char(const char src[]) { - return hexchar2char(src[0]) * char(16) + hexchar2char(src[1]); - } - - std::map decodeFormParam(const std::string &x_www_form_urlencoded) { - std::map decoded_params{}; - enum state { - key, - value - }; - state current_state = key; - std::ostringstream ss; - std::string entry_key; - for (auto iter = x_www_form_urlencoded.cbegin(), _end = x_www_form_urlencoded.cend(); iter != _end; ++iter) { - const char ¤t_char = *iter; - - switch (current_char) { - case '=': { - if (current_state != key) - throw std::invalid_argument("Two '=' must always be seperated by a '&'"); - - entry_key = ss.str(); - - ss.clear(); - ss.seekp(0); - - current_state = value; - break; - } - - case '&': { - if (current_state != value) - throw std::invalid_argument("Two '&' must always be seperated by a '='"); - decoded_params.emplace(entry_key, ss.str()); - - ss.clear(); - ss.seekp(0); - - current_state = key; - break; - } - - case '+': { - ss << ' '; - break; - } - - case '%': { - char next_char; - bool first = true; - uint runs = 0; - do { - char hex_chars[2]; - if (_end == ++iter) - throw std::invalid_argument("string must have at least two chars after '%'"); - hex_chars[0] = *iter; - if (_end == ++iter) - throw std::invalid_argument("string must have at least two chars after '%'"); - hex_chars[1] = *iter; - - next_char = two_hexchars2char(hex_chars); - ss << next_char; - if (first) { - first = false; - if ((0b11110000 & next_char) == 0b11110000 and not(0b00001000 & next_char)) - runs = 3; - else if ((0b11100000 & next_char) == 0b11100000 and not(0b00010000 & next_char)) - runs = 2; - else if ((0b11000000 & next_char) == 0b11000000 and not(0b00100000 & next_char)) - runs = 1; - } - } while (runs-- > 0); - break; - } - default: - ss << current_char; - break; - } - } - decoded_params.emplace(entry_key, ss.str()); - return decoded_params; - } - - -// TODO: stream to ouptput instead of writing a stream - template - std::string escapeJsonString(const S &input) { - std::ostringstream ss; - for (const char ¤t_char : input) { - switch (current_char) { - case '\\': - ss << "\\\\"; - break; - case '"': - ss << "\\\""; - break; - case '/': - ss << "\\/"; - break; - case '\b': - ss << "\\b"; - break; - case '\f': - ss << "\\f"; - break; - case '\n': - ss << "\\n"; - break; - case '\r': - ss << "\\r"; - break; - case '\t': - ss << "\\t"; - break; - default: - ss << current_char; - break; - } - } - return ss.str(); - } -}; -#endif //TENTRIS_HTTPUTILS_HPP diff --git a/src/lib/tentris/util/LogHelper.hpp b/src/lib/tentris/util/LogHelper.hpp index 2fffaf0d..aad64d0f 100644 --- a/src/lib/tentris/util/LogHelper.hpp +++ b/src/lib/tentris/util/LogHelper.hpp @@ -76,7 +76,7 @@ namespace tentris::logging { if (not logfile and not logstdout) logging::core::get()->set_logging_enabled(false); if (logfile) - auto file_log = logging::add_file_log( + logging::add_file_log( keywords::file_name = fs::path(logfiledir).append("tentris_%N.log"), keywords::rotation_size = 5 * 512 * 1024, keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), @@ -105,13 +105,15 @@ namespace tentris::logging { inline void log_duration(std::chrono::time_point start_time, std::chrono::time_point end_time) { using namespace std::chrono; - auto duration = end_time - start_time; + const auto duration = end_time - start_time; // xx h xx min xx s - log("duration: {} h {} min {} s"_format( - (duration_cast(duration) % 24).count(), - (duration_cast(duration) % 60).count(), - (duration_cast(duration) % 60).count())); + log("duration: {} h {} min {}.{:03d} s = {} ms\n"_format( + (std::chrono::duration_cast(duration)).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 60).count(), + (std::chrono::duration_cast(duration) % 1000).count(), + std::chrono::duration_cast(duration).count())); } /** diff --git a/src/lib/tentris/util/SyncedLRUCache.hpp b/src/lib/tentris/util/SyncedLRUCache.hpp index ef02f669..9daf5c20 100644 --- a/src/lib/tentris/util/SyncedLRUCache.hpp +++ b/src/lib/tentris/util/SyncedLRUCache.hpp @@ -37,7 +37,8 @@ #include #include #include -#include + +#include namespace tentris::util::sync { @@ -52,14 +53,14 @@ namespace tentris::util::sync { template class SyncedLRUCache { public: - using map_type = tsl::hopscotch_map>::iterator, absl::Hash>; + using map_type = tsl::hopscotch_map>::iterator, Dice::hash::DiceHash>; using node_type = KeyValuePair; using list_type = std::list>; using Lock = std::mutex; using Guard = std::lock_guard; using value_ptr = std::shared_ptr; - // Dissallow copying. + // Disallow copying. SyncedLRUCache(const SyncedLRUCache &) = delete; SyncedLRUCache &operator=(const SyncedLRUCache &) = delete; @@ -103,7 +104,7 @@ namespace tentris::util::sync { [[nodiscard]] value_ptr &operator[](const Key &key) { Guard g(lock_); const auto iter = cache_.find(key); - logTrace("cache size: {} lru size: {}"_format(cache_.size(), keys_.size())); + logging::logTrace(fmt::format("cache size: {} lru size: {}", cache_.size(), keys_.size())); if (iter == cache_.end()) { auto &key_value = keys_.emplace_front(key); cache_[key] = keys_.begin(); diff --git a/src/lib/tentris/util/UndirectedGraph.hpp b/src/lib/tentris/util/UndirectedGraph.hpp deleted file mode 100644 index 711043a1..00000000 --- a/src/lib/tentris/util/UndirectedGraph.hpp +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef SPARSETENSOR_EINSUM_UTIL_UNDIRECTEDGRAPH_HPP -#define SPARSETENSOR_EINSUM_UTIL_UNDIRECTEDGRAPH_HPP - -#include -#include - -#include "tentris/util/All.hpp" - - -namespace tentris::util { - -/** - * Simple undirected Graph implementation. - * @tparam T type of the nodes. - */ - template - class UndirectedGraph { - public: - /** - * New empty UndirectedGraph. - */ - UndirectedGraph() = default; - - private: - /** - * Set of all nodes. - */ - std::set nodes{}; - /** - * Set of all directed edges. (a,a)-edged are allowed. - */ - std::map> edges{}; - - public: - /** - * Adds all edges between the given nodes (complete graph). - * @param nodes nodes to span the complete graph. - */ - void addCompleteGraph(const std::set &nodes) { - // add all combinations - - for (const T &node_a:nodes) - for (const T &node_b:nodes) - addEdge(node_a, node_b); - } - - /** - * Add a undirected edge between two nodes. - * @param node_a first node - * @param node_b second node - */ - void addEdge(T node_a, T node_b) { - // insert edge from a to b - nodes.insert(node_a); - std::set &adjacent_nodes_x = edges[node_a]; - adjacent_nodes_x.insert(node_b); - - // insert edge from b to a - nodes.insert(node_b); - std::set &adjacent_nodes_y = edges[node_b]; - adjacent_nodes_y.insert(node_a); - } - - /** - * Get all connected components e.g. all sets of nodes that are reachable form each other. - * @return set of connected components. - */ - std::vector> getConnectedComponents() const { - // this is basically a breadth first search - // see: https://en.wikipedia.org/wiki/Breadth-first_search - std::set unfinished_nodes{this->nodes}; - - std::set open_nodes{}; - - std::vector> connected_components{}; - - while (not unfinished_nodes.empty()) { - std::set connected_component{}; - - T first_node = *unfinished_nodes.begin(); - open_nodes.insert(first_node); - connected_component.insert(first_node); - unfinished_nodes.erase(first_node); - - while (not open_nodes.empty()) { - T node = *open_nodes.begin(); - open_nodes.erase(node); - const std::set &adjacent_nodes = edges.at(node); - for (T adj_node : adjacent_nodes) { - if (unfinished_nodes.count(adj_node)) { - connected_component.insert(adj_node); - open_nodes.insert(adj_node); - unfinished_nodes.erase(adj_node); - } - } - } - - connected_components.emplace_back(connected_component); - } - - return connected_components; - } - - /** - * Get all neignbors of a node. - * @param node the node. - * @return the neigbors. - */ - std::set neighbors(const T node) const { - return edges.at(node); - } - - /** - * Get all nodes. - * @return all nodes. - */ - std::set getNodes() const { - return nodes; - } - - /** - * Resets the graph to an empty graph. - */ - void clear() { - nodes.clear(); - edges.clear(); - } - - - /** - * Iterator of the nodes. - * @return Node iterator - */ - typename std::set::const_iterator cbegin() const { - return nodes.cbegin(); - }; - - /** - * End of nodes Iterator. - * @return iterator end - */ - typename std::set::const_iterator cend() const { - return nodes.cend(); - }; - }; - -} - -#endif //SPARSETENSOR_EINSUM_UTIL_UNDIRECTEDGRAPH_HPP diff --git a/src/lib/tentris/util/container/ExtendHash.hpp b/src/lib/tentris/util/container/ExtendHash.hpp deleted file mode 100644 index c0385cef..00000000 --- a/src/lib/tentris/util/container/ExtendHash.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef SPARSETENSOR_UTIL_EXTENDHASH_HPP -#define SPARSETENSOR_UTIL_EXTENDHASH_HPP - -#include -#include -#include - -namespace std { - template - struct hash> { - size_t operator()(const unordered_set &v) const { - std::hash hasher; - size_t seed = 0; - for (int i : v) { - seed ^= hasher(i); // + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } - return seed; - } - }; - -// Code from https://stackoverflow.com/a/7115547 - namespace { - - // Code from boost - // Reciprocal of the golden ratio helps spread entropy - // and handles duplicates. - // See Mike Seymour in magic-numbers-in-boosthash-combine: - // http://stackoverflow.com/questions/4948780 - - template - inline void hash_combine(std::size_t &seed, T const &v) { - seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } - - // Recursive template code derived from Matthieu M. - template::value - 1> - struct HashValueImpl { - static void apply(size_t &seed, Tuple const &tuple) { - HashValueImpl::apply(seed, tuple); - hash_combine(seed, std::get(tuple)); - } - }; - - template - struct HashValueImpl { - static void apply(size_t &seed, Tuple const &tuple) { - hash_combine(seed, std::get<0>(tuple)); - } - }; - } - - template - struct hash> { - size_t operator()(std::tuple const &tt) const { - size_t seed = 0; - HashValueImpl >::apply(seed, tt); - return seed; - } - - }; -} - -#endif //SPARSETENSOR_UTIL_EXTENDHASH_HPP diff --git a/src/lib/tentris/util/iterables/KeysValues.hpp b/src/lib/tentris/util/iterables/KeysValues.hpp deleted file mode 100644 index 373240d6..00000000 --- a/src/lib/tentris/util/iterables/KeysValues.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef SPARSETENSOR_UTIL_KEYSVALUE -#define SPARSETENSOR_UTIL_KEYSVALUE - -#include - -namespace tentris::util::iterable::keys { - template - class Keys { - Associative &associative; - public: - Keys(Associative &associative) : associative(associative) {} - - decltype(auto) begin() const { return iterator(associative.begin()); } - - decltype(auto) end() const { return iterator(associative.end()); } - - template - class iterator { - using iter_key_t = std::remove_const_t::value_type::first_type>; - Iterator iter; - public: - iterator(Iterator iter) : iter(iter) {} - - iterator &operator++() { - ++iter; - return *this; - } - - bool operator!=(const iterator &other) const { - return iter != other.iter; - } - - const iter_key_t &operator*() { - return iter->first; - } - }; - }; -}; - -namespace tentris::util::iterable::values { - template - class Values { - Associative &associative; - public: - Values(Associative &associative) : associative(associative) {} - - decltype(auto) begin() const { return iterator{associative.begin()}; } - - decltype(auto) end() const { return iterator{associative.end()}; } - - template - class iterator { - using iter_val_t = std::decay_t::value_type::second_type>; - Iterator iter; - public: - iterator(Iterator iter) : iter{iter} {} - - iterator &operator++() { - ++iter; - return *this; - } - - bool operator!=(const iterator &other) const { - return iter != other.iter; - } - - const iter_val_t &operator*() { - return iter->second; - } - }; - }; -}; - -template -decltype(auto) keys(Associative &associative) { - return tentris::util::iterable::keys::Keys(associative); -} - -template -decltype(auto) values(Associative &associative) { - return tentris::util::iterable::values::Values(associative); -} - - -#endif //SPARSETENSOR_UTIL_KEYSVALUE diff --git a/tests/BinarySearchTest.cpp b/tests/BinarySearchTest.cpp deleted file mode 100644 index 979465aa..00000000 --- a/tests/BinarySearchTest.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include "tentris/util/container/BinarySearch.hpp" -using namespace tentris::util::container; -TEST(TestBinarySearch, insertInRightOrder) { - - // Data - std::vector keys{3, 8, 7, 2, 5, 9, 6}; - - // Load data - std::vector sorted{}; - for (size_t i = 0; i < keys.size(); ++i) { - size_t pos = insert_pos(sorted, keys[i]); - sorted.insert(sorted.begin() + pos, keys[i]); - } - - std::vector correct_sorted{2, 3, 5, 6, 7, 8, 9}; - for (size_t i = 0; i < keys.size(); ++i) { - ASSERT_EQ(sorted[i], correct_sorted[i]); - } -} - -TEST(TestBinarySearch, findInsertPos) { - // Data - std::vector sorted{-10, -7, 0, 2, 3, 5, 6, 7, 8, 9, 15, 18, 123, 143, 199, 255}; - - // at value 5 - ASSERT_EQ(insert_pos(sorted, 2, 4), 5); - // at value 0 - ASSERT_EQ(insert_pos(sorted, 2, -7), 2); - // at value -7 - ASSERT_EQ(insert_pos(sorted, 0, -7), 1); - - // find a value in range - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 6), 6); - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 9), 9); - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 15), 10); - - // values not in range or not sequence not found - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 18), 11); - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 16), 11); - ASSERT_EQ(insert_pos(sorted, 2, 10 + 1, 14), 10); -} - -TEST(TestBinarySearch, findInRange) { - // Data - std::vector sorted{-10, -7, 0, 2, 3, 5, 6, 7, 8, 9, 15, 18, 123, 143, 199, 255}; - - // start position at 2 and 4 not in sequence - ASSERT_EQ(search(sorted, 2, 4), NOT_FOUND); - // start position at 2, so -7 before start position - ASSERT_EQ(search(sorted, 2, -7), NOT_FOUND); - // start position at 0 so -7 in search area. - ASSERT_EQ(search(sorted, 0, -7), 1); - - // find a value in range - ASSERT_EQ(search(sorted, 2, 10 + 1, 6), 6); - ASSERT_EQ(search(sorted, 2, 10 + 1, 9), 9); - ASSERT_EQ(search(sorted, 2, 10 + 1, 15), 10); - - // values not in range or not sequence not found - ASSERT_EQ(search(sorted, 2, 10 + 1, 18), NOT_FOUND); - ASSERT_EQ(search(sorted, 2, 10 + 1, 16), NOT_FOUND); - ASSERT_EQ(search(sorted, 2, 10 + 1, 14), NOT_FOUND); -} - -//todo Have more critical cases tests - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 59c1cd47..9be13d41 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,23 +1,30 @@ -cmake_minimum_required(VERSION 3.13) # since 3.10 gtest_discover_tests is included -set(CMAKE_CXX_STANDARD 17) - +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.1 + GIT_SHALLOW TRUE +) +set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest ALIAS gtest) +add_library(GTest::Main ALIAS gtest_main) include(GoogleTest) - -# add the exectutable for all tests +# add the executable for all tests add_executable(tests Tests.cpp) target_link_libraries(tests - ${CONAN_LIBS} + GTest::GTest + GTest::Main tentris ) -set_property(TARGET tests PROPERTY CXX_STANDARD 17) +set_property(TARGET tests PROPERTY CXX_STANDARD 20) # detect the tests gtest_discover_tests(tests) # copy files for testing to the binary folder -file(COPY query DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY queries DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -file(COPY dataset DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/TestRDFNode.cpp b/tests/TestRDFNode.cpp deleted file mode 100644 index b5318fa3..00000000 --- a/tests/TestRDFNode.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include - -#include -// TODO: make it a real testcase - -namespace { -} - - - diff --git a/tests/TestSPARQLParser.cpp b/tests/TestSPARQLParser.cpp index 2bc63301..f1b8dbaa 100644 --- a/tests/TestSPARQLParser.cpp +++ b/tests/TestSPARQLParser.cpp @@ -7,27 +7,28 @@ #include #include - -namespace { - namespace fs = std::filesystem; - using namespace tentris::store::sparql; - using Term = rdf_parser::store::rdf::Term; - using BNode = rdf_parser::store::rdf::BNode; - using Literal = rdf_parser::store::rdf::Literal; - using URIRef = rdf_parser::store::rdf::URIRef; -} - -std::vector load_queries(const std::string &query_file_path) { - std::vector queries{}; - std::ifstream query_file(query_file_path); - std::string query; - while (std::getline(query_file, query)) { - static std::regex white_spaces{"\\s+"}; - if (not std::regex_match(query, white_spaces)) - queries.push_back(query); - } - return queries; -} +namespace tentris::tests::sparql_parser { + namespace fs = std::filesystem; + using namespace tentris::store::sparql; + using Term = Dice::rdf::Term; + using BNode = Dice::rdf::BNode; + using Literal = Dice::rdf::Literal; + using URIRef = Dice::rdf::URIRef; + using Triple = Dice::rdf::Triple; + using TriplePattern = Dice::sparql::TriplePattern; + using Variable = Dice::sparql::Variable; + + std::vector load_queries(const std::string &query_file_path) { + std::vector queries{}; + std::ifstream query_file(query_file_path); + std::string query; + while (std::getline(query_file, query)) { + static std::regex white_spaces{"\\s+"}; + if (not std::regex_match(query, white_spaces)) + queries.push_back(query); + } + return queries; + } bool parse_queries(const std::string &file) { std::vector queries = load_queries(file); @@ -39,98 +40,121 @@ bool parse_queries(const std::string &file) { for (const auto&[line_number, query] : iter::enumerate(queries, 1)) { fmt::print(" # {:3d} {}\n", line_number, query); try { - ParsedSPARQL{query}; - fmt::print(" # OK\n"); - } catch (std::exception &ex) { - lines_with_errors.insert(line_number); - no_errors = false; - fmt::print(" # failed\n"); - fmt::print("{}\n", ex.what()); - std::cout << ex.what() << std::endl; - } - } - fmt::print(" ### failed queries: {}\n", lines_with_errors); - return no_errors; + ParsedSPARQL{query}; + fmt::print(" # OK\n"); + } catch (std::exception &ex) { + lines_with_errors.insert(line_number); + no_errors = false; + fmt::print(" # failed\n"); + fmt::print("{}\n", ex.what()); + std::cout << ex.what() << std::endl; + } + } + fmt::print(" ### failed queries: {}\n", fmt::join(lines_with_errors, ",")); + return no_errors; } TEST(TestSPARQLParser, ParseBenchmarkQueries) { - namespace fs = std::filesystem; - const std::string sp2b_file = "queries/sp2b.txt"; - const std::string dbpedia_file = "queries/DBpedia.txt"; - const std::string swdf_file = "queries/swdf.txt"; - ASSERT_TRUE(fs::is_regular_file(sp2b_file)); - ASSERT_TRUE(fs::is_regular_file(dbpedia_file)); - ASSERT_TRUE(fs::is_regular_file(swdf_file)); - - bool no_errors = true; - no_errors &= parse_queries(sp2b_file); - no_errors &= parse_queries(dbpedia_file); - no_errors &= parse_queries(swdf_file); - ASSERT_TRUE(no_errors); + namespace fs = std::filesystem; + const std::string sp2b_file = "queries/sp2b.txt"; + const std::string dbpedia_file = "queries/DBpedia.txt"; + const std::string swdf_file = "queries/swdf.txt"; + ASSERT_TRUE(fs::is_regular_file(sp2b_file)); + ASSERT_TRUE(fs::is_regular_file(dbpedia_file)); + ASSERT_TRUE(fs::is_regular_file(swdf_file)); + + bool no_errors = true; + no_errors &= parse_queries(sp2b_file); + no_errors &= parse_queries(dbpedia_file); + no_errors &= parse_queries(swdf_file); + ASSERT_TRUE(no_errors); + } + + TEST(TestSPARQLParser, ParseSingleQuery) { + + const std::string query = "PREFIX dc: PREFIX : PREFIX rdfs: PREFIX dbpedia2: PREFIX foaf: PREFIX owl: PREFIX xsd: PREFIX dbpedia: PREFIX rdf: PREFIX skos: " + " SELECT * WHERE { " + "?data1 rdf:type . " + " ?wins2 10 . " + "?data3 rdf:type . " + " }"; + using namespace tentris::store::sparql; + ParsedSPARQL q{query}; + } + + TEST(TestSPARQLParser, ParseSingleQuery2) { + + const std::string query = "" + " SELECT * WHERE { _:data ?b }"; + using namespace tentris::store::sparql; + ParsedSPARQL q{query}; + } + + TEST(TestSPARQLParser, ParseSingleQuery3) { + + const std::string query = "" + "PREFIX semw: \n" + "PREFIX owl: \n" + "PREFIX rdf: \n" + "SELECT * WHERE \n" + "{ rdf:type owl:Ontology }"; + using namespace tentris::store::sparql; + ParsedSPARQL q{query}; + } + + TEST(TestSPARQLParser, parse_a_query) { + + const std::string query = "PREFIX rdf: PREFIX bench: PREFIX dc: PREFIX dcterms: PREFIX foaf: PREFIX swrc: " + "SELECT DISTINCT ?name1 ?name2 WHERE { " + "?article1 rdf:type bench:Article . " + "?article2 rdf:type bench:Article . " + "?article1 dc:creator ?author1 . " + "?author1 foaf:name ?name1 . " + "?article2 dc:creator ?author2 . " + "?author2 foaf:name ?name2 . " + "?article1 swrc:journal ?journal . " + "?article2 swrc:journal ?journal }"; + ParsedSPARQL q{query}; + + const Term type = Dice::rdf::parse_term(""); + const Term creator = Dice::rdf::parse_term(""); + const Term journal = Dice::rdf::parse_term(""); + const Term name = Dice::rdf::parse_term(""); + + const Term article = Dice::rdf::parse_term(""); + + const Variable article1 = Variable{"article1"}; + const Variable article2 = Variable{"article2"}; + const Variable author1 = Variable{"author1"}; + const Variable author2 = Variable{"author2"}; + const Variable name1 = Variable{"name1"}; + const Variable name2 = Variable{"name2"}; + const Variable journal_var = Variable{"journal"}; + std::vector actual_bgps{ + {article1, type, article}, + {article2, type, article}, + {article1, creator, author1}, + {author1, name, name1}, + {article2, creator, author2}, + {author2, name, name2}, + {article1, journal, journal_var}, + {article2, journal, journal_var} + }; + + auto query_variables = std::vector{name1, name2}; + auto variables = robin_hood::unordered_set{article1, article2, author1, author2, name1, name2, + journal_var}; + + ASSERT_EQ(q.getBgps(), actual_bgps); + + ASSERT_EQ(q.getQueryVariables(), query_variables); + + ASSERT_EQ(q.getSelectModifier(), SelectModifier::DISTINCT); + + ASSERT_EQ(q.getVariables(), variables); + + ASSERT_EQ(q.getAnonymVariables(), robin_hood::unordered_set{}); + + } } -TEST(TestSPARQLParser, DISABLED_ParseSingleQuery) { - - const std::string query = "PREFIX dc: PREFIX : PREFIX rdfs: PREFIX dbpedia2: PREFIX foaf: PREFIX owl: PREFIX xsd: PREFIX dbpedia: PREFIX rdf: PREFIX skos: " - " SELECT * WHERE { ?data rdf:type . ?wins 10 }"; - using namespace tentris::store::sparql; - ParsedSPARQL q{query}; - fmt::print("{}", q); -} - -TEST(TestSPARQLParser, parse_a_query) { - - const std::string query = "PREFIX rdf: PREFIX bench: PREFIX dc: PREFIX dcterms: PREFIX foaf: PREFIX swrc: " - "SELECT DISTINCT ?name1 ?name2 WHERE { " - "?article1 rdf:type bench:Article . " - "?article2 rdf:type bench:Article . " - "?article1 dc:creator ?author1 . " - "?author1 foaf:name ?name1 . " - "?article2 dc:creator ?author2 . " - "?author2 foaf:name ?name2 . " - "?article1 swrc:journal ?journal . " - "?article2 swrc:journal ?journal }"; - ParsedSPARQL q{query}; - - const Term type = Term::make_term(""); - const Term creator = Term::make_term(""); - const Term journal = Term::make_term(""); - const Term name = Term::make_term(""); - - const Term article = Term::make_term(""); - - const Variable article1 = Variable{"article1"}; - const Variable article2 = Variable{"article2"}; - const Variable author1 = Variable{"author1"}; - const Variable author2 = Variable{"author2"}; - const Variable name1 = Variable{"name1"}; - const Variable name2 = Variable{"name2"}; - const Variable journal_var = Variable{"journal"}; - std::set actual_bgps{ - {article1, type, article}, - {article2, type, article}, - {article1, creator, author1}, - {author1, name, name1}, - {article2, creator, author2}, - {author2, name, name2}, - {article1, journal, journal_var}, - {article2, journal, journal_var} - }; - - auto query_variables = std::vector{name1, name2}; - auto variables = std::set{article1, article2, author1, author2, name1, name2, journal_var}; - - ASSERT_EQ(q.getBgps(), actual_bgps); - - ASSERT_EQ(q.getQueryVariables(), query_variables); - - ASSERT_EQ(q.getSelectModifier(), SelectModifier::DISTINCT); - - ASSERT_EQ(q.getVariables(), variables); - - ASSERT_EQ(q.getAnonymVariables(), std::set{}); - - fmt::print("{}", q); -} - - diff --git a/tests/TestTermStore.cpp b/tests/TestTermStore.cpp index e6ec09b3..e8d44aa5 100644 --- a/tests/TestTermStore.cpp +++ b/tests/TestTermStore.cpp @@ -1,23 +1,26 @@ #include + #include -namespace { - using namespace tentris::store::rdf; -} -TEST(TestTermStore, double_write) { - TermStore store{}; - auto id_ = store[Term::make_term("\"xx\"")]; - auto id2_ = store[Term::make_term("\"xx\"")]; - ASSERT_EQ(id_, id2_); - ASSERT_EQ(store.size(), 1); -} +namespace tentris::tests::term_store { + + using namespace tentris::store::rdf; + + TEST(TestTermStore, double_write) { + TermStore store{}; + auto id_ = store[Dice::rdf::parse_term("\"xx\"")]; + auto id2_ = store[Dice::rdf::parse_term("\"xx\"")]; + ASSERT_EQ(id_, id2_); + ASSERT_EQ(store.size(), 1); + } -TEST(TestTermStore, double_write_same_str) { - TermStore store{}; - auto id_ = store[Term::make_term(R"("Journal 1 (1940) "^^)")]; - auto id2_ = store[Term::make_term("\"Journal 1 (1940) \"")]; - ASSERT_EQ(id_, id2_); - ASSERT_EQ(store.size(), 1); -} + TEST(TestTermStore, double_write_same_str) { + TermStore store{}; + auto id_ = store[Dice::rdf::parse_term(R"("Journal 1 (1940) "^^)")]; + auto id2_ = store[Dice::rdf::parse_term("\"Journal 1 (1940) \"")]; + ASSERT_EQ(id_, id2_); + ASSERT_EQ(store.size(), 1); + } +} \ No newline at end of file diff --git a/tests/TestTripleStore.cpp b/tests/TestTripleStore.cpp deleted file mode 100644 index b4293460..00000000 --- a/tests/TestTripleStore.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include - -#include -#include - -namespace { - namespace fs = std::filesystem; - - using namespace tentris::store; -} - - - diff --git a/tests/Tests.cpp b/tests/Tests.cpp index 6256ef57..3ae4ecc9 100644 --- a/tests/Tests.cpp +++ b/tests/Tests.cpp @@ -1,9 +1,7 @@ #include -#include "TestRDFNode.cpp" #include "TestSPARQLParser.cpp" #include "TestTermStore.cpp" -#include "TestTripleStore.cpp" int main(int argc, char **argv) { diff --git a/tests/dataset/COPYING b/tests/dataset/COPYING deleted file mode 100644 index 0f73cd2a..00000000 --- a/tests/dataset/COPYING +++ /dev/null @@ -1,36 +0,0 @@ -=============================================================================== -| Software License Agreement (BSD License) | -| | -| Copyright (c) 2007-2008, Freiburg University Database Group | -| All rights reserved. | -| | -| Redistribution and use of this software in source and binary forms, | -| with or without modification, are permitted provided that the following | -| conditions are met: | -| | -| * Redistributions of source code must retain the above | -| copyright notice, this list of conditions and the | -| following disclaimer. | -| | -| * Redistributions in binary form must reproduce the above | -| copyright notice, this list of conditions and the | -| following disclaimer in the documentation and/or other | -| materials provided with the distribution. | -| | -| * Neither the name of the Freiburg University Database Group nor the names | -| of its contributors may be used to endorse or promote products derived | -| from this software without specific prior written permission of the | -| Freiburg University Database Group. | -| | -| 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. | -=============================================================================== diff --git a/tests/dataset/README b/tests/dataset/README deleted file mode 100644 index 5def2587..00000000 --- a/tests/dataset/README +++ /dev/null @@ -1,75 +0,0 @@ -******************************************************************* -************ THE SP^2B SPARQL PERFORMANCE BENCHMARK *************** -******************************************************************* -SP^2B is a Performance Benchmark for the SPARQL query language. -The project homepage at - - http://dbis.informatik.uni-freiburg.de/index.php?project=SP2B - -provides more information on the benchmark. -******************************************************************* - -================================= -******* TABLE OF CONTENTS ******* -================================= -= 1. Introduction = -= 2. Getting Started = -= 3. License = -= 4. Project Members = -= 5. Contact = -================================= - - -1. Introduction ---------------- -SP^2B is a Performance Benchmark for the SPARQL query language. -The benchmark comprises both a data generator for generating -arbitrarily large, DBLP-like data sets, and a set of SPARQL -benchmark queries, which cover a variety of typical SPARQL -operator constellations and RDF access patterns. - -SP^2B is a research project from Freiburg University and has -been laid out to provide a basis for comparing SPARQL optimization -approaches in a uniform benchmark scenario. Of course, it can also -be used to compare SPARQL engines against each other, detect -deficiencies in existing implementations, and to tune SPARQL -engines. - - -2. Getting Started ------------------- -Depending on the type of archive you downloaded from the SP^2B -homepage, the following subdirectories might be available. - -sp2b - | - |-- ./src contains the source code of the data generator - |-- ./bin contains precompiled binaries of the data generator - |-- ./queries contains the benchmark queries - -If you are just interested in applying the benchmark, you should -simply use the precompiled binary (we provide binaries for Linux and -Windows). You will find more detailed information on the individual -components in the README files in the respective directory. - - -3. License ----------- -The SP^2B data generator has been published under the Berkeley -License and is open source. Detailed licensing information can -be found in the attached COPYING file. - - -4. Project Members ------------------- -- Michael Schmidt (Freiburg University) -- Thomas Hornung (Freiburg University) -- Norbert Kuechlin (Freiburg University) -- Georg Lausen (Freiburg University) -- Christoph Pinkel (MTC Infomedia OHG) - - -5. Contact ----------- -Please send bug reports, questions, and general feedback to -Michael Schmidt [mschmidt@informatik.uni-freiburg.de]. diff --git a/tests/dataset/sp2b.n3 b/tests/dataset/sp2b.n3 deleted file mode 100644 index 8011ada6..00000000 --- a/tests/dataset/sp2b.n3 +++ /dev/null @@ -1,700 +0,0 @@ -@prefix dc: . -@prefix dcterms: . -@prefix rdf: . -@prefix rdfs: . -@prefix swrc: . -@prefix foaf: . -@prefix bench: . -@prefix xsd: . -@prefix person: . -bench:Journal rdfs:subClassOf foaf:Document. -bench:Proceedings rdfs:subClassOf foaf:Document. -bench:Inproceedings rdfs:subClassOf foaf:Document. -bench:Article rdfs:subClassOf foaf:Document. -bench:Www rdfs:subClassOf foaf:Document. -bench:MastersThesis rdfs:subClassOf foaf:Document. -bench:PhDThesis rdfs:subClassOf foaf:Document. -bench:Incollection rdfs:subClassOf foaf:Document. -bench:Book rdfs:subClassOf foaf:Document. - rdf:type foaf:Person. - foaf:name "Paul Erdoes"^^xsd:string. - rdf:type foaf:Document. - rdf:type bench:Journal. - swrc:number "1"^^xsd:integer. - dc:title "Journal 1 (1940)"^^xsd:string. - swrc:volume "1"^^xsd:integer. - dcterms:issued "1940"^^xsd:integer. - rdf:type bench:Article. - bench:abstract "unmuzzling measles decentralizing hogfishes gantleted richer succories dwelling scrapped prat islanded burlily thanklessly swiveled polers oinked apnea maxillary dumpers bering evasiveness toto teashop reaccepts gunneries exorcises pirog desexes summable heliocentricity excretions recelebrating dually plateauing reoccupations embossers cerebrum gloves mohairs admiralties bewigged playgoers cheques batting waspishly stilbestrol villainousness miscalling firefanged skeins equalled sandwiching bewitchment cheaters riffled kerneling napoleons rifer splinting surmisers satisfying undamped sharpers forbearer anesthetization undermentioned outflanking funnyman commuted lachrymation floweret arcadian acridities unrealistic substituting surges preheats loggias reconciliating photocatalyst lenity tautological jambing sodality outcrop slipcases phenylketonuria grunts venturers valiantly unremorsefully extradites stollens ponderers conditione loathly cancels debiting parrots paraguayans resonates"^^xsd:string. - bench:cdrom "http://www.hogfishes.tld/richer/succories.html"^^xsd:string. - rdfs:seeAlso "http://www.gantleted.tld/succories/dwelling.html"^^xsd:string. - swrc:month "4"^^xsd:integer. - swrc:note "overbites terminals giros podgy vagus kinkiest xix recollected"^^xsd:string. - swrc:pages "110"^^xsd:integer. - dc:title "richer dwelling scrapped"^^xsd:string. - foaf:homepage "http://www.succories.tld/scrapped/prat.html"^^xsd:string. - swrc:journal . -_:Adamanta_Schlitt rdf:type foaf:Person. -_:Adamanta_Schlitt foaf:name "Adamanta Schlitt"^^xsd:string. - dc:creator _:Adamanta_Schlitt. - dc:creator . - dcterms:references _:references1. -_:references1 rdf:type rdf:Bag. -_:references1 rdf:_1 . -_:references1 rdf:_2 . -_:references1 rdf:_3 . -_:references1 rdf:_4 . - rdf:type bench:Article. - bench:abstract "householder overeducated objurgate treaties preprocessor despising loftily yabber reprovingly blungers dwarflike effulgences coreless tuberculoses environs hulled preexamination oralogy tibetans slavishly hipless prs bluejays cuppier nonsurgical skimpiest outpoured dissociated heartier petitionee brill neologic intermuscular fobbed transcribed swifters redigesting ostinato recalculation safest signiory latchets inflecting trephines hops exec junketeers isolators reducing nethermost nonfiction retrogressions eliminates unknowns mongoloids danker raunchiness perspicuously disjoined nigglings midmonths labium peeped daydreams permuting immediately canzona interrelated cooked reformers goodwife technicolor plenishes nippy bounden occulters blubberer amenities desecrated tetrachlorides loutish polygony malines cliffhanger entailments reindexed bedstraws thoughtless elation swampland earings circumscribed paralyzingly pouchy surrejoinders chestiest measurage tonsils pasturage thurifer teazle"^^xsd:string. - bench:cdrom "http://www.dwelling.tld/prat/islanded.html"^^xsd:string. - rdfs:seeAlso "http://www.scrapped.tld/islanded/burlily.html"^^xsd:string. - swrc:month "8"^^xsd:integer. - swrc:note "fringier rhythmical wastebaskets powderer immigrates inserter plights corollaries"^^xsd:string. - swrc:pages "114"^^xsd:integer. - dc:title "prat burlily thanklessly"^^xsd:string. - foaf:homepage "http://www.islanded.tld/thanklessly/swiveled.html"^^xsd:string. - swrc:journal . -_:Cecil_Kochler rdf:type foaf:Person. -_:Cecil_Kochler foaf:name "Cecil Kochler"^^xsd:string. - dc:creator _:Cecil_Kochler. - dc:creator . - rdf:type bench:Article. - bench:abstract "gaudiness irades inadvisability disciplinarians majors manifestly decaffeinates scalepan folklorists attractive yeller cognizably reminds teratoid coadjutors thuggeries nondestructive maladjustments subpartnership cordilleras recirculations alkalin succulently marquise underlaid neurosurgeon innervated hunts barrens emanative blowpipe varies thickest machinability orbiters tormentor owner zanier corkscrewed promiscuousness clewed reassemble hesitation fainting croupy bacchanalia regainers teardown margarins inconvenience triunities dipped votarists kilogram timbrel presell woodcraft reupholstered xerosis steamers neurological warranter flashings oops detonations chippering photospherically pouchiest canvasses pyorrheas cartons acquirable refocus vividness administrated remedying prophetically allayed zinged fridge stained unintentional antiquarians dilutes quantitatively shovels vitric mendelism kookiest leavening embrocation casteless uroliths sashes marrieds fungic gasogenes obnoxiously dismounting endorser libations"^^xsd:string. - bench:cdrom "http://www.burlily.tld/swiveled/polers.html"^^xsd:string. - rdfs:seeAlso "http://www.thanklessly.tld/polers/oinked.html"^^xsd:string. - swrc:note "harrower claymores shiftlessly feedstuffs lyricizing hierarchs composedly taunting"^^xsd:string. - swrc:pages "117"^^xsd:integer. - dc:title "swiveled oinked apnea"^^xsd:string. - foaf:homepage "http://www.polers.tld/apnea/maxillary.html"^^xsd:string. - swrc:journal . -_:Amalia_Krajcik rdf:type foaf:Person. -_:Amalia_Krajcik foaf:name "Amalia Krajcik"^^xsd:string. - dc:creator _:Amalia_Krajcik. - dc:creator . - rdf:type bench:Article. - bench:abstract "dragged lobsters careering triplets hepatics colonies defalcate transplantations forfends voucherable intercepting jeered immunopathology addends surveiled wagers joysticks nonliving agric proliferating disintegrator oblongish leapfrogged overabundant legworks easeful cognize hoatzin toiled nonspecialized vrouw squads tantalums overweight readmits loopholing tattles irradiates befriends insinuators restorers rebroadcasting grousing overdrinking frow demarcators tasselling crocked wharfinger reconverting washboards overdrank recalculations dumps carousels acidly deponent venges shivas northers mutualist harebrained earthworms lunk forefended overtaking sourdoughs traditionless spoliator earthlier stenographers reallocating aslope seawaters ruminative patronly hydrozoon webbier foxiness toddy playlets mouthiest delegati renege briefless regularities planarity stubborner waterbeds disinclines antonyms anesthetize chanticleer administrants preengaging unitarians reevaluate rekeys ochroid climatotherapy crocks"^^xsd:string. - bench:cdrom "http://www.oinked.tld/maxillary/dumpers.html"^^xsd:string. - rdfs:seeAlso "http://www.apnea.tld/dumpers/bering.html"^^xsd:string. - swrc:note "carpetbag peonism metropolitanize twanged pedros nonforfeitable dissociative apostacy"^^xsd:string. - swrc:pages "120"^^xsd:integer. - dc:title "maxillary bering evasiveness"^^xsd:string. - foaf:homepage "http://www.dumpers.tld/evasiveness/toto.html"^^xsd:string. - swrc:journal . -_:Martina_Mcclary rdf:type foaf:Person. -_:Martina_Mcclary foaf:name "Martina Mcclary"^^xsd:string. - dc:creator _:Martina_Mcclary. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.bering.tld/toto/teashop.html"^^xsd:string. - swrc:note "malting footgear abominators trilobate jigsawed kickstands prated songstresses"^^xsd:string. - swrc:pages "122"^^xsd:integer. - dc:title "evasiveness teashop reaccepts"^^xsd:string. - foaf:homepage "http://www.toto.tld/reaccepts/gunneries.html"^^xsd:string. - swrc:journal . -_:Ashley_Kesselring rdf:type foaf:Person. -_:Ashley_Kesselring foaf:name "Ashley Kesselring"^^xsd:string. - dc:creator _:Ashley_Kesselring. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.teashop.tld/gunneries/exorcises.html"^^xsd:string. - swrc:note "incorporeal piazadora hearings legation subendorsed hippocampus miscalculates whetters"^^xsd:string. - swrc:pages "124"^^xsd:integer. - dc:title "reaccepts exorcises pirog"^^xsd:string. - foaf:homepage "http://www.gunneries.tld/pirog/desexes.html"^^xsd:string. - swrc:journal . -_:Tatsukichi_Gerstenberger rdf:type foaf:Person. -_:Tatsukichi_Gerstenberger foaf:name "Tatsukichi Gerstenberger"^^xsd:string. - dc:creator _:Tatsukichi_Gerstenberger. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.exorcises.tld/desexes/summable.html"^^xsd:string. - swrc:note "leviathans misadvised tiltyard numberable yawing prosecutrices pegboxes feeblish"^^xsd:string. - swrc:pages "126"^^xsd:integer. - dc:title "pirog summable heliocentricity"^^xsd:string. - foaf:homepage "http://www.desexes.tld/heliocentricity/excretions.html"^^xsd:string. - swrc:journal . -_:Fahroni_Anglea rdf:type foaf:Person. -_:Fahroni_Anglea foaf:name "Fahroni Anglea"^^xsd:string. - dc:creator _:Fahroni_Anglea. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.summable.tld/excretions/recelebrating.html"^^xsd:string. - swrc:note "extenuation stranders abbesses strongboxes chromas oats pulling leatheriness"^^xsd:string. - swrc:pages "128"^^xsd:integer. - dc:title "heliocentricity recelebrating dually"^^xsd:string. - foaf:homepage "http://www.excretions.tld/dually/plateauing.html"^^xsd:string. - swrc:journal . -_:Akina_Jang rdf:type foaf:Person. -_:Akina_Jang foaf:name "Akina Jang"^^xsd:string. - dc:creator _:Akina_Jang. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.recelebrating.tld/plateauing/reoccupations.html"^^xsd:string. - swrc:note "witchy horologe bigamistic furrows eloquence cobwebbier divorcing incidentally"^^xsd:string. - swrc:pages "130"^^xsd:integer. - dc:title "dually reoccupations embossers"^^xsd:string. - foaf:homepage "http://www.plateauing.tld/embossers/cerebrum.html"^^xsd:string. - swrc:journal . -_:Edmondo_Rommelfanger rdf:type foaf:Person. -_:Edmondo_Rommelfanger foaf:name "Edmondo Rommelfanger"^^xsd:string. - dc:creator _:Edmondo_Rommelfanger. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.reoccupations.tld/cerebrum/gloves.html"^^xsd:string. - swrc:note "retorts insoles stockman queening allergist doyenne placarders septuagenarians"^^xsd:string. - swrc:pages "132"^^xsd:integer. - dc:title "embossers gloves mohairs"^^xsd:string. - foaf:homepage "http://www.cerebrum.tld/mohairs/admiralties.html"^^xsd:string. - swrc:journal . -_:Pilib_Seu rdf:type foaf:Person. -_:Pilib_Seu foaf:name "Pilib Seu"^^xsd:string. - dc:creator _:Pilib_Seu. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.gloves.tld/admiralties/bewigged.html"^^xsd:string. - swrc:note "safecracker cacaos mignonette tailored whews beholden branchless primitiveness"^^xsd:string. - swrc:pages "134"^^xsd:integer. - dc:title "mohairs bewigged playgoers"^^xsd:string. - foaf:homepage "http://www.admiralties.tld/playgoers/cheques.html"^^xsd:string. - swrc:journal . -_:Dell_Kosel rdf:type foaf:Person. -_:Dell_Kosel foaf:name "Dell Kosel"^^xsd:string. - dc:creator _:Dell_Kosel. - rdf:type bench:Article. - rdfs:seeAlso "http://www.bewigged.tld/cheques/batting.html"^^xsd:string. - swrc:note "crimper tonners unfair southpaws scorify supportance jumpiest whanged"^^xsd:string. - swrc:pages "136"^^xsd:integer. - dc:title "playgoers batting waspishly"^^xsd:string. - foaf:homepage "http://www.cheques.tld/waspishly/stilbestrol.html"^^xsd:string. - swrc:journal . -_:Prebrana_Kekiwi rdf:type foaf:Person. -_:Prebrana_Kekiwi foaf:name "Prebrana Kekiwi"^^xsd:string. - dc:creator _:Prebrana_Kekiwi. - rdf:type bench:Article. - rdfs:seeAlso "http://www.batting.tld/stilbestrol/villainousness.html"^^xsd:string. - swrc:pages "137"^^xsd:integer. - dc:title "waspishly villainousness miscalling"^^xsd:string. - foaf:homepage "http://www.stilbestrol.tld/miscalling/firefanged.html"^^xsd:string. - swrc:journal . -_:Korechika_Mamer rdf:type foaf:Person. -_:Korechika_Mamer foaf:name "Korechika Mamer"^^xsd:string. - dc:creator _:Korechika_Mamer. - rdf:type bench:Article. - rdfs:seeAlso "http://www.villainousness.tld/firefanged/skeins.html"^^xsd:string. - swrc:pages "138"^^xsd:integer. - dc:title "miscalling skeins equalled"^^xsd:string. - foaf:homepage "http://www.firefanged.tld/equalled/sandwiching.html"^^xsd:string. - swrc:journal . -_:Dorel_Brandt rdf:type foaf:Person. -_:Dorel_Brandt foaf:name "Dorel Brandt"^^xsd:string. - dc:creator _:Dorel_Brandt. - rdf:type bench:Article. - rdfs:seeAlso "http://www.skeins.tld/sandwiching/bewitchment.html"^^xsd:string. - swrc:pages "139"^^xsd:integer. - dc:title "equalled bewitchment cheaters"^^xsd:string. - foaf:homepage "http://www.sandwiching.tld/cheaters/riffled.html"^^xsd:string. - swrc:journal . -_:Yayang_Kuczenski rdf:type foaf:Person. -_:Yayang_Kuczenski foaf:name "Yayang Kuczenski"^^xsd:string. - dc:creator _:Yayang_Kuczenski. - rdf:type bench:Article. - rdfs:seeAlso "http://www.bewitchment.tld/riffled/kerneling.html"^^xsd:string. - swrc:pages "140"^^xsd:integer. - dc:title "cheaters kerneling napoleons"^^xsd:string. - foaf:homepage "http://www.riffled.tld/napoleons/rifer.html"^^xsd:string. - swrc:journal . -_:Trina_Sjerven rdf:type foaf:Person. -_:Trina_Sjerven foaf:name "Trina Sjerven"^^xsd:string. - dc:creator _:Trina_Sjerven. - rdf:type bench:Article. - rdfs:seeAlso "http://www.kerneling.tld/rifer/splinting.html"^^xsd:string. - swrc:pages "141"^^xsd:integer. - dc:title "napoleons splinting surmisers"^^xsd:string. - foaf:homepage "http://www.rifer.tld/surmisers/satisfying.html"^^xsd:string. - swrc:journal . -_:Caden_Failing rdf:type foaf:Person. -_:Caden_Failing foaf:name "Caden Failing"^^xsd:string. - dc:creator _:Caden_Failing. - rdf:type bench:Article. - rdfs:seeAlso "http://www.splinting.tld/satisfying/undamped.html"^^xsd:string. - swrc:pages "142"^^xsd:integer. - dc:title "surmisers undamped sharpers"^^xsd:string. - foaf:homepage "http://www.satisfying.tld/sharpers/forbearer.html"^^xsd:string. - swrc:journal . -_:Mya_Swilley rdf:type foaf:Person. -_:Mya_Swilley foaf:name "Mya Swilley"^^xsd:string. - dc:creator _:Mya_Swilley. - rdf:type bench:Article. - rdfs:seeAlso "http://www.undamped.tld/forbearer/anesthetization.html"^^xsd:string. - swrc:pages "143"^^xsd:integer. - dc:title "sharpers anesthetization undermentioned"^^xsd:string. - foaf:homepage "http://www.forbearer.tld/undermentioned/outflanking.html"^^xsd:string. - swrc:journal . -_:Juriaan_Schremp rdf:type foaf:Person. -_:Juriaan_Schremp foaf:name "Juriaan Schremp"^^xsd:string. - dc:creator _:Juriaan_Schremp. - rdf:type bench:Article. - rdfs:seeAlso "http://www.anesthetization.tld/outflanking/funnyman.html"^^xsd:string. - swrc:pages "144"^^xsd:integer. - dc:title "undermentioned funnyman commuted"^^xsd:string. - foaf:homepage "http://www.outflanking.tld/commuted/lachrymation.html"^^xsd:string. - swrc:journal . -_:Kozue_Efthimiou rdf:type foaf:Person. -_:Kozue_Efthimiou foaf:name "Kozue Efthimiou"^^xsd:string. - dc:creator _:Kozue_Efthimiou. - swrc:editor _:Sharise_Heagy. -_:Sharise_Heagy rdf:type foaf:Person. -_:Sharise_Heagy foaf:name "Sharise Heagy"^^xsd:string. - rdf:type bench:Journal. - swrc:number "1"^^xsd:integer. - dc:title "Journal 1 (1941)"^^xsd:string. - swrc:volume "2"^^xsd:integer. - dcterms:issued "1941"^^xsd:integer. - rdf:type bench:Article. - rdfs:seeAlso "http://www.commuted.tld/floweret/arcadian.html"^^xsd:string. - swrc:pages "181"^^xsd:integer. - dc:title "lachrymation arcadian acridities"^^xsd:string. - foaf:homepage "http://www.floweret.tld/acridities/unrealistic.html"^^xsd:string. - swrc:journal . - dc:creator _:Mya_Swilley. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.arcadian.tld/unrealistic/substituting.html"^^xsd:string. - swrc:pages "100"^^xsd:integer. - dc:title "acridities substituting surges"^^xsd:string. - foaf:homepage "http://www.unrealistic.tld/surges/preheats.html"^^xsd:string. - swrc:journal . - dc:creator _:Kozue_Efthimiou. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.substituting.tld/preheats/loggias.html"^^xsd:string. - swrc:pages "182"^^xsd:integer. - dc:title "surges loggias reconciliating"^^xsd:string. - foaf:homepage "http://www.preheats.tld/reconciliating/photocatalyst.html"^^xsd:string. - swrc:journal . - dc:creator _:Pilib_Seu. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.loggias.tld/photocatalyst/lenity.html"^^xsd:string. - swrc:pages "97"^^xsd:integer. - dc:title "reconciliating lenity tautological"^^xsd:string. - foaf:homepage "http://www.photocatalyst.tld/tautological/jambing.html"^^xsd:string. - swrc:journal . - dc:creator _:Prebrana_Kekiwi. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.lenity.tld/jambing/sodality.html"^^xsd:string. - swrc:pages "171"^^xsd:integer. - dc:title "tautological sodality outcrop"^^xsd:string. - foaf:homepage "http://www.jambing.tld/outcrop/slipcases.html"^^xsd:string. - swrc:journal . - dc:creator _:Dorel_Brandt. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.sodality.tld/slipcases/phenylketonuria.html"^^xsd:string. - swrc:pages "110"^^xsd:integer. - dc:title "outcrop phenylketonuria grunts"^^xsd:string. - foaf:homepage "http://www.slipcases.tld/grunts/venturers.html"^^xsd:string. - swrc:journal . - dc:creator _:Trina_Sjerven. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.phenylketonuria.tld/venturers/valiantly.html"^^xsd:string. - swrc:pages "184"^^xsd:integer. - dc:title "grunts valiantly unremorsefully"^^xsd:string. - foaf:homepage "http://www.venturers.tld/unremorsefully/extradites.html"^^xsd:string. - swrc:journal . -_:Phaethon_Gearon rdf:type foaf:Person. -_:Phaethon_Gearon foaf:name "Phaethon Gearon"^^xsd:string. - dc:creator _:Phaethon_Gearon. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.valiantly.tld/extradites/stollens.html"^^xsd:string. - swrc:pages "111"^^xsd:integer. - dc:title "unremorsefully stollens ponderers"^^xsd:string. - foaf:homepage "http://www.extradites.tld/ponderers/conditione.html"^^xsd:string. - swrc:journal . -_:Lone_Pavese rdf:type foaf:Person. -_:Lone_Pavese foaf:name "Lone Pavese"^^xsd:string. - dc:creator _:Lone_Pavese. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.stollens.tld/conditione/loathly.html"^^xsd:string. - swrc:pages "169"^^xsd:integer. - dc:title "ponderers loathly cancels"^^xsd:string. - foaf:homepage "http://www.conditione.tld/cancels/debiting.html"^^xsd:string. - swrc:journal . -_:Motoyasu_Calligy rdf:type foaf:Person. -_:Motoyasu_Calligy foaf:name "Motoyasu Calligy"^^xsd:string. - dc:creator _:Motoyasu_Calligy. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.loathly.tld/debiting/parrots.html"^^xsd:string. - swrc:pages "112"^^xsd:integer. - dc:title "cancels parrots paraguayans"^^xsd:string. - foaf:homepage "http://www.debiting.tld/paraguayans/resonates.html"^^xsd:string. - swrc:journal . -_:Firdaus_Casavez rdf:type foaf:Person. -_:Firdaus_Casavez foaf:name "Firdaus Casavez"^^xsd:string. - dc:creator _:Firdaus_Casavez. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.parrots.tld/resonates/overbites.html"^^xsd:string. - swrc:pages "178"^^xsd:integer. - dc:title "paraguayans overbites terminals"^^xsd:string. - foaf:homepage "http://www.resonates.tld/terminals/giros.html"^^xsd:string. - swrc:journal . -_:Angha_Bievenue rdf:type foaf:Person. -_:Angha_Bievenue foaf:name "Angha Bievenue"^^xsd:string. - dc:creator _:Angha_Bievenue. - rdf:type bench:Article. - rdfs:seeAlso "http://www.overbites.tld/giros/podgy.html"^^xsd:string. - swrc:pages "101"^^xsd:integer. - dc:title "terminals podgy vagus"^^xsd:string. - foaf:homepage "http://www.giros.tld/vagus/kinkiest.html"^^xsd:string. - swrc:journal . -_:Araceli_Ertel rdf:type foaf:Person. -_:Araceli_Ertel foaf:name "Araceli Ertel"^^xsd:string. - dc:creator _:Araceli_Ertel. - rdf:type bench:Article. - rdfs:seeAlso "http://www.podgy.tld/kinkiest/xix.html"^^xsd:string. - swrc:pages "183"^^xsd:integer. - dc:title "vagus xix recollected"^^xsd:string. - foaf:homepage "http://www.kinkiest.tld/recollected/householder.html"^^xsd:string. - swrc:journal . -_:Nik_Reposa rdf:type foaf:Person. -_:Nik_Reposa foaf:name "Nik Reposa"^^xsd:string. - dc:creator _:Nik_Reposa. - rdf:type bench:Article. - rdfs:seeAlso "http://www.xix.tld/householder/overeducated.html"^^xsd:string. - swrc:pages "98"^^xsd:integer. - dc:title "recollected overeducated objurgate"^^xsd:string. - foaf:homepage "http://www.householder.tld/objurgate/treaties.html"^^xsd:string. - swrc:journal . -_:Shiko_Seagroves rdf:type foaf:Person. -_:Shiko_Seagroves foaf:name "Shiko Seagroves"^^xsd:string. - dc:creator _:Shiko_Seagroves. - rdf:type bench:Article. - rdfs:seeAlso "http://www.overeducated.tld/treaties/preprocessor.html"^^xsd:string. - swrc:pages "124"^^xsd:integer. - dc:title "objurgate preprocessor despising"^^xsd:string. - foaf:homepage "http://www.treaties.tld/despising/loftily.html"^^xsd:string. - swrc:journal . -_:Dianmu_Aver rdf:type foaf:Person. -_:Dianmu_Aver foaf:name "Dianmu Aver"^^xsd:string. - dc:creator _:Dianmu_Aver. - rdf:type bench:Article. - rdfs:seeAlso "http://www.preprocessor.tld/loftily/yabber.html"^^xsd:string. - swrc:pages "91"^^xsd:integer. - dc:title "despising yabber reprovingly"^^xsd:string. - foaf:homepage "http://www.loftily.tld/reprovingly/blungers.html"^^xsd:string. - swrc:journal . -_:Helene_Nik rdf:type foaf:Person. -_:Helene_Nik foaf:name "Helene Nik"^^xsd:string. - dc:creator _:Helene_Nik. - rdf:type bench:Article. - rdfs:seeAlso "http://www.yabber.tld/blungers/dwarflike.html"^^xsd:string. - swrc:pages "125"^^xsd:integer. - dc:title "reprovingly dwarflike effulgences"^^xsd:string. - foaf:homepage "http://www.blungers.tld/effulgences/coreless.html"^^xsd:string. - swrc:journal . -_:Yemena_Knebel rdf:type foaf:Person. -_:Yemena_Knebel foaf:name "Yemena Knebel"^^xsd:string. - dc:creator _:Yemena_Knebel. - rdf:type bench:Article. - rdfs:seeAlso "http://www.dwarflike.tld/coreless/tuberculoses.html"^^xsd:string. - swrc:pages "92"^^xsd:integer. - dc:title "effulgences tuberculoses environs"^^xsd:string. - foaf:homepage "http://www.coreless.tld/environs/hulled.html"^^xsd:string. - swrc:journal . -_:Melisa_Patriarco rdf:type foaf:Person. -_:Melisa_Patriarco foaf:name "Melisa Patriarco"^^xsd:string. - dc:creator _:Melisa_Patriarco. - rdf:type bench:Article. - rdfs:seeAlso "http://www.tuberculoses.tld/hulled/preexamination.html"^^xsd:string. - swrc:pages "126"^^xsd:integer. - dc:title "environs preexamination oralogy"^^xsd:string. - foaf:homepage "http://www.hulled.tld/oralogy/tibetans.html"^^xsd:string. - swrc:journal . -_:Dama_Leino rdf:type foaf:Person. -_:Dama_Leino foaf:name "Dama Leino"^^xsd:string. - dc:creator _:Dama_Leino. - rdf:type bench:Article. - rdfs:seeAlso "http://www.preexamination.tld/tibetans/slavishly.html"^^xsd:string. - swrc:pages "89"^^xsd:integer. - dc:title "oralogy slavishly hipless"^^xsd:string. - foaf:homepage "http://www.tibetans.tld/hipless/prs.html"^^xsd:string. - swrc:journal . -_:Satoru_Beaumont rdf:type foaf:Person. -_:Satoru_Beaumont foaf:name "Satoru Beaumont"^^xsd:string. - dc:creator _:Satoru_Beaumont. - rdf:type bench:Article. - rdfs:seeAlso "http://www.slavishly.tld/prs/bluejays.html"^^xsd:string. - swrc:pages "147"^^xsd:integer. - dc:title "hipless bluejays cuppier"^^xsd:string. - foaf:homepage "http://www.prs.tld/cuppier/nonsurgical.html"^^xsd:string. - swrc:journal . -_:Booker_Spiker rdf:type foaf:Person. -_:Booker_Spiker foaf:name "Booker Spiker"^^xsd:string. - dc:creator _:Booker_Spiker. - rdf:type bench:Article. - rdfs:seeAlso "http://www.bluejays.tld/nonsurgical/skimpiest.html"^^xsd:string. - swrc:pages "70"^^xsd:integer. - dc:title "cuppier skimpiest outpoured"^^xsd:string. - foaf:homepage "http://www.nonsurgical.tld/outpoured/dissociated.html"^^xsd:string. - swrc:journal . -_:Eupeithes_Bevens rdf:type foaf:Person. -_:Eupeithes_Bevens foaf:name "Eupeithes Bevens"^^xsd:string. - dc:creator _:Eupeithes_Bevens. - rdf:type bench:Article. - rdfs:seeAlso "http://www.skimpiest.tld/dissociated/heartier.html"^^xsd:string. - swrc:pages "144"^^xsd:integer. - dc:title "outpoured heartier petitionee"^^xsd:string. - foaf:homepage "http://www.dissociated.tld/petitionee/brill.html"^^xsd:string. - swrc:journal . -_:Miranda_Leinen rdf:type foaf:Person. -_:Miranda_Leinen foaf:name "Miranda Leinen"^^xsd:string. - dc:creator _:Miranda_Leinen. - swrc:editor _:Lane_Portes. -_:Lane_Portes rdf:type foaf:Person. -_:Lane_Portes foaf:name "Lane Portes"^^xsd:string. - rdf:type bench:Incollection. - bench:booktitle "heartier brill neologic"^^xsd:string. - rdfs:seeAlso "http://www.petitionee.tld/neologic/intermuscular.html"^^xsd:string. - swrc:pages "119"^^xsd:integer. - dc:title "brill intermuscular fobbed"^^xsd:string. - foaf:homepage "http://www.neologic.tld/fobbed/transcribed.html"^^xsd:string. - dcterms:issued "1941"^^xsd:integer. -_:Vasilista_Hamic rdf:type foaf:Person. -_:Vasilista_Hamic foaf:name "Vasilista Hamic"^^xsd:string. - dc:creator _:Vasilista_Hamic. - rdf:type bench:Journal. - swrc:number "1"^^xsd:integer. - dc:title "Journal 1 (1942)"^^xsd:string. - swrc:volume "3"^^xsd:integer. - dcterms:issued "1942"^^xsd:integer. - rdf:type bench:Article. - rdfs:seeAlso "http://www.fobbed.tld/swifters/redigesting.html"^^xsd:string. - swrc:pages "88"^^xsd:integer. - dc:title "transcribed redigesting ostinato"^^xsd:string. - foaf:homepage "http://www.swifters.tld/ostinato/recalculation.html"^^xsd:string. - swrc:journal . - dc:creator _:Yemena_Knebel. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.redigesting.tld/recalculation/safest.html"^^xsd:string. - swrc:pages "86"^^xsd:integer. - dc:title "ostinato safest signiory"^^xsd:string. - foaf:homepage "http://www.recalculation.tld/signiory/latchets.html"^^xsd:string. - swrc:journal . - dc:creator _:Melisa_Patriarco. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.safest.tld/latchets/inflecting.html"^^xsd:string. - swrc:pages "87"^^xsd:integer. - dc:title "signiory inflecting trephines"^^xsd:string. - foaf:homepage "http://www.latchets.tld/trephines/hops.html"^^xsd:string. - swrc:journal . - dc:creator _:Dama_Leino. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.inflecting.tld/hops/exec.html"^^xsd:string. - swrc:pages "87"^^xsd:integer. - dc:title "trephines exec junketeers"^^xsd:string. - foaf:homepage "http://www.hops.tld/junketeers/isolators.html"^^xsd:string. - swrc:journal . - dc:creator _:Satoru_Beaumont. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.exec.tld/isolators/reducing.html"^^xsd:string. - swrc:note "intercollegiate iniquitously lycanthropies electrophoresed dinting rezoning pledgee protoactinium"^^xsd:string. - dc:title "junketeers reducing nethermost"^^xsd:string. - foaf:homepage "http://www.isolators.tld/nethermost/nonfiction.html"^^xsd:string. - swrc:journal . - dc:creator _:Booker_Spiker. - dc:creator . - rdf:type bench:Article. - bench:abstract "darned emblements shrewed alluvials depressional airlifts tests sliming felicitator virological results contradistinctions unexciting debateable tenants earrings overrigid hidebound faire poloist comets paraphraser whangs uncovering infested heaver euphorically ameliorative aglets preciosity curring compositely antennal undrinkable charter uncashed huntedly czardoms unidentifiable reversing monstrances gravies quadrigamist mysteriously trenchers artfully mangled operably ionizing tenantry armfuls appendant submontane stoutening piling defunctness bestializing overconfident triadism vivifier vivisection distally polyclinic foretime triarchy homerooms totterer diarist needlessness lambies geed charts begets floorthrough chargee affixion pincers quipped cabinetmaking unsupervised deathcups dogfight wormhole emptied drifter sluggishness senescent representable honors bullier superintended investigated paragraphed claywares resourcefulness psychos morphia ventricular iteming immunoreactive"^^xsd:string. - bench:cdrom "http://www.reducing.tld/nonfiction/retrogressions.html"^^xsd:string. - rdfs:seeAlso "http://www.nethermost.tld/retrogressions/eliminates.html"^^xsd:string. - swrc:month "1"^^xsd:integer. - swrc:pages "69"^^xsd:integer. - dc:title "nonfiction eliminates unknowns"^^xsd:string. - foaf:homepage "http://www.retrogressions.tld/unknowns/mongoloids.html"^^xsd:string. - swrc:journal . - dc:creator _:Eupeithes_Bevens. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.eliminates.tld/mongoloids/danker.html"^^xsd:string. - swrc:note "weasand yearlings timidities untold fellowman adamantine museful medallions"^^xsd:string. - swrc:pages "50"^^xsd:integer. - dc:title "unknowns danker raunchiness"^^xsd:string. - foaf:homepage "http://www.mongoloids.tld/raunchiness/perspicuously.html"^^xsd:string. - swrc:journal . - dc:creator _:Miranda_Leinen. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.danker.tld/perspicuously/disjoined.html"^^xsd:string. - swrc:pages "51"^^xsd:integer. - dc:title "raunchiness disjoined nigglings"^^xsd:string. - foaf:homepage "http://www.perspicuously.tld/nigglings/midmonths.html"^^xsd:string. - swrc:journal . - dc:creator _:Vasilista_Hamic. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.disjoined.tld/midmonths/labium.html"^^xsd:string. - swrc:note "malingerers gnashes chuffs redundance matriculant flexes repairing keepable"^^xsd:string. - swrc:pages "80"^^xsd:integer. - dc:title "nigglings labium peeped"^^xsd:string. - foaf:homepage "http://www.midmonths.tld/peeped/daydreams.html"^^xsd:string. - swrc:journal . - dc:creator _:Angha_Bievenue. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.labium.tld/daydreams/permuting.html"^^xsd:string. - swrc:pages "74"^^xsd:integer. - dc:title "peeped permuting immediately"^^xsd:string. - foaf:homepage "http://www.daydreams.tld/immediately/canzona.html"^^xsd:string. - swrc:journal . -_:Kichibei_Opitz rdf:type foaf:Person. -_:Kichibei_Opitz foaf:name "Kichibei Opitz"^^xsd:string. - dc:creator _:Kichibei_Opitz. - dc:creator . - rdf:type bench:Article. - rdfs:seeAlso "http://www.permuting.tld/canzona/interrelated.html"^^xsd:string. - swrc:note "etchers mitering laboratorian tiptoed humoring hairiest ouzels frequented"^^xsd:string. - swrc:pages "69"^^xsd:integer. - dc:title "immediately interrelated cooked"^^xsd:string. - foaf:homepage "http://www.canzona.tld/cooked/reformers.html"^^xsd:string. - swrc:journal . -_:Luzia_Rahib rdf:type foaf:Person. -_:Luzia_Rahib foaf:name "Luzia Rahib"^^xsd:string. - dc:creator _:Luzia_Rahib. - rdf:type bench:Article. - rdfs:seeAlso "http://www.interrelated.tld/reformers/goodwife.html"^^xsd:string. - swrc:pages "72"^^xsd:integer. - dc:title "cooked goodwife technicolor"^^xsd:string. - foaf:homepage "http://www.reformers.tld/technicolor/plenishes.html"^^xsd:string. - swrc:journal . -_:Gia_Stonesifer rdf:type foaf:Person. -_:Gia_Stonesifer foaf:name "Gia Stonesifer"^^xsd:string. - dc:creator _:Gia_Stonesifer. - rdf:type bench:Article. - rdfs:seeAlso "http://www.goodwife.tld/plenishes/nippy.html"^^xsd:string. - swrc:pages "70"^^xsd:integer. - dc:title "technicolor nippy bounden"^^xsd:string. - foaf:homepage "http://www.plenishes.tld/bounden/occulters.html"^^xsd:string. - swrc:journal . -_:Uqbah_Oconnell rdf:type foaf:Person. -_:Uqbah_Oconnell foaf:name "Uqbah Oconnell"^^xsd:string. - dc:creator _:Uqbah_Oconnell. - rdf:type bench:Article. - rdfs:seeAlso "http://www.nippy.tld/occulters/blubberer.html"^^xsd:string. - swrc:pages "71"^^xsd:integer. - dc:title "bounden blubberer amenities"^^xsd:string. - foaf:homepage "http://www.occulters.tld/amenities/desecrated.html"^^xsd:string. - swrc:journal . -_:Heidelore_Ruschmann rdf:type foaf:Person. -_:Heidelore_Ruschmann foaf:name "Heidelore Ruschmann"^^xsd:string. - dc:creator _:Heidelore_Ruschmann. - rdf:type bench:Article. - rdfs:seeAlso "http://www.blubberer.tld/desecrated/tetrachlorides.html"^^xsd:string. - swrc:note "negatively witnesses pharisaical flaunting divergence semitraditional negotiators greaves"^^xsd:string. - swrc:pages "76"^^xsd:integer. - dc:title "amenities tetrachlorides loutish"^^xsd:string. - foaf:homepage "http://www.desecrated.tld/loutish/polygony.html"^^xsd:string. - swrc:journal . -_:Octave_Diana rdf:type foaf:Person. -_:Octave_Diana foaf:name "Octave Diana"^^xsd:string. - dc:creator _:Octave_Diana. - rdf:type bench:Article. - rdfs:seeAlso "http://www.tetrachlorides.tld/polygony/malines.html"^^xsd:string. - swrc:pages "70"^^xsd:integer. - dc:title "loutish malines cliffhanger"^^xsd:string. - foaf:homepage "http://www.polygony.tld/cliffhanger/entailments.html"^^xsd:string. - swrc:journal . -_:Mitsuo_Neff rdf:type foaf:Person. -_:Mitsuo_Neff foaf:name "Mitsuo Neff"^^xsd:string. - dc:creator _:Mitsuo_Neff. - rdf:type bench:Article. - dc:title "malines entailments reindexed"^^xsd:string. - foaf:homepage "http://www.cliffhanger.tld/reindexed/bedstraws.html"^^xsd:string. - swrc:journal . -_:Alexandros_Lagazo rdf:type foaf:Person. -_:Alexandros_Lagazo foaf:name "Alexandros Lagazo"^^xsd:string. - dc:creator _:Alexandros_Lagazo. - rdf:type bench:Article. - rdfs:seeAlso "http://www.entailments.tld/bedstraws/thoughtless.html"^^xsd:string. - swrc:pages "69"^^xsd:integer. - dc:title "reindexed thoughtless elation"^^xsd:string. - foaf:homepage "http://www.bedstraws.tld/elation/swampland.html"^^xsd:string. - swrc:journal . -_:Elvera_Zito rdf:type foaf:Person. -_:Elvera_Zito foaf:name "Elvera Zito"^^xsd:string. - dc:creator _:Elvera_Zito. - rdf:type bench:Article. - rdfs:seeAlso "http://www.thoughtless.tld/swampland/earings.html"^^xsd:string. - swrc:pages "69"^^xsd:integer. - dc:title "elation earings circumscribed"^^xsd:string. - foaf:homepage "http://www.swampland.tld/circumscribed/paralyzingly.html"^^xsd:string. - swrc:journal . -_:Shahaama_Berum rdf:type foaf:Person. -_:Shahaama_Berum foaf:name "Shahaama Berum"^^xsd:string. - dc:creator _:Shahaama_Berum. - rdf:type bench:Article. - rdfs:seeAlso "http://www.earings.tld/paralyzingly/pouchy.html"^^xsd:string. - swrc:pages "5"^^xsd:integer. - dc:title "circumscribed pouchy surrejoinders"^^xsd:string. - foaf:homepage "http://www.paralyzingly.tld/surrejoinders/chestiest.html"^^xsd:string. - swrc:journal . -_:Dena_Mcentire rdf:type foaf:Person. -_:Dena_Mcentire foaf:name "Dena Mcentire"^^xsd:string. - dc:creator _:Dena_Mcentire. - rdf:type bench:Article. - rdfs:seeAlso "http://www.pouchy.tld/chestiest/measurage.html"^^xsd:string. - swrc:pages "10"^^xsd:integer. - dc:title "surrejoinders measurage tonsils"^^xsd:string. - foaf:homepage "http://www.chestiest.tld/tonsils/pasturage.html"^^xsd:string. - swrc:journal . -_:Shaka_Enget rdf:type foaf:Person. -_:Shaka_Enget foaf:name "Shaka Enget"^^xsd:string. - dc:creator _:Shaka_Enget. - rdf:type bench:Article. - rdfs:seeAlso "http://www.measurage.tld/pasturage/thurifer.html"^^xsd:string. - swrc:pages "166"^^xsd:integer. - dc:title "tonsils thurifer teazle"^^xsd:string. - foaf:homepage "http://www.pasturage.tld/teazle/fringier.html"^^xsd:string. - swrc:journal . -_:Argos_Cumings rdf:type foaf:Person. -_:Argos_Cumings foaf:name "Argos Cumings"^^xsd:string. - dc:creator _:Argos_Cumings. - rdf:type bench:Article. - rdfs:seeAlso "http://www.thurifer.tld/fringier/rhythmical.html"^^xsd:string. - swrc:pages "155"^^xsd:integer. - dc:title "teazle rhythmical wastebaskets"^^xsd:string. - foaf:homepage "http://www.fringier.tld/wastebaskets/powderer.html"^^xsd:string. - swrc:journal . -_:Torah_Giller rdf:type foaf:Person. -_:Torah_Giller foaf:name "Torah Giller"^^xsd:string. - dc:creator _:Torah_Giller. - rdf:type bench:Article. - rdfs:seeAlso "http://www.rhythmical.tld/powderer/immigrates.html"^^xsd:string. - swrc:pages "106"^^xsd:integer. - dc:title "wastebaskets immigrates inserter"^^xsd:string. - foaf:homepage "http://www.powderer.tld/inserter/plights.html"^^xsd:string. - swrc:journal . -_:Guido_Genier rdf:type foaf:Person. -_:Guido_Genier foaf:name "Guido Genier"^^xsd:string. - dc:creator _:Guido_Genier. - rdf:type bench:Article. - rdfs:seeAlso "http://www.immigrates.tld/plights/corollaries.html"^^xsd:string. - swrc:pages "41"^^xsd:integer. - dc:title "inserter corollaries gaudiness"^^xsd:string. - foaf:homepage "http://www.plights.tld/gaudiness/irades.html"^^xsd:string. - swrc:journal . -_:Diana_Gulbranson rdf:type foaf:Person. -_:Diana_Gulbranson foaf:name "Diana Gulbranson"^^xsd:string. - dc:creator _:Diana_Gulbranson. - swrc:editor _:Sadayoshi_Englemann. -_:Sadayoshi_Englemann rdf:type foaf:Person. -_:Sadayoshi_Englemann foaf:name "Sadayoshi Englemann"^^xsd:string. diff --git a/tests/dataset/sp2b.nt b/tests/dataset/sp2b.nt deleted file mode 100644 index 0248fb87..00000000 --- a/tests/dataset/sp2b.nt +++ /dev/null @@ -1,691 +0,0 @@ - "Journal 1 (1940)"^^ . - . - . - . - . - . - . - . - . - . - . - "Paul Erdoes"^^ . - . - . - "1"^^ . - "1"^^ . - "1940"^^ . - _:genid22 . - . - "unmuzzling measles decentralizing hogfishes gantleted richer succories dwelling scrapped prat islanded burlily thanklessly swiveled polers oinked apnea maxillary dumpers bering evasiveness toto teashop reaccepts gunneries exorcises pirog desexes summable heliocentricity excretions recelebrating dually plateauing reoccupations embossers cerebrum gloves mohairs admiralties bewigged playgoers cheques batting waspishly stilbestrol villainousness miscalling firefanged skeins equalled sandwiching bewitchment cheaters riffled kerneling napoleons rifer splinting surmisers satisfying undamped sharpers forbearer anesthetization undermentioned outflanking funnyman commuted lachrymation floweret arcadian acridities unrealistic substituting surges preheats loggias reconciliating photocatalyst lenity tautological jambing sodality outcrop slipcases phenylketonuria grunts venturers valiantly unremorsefully extradites stollens ponderers conditione loathly cancels debiting parrots paraguayans resonates"^^ . - "http://www.hogfishes.tld/richer/succories.html"^^ . - "http://www.gantleted.tld/succories/dwelling.html"^^ . - "4"^^ . - "overbites terminals giros podgy vagus kinkiest xix recollected"^^ . - "110"^^ . - "richer dwelling scrapped"^^ . - "http://www.succories.tld/scrapped/prat.html"^^ . - . - _:genid1 . - . - _:genid2 . -_:genid1 . -_:genid1 "Adamanta Schlitt"^^ . -_:genid2 . -_:genid2 . -_:genid2 . -_:genid2 . -_:genid2 . - . - "householder overeducated objurgate treaties preprocessor despising loftily yabber reprovingly blungers dwarflike effulgences coreless tuberculoses environs hulled preexamination oralogy tibetans slavishly hipless prs bluejays cuppier nonsurgical skimpiest outpoured dissociated heartier petitionee brill neologic intermuscular fobbed transcribed swifters redigesting ostinato recalculation safest signiory latchets inflecting trephines hops exec junketeers isolators reducing nethermost nonfiction retrogressions eliminates unknowns mongoloids danker raunchiness perspicuously disjoined nigglings midmonths labium peeped daydreams permuting immediately canzona interrelated cooked reformers goodwife technicolor plenishes nippy bounden occulters blubberer amenities desecrated tetrachlorides loutish polygony malines cliffhanger entailments reindexed bedstraws thoughtless elation swampland earings circumscribed paralyzingly pouchy surrejoinders chestiest measurage tonsils pasturage thurifer teazle"^^ . - "http://www.dwelling.tld/prat/islanded.html"^^ . - "http://www.scrapped.tld/islanded/burlily.html"^^ . - "8"^^ . - "fringier rhythmical wastebaskets powderer immigrates inserter plights corollaries"^^ . - "114"^^ . - "prat burlily thanklessly"^^ . - "http://www.islanded.tld/thanklessly/swiveled.html"^^ . - . - _:genid3 . - . -_:genid3 . -_:genid3 "Cecil Kochler"^^ . - . - "gaudiness irades inadvisability disciplinarians majors manifestly decaffeinates scalepan folklorists attractive yeller cognizably reminds teratoid coadjutors thuggeries nondestructive maladjustments subpartnership cordilleras recirculations alkalin succulently marquise underlaid neurosurgeon innervated hunts barrens emanative blowpipe varies thickest machinability orbiters tormentor owner zanier corkscrewed promiscuousness clewed reassemble hesitation fainting croupy bacchanalia regainers teardown margarins inconvenience triunities dipped votarists kilogram timbrel presell woodcraft reupholstered xerosis steamers neurological warranter flashings oops detonations chippering photospherically pouchiest canvasses pyorrheas cartons acquirable refocus vividness administrated remedying prophetically allayed zinged fridge stained unintentional antiquarians dilutes quantitatively shovels vitric mendelism kookiest leavening embrocation casteless uroliths sashes marrieds fungic gasogenes obnoxiously dismounting endorser libations"^^ . - "http://www.burlily.tld/swiveled/polers.html"^^ . - "http://www.thanklessly.tld/polers/oinked.html"^^ . - "harrower claymores shiftlessly feedstuffs lyricizing hierarchs composedly taunting"^^ . - "117"^^ . - "swiveled oinked apnea"^^ . - "http://www.polers.tld/apnea/maxillary.html"^^ . - . - _:genid4 . - . -_:genid4 . -_:genid4 "Amalia Krajcik"^^ . - . - "dragged lobsters careering triplets hepatics colonies defalcate transplantations forfends voucherable intercepting jeered immunopathology addends surveiled wagers joysticks nonliving agric proliferating disintegrator oblongish leapfrogged overabundant legworks easeful cognize hoatzin toiled nonspecialized vrouw squads tantalums overweight readmits loopholing tattles irradiates befriends insinuators restorers rebroadcasting grousing overdrinking frow demarcators tasselling crocked wharfinger reconverting washboards overdrank recalculations dumps carousels acidly deponent venges shivas northers mutualist harebrained earthworms lunk forefended overtaking sourdoughs traditionless spoliator earthlier stenographers reallocating aslope seawaters ruminative patronly hydrozoon webbier foxiness toddy playlets mouthiest delegati renege briefless regularities planarity stubborner waterbeds disinclines antonyms anesthetize chanticleer administrants preengaging unitarians reevaluate rekeys ochroid climatotherapy crocks"^^ . - "http://www.oinked.tld/maxillary/dumpers.html"^^ . - "http://www.apnea.tld/dumpers/bering.html"^^ . - "carpetbag peonism metropolitanize twanged pedros nonforfeitable dissociative apostacy"^^ . - "120"^^ . - "maxillary bering evasiveness"^^ . - "http://www.dumpers.tld/evasiveness/toto.html"^^ . - . - _:genid5 . - . -_:genid5 . -_:genid5 "Martina Mcclary"^^ . - . - "http://www.bering.tld/toto/teashop.html"^^ . - "malting footgear abominators trilobate jigsawed kickstands prated songstresses"^^ . - "122"^^ . - "evasiveness teashop reaccepts"^^ . - "http://www.toto.tld/reaccepts/gunneries.html"^^ . - . - _:genid6 . - . -_:genid6 . -_:genid6 "Ashley Kesselring"^^ . - . - "http://www.teashop.tld/gunneries/exorcises.html"^^ . - "incorporeal piazadora hearings legation subendorsed hippocampus miscalculates whetters"^^ . - "124"^^ . - "reaccepts exorcises pirog"^^ . - "http://www.gunneries.tld/pirog/desexes.html"^^ . - . - _:genid7 . - . -_:genid7 . -_:genid7 "Tatsukichi Gerstenberger"^^ . - . - "http://www.exorcises.tld/desexes/summable.html"^^ . - "leviathans misadvised tiltyard numberable yawing prosecutrices pegboxes feeblish"^^ . - "126"^^ . - "pirog summable heliocentricity"^^ . - "http://www.desexes.tld/heliocentricity/excretions.html"^^ . - . - _:genid8 . - . -_:genid8 . -_:genid8 "Fahroni Anglea"^^ . - . - "http://www.summable.tld/excretions/recelebrating.html"^^ . - "extenuation stranders abbesses strongboxes chromas oats pulling leatheriness"^^ . - "128"^^ . - "heliocentricity recelebrating dually"^^ . - "http://www.excretions.tld/dually/plateauing.html"^^ . - . - _:genid9 . - . -_:genid9 . -_:genid9 "Akina Jang"^^ . - . - "http://www.recelebrating.tld/plateauing/reoccupations.html"^^ . - "witchy horologe bigamistic furrows eloquence cobwebbier divorcing incidentally"^^ . - "130"^^ . - "dually reoccupations embossers"^^ . - "http://www.plateauing.tld/embossers/cerebrum.html"^^ . - . - _:genid10 . - . -_:genid10 . -_:genid10 "Edmondo Rommelfanger"^^ . - . - "http://www.reoccupations.tld/cerebrum/gloves.html"^^ . - "retorts insoles stockman queening allergist doyenne placarders septuagenarians"^^ . - "132"^^ . - "embossers gloves mohairs"^^ . - "http://www.cerebrum.tld/mohairs/admiralties.html"^^ . - . - _:genid11 . - . -_:genid11 . -_:genid11 "Pilib Seu"^^ . - . - "http://www.gloves.tld/admiralties/bewigged.html"^^ . - "safecracker cacaos mignonette tailored whews beholden branchless primitiveness"^^ . - "134"^^ . - "mohairs bewigged playgoers"^^ . - "http://www.admiralties.tld/playgoers/cheques.html"^^ . - . - _:genid12 . -_:genid12 . -_:genid12 "Dell Kosel"^^ . - . - "http://www.bewigged.tld/cheques/batting.html"^^ . - "crimper tonners unfair southpaws scorify supportance jumpiest whanged"^^ . - "136"^^ . - "playgoers batting waspishly"^^ . - "http://www.cheques.tld/waspishly/stilbestrol.html"^^ . - . - _:genid13 . -_:genid13 . -_:genid13 "Prebrana Kekiwi"^^ . - . - "http://www.batting.tld/stilbestrol/villainousness.html"^^ . - "137"^^ . - "waspishly villainousness miscalling"^^ . - "http://www.stilbestrol.tld/miscalling/firefanged.html"^^ . - . - _:genid14 . -_:genid14 . -_:genid14 "Korechika Mamer"^^ . - . - "http://www.villainousness.tld/firefanged/skeins.html"^^ . - "138"^^ . - "miscalling skeins equalled"^^ . - "http://www.firefanged.tld/equalled/sandwiching.html"^^ . - . - _:genid15 . -_:genid15 . -_:genid15 "Dorel Brandt"^^ . - . - "http://www.skeins.tld/sandwiching/bewitchment.html"^^ . - "139"^^ . - "equalled bewitchment cheaters"^^ . - "http://www.sandwiching.tld/cheaters/riffled.html"^^ . - . - _:genid16 . -_:genid16 . -_:genid16 "Yayang Kuczenski"^^ . - . - "http://www.bewitchment.tld/riffled/kerneling.html"^^ . - "140"^^ . - "cheaters kerneling napoleons"^^ . - "http://www.riffled.tld/napoleons/rifer.html"^^ . - . - _:genid17 . -_:genid17 . -_:genid17 "Trina Sjerven"^^ . - . - "http://www.kerneling.tld/rifer/splinting.html"^^ . - "141"^^ . - "napoleons splinting surmisers"^^ . - "http://www.rifer.tld/surmisers/satisfying.html"^^ . - . - _:genid18 . -_:genid18 . -_:genid18 "Caden Failing"^^ . - . - "http://www.splinting.tld/satisfying/undamped.html"^^ . - "142"^^ . - "surmisers undamped sharpers"^^ . - "http://www.satisfying.tld/sharpers/forbearer.html"^^ . - . - _:genid19 . -_:genid19 . -_:genid19 "Mya Swilley"^^ . - . - "http://www.undamped.tld/forbearer/anesthetization.html"^^ . - "143"^^ . - "sharpers anesthetization undermentioned"^^ . - "http://www.forbearer.tld/undermentioned/outflanking.html"^^ . - . - _:genid20 . -_:genid20 . -_:genid20 "Juriaan Schremp"^^ . - . - "http://www.anesthetization.tld/outflanking/funnyman.html"^^ . - "144"^^ . - "undermentioned funnyman commuted"^^ . - "http://www.outflanking.tld/commuted/lachrymation.html"^^ . - . - _:genid21 . -_:genid21 . -_:genid21 "Kozue Efthimiou"^^ . -_:genid22 . -_:genid22 "Sharise Heagy"^^ . - . - "1"^^ . - "Journal 1 (1941)"^^ . - "2"^^ . - "1941"^^ . - _:genid40 . - . - "http://www.commuted.tld/floweret/arcadian.html"^^ . - "181"^^ . - "lachrymation arcadian acridities"^^ . - "http://www.floweret.tld/acridities/unrealistic.html"^^ . - . - _:genid19 . - . - . - "http://www.arcadian.tld/unrealistic/substituting.html"^^ . - "100"^^ . - "acridities substituting surges"^^ . - "http://www.unrealistic.tld/surges/preheats.html"^^ . - . - _:genid21 . - . - . - "http://www.substituting.tld/preheats/loggias.html"^^ . - "182"^^ . - "surges loggias reconciliating"^^ . - "http://www.preheats.tld/reconciliating/photocatalyst.html"^^ . - . - _:genid11 . - . - . - "http://www.loggias.tld/photocatalyst/lenity.html"^^ . - "97"^^ . - "reconciliating lenity tautological"^^ . - "http://www.photocatalyst.tld/tautological/jambing.html"^^ . - . - _:genid13 . - . - . - "http://www.lenity.tld/jambing/sodality.html"^^ . - "171"^^ . - "tautological sodality outcrop"^^ . - "http://www.jambing.tld/outcrop/slipcases.html"^^ . - . - _:genid15 . - . - . - "http://www.sodality.tld/slipcases/phenylketonuria.html"^^ . - "110"^^ . - "outcrop phenylketonuria grunts"^^ . - "http://www.slipcases.tld/grunts/venturers.html"^^ . - . - _:genid17 . - . - . - "http://www.phenylketonuria.tld/venturers/valiantly.html"^^ . - "184"^^ . - "grunts valiantly unremorsefully"^^ . - "http://www.venturers.tld/unremorsefully/extradites.html"^^ . - . - _:genid23 . - . -_:genid23 . -_:genid23 "Phaethon Gearon"^^ . - . - "http://www.valiantly.tld/extradites/stollens.html"^^ . - "111"^^ . - "unremorsefully stollens ponderers"^^ . - "http://www.extradites.tld/ponderers/conditione.html"^^ . - . - _:genid24 . - . -_:genid24 . -_:genid24 "Lone Pavese"^^ . - . - "http://www.stollens.tld/conditione/loathly.html"^^ . - "169"^^ . - "ponderers loathly cancels"^^ . - "http://www.conditione.tld/cancels/debiting.html"^^ . - . - _:genid25 . - . -_:genid25 . -_:genid25 "Motoyasu Calligy"^^ . - . - "http://www.loathly.tld/debiting/parrots.html"^^ . - "112"^^ . - "cancels parrots paraguayans"^^ . - "http://www.debiting.tld/paraguayans/resonates.html"^^ . - . - _:genid26 . - . -_:genid26 . -_:genid26 "Firdaus Casavez"^^ . - . - "http://www.parrots.tld/resonates/overbites.html"^^ . - "178"^^ . - "paraguayans overbites terminals"^^ . - "http://www.resonates.tld/terminals/giros.html"^^ . - . - _:genid27 . -_:genid27 . -_:genid27 "Angha Bievenue"^^ . - . - "http://www.overbites.tld/giros/podgy.html"^^ . - "101"^^ . - "terminals podgy vagus"^^ . - "http://www.giros.tld/vagus/kinkiest.html"^^ . - . - _:genid28 . -_:genid28 . -_:genid28 "Araceli Ertel"^^ . - . - "http://www.podgy.tld/kinkiest/xix.html"^^ . - "183"^^ . - "vagus xix recollected"^^ . - "http://www.kinkiest.tld/recollected/householder.html"^^ . - . - _:genid29 . -_:genid29 . -_:genid29 "Nik Reposa"^^ . - . - "http://www.xix.tld/householder/overeducated.html"^^ . - "98"^^ . - "recollected overeducated objurgate"^^ . - "http://www.householder.tld/objurgate/treaties.html"^^ . - . - _:genid30 . -_:genid30 . -_:genid30 "Shiko Seagroves"^^ . - . - "http://www.overeducated.tld/treaties/preprocessor.html"^^ . - "124"^^ . - "objurgate preprocessor despising"^^ . - "http://www.treaties.tld/despising/loftily.html"^^ . - . - _:genid31 . -_:genid31 . -_:genid31 "Dianmu Aver"^^ . - . - "http://www.preprocessor.tld/loftily/yabber.html"^^ . - "91"^^ . - "despising yabber reprovingly"^^ . - "http://www.loftily.tld/reprovingly/blungers.html"^^ . - . - _:genid32 . -_:genid32 . -_:genid32 "Helene Nik"^^ . - . - "http://www.yabber.tld/blungers/dwarflike.html"^^ . - "125"^^ . - "reprovingly dwarflike effulgences"^^ . - "http://www.blungers.tld/effulgences/coreless.html"^^ . - . - _:genid33 . -_:genid33 . -_:genid33 "Yemena Knebel"^^ . - . - "http://www.dwarflike.tld/coreless/tuberculoses.html"^^ . - "92"^^ . - "effulgences tuberculoses environs"^^ . - "http://www.coreless.tld/environs/hulled.html"^^ . - . - _:genid34 . -_:genid34 . -_:genid34 "Melisa Patriarco"^^ . - . - "http://www.tuberculoses.tld/hulled/preexamination.html"^^ . - "126"^^ . - "environs preexamination oralogy"^^ . - "http://www.hulled.tld/oralogy/tibetans.html"^^ . - . - _:genid35 . -_:genid35 . -_:genid35 "Dama Leino"^^ . - . - "http://www.preexamination.tld/tibetans/slavishly.html"^^ . - "89"^^ . - "oralogy slavishly hipless"^^ . - "http://www.tibetans.tld/hipless/prs.html"^^ . - . - _:genid36 . -_:genid36 . -_:genid36 "Satoru Beaumont"^^ . - . - "http://www.slavishly.tld/prs/bluejays.html"^^ . - "147"^^ . - "hipless bluejays cuppier"^^ . - "http://www.prs.tld/cuppier/nonsurgical.html"^^ . - . - _:genid37 . -_:genid37 . -_:genid37 "Booker Spiker"^^ . - . - "http://www.bluejays.tld/nonsurgical/skimpiest.html"^^ . - "70"^^ . - "cuppier skimpiest outpoured"^^ . - "http://www.nonsurgical.tld/outpoured/dissociated.html"^^ . - . - _:genid38 . -_:genid38 . -_:genid38 "Eupeithes Bevens"^^ . - . - "http://www.skimpiest.tld/dissociated/heartier.html"^^ . - "144"^^ . - "outpoured heartier petitionee"^^ . - "http://www.dissociated.tld/petitionee/brill.html"^^ . - . - _:genid39 . -_:genid39 . -_:genid39 "Miranda Leinen"^^ . -_:genid40 . -_:genid40 "Lane Portes"^^ . - . - "heartier brill neologic"^^ . - "http://www.petitionee.tld/neologic/intermuscular.html"^^ . - "119"^^ . - "brill intermuscular fobbed"^^ . - "http://www.neologic.tld/fobbed/transcribed.html"^^ . - "1941"^^ . - _:genid41 . -_:genid41 . -_:genid41 "Vasilista Hamic"^^ . - . - "1"^^ . - "Journal 1 (1942)"^^ . - "3"^^ . - "1942"^^ . - _:genid58 . - . - "http://www.fobbed.tld/swifters/redigesting.html"^^ . - "88"^^ . - "transcribed redigesting ostinato"^^ . - "http://www.swifters.tld/ostinato/recalculation.html"^^ . - . - _:genid33 . - . - . - "http://www.redigesting.tld/recalculation/safest.html"^^ . - "86"^^ . - "ostinato safest signiory"^^ . - "http://www.recalculation.tld/signiory/latchets.html"^^ . - . - _:genid34 . - . - . - "http://www.safest.tld/latchets/inflecting.html"^^ . - "87"^^ . - "signiory inflecting trephines"^^ . - "http://www.latchets.tld/trephines/hops.html"^^ . - . - _:genid35 . - . - . - "http://www.inflecting.tld/hops/exec.html"^^ . - "87"^^ . - "trephines exec junketeers"^^ . - "http://www.hops.tld/junketeers/isolators.html"^^ . - . - _:genid36 . - . - . - "http://www.exec.tld/isolators/reducing.html"^^ . - "intercollegiate iniquitously lycanthropies electrophoresed dinting rezoning pledgee protoactinium"^^ . - "junketeers reducing nethermost"^^ . - "http://www.isolators.tld/nethermost/nonfiction.html"^^ . - . - _:genid37 . - . - . - "darned emblements shrewed alluvials depressional airlifts tests sliming felicitator virological results contradistinctions unexciting debateable tenants earrings overrigid hidebound faire poloist comets paraphraser whangs uncovering infested heaver euphorically ameliorative aglets preciosity curring compositely antennal undrinkable charter uncashed huntedly czardoms unidentifiable reversing monstrances gravies quadrigamist mysteriously trenchers artfully mangled operably ionizing tenantry armfuls appendant submontane stoutening piling defunctness bestializing overconfident triadism vivifier vivisection distally polyclinic foretime triarchy homerooms totterer diarist needlessness lambies geed charts begets floorthrough chargee affixion pincers quipped cabinetmaking unsupervised deathcups dogfight wormhole emptied drifter sluggishness senescent representable honors bullier superintended investigated paragraphed claywares resourcefulness psychos morphia ventricular iteming immunoreactive"^^ . - "http://www.reducing.tld/nonfiction/retrogressions.html"^^ . - "http://www.nethermost.tld/retrogressions/eliminates.html"^^ . - "1"^^ . - "69"^^ . - "nonfiction eliminates unknowns"^^ . - "http://www.retrogressions.tld/unknowns/mongoloids.html"^^ . - . - _:genid38 . - . - . - "http://www.eliminates.tld/mongoloids/danker.html"^^ . - "weasand yearlings timidities untold fellowman adamantine museful medallions"^^ . - "50"^^ . - "unknowns danker raunchiness"^^ . - "http://www.mongoloids.tld/raunchiness/perspicuously.html"^^ . - . - _:genid39 . - . - . - "http://www.danker.tld/perspicuously/disjoined.html"^^ . - "51"^^ . - "raunchiness disjoined nigglings"^^ . - "http://www.perspicuously.tld/nigglings/midmonths.html"^^ . - . - _:genid41 . - . - . - "http://www.disjoined.tld/midmonths/labium.html"^^ . - "malingerers gnashes chuffs redundance matriculant flexes repairing keepable"^^ . - "80"^^ . - "nigglings labium peeped"^^ . - "http://www.midmonths.tld/peeped/daydreams.html"^^ . - . - _:genid27 . - . - . - "http://www.labium.tld/daydreams/permuting.html"^^ . - "74"^^ . - "peeped permuting immediately"^^ . - "http://www.daydreams.tld/immediately/canzona.html"^^ . - . - _:genid42 . - . -_:genid42 . -_:genid42 "Kichibei Opitz"^^ . - . - "http://www.permuting.tld/canzona/interrelated.html"^^ . - "etchers mitering laboratorian tiptoed humoring hairiest ouzels frequented"^^ . - "69"^^ . - "immediately interrelated cooked"^^ . - "http://www.canzona.tld/cooked/reformers.html"^^ . - . - _:genid43 . -_:genid43 . -_:genid43 "Luzia Rahib"^^ . - . - "http://www.interrelated.tld/reformers/goodwife.html"^^ . - "72"^^ . - "cooked goodwife technicolor"^^ . - "http://www.reformers.tld/technicolor/plenishes.html"^^ . - . - _:genid44 . -_:genid44 . -_:genid44 "Gia Stonesifer"^^ . - . - "http://www.goodwife.tld/plenishes/nippy.html"^^ . - "70"^^ . - "technicolor nippy bounden"^^ . - "http://www.plenishes.tld/bounden/occulters.html"^^ . - . - _:genid45 . -_:genid45 . -_:genid45 "Uqbah Oconnell"^^ . - . - "http://www.nippy.tld/occulters/blubberer.html"^^ . - "71"^^ . - "bounden blubberer amenities"^^ . - "http://www.occulters.tld/amenities/desecrated.html"^^ . - . - _:genid46 . -_:genid46 . -_:genid46 "Heidelore Ruschmann"^^ . - . - "http://www.blubberer.tld/desecrated/tetrachlorides.html"^^ . - "negatively witnesses pharisaical flaunting divergence semitraditional negotiators greaves"^^ . - "76"^^ . - "amenities tetrachlorides loutish"^^ . - "http://www.desecrated.tld/loutish/polygony.html"^^ . - . - _:genid47 . -_:genid47 . -_:genid47 "Octave Diana"^^ . - . - "http://www.tetrachlorides.tld/polygony/malines.html"^^ . - "70"^^ . - "loutish malines cliffhanger"^^ . - "http://www.polygony.tld/cliffhanger/entailments.html"^^ . - . - _:genid48 . -_:genid48 . -_:genid48 "Mitsuo Neff"^^ . - . - "malines entailments reindexed"^^ . - "http://www.cliffhanger.tld/reindexed/bedstraws.html"^^ . - . - _:genid49 . -_:genid49 . -_:genid49 "Alexandros Lagazo"^^ . - . - "http://www.entailments.tld/bedstraws/thoughtless.html"^^ . - "69"^^ . - "reindexed thoughtless elation"^^ . - "http://www.bedstraws.tld/elation/swampland.html"^^ . - . - _:genid50 . -_:genid50 . -_:genid50 "Elvera Zito"^^ . - . - "http://www.thoughtless.tld/swampland/earings.html"^^ . - "69"^^ . - "elation earings circumscribed"^^ . - "http://www.swampland.tld/circumscribed/paralyzingly.html"^^ . - . - _:genid51 . -_:genid51 . -_:genid51 "Shahaama Berum"^^ . - . - "http://www.earings.tld/paralyzingly/pouchy.html"^^ . - "5"^^ . - "circumscribed pouchy surrejoinders"^^ . - "http://www.paralyzingly.tld/surrejoinders/chestiest.html"^^ . - . - _:genid52 . -_:genid52 . -_:genid52 "Dena Mcentire"^^ . - . - "http://www.pouchy.tld/chestiest/measurage.html"^^ . - "10"^^ . - "surrejoinders measurage tonsils"^^ . - "http://www.chestiest.tld/tonsils/pasturage.html"^^ . - . - _:genid53 . -_:genid53 . -_:genid53 "Shaka Enget"^^ . - . - "http://www.measurage.tld/pasturage/thurifer.html"^^ . - "166"^^ . - "tonsils thurifer teazle"^^ . - "http://www.pasturage.tld/teazle/fringier.html"^^ . - . - _:genid54 . -_:genid54 . -_:genid54 "Argos Cumings"^^ . - . - "http://www.thurifer.tld/fringier/rhythmical.html"^^ . - "155"^^ . - "teazle rhythmical wastebaskets"^^ . - "http://www.fringier.tld/wastebaskets/powderer.html"^^ . - . - _:genid55 . -_:genid55 . -_:genid55 "Torah Giller"^^ . - . - "http://www.rhythmical.tld/powderer/immigrates.html"^^ . - "106"^^ . - "wastebaskets immigrates inserter"^^ . - "http://www.powderer.tld/inserter/plights.html"^^ . - . - _:genid56 . -_:genid56 . -_:genid56 "Guido Genier"^^ . - . - "http://www.immigrates.tld/plights/corollaries.html"^^ . - "41"^^ . - "inserter corollaries gaudiness"^^ . - "http://www.plights.tld/gaudiness/irades.html"^^ . - . - _:genid57 . -_:genid57 . -_:genid57 "Diana Gulbranson"^^ . -_:genid58 . -_:genid58 "Sadayoshi Englemann"^^ . \ No newline at end of file diff --git a/tests/dataset/umlaut.nt b/tests/dataset/umlaut.nt deleted file mode 100644 index 3c78500d..00000000 --- a/tests/dataset/umlaut.nt +++ /dev/null @@ -1 +0,0 @@ - "Univ Zürich" . \ No newline at end of file diff --git a/tests/ntriplefiles/ntriples.nt b/tests/ntriplefiles/ntriples.nt deleted file mode 100644 index f6886aeb..00000000 --- a/tests/ntriplefiles/ntriples.nt +++ /dev/null @@ -1,7 +0,0 @@ - "That Seventies Show"^^ . # literal with XML Schema string datatype - "That Seventies Show" . # same as above - "That Seventies Show"@en . # literal with a language tag - "Cette Série des Années Septante"@fr-be . # literal outside of ASCII range with a region subtag - "This is a multi-line\nliteral with many quotes (\"\"\"\"\")\nand two apostrophes ('')." . - "2"^^ . # xsd:integer - "1.663E-4"^^ . # xsd:double diff --git a/tests/query/COPYING b/tests/query/COPYING deleted file mode 100644 index 0f73cd2a..00000000 --- a/tests/query/COPYING +++ /dev/null @@ -1,36 +0,0 @@ -=============================================================================== -| Software License Agreement (BSD License) | -| | -| Copyright (c) 2007-2008, Freiburg University Database Group | -| All rights reserved. | -| | -| Redistribution and use of this software in source and binary forms, | -| with or without modification, are permitted provided that the following | -| conditions are met: | -| | -| * Redistributions of source code must retain the above | -| copyright notice, this list of conditions and the | -| following disclaimer. | -| | -| * Redistributions in binary form must reproduce the above | -| copyright notice, this list of conditions and the | -| following disclaimer in the documentation and/or other | -| materials provided with the distribution. | -| | -| * Neither the name of the Freiburg University Database Group nor the names | -| of its contributors may be used to endorse or promote products derived | -| from this software without specific prior written permission of the | -| Freiburg University Database Group. | -| | -| 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. | -=============================================================================== diff --git a/tests/query/README b/tests/query/README deleted file mode 100644 index 5def2587..00000000 --- a/tests/query/README +++ /dev/null @@ -1,75 +0,0 @@ -******************************************************************* -************ THE SP^2B SPARQL PERFORMANCE BENCHMARK *************** -******************************************************************* -SP^2B is a Performance Benchmark for the SPARQL query language. -The project homepage at - - http://dbis.informatik.uni-freiburg.de/index.php?project=SP2B - -provides more information on the benchmark. -******************************************************************* - -================================= -******* TABLE OF CONTENTS ******* -================================= -= 1. Introduction = -= 2. Getting Started = -= 3. License = -= 4. Project Members = -= 5. Contact = -================================= - - -1. Introduction ---------------- -SP^2B is a Performance Benchmark for the SPARQL query language. -The benchmark comprises both a data generator for generating -arbitrarily large, DBLP-like data sets, and a set of SPARQL -benchmark queries, which cover a variety of typical SPARQL -operator constellations and RDF access patterns. - -SP^2B is a research project from Freiburg University and has -been laid out to provide a basis for comparing SPARQL optimization -approaches in a uniform benchmark scenario. Of course, it can also -be used to compare SPARQL engines against each other, detect -deficiencies in existing implementations, and to tune SPARQL -engines. - - -2. Getting Started ------------------- -Depending on the type of archive you downloaded from the SP^2B -homepage, the following subdirectories might be available. - -sp2b - | - |-- ./src contains the source code of the data generator - |-- ./bin contains precompiled binaries of the data generator - |-- ./queries contains the benchmark queries - -If you are just interested in applying the benchmark, you should -simply use the precompiled binary (we provide binaries for Linux and -Windows). You will find more detailed information on the individual -components in the README files in the respective directory. - - -3. License ----------- -The SP^2B data generator has been published under the Berkeley -License and is open source. Detailed licensing information can -be found in the attached COPYING file. - - -4. Project Members ------------------- -- Michael Schmidt (Freiburg University) -- Thomas Hornung (Freiburg University) -- Norbert Kuechlin (Freiburg University) -- Georg Lausen (Freiburg University) -- Christoph Pinkel (MTC Infomedia OHG) - - -5. Contact ----------- -Please send bug reports, questions, and general feedback to -Michael Schmidt [mschmidt@informatik.uni-freiburg.de]. diff --git a/tests/query/q-add1.sparql b/tests/query/q-add1.sparql deleted file mode 100644 index 52ea9e7e..00000000 --- a/tests/query/q-add1.sparql +++ /dev/null @@ -1,11 +0,0 @@ -PREFIX rdf: -PREFIX rdfs: -PREFIX foaf: -PREFIX dc: -PREFIX dcterms: - -SELECT ?type ?prop ?val -WHERE { - rdf:type ?type . - ?prop ?val -} diff --git a/tests/query/q01.sparql b/tests/query/q01.sparql deleted file mode 100644 index 1fa616e3..00000000 --- a/tests/query/q01.sparql +++ /dev/null @@ -1,12 +0,0 @@ -PREFIX rdf: -PREFIX dc: -PREFIX dcterms: -PREFIX bench: -PREFIX xsd: - -SELECT ?yr -WHERE { - ?journal rdf:type bench:Journal . - ?journal dc:title "Journal 1 (1940)"^^xsd:string . - ?journal dcterms:issued ?yr -} diff --git a/tests/query/q02.sparql b/tests/query/q02.sparql deleted file mode 100644 index 4535674c..00000000 --- a/tests/query/q02.sparql +++ /dev/null @@ -1,21 +0,0 @@ -PREFIX rdf: -PREFIX rdfs: -PREFIX swrc: -PREFIX foaf: -PREFIX bench: -PREFIX dc: -PREFIX dcterms: - -SELECT ?inproc ?author ?booktitle ?title - ?proc ?ee ?page ?url ?yr -WHERE { - ?inproc rdf:type bench:Inproceedings . - ?inproc dc:creator ?author . - ?inproc bench:booktitle ?booktitle . - ?inproc dc:title ?title . - ?inproc dcterms:partOf ?proc . - ?inproc rdfs:seeAlso ?ee . - ?inproc swrc:pages ?page . - ?inproc foaf:homepage ?url . - ?inproc dcterms:issued ?yr -} diff --git a/tests/query/q03.sparql b/tests/query/q03.sparql deleted file mode 100644 index ca2bd79b..00000000 --- a/tests/query/q03.sparql +++ /dev/null @@ -1,9 +0,0 @@ -PREFIX rdf: -PREFIX bench: -PREFIX swrc: - -SELECT DISTINCT ?article -WHERE { - ?article rdf:type bench:Article . - ?article ?property ?value -} diff --git a/tests/query/q04.sparql b/tests/query/q04.sparql deleted file mode 100644 index 31af3042..00000000 --- a/tests/query/q04.sparql +++ /dev/null @@ -1,18 +0,0 @@ -PREFIX rdf: -PREFIX bench: -PREFIX dc: -PREFIX dcterms: -PREFIX foaf: -PREFIX swrc: - -SELECT DISTINCT ?name1 ?name2 -WHERE { - ?article1 rdf:type bench:Article . - ?article2 rdf:type bench:Article . - ?article1 dc:creator ?author1 . - ?author1 foaf:name ?name1 . - ?article2 dc:creator ?author2 . - ?author2 foaf:name ?name2 . - ?article1 swrc:journal ?journal . - ?article2 swrc:journal ?journal -} diff --git a/tests/query/q05.sparql b/tests/query/q05.sparql deleted file mode 100644 index 0cc61080..00000000 --- a/tests/query/q05.sparql +++ /dev/null @@ -1,15 +0,0 @@ -PREFIX rdf: -PREFIX foaf: -PREFIX bench: -PREFIX dc: - -SELECT DISTINCT ?person ?name -WHERE { - ?article rdf:type bench:Article . - ?article dc:creator ?person . - ?inproc rdf:type bench:Inproceedings . - ?inproc dc:creator ?person . - ?inproc dc:creator ?person2 . - ?person foaf:name ?name . - ?person2 foaf:name ?name2 -} diff --git a/tests/query/q06.sparql b/tests/query/q06.sparql deleted file mode 100644 index d31adde2..00000000 --- a/tests/query/q06.sparql +++ /dev/null @@ -1,14 +0,0 @@ -PREFIX rdf: -PREFIX rdfs: -PREFIX foaf: -PREFIX dc: -PREFIX dcterms: - -SELECT ?yr ?name ?document -WHERE { - ?class rdfs:subClassOf foaf:Document . - ?document rdf:type ?class . - ?document dcterms:issued ?yr . - ?document dc:creator ?author . - ?author foaf:name ?name -} diff --git a/tests/query/q07.sparql b/tests/query/q07.sparql deleted file mode 100644 index 9ad7ac89..00000000 --- a/tests/query/q07.sparql +++ /dev/null @@ -1,14 +0,0 @@ -PREFIX rdf: -PREFIX rdfs: -PREFIX foaf: -PREFIX dc: -PREFIX dcterms: - -SELECT DISTINCT ?title -WHERE { - ?class rdfs:subClassOf foaf:Document . - ?doc rdf:type ?class . - ?doc dc:title ?title . - ?bag2 ?member2 ?doc . - ?doc2 dcterms:references ?bag2 -} diff --git a/tests/query/q08.sparql b/tests/query/q08.sparql deleted file mode 100644 index 80437297..00000000 --- a/tests/query/q08.sparql +++ /dev/null @@ -1,15 +0,0 @@ -PREFIX xsd: -PREFIX rdf: -PREFIX foaf: -PREFIX dc: - -SELECT DISTINCT ?name -WHERE { - ?erdoes rdf:type foaf:Person . - ?erdoes foaf:name "Paul Erdoes"^^xsd:string . - ?document dc:creator ?erdoes . - ?document dc:creator ?author . - ?document2 dc:creator ?author . - ?document2 dc:creator ?author2 . - ?author2 foaf:name ?name -} diff --git a/tests/query/q09.sparql b/tests/query/q09.sparql deleted file mode 100644 index 50e1f7e4..00000000 --- a/tests/query/q09.sparql +++ /dev/null @@ -1,8 +0,0 @@ -PREFIX rdf: -PREFIX foaf: - -SELECT DISTINCT ?predicate -WHERE { - ?person rdf:type foaf:Person . - ?subject ?predicate ?person -} diff --git a/tests/query/q10.sparql b/tests/query/q10.sparql deleted file mode 100644 index 35444303..00000000 --- a/tests/query/q10.sparql +++ /dev/null @@ -1,6 +0,0 @@ -PREFIX person: - -SELECT ?subject ?predicate -WHERE { - ?subject ?predicate person:Paul_Erdoes -} diff --git a/tests/query/q11.sparql b/tests/query/q11.sparql deleted file mode 100644 index d66d4efa..00000000 --- a/tests/query/q11.sparql +++ /dev/null @@ -1,6 +0,0 @@ -PREFIX rdfs: - -SELECT ?ee -WHERE { - ?publication rdfs:seeAlso ?ee -} diff --git a/tests/query/q12.sparql b/tests/query/q12.sparql deleted file mode 100644 index 20db089b..00000000 --- a/tests/query/q12.sparql +++ /dev/null @@ -1,14 +0,0 @@ -PREFIX rdf: -PREFIX foaf: -PREFIX bench: -PREFIX dc: - -SELECT DISTINCT ?person1 ?person2 { - ?article rdf:type bench:Article . - ?article dc:creator ?person1 . - ?inproc rdf:type bench:Inproceedings . - ?inproc dc:creator ?person2 . - ?inproc dc:creator ?person1 . - ?person1 foaf:name ?name1 . - ?person2 foaf:name ?name2 -} diff --git a/thirdparty/RapidJSON/include/rapidjson/allocators.h b/thirdparty/RapidJSON/include/rapidjson/allocators.h index 0b8f5e14..44ec5295 100644 --- a/thirdparty/RapidJSON/include/rapidjson/allocators.h +++ b/thirdparty/RapidJSON/include/rapidjson/allocators.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/cursorstreamwrapper.h b/thirdparty/RapidJSON/include/rapidjson/cursorstreamwrapper.h index 52c11a7c..fd6513db 100644 --- a/thirdparty/RapidJSON/include/rapidjson/cursorstreamwrapper.h +++ b/thirdparty/RapidJSON/include/rapidjson/cursorstreamwrapper.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/document.h b/thirdparty/RapidJSON/include/rapidjson/document.h index 68aaae7e..028235ec 100644 --- a/thirdparty/RapidJSON/include/rapidjson/document.h +++ b/thirdparty/RapidJSON/include/rapidjson/document.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -289,12 +289,14 @@ class GenericMemberIterator; //! non-const GenericMemberIterator template class GenericMemberIterator { +public: //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template class GenericMemberIterator { +public: //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; @@ -2001,17 +2003,18 @@ class GenericValue { // Initial flags of different types. kNullFlag = kNullType, - kTrueFlag = kTrueType | kBoolFlag, - kFalseFlag = kFalseType | kBoolFlag, - kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, - kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, - kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, - kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, - kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, - kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, - kConstStringFlag = kStringType | kStringFlag, - kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, - kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), kObjectFlag = kObjectType, kArrayFlag = kArrayType, @@ -2609,6 +2612,7 @@ class GenericArray { GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } ~GenericArray() {} + operator ValueType&() const { return value_; } SizeType Size() const { return value_.Size(); } SizeType Capacity() const { return value_.Capacity(); } bool Empty() const { return value_.Empty(); } @@ -2664,6 +2668,7 @@ class GenericObject { GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } ~GenericObject() {} + operator ValueType&() const { return value_; } SizeType MemberCount() const { return value_.MemberCount(); } SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } diff --git a/thirdparty/RapidJSON/include/rapidjson/encodedstream.h b/thirdparty/RapidJSON/include/rapidjson/encodedstream.h index 223601c0..cf046b89 100644 --- a/thirdparty/RapidJSON/include/rapidjson/encodedstream.h +++ b/thirdparty/RapidJSON/include/rapidjson/encodedstream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/encodings.h b/thirdparty/RapidJSON/include/rapidjson/encodings.h index 0b244679..50ad18bd 100644 --- a/thirdparty/RapidJSON/include/rapidjson/encodings.h +++ b/thirdparty/RapidJSON/include/rapidjson/encodings.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/error/en.h b/thirdparty/RapidJSON/include/rapidjson/error/en.h index 2db838bf..37a62ebc 100644 --- a/thirdparty/RapidJSON/include/rapidjson/error/en.h +++ b/thirdparty/RapidJSON/include/rapidjson/error/en.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/error/error.h b/thirdparty/RapidJSON/include/rapidjson/error/error.h index 9311d2f0..71f6ec4d 100644 --- a/thirdparty/RapidJSON/include/rapidjson/error/error.h +++ b/thirdparty/RapidJSON/include/rapidjson/error/error.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/filereadstream.h b/thirdparty/RapidJSON/include/rapidjson/filereadstream.h index 6b343707..f8bb43cb 100644 --- a/thirdparty/RapidJSON/include/rapidjson/filereadstream.h +++ b/thirdparty/RapidJSON/include/rapidjson/filereadstream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/filewritestream.h b/thirdparty/RapidJSON/include/rapidjson/filewritestream.h index 8b48fee1..5d89588c 100644 --- a/thirdparty/RapidJSON/include/rapidjson/filewritestream.h +++ b/thirdparty/RapidJSON/include/rapidjson/filewritestream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/fwd.h b/thirdparty/RapidJSON/include/rapidjson/fwd.h index b74a2b81..d62f77f0 100644 --- a/thirdparty/RapidJSON/include/rapidjson/fwd.h +++ b/thirdparty/RapidJSON/include/rapidjson/fwd.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/biginteger.h b/thirdparty/RapidJSON/include/rapidjson/internal/biginteger.h index 8eb87c7c..12455788 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/biginteger.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/biginteger.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/clzll.h b/thirdparty/RapidJSON/include/rapidjson/internal/clzll.h index 47bb7ab1..8fc5118a 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/clzll.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/clzll.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/diyfp.h b/thirdparty/RapidJSON/include/rapidjson/internal/diyfp.h index 8f7d853a..a40797ec 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/diyfp.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/diyfp.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/dtoa.h b/thirdparty/RapidJSON/include/rapidjson/internal/dtoa.h index bf2e9b2e..621402fd 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/dtoa.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/dtoa.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/ieee754.h b/thirdparty/RapidJSON/include/rapidjson/internal/ieee754.h index c2684ba2..68c9e966 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/ieee754.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/ieee754.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/itoa.h b/thirdparty/RapidJSON/include/rapidjson/internal/itoa.h index 9b1c45cc..9fe8c932 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/itoa.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/itoa.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/meta.h b/thirdparty/RapidJSON/include/rapidjson/internal/meta.h index d401edf8..27092dc0 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/meta.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/meta.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/pow10.h b/thirdparty/RapidJSON/include/rapidjson/internal/pow10.h index 02f475d7..eae1a43e 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/pow10.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/pow10.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/regex.h b/thirdparty/RapidJSON/include/rapidjson/internal/regex.h index af7e06de..6446c403 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/regex.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/regex.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/stack.h b/thirdparty/RapidJSON/include/rapidjson/internal/stack.h index 45dca6a8..73abd706 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/stack.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/stack.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/strfunc.h b/thirdparty/RapidJSON/include/rapidjson/internal/strfunc.h index 226439a7..baecb6cc 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/strfunc.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/strfunc.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/strtod.h b/thirdparty/RapidJSON/include/rapidjson/internal/strtod.h index dfca22b6..d61a67a4 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/strtod.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/strtod.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/internal/swap.h b/thirdparty/RapidJSON/include/rapidjson/internal/swap.h index 666e49f9..2cf92f93 100644 --- a/thirdparty/RapidJSON/include/rapidjson/internal/swap.h +++ b/thirdparty/RapidJSON/include/rapidjson/internal/swap.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/istreamwrapper.h b/thirdparty/RapidJSON/include/rapidjson/istreamwrapper.h index c4950b9d..01437ec0 100644 --- a/thirdparty/RapidJSON/include/rapidjson/istreamwrapper.h +++ b/thirdparty/RapidJSON/include/rapidjson/istreamwrapper.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/memorybuffer.h b/thirdparty/RapidJSON/include/rapidjson/memorybuffer.h index 39bee1de..ffbc41ed 100644 --- a/thirdparty/RapidJSON/include/rapidjson/memorybuffer.h +++ b/thirdparty/RapidJSON/include/rapidjson/memorybuffer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/memorystream.h b/thirdparty/RapidJSON/include/rapidjson/memorystream.h index 1d71d8a4..77af6c99 100644 --- a/thirdparty/RapidJSON/include/rapidjson/memorystream.h +++ b/thirdparty/RapidJSON/include/rapidjson/memorystream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/ostreamwrapper.h b/thirdparty/RapidJSON/include/rapidjson/ostreamwrapper.h index 6f4667c0..11ed4d33 100644 --- a/thirdparty/RapidJSON/include/rapidjson/ostreamwrapper.h +++ b/thirdparty/RapidJSON/include/rapidjson/ostreamwrapper.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/pointer.h b/thirdparty/RapidJSON/include/rapidjson/pointer.h index b8143b63..90e5903b 100644 --- a/thirdparty/RapidJSON/include/rapidjson/pointer.h +++ b/thirdparty/RapidJSON/include/rapidjson/pointer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/prettywriter.h b/thirdparty/RapidJSON/include/rapidjson/prettywriter.h index 94eeb69d..fe45df1d 100644 --- a/thirdparty/RapidJSON/include/rapidjson/prettywriter.h +++ b/thirdparty/RapidJSON/include/rapidjson/prettywriter.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/rapidjson.h b/thirdparty/RapidJSON/include/rapidjson/rapidjson.h index c5f4d65f..78aa89a0 100644 --- a/thirdparty/RapidJSON/include/rapidjson/rapidjson.h +++ b/thirdparty/RapidJSON/include/rapidjson/rapidjson.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/reader.h b/thirdparty/RapidJSON/include/rapidjson/reader.h index 30e45e1f..09ace4eb 100644 --- a/thirdparty/RapidJSON/include/rapidjson/reader.h +++ b/thirdparty/RapidJSON/include/rapidjson/reader.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/stream.h b/thirdparty/RapidJSON/include/rapidjson/stream.h index 7f2643e4..1fd70915 100644 --- a/thirdparty/RapidJSON/include/rapidjson/stream.h +++ b/thirdparty/RapidJSON/include/rapidjson/stream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/stringbuffer.h b/thirdparty/RapidJSON/include/rapidjson/stringbuffer.h index 4e38b82c..82ad3ca6 100644 --- a/thirdparty/RapidJSON/include/rapidjson/stringbuffer.h +++ b/thirdparty/RapidJSON/include/rapidjson/stringbuffer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/include/rapidjson/writer.h b/thirdparty/RapidJSON/include/rapidjson/writer.h index 51dd86d5..8b389219 100644 --- a/thirdparty/RapidJSON/include/rapidjson/writer.h +++ b/thirdparty/RapidJSON/include/rapidjson/writer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/thirdparty/RapidJSON/license.txt b/thirdparty/RapidJSON/license.txt new file mode 100644 index 00000000..995dca48 --- /dev/null +++ b/thirdparty/RapidJSON/license.txt @@ -0,0 +1,57 @@ +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. + +If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +A copy of the MIT License is included in this file. + +Other dependencies and licenses: + +Open Source Software Licensed Under the BSD License: +-------------------------------------------------------------------- + +The msinttypes r29 +Copyright (c) 2006-2013 Alexander Chemeris +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Open Source Software Licensed Under the JSON License: +-------------------------------------------------------------------- + +json.org +Copyright (c) 2002 JSON.org +All Rights Reserved. + +JSON_checker +Copyright (c) 2002 JSON.org +All Rights Reserved. + + +Terms of the JSON License: +--------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Terms of the MIT License: +-------------------------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/cxxopts/LICENSE b/thirdparty/csv-parser/LICENSE similarity index 87% rename from thirdparty/cxxopts/LICENSE rename to thirdparty/csv-parser/LICENSE index 324a2035..da835973 100644 --- a/thirdparty/cxxopts/LICENSE +++ b/thirdparty/csv-parser/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2014 Jarryd Beck +MIT License + +Copyright (c) 2017-2019 Vincent La Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7,13 +9,13 @@ 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 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. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/csv-parser/csv.hpp b/thirdparty/csv-parser/csv.hpp new file mode 100644 index 00000000..a3d164ea --- /dev/null +++ b/thirdparty/csv-parser/csv.hpp @@ -0,0 +1,8355 @@ +#pragma once +/* +CSV for C++, version 2.1.0 +https://github.com/vincentlaucsb/csv-parser + +MIT License + +Copyright (c) 2017-2020 Vincent La + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef CSV_HPP +#define CSV_HPP + +/** @file + * @brief Defines functionality needed for basic CSV parsing + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_MMAP_HEADER +#define MIO_MMAP_HEADER + +// #include "mio/page.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER + + +#include +#include +#include +#include + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif // WIN32_LEAN_AND_MEAN +# include +#else // ifdef _WIN32 +# define INVALID_HANDLE_VALUE -1 +#endif // ifdef _WIN32 + +namespace mio { + +// This value may be provided as the `length` parameter to the constructor or +// `map`, in which case a memory mapping of the entire file is created. +enum { map_entire_file = 0 }; + +#ifdef _WIN32 +using file_handle_type = HANDLE; +#else +using file_handle_type = int; +#endif + +// This value represents an invalid file handle type. This can be used to +// determine whether `basic_mmap::file_handle` is valid, for example. +const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; + +template +struct basic_mmap +{ + using value_type = ByteT; + using size_type = size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using difference_type = std::ptrdiff_t; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using iterator_category = std::random_access_iterator_tag; + using handle_type = file_handle_type; + + static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); + +private: + // Points to the first requested byte, and not to the actual start of the mapping. + pointer data_ = nullptr; + + // Length--in bytes--requested by user (which may not be the length of the + // full mapping) and the length of the full mapping. + size_type length_ = 0; + size_type mapped_length_ = 0; + + // Letting user map a file using both an existing file handle and a path + // introcudes some complexity (see `is_handle_internal_`). + // On POSIX, we only need a file handle to create a mapping, while on + // Windows systems the file handle is necessary to retrieve a file mapping + // handle, but any subsequent operations on the mapped region must be done + // through the latter. + handle_type file_handle_ = INVALID_HANDLE_VALUE; +#ifdef _WIN32 + handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; +#endif + + // Letting user map a file using both an existing file handle and a path + // introcudes some complexity in that we must not close the file handle if + // user provided it, but we must close it if we obtained it using the + // provided path. For this reason, this flag is used to determine when to + // close `file_handle_`. + bool is_handle_internal_; + +public: + /** + * The default constructed mmap object is in a non-mapped state, that is, + * any operation that attempts to access nonexistent underlying data will + * result in undefined behaviour/segmentation faults. + */ + basic_mmap() = default; + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * `basic_mmap` has single-ownership semantics, so transferring ownership + * may only be accomplished by moving the object. + */ + basic_mmap(const basic_mmap&) = delete; + basic_mmap(basic_mmap&&); + basic_mmap& operator=(const basic_mmap&) = delete; + basic_mmap& operator=(basic_mmap&&); + + /** + * If this is a read-write mapping, the destructor invokes sync. Regardless + * of the access mode, unmap is invoked as a final step. + */ + ~basic_mmap(); + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept { return file_handle_; } + handle_type mapping_handle() const noexcept; + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return file_handle_ != invalid_handle; } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return length() == 0; } + + /** Returns true if a mapping was established. */ + bool is_mapped() const noexcept; + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return length(); } + size_type length() const noexcept { return length_; } + size_type mapped_length() const noexcept { return mapped_length_; } + + /** Returns the offset relative to the start of the mapping. */ + size_type mapping_offset() const noexcept + { + return mapped_length_ - length_; + } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return data_; } + const_pointer data() const noexcept { return data_; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator begin() noexcept { return data(); } + const_iterator begin() const noexcept { return data(); } + const_iterator cbegin() const noexcept { return data(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return data() + length(); } + const_iterator end() const noexcept { return data() + length(); } + const_iterator cend() const noexcept { return data() + length(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept + { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept + { return const_reverse_iterator(end()); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept + { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept + { return const_reverse_iterator(begin()); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return data_[i]; } + const_reference operator[](const size_type i) const noexcept { return data_[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap(); + + void swap(basic_mmap& other); + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template + typename std::enable_if::type + sync(std::error_code& error); + + /** + * All operators compare the address of the first byte and size of the two mapped + * regions. + */ + +private: + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer get_mapping_start() noexcept + { + return !data() ? nullptr : data() - mapping_offset(); + } + + const_pointer get_mapping_start() const noexcept + { + return !data() ? nullptr : data() - mapping_offset(); + } + + /** + * The destructor syncs changes to disk if `AccessMode` is `write`, but not + * if it's `read`, but since the destructor cannot be templated, we need to + * do SFINAE in a dedicated function, where one syncs and the other is a noop. + */ + template + typename std::enable_if::type + conditional_sync(); + template + typename std::enable_if::type conditional_sync(); +}; + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b); + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_source = basic_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_sink = basic_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using mmap_source = basic_mmap_source; +using ummap_source = basic_mmap_source; + +using mmap_sink = basic_mmap_sink; +using ummap_sink = basic_mmap_sink; + +/** + * Convenience factory method that constructs a mapping for any `basic_mmap` or + * `basic_mmap` type. + */ +template< + typename MMap, + typename MappingToken +> MMap make_mmap(const MappingToken& token, + int64_t offset, int64_t length, std::error_code& error) +{ + MMap mmap; + mmap.map(token, offset, length, error); + return mmap; +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_source::handle_type`. + */ +template +mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, + mmap_source::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) +{ + return make_mmap_source(token, 0, map_entire_file, error); +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_sink::handle_type`. + */ +template +mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, + mmap_sink::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) +{ + return make_mmap_sink(token, 0, map_entire_file, error); +} + +} // namespace mio + +// #include "detail/mmap.ipp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_BASIC_MMAP_IMPL +#define MIO_BASIC_MMAP_IMPL + +// #include "mio/mmap.hpp" + +// #include "mio/page.hpp" + +// #include "mio/detail/string_util.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_STRING_UTIL_HEADER +#define MIO_STRING_UTIL_HEADER + +#include + +namespace mio { +namespace detail { + +template< + typename S, + typename C = typename std::decay::type, + typename = decltype(std::declval().data()), + typename = typename std::enable_if< + std::is_same::value +#ifdef _WIN32 + || std::is_same::value +#endif + >::type +> struct char_type_helper { + using type = typename C::value_type; +}; + +template +struct char_type { + using type = typename char_type_helper::type; +}; + +// TODO: can we avoid this brute force approach? +template<> +struct char_type { + using type = char; +}; + +template<> +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +#ifdef _WIN32 +template<> +struct char_type { + using type = wchar_t; +}; + +template<> +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; +#endif // _WIN32 + +template +struct is_c_str_helper +{ + static constexpr bool value = std::is_same< + CharT*, + // TODO: I'm so sorry for this... Can this be made cleaner? + typename std::add_pointer< + typename std::remove_cv< + typename std::remove_pointer< + typename std::decay< + S + >::type + >::type + >::type + >::type + >::value; +}; + +template +struct is_c_str +{ + static constexpr bool value = is_c_str_helper::value; +}; + +#ifdef _WIN32 +template +struct is_c_wstr +{ + static constexpr bool value = is_c_str_helper::value; +}; +#endif // _WIN32 + +template +struct is_c_str_or_c_wstr +{ + static constexpr bool value = is_c_str::value +#ifdef _WIN32 + || is_c_wstr::value +#endif + ; +}; + +template< + typename String, + typename = decltype(std::declval().data()), + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(const String& path) +{ + return path.data(); +} + +template< + typename String, + typename = decltype(std::declval().empty()), + typename = typename std::enable_if::value>::type +> bool empty(const String& path) +{ + return path.empty(); +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(String path) +{ + return path; +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> bool empty(String path) +{ + return !path || (*path == 0); +} + +} // namespace detail +} // namespace mio + +#endif // MIO_STRING_UTIL_HEADER + + +#include + +#ifndef _WIN32 +# include +# include +# include +# include +#endif + +namespace mio { +namespace detail { + +#ifdef _WIN32 +namespace win { + +/** Returns the 4 upper bytes of an 8-byte integer. */ +inline DWORD int64_high(int64_t n) noexcept +{ + return n >> 32; +} + +/** Returns the 4 lower bytes of an 8-byte integer. */ +inline DWORD int64_low(int64_t n) noexcept +{ + return n & 0xffffffff; +} + +template< + typename String, + typename = typename std::enable_if< + std::is_same::type, char>::value + >::type +> file_handle_type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileA(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +template +typename std::enable_if< + std::is_same::type, wchar_t>::value, + file_handle_type +>::type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileW(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +} // win +#endif // _WIN32 + +/** + * Returns the last platform specific system error (errno on POSIX and + * GetLastError on Win) as a `std::error_code`. + */ +inline std::error_code last_error() noexcept +{ + std::error_code error; +#ifdef _WIN32 + error.assign(GetLastError(), std::system_category()); +#else + error.assign(errno, std::system_category()); +#endif + return error; +} + +template +file_handle_type open_file(const String& path, const access_mode mode, + std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return invalid_handle; + } +#ifdef _WIN32 + const auto handle = win::open_file_helper(path, mode); +#else // POSIX + const auto handle = ::open(c_str(path), + mode == access_mode::read ? O_RDONLY : O_RDWR); +#endif + if(handle == invalid_handle) + { + error = detail::last_error(); + } + return handle; +} + +inline size_t query_file_size(file_handle_type handle, std::error_code& error) +{ + error.clear(); +#ifdef _WIN32 + LARGE_INTEGER file_size; + if(::GetFileSizeEx(handle, &file_size) == 0) + { + error = detail::last_error(); + return 0; + } + return static_cast(file_size.QuadPart); +#else // POSIX + struct stat sbuf; + if(::fstat(handle, &sbuf) == -1) + { + error = detail::last_error(); + return 0; + } + return sbuf.st_size; +#endif +} + +struct mmap_context +{ + char* data; + int64_t length; + int64_t mapped_length; +#ifdef _WIN32 + file_handle_type file_mapping_handle; +#endif +}; + +inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, + const int64_t length, const access_mode mode, std::error_code& error) +{ + const int64_t aligned_offset = make_offset_page_aligned(offset); + const int64_t length_to_map = offset - aligned_offset + length; +#ifdef _WIN32 + const int64_t max_file_size = offset + length; + const auto file_mapping_handle = ::CreateFileMapping( + file_handle, + 0, + mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, + win::int64_high(max_file_size), + win::int64_low(max_file_size), + 0); + if(file_mapping_handle == invalid_handle) + { + error = detail::last_error(); + return {}; + } + char* mapping_start = static_cast(::MapViewOfFile( + file_mapping_handle, + mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, + win::int64_high(aligned_offset), + win::int64_low(aligned_offset), + length_to_map)); + if(mapping_start == nullptr) + { + // Close file handle if mapping it failed. + ::CloseHandle(file_mapping_handle); + error = detail::last_error(); + return {}; + } +#else // POSIX + char* mapping_start = static_cast(::mmap( + 0, // Don't give hint as to where to map. + length_to_map, + mode == access_mode::read ? PROT_READ : PROT_WRITE, + MAP_SHARED, + file_handle, + aligned_offset)); + if(mapping_start == MAP_FAILED) + { + error = detail::last_error(); + return {}; + } +#endif + mmap_context ctx; + ctx.data = mapping_start + offset - aligned_offset; + ctx.length = length; + ctx.mapped_length = length_to_map; +#ifdef _WIN32 + ctx.file_mapping_handle = file_mapping_handle; +#endif + return ctx; +} + +} // namespace detail + +// -- basic_mmap -- + +template +basic_mmap::~basic_mmap() +{ + conditional_sync(); + unmap(); +} + +template +basic_mmap::basic_mmap(basic_mmap&& other) + : data_(std::move(other.data_)) + , length_(std::move(other.length_)) + , mapped_length_(std::move(other.mapped_length_)) + , file_handle_(std::move(other.file_handle_)) +#ifdef _WIN32 + , file_mapping_handle_(std::move(other.file_mapping_handle_)) +#endif + , is_handle_internal_(std::move(other.is_handle_internal_)) +{ + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif +} + +template +basic_mmap& +basic_mmap::operator=(basic_mmap&& other) +{ + if(this != &other) + { + // First the existing mapping needs to be removed. + unmap(); + data_ = std::move(other.data_); + length_ = std::move(other.length_); + mapped_length_ = std::move(other.mapped_length_); + file_handle_ = std::move(other.file_handle_); +#ifdef _WIN32 + file_mapping_handle_ = std::move(other.file_mapping_handle_); +#endif + is_handle_internal_ = std::move(other.is_handle_internal_); + + // The moved from basic_mmap's fields need to be reset, because + // otherwise other's destructor will unmap the same mapping that was + // just moved into this. + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif + other.is_handle_internal_ = false; + } + return *this; +} + +template +typename basic_mmap::handle_type +basic_mmap::mapping_handle() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_; +#else + return file_handle_; +#endif +} + +template +template +void basic_mmap::map(const String& path, const size_type offset, + const size_type length, std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + const auto handle = detail::open_file(path, AccessMode, error); + if(error) + { + return; + } + + map(handle, offset, length, error); + // This MUST be after the call to map, as that sets this to true. + if(!error) + { + is_handle_internal_ = true; + } +} + +template +void basic_mmap::map(const handle_type handle, + const size_type offset, const size_type length, std::error_code& error) +{ + error.clear(); + if(handle == invalid_handle) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + const auto file_size = detail::query_file_size(handle, error); + if(error) + { + return; + } + + if(offset + length > file_size) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + + const auto ctx = detail::memory_map(handle, offset, + length == map_entire_file ? (file_size - offset) : length, + AccessMode, error); + if(!error) + { + // We must unmap the previous mapping that may have existed prior to this call. + // Note that this must only be invoked after a new mapping has been created in + // order to provide the strong guarantee that, should the new mapping fail, the + // `map` function leaves this instance in a state as though the function had + // never been invoked. + unmap(); + file_handle_ = handle; + is_handle_internal_ = false; + data_ = reinterpret_cast(ctx.data); + length_ = ctx.length; + mapped_length_ = ctx.mapped_length; +#ifdef _WIN32 + file_mapping_handle_ = ctx.file_mapping_handle; +#endif + } +} + +template +template +typename std::enable_if::type +basic_mmap::sync(std::error_code& error) +{ + error.clear(); + if(!is_open()) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + if(data()) + { +#ifdef _WIN32 + if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 + || ::FlushFileBuffers(file_handle_) == 0) +#else // POSIX + if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) +#endif + { + error = detail::last_error(); + return; + } + } +#ifdef _WIN32 + if(::FlushFileBuffers(file_handle_) == 0) + { + error = detail::last_error(); + } +#endif +} + +template +void basic_mmap::unmap() +{ + if(!is_open()) { return; } + // TODO do we care about errors here? +#ifdef _WIN32 + if(is_mapped()) + { + ::UnmapViewOfFile(get_mapping_start()); + ::CloseHandle(file_mapping_handle_); + } +#else // POSIX + if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } +#endif + + // If `file_handle_` was obtained by our opening it (when map is called with + // a path, rather than an existing file handle), we need to close it, + // otherwise it must not be closed as it may still be used outside this + // instance. + if(is_handle_internal_) + { +#ifdef _WIN32 + ::CloseHandle(file_handle_); +#else // POSIX + ::close(file_handle_); +#endif + } + + // Reset fields to their default values. + data_ = nullptr; + length_ = mapped_length_ = 0; + file_handle_ = invalid_handle; +#ifdef _WIN32 + file_mapping_handle_ = invalid_handle; +#endif +} + +template +bool basic_mmap::is_mapped() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_ != invalid_handle; +#else // POSIX + return is_open(); +#endif +} + +template +void basic_mmap::swap(basic_mmap& other) +{ + if(this != &other) + { + using std::swap; + swap(data_, other.data_); + swap(file_handle_, other.file_handle_); +#ifdef _WIN32 + swap(file_mapping_handle_, other.file_mapping_handle_); +#endif + swap(length_, other.length_); + swap(mapped_length_, other.mapped_length_); + swap(is_handle_internal_, other.is_handle_internal_); + } +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // This is invoked from the destructor, so not much we can do about + // failures here. + std::error_code ec; + sync(ec); +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // noop +} + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b) +{ + return a.data() == b.data() + && a.size() == b.size(); +} + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a == b); +} + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() < b.size(); } + return a.data() < b.data(); +} + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a > b); +} + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() > b.size(); } + return a.data() > b.data(); +} + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a < b); +} + +} // namespace mio + +#endif // MIO_BASIC_MMAP_IMPL + + +#endif // MIO_MMAP_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MIO_SHARED_MMAP_HEADER +#define MIO_SHARED_MMAP_HEADER + +// #include "mio/mmap.hpp" + + +#include // std::error_code +#include // std::shared_ptr + +namespace mio { + +/** + * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with + * `std::shared_ptr` semantics. + * + * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if + * shared semantics are not required. + */ +template< + access_mode AccessMode, + typename ByteT +> class basic_shared_mmap +{ + using impl_type = basic_mmap; + std::shared_ptr pimpl_; + +public: + using value_type = typename impl_type::value_type; + using size_type = typename impl_type::size_type; + using reference = typename impl_type::reference; + using const_reference = typename impl_type::const_reference; + using pointer = typename impl_type::pointer; + using const_pointer = typename impl_type::const_pointer; + using difference_type = typename impl_type::difference_type; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + using iterator_category = typename impl_type::iterator_category; + using handle_type = typename impl_type::handle_type; + using mmap_type = impl_type; + + basic_shared_mmap() = default; + basic_shared_mmap(const basic_shared_mmap&) = default; + basic_shared_mmap& operator=(const basic_shared_mmap&) = default; + basic_shared_mmap(basic_shared_mmap&&) = default; + basic_shared_mmap& operator=(basic_shared_mmap&&) = default; + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap(mmap_type&& mmap) + : pimpl_(std::make_shared(std::move(mmap))) + {} + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap& operator=(mmap_type&& mmap) + { + pimpl_ = std::make_shared(std::move(mmap)); + return *this; + } + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap& operator=(std::shared_ptr mmap) + { + pimpl_ = std::move(mmap); + return *this; + } + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * If this is a read-write mapping and the last reference to the mapping, + * the destructor invokes sync. Regardless of the access mode, unmap is + * invoked as a final step. + */ + ~basic_shared_mmap() = default; + + /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ + std::shared_ptr get_shared_ptr() { return pimpl_; } + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept + { + return pimpl_ ? pimpl_->file_handle() : invalid_handle; + } + + handle_type mapping_handle() const noexcept + { + return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; + } + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type mapped_length() const noexcept + { + return pimpl_ ? pimpl_->mapped_length() : 0; + } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return pimpl_->data(); } + const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + iterator begin() noexcept { return pimpl_->begin(); } + const_iterator begin() const noexcept { return pimpl_->begin(); } + const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return pimpl_->end(); } + const_iterator end() const noexcept { return pimpl_->end(); } + const_iterator cend() const noexcept { return pimpl_->cend(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } + const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } + const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return pimpl_->rend(); } + const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } + const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } + const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(path, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map_impl(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(handle, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map_impl(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap() { if(pimpl_) pimpl_->unmap(); } + + void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } + + /** All operators compare the underlying `basic_mmap`'s addresses. */ + + friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ == b.pimpl_; + } + + friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return !(a == b); + } + + friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ < b.pimpl_; + } + + friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ <= b.pimpl_; + } + + friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ > b.pimpl_; + } + + friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ >= b.pimpl_; + } + +private: + template + void map_impl(const MappingToken& token, const size_type offset, + const size_type length, std::error_code& error) + { + if(!pimpl_) + { + mmap_type mmap = make_mmap(token, offset, length, error); + if(error) { return; } + pimpl_ = std::make_shared(std::move(mmap)); + } + else + { + pimpl_->map(token, offset, length, error); + } + } +}; + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_source = basic_shared_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_sink = basic_shared_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using shared_mmap_source = basic_shared_mmap_source; +using shared_ummap_source = basic_shared_mmap_source; + +using shared_mmap_sink = basic_shared_mmap_sink; +using shared_ummap_sink = basic_shared_mmap_sink; + +} // namespace mio + +#endif // MIO_SHARED_MMAP_HEADER + +/** @file + * @brief Contains the main CSV parsing algorithm and various utility functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** @file + * A standalone header file containing shared code + */ + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#define WIN32_LEAN_AND_MEAN +#undef max +#undef min +#elif defined(__linux__) +#include +#endif + + /** Helper macro which should be #defined as "inline" + * in the single header version + */ +#define CSV_INLINE inline + +#include + +// Copyright 2017-2019 by Martin Moene +// +// string-view lite, a C++17-like string_view for C++98 and later. +// For more information see https://github.com/martinmoene/string-view-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef NONSTD_SV_LITE_H_INCLUDED +#define NONSTD_SV_LITE_H_INCLUDED + +#define string_view_lite_MAJOR 1 +#define string_view_lite_MINOR 1 +#define string_view_lite_PATCH 0 + +#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) + +#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) +#define nssv_STRINGIFY_( x ) #x + +// string-view lite configuration: + +#define nssv_STRING_VIEW_DEFAULT 0 +#define nssv_STRING_VIEW_NONSTD 1 +#define nssv_STRING_VIEW_STD 2 + +#if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) +# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) +#endif + +#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW ) +# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... +#endif + +#ifndef nssv_CONFIG_STD_SV_OPERATOR +# define nssv_CONFIG_STD_SV_OPERATOR 0 +#endif + +#ifndef nssv_CONFIG_USR_SV_OPERATOR +# define nssv_CONFIG_USR_SV_OPERATOR 1 +#endif + +#ifdef nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef nssv_CONFIG_NO_EXCEPTIONS +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +# define nssv_CONFIG_NO_EXCEPTIONS 0 +# else +# define nssv_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nssv_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define nssv_CPLUSPLUS __cplusplus +# endif +#endif + +#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) +#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) +#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) +#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) + +// use C++17 std::string_view if available and requested: + +#if nssv_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define nssv_HAVE_STD_STRING_VIEW 1 +# else +# define nssv_HAVE_STD_STRING_VIEW 0 +# endif +#else +# define nssv_HAVE_STD_STRING_VIEW 0 +#endif + +#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) + +#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) +#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH + +// +// Use C++17 std::string_view: +// + +#if nssv_USES_STD_STRING_VIEW + +#include + +// Extensions for std::string: + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( std::basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +template< class CharT, class Traits, class Allocator > +std::basic_string_view +to_string_view( std::basic_string const & s ) +{ + return std::basic_string_view( s.data(), s.size() ); +} + +// Literal operators sv and _sv: + +#if nssv_CONFIG_STD_SV_OPERATOR + +using namespace std::literals::string_view_literals; + +#endif + +#if nssv_CONFIG_USR_SV_OPERATOR + +inline namespace literals { +inline namespace string_view_literals { + + +constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) +{ + return std::string_view{ str, len }; +} + +constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) +{ + return std::u16string_view{ str, len }; +} + +constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) +{ + return std::u32string_view{ str, len }; +} + +constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) +{ + return std::wstring_view{ str, len }; +} + +}} // namespace literals::string_view_literals + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +} // namespace nonstd + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +using std::string_view; +using std::wstring_view; +using std::u16string_view; +using std::u32string_view; +using std::basic_string_view; + +// literal "sv" and "_sv", see above + +using std::operator==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; + +using std::operator<<; + +} // namespace nonstd + +#else // nssv_HAVE_STD_STRING_VIEW + +// +// Before C++17: use string_view lite: +// + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define nssv_COMPILER_MSVC_VER (_MSC_VER ) +# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define nssv_COMPILER_MSVC_VER 0 +# define nssv_COMPILER_MSVC_VERSION 0 +#endif + +#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) + +#if defined(__clang__) +# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define nssv_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define nssv_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define nssv_HAS_CPP0X _HAS_CPP0X +#else +# define nssv_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: + +#if nssv_COMPILER_MSVC_VER >= 1900 +# undef nssv_CPP11_OR_GREATER +# define nssv_CPP11_OR_GREATER 1 +#endif + +#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) +#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) +#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) +#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) +#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) +#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) + +#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) +#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 +#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 +#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 +#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 +#define nssv_HAVE_NULLPTR nssv_CPP11_100 +#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 +#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 +#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 +#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 +#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 + +#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#endif + +// Presence of C++14 language features: + +#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 + +// Presence of C++17 language features: + +#define nssv_HAVE_NODISCARD nssv_CPP17_000 + +// Presence of C++ library features: + +#define nssv_HAVE_STD_HASH nssv_CPP11_120 + +// C++ feature usage: + +#if nssv_HAVE_CONSTEXPR_11 +# define nssv_constexpr constexpr +#else +# define nssv_constexpr /*constexpr*/ +#endif + +#if nssv_HAVE_CONSTEXPR_14 +# define nssv_constexpr14 constexpr +#else +# define nssv_constexpr14 /*constexpr*/ +#endif + +#if nssv_HAVE_EXPLICIT_CONVERSION +# define nssv_explicit explicit +#else +# define nssv_explicit /*explicit*/ +#endif + +#if nssv_HAVE_INLINE_NAMESPACE +# define nssv_inline_ns inline +#else +# define nssv_inline_ns /*inline*/ +#endif + +#if nssv_HAVE_NOEXCEPT +# define nssv_noexcept noexcept +#else +# define nssv_noexcept /*noexcept*/ +#endif + +//#if nssv_HAVE_REF_QUALIFIER +//# define nssv_ref_qual & +//# define nssv_refref_qual && +//#else +//# define nssv_ref_qual /*&*/ +//# define nssv_refref_qual /*&&*/ +//#endif + +#if nssv_HAVE_NULLPTR +# define nssv_nullptr nullptr +#else +# define nssv_nullptr NULL +#endif + +#if nssv_HAVE_NODISCARD +# define nssv_nodiscard [[nodiscard]] +#else +# define nssv_nodiscard /*[[nodiscard]]*/ +#endif + +// Additional includes: + +#include +#include +#include +#include +#include +#include // std::char_traits<> + +#if ! nssv_CONFIG_NO_EXCEPTIONS +# include +#endif + +#if nssv_CPP11_OR_GREATER +# include +#endif + +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wreserved-user-defined-literal" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wuser-defined-literals" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wliteral-suffix" +#endif // __clang__ + +#if nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) +# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +#else +# define nssv_SUPPRESS_MSGSL_WARNING(expr) +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) +# define nssv_DISABLE_MSVC_WARNINGS(codes) +#endif + +#if defined(__clang__) +# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) +#else +# define nssv_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not +// start with an underscore are reserved +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead + +nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) +//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) +//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) + +namespace nonstd { namespace sv_lite { + +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; + +// +// basic_string_view: +// + +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: + + typedef Traits traits_type; + typedef CharT value_type; + + typedef CharT * pointer; + typedef CharT const * const_pointer; + typedef CharT & reference; + typedef CharT const & const_reference; + + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef std::reverse_iterator< const_iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // 24.4.2.1 Construction and assignment: + + nssv_constexpr basic_string_view() nssv_noexcept + : data_( nssv_nullptr ) + , size_( 0 ) + {} + +#if nssv_CPP11_OR_GREATER + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept + : data_( other.data_) + , size_( other.size_) + {} +#endif + + nssv_constexpr basic_string_view( CharT const * s, size_type count ) + : data_( s ) + , size_( count ) + {} + + nssv_constexpr basic_string_view( CharT const * s) + : data_( s ) + , size_( Traits::length(s) ) + {} + + // Assignment: + +#if nssv_CPP11_OR_GREATER + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept + { + data_ = other.data_; + size_ = other.size_; + return *this; + } +#endif + + // 24.4.2.2 Iterator support: + + nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } + nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } + + nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } + nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } + + nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } + nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } + + nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } + nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } + + // 24.4.2.3 Capacity: + + nssv_constexpr size_type size() const nssv_noexcept { return size_; } + nssv_constexpr size_type length() const nssv_noexcept { return size_; } + nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } + + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; + } + + // 24.4.2.4 Element access: + + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); + } + + nssv_constexpr14 const_reference at( size_type pos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos < size() ); +#else + if ( pos >= size() ) + { + throw std::out_of_range("nonst::string_view::at()"); + } +#endif + return data_at( pos ); + } + + nssv_constexpr const_reference front() const { return data_at( 0 ); } + nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } + + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } + + // 24.4.2.5 Modifiers: + + nssv_constexpr14 void remove_prefix( size_type n ) + { + assert( n <= size() ); + data_ += n; + size_ -= n; + } + + nssv_constexpr14 void remove_suffix( size_type n ) + { + assert( n <= size() ); + size_ -= n; + } + + nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } + + // 24.4.2.6 String operations: + + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonst::string_view::copy()"); + } +#endif + const size_type rlen = (std::min)( n, size() - pos ); + + (void) Traits::copy( dest, data() + pos, rlen ); + + return rlen; + } + + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonst::string_view::substr()"); + } +#endif + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); + } + + // compare(), 6x: + + nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) + { + if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) + return result; + + return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) + { + return substr( pos1, n1 ).compare( other ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) + { + return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); + } + + nssv_constexpr int compare( CharT const * s ) const // (4) + { + return compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) + { + return substr( pos1, n1 ).compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) + { + return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); + } + + // 24.4.2.7 Searching: + + // starts_with(), 3x, since C++20: + + nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( 0, v.size(), v ) == 0; + } + + nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) + { + return starts_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); + } + + // ends_with(), 3x, since C++20: + + nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; + } + + nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) + { + return ends_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } + + // find(), 4x: + + nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return assert( v.size() == 0 || v.data() != nssv_nullptr ) + , pos >= size() + ? npos + : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find( basic_string_view( s, n ), pos ); + } + + nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); + } + + // rfind(), 4x: + + nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + if ( size() < v.size() ) + return npos; + + if ( v.empty() ) + return (std::min)( size(), pos ); + + const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); + const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); + + return result != last ? size_type( result - cbegin() ) : npos; + } + + nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return rfind( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) + { + return rfind( basic_string_view( s, n ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) + { + return rfind( basic_string_view( s ), pos ); + } + + // find_first_of(), 4x: + + nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find_first_of( basic_string_view( s, n ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_of( basic_string_view( s ), pos ); + } + + // find_last_of(), 4x: + + nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_of( v, size() - 1 ) + : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_of( basic_string_view( s ), pos ); + } + + // find_first_not_of(), 4x: + + nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_first_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_not_of( basic_string_view( s ), pos ); + } + + // find_last_not_of(), 4x: + + nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_not_of( v, size() - 1 ) + : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_not_of( basic_string_view( s ), pos ); + } + + // Constants: + +#if nssv_CPP17_OR_GREATER + static nssv_constexpr size_type npos = size_type(-1); +#elif nssv_CPP11_OR_GREATER + enum : size_type { npos = size_type(-1) }; +#else + enum { npos = size_type(-1) }; +#endif + +private: + struct not_in_view + { + const basic_string_view v; + + nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} + + nssv_constexpr bool operator()( CharT c ) const + { + return npos == v.find_first_of( c ); + } + }; + + nssv_constexpr size_type to_pos( const_iterator it ) const + { + return it == cend() ? npos : size_type( it - cbegin() ); + } + + nssv_constexpr size_type to_pos( const_reverse_iterator it ) const + { + return it == crend() ? npos : size_type( crend() - it - 1 ); + } + + nssv_constexpr const_reference data_at( size_type pos ) const + { +#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) + return data_[pos]; +#else + return assert( pos < size() ), data_[pos]; +#endif + } + +private: + const_pointer data_; + size_type size_; + +public: +#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + + template< class Allocator > + basic_string_view( std::basic_string const & s ) nssv_noexcept + : data_( s.data() ) + , size_( s.size() ) + {} + +#if nssv_HAVE_EXPLICIT_CONVERSION + + template< class Allocator > + explicit operator std::basic_string() const + { + return to_string( Allocator() ); + } + +#endif // nssv_HAVE_EXPLICIT_CONVERSION + +#if nssv_CPP11_OR_GREATER + + template< class Allocator = std::allocator > + std::basic_string + to_string( Allocator const & a = Allocator() ) const + { + return std::basic_string( begin(), end(), a ); + } + +#else + + std::basic_string + to_string() const + { + return std::basic_string( begin(), end() ); + } + + template< class Allocator > + std::basic_string + to_string( Allocator const & a ) const + { + return std::basic_string( begin(), end(), a ); + } + +#endif // nssv_CPP11_OR_GREATER + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +}; + +// +// Non-member functions: +// + +// 24.4.3 Non-member comparison functions: +// lexicographically compare two string views (function template): + +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator!= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator< ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator<= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator> ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator>= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +// Let S be basic_string_view, and sv be an instance of S. +// Implementations shall provide sufficient additional overloads marked +// constexpr and noexcept so that an object t with an implicit conversion +// to S can be compared according to Table 67. + +#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type + +#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 ) +# define nssv_MSVC_ORDER(x) , int=x +#else +# define nssv_MSVC_ORDER(x) /*, int=x*/ +#endif + +// == + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator==( + basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator==( + nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +// != + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator!= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator!= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0 ; } + +// < + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator< ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator< ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +// <= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator<= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator<= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +// > + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator> ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator> ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +// >= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator>= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator>= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +#undef nssv_MSVC_ORDER +#undef nssv_BASIC_STRING_VIEW_I + +#endif // nssv_CPP11_OR_GREATER + +// 24.4.4 Inserters and extractors: + +namespace detail { + +template< class Stream > +void write_padding( Stream & os, std::streamsize n ) +{ + for ( std::streamsize i = 0; i < n; ++i ) + os.rdbuf()->sputc( os.fill() ); +} + +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); + + if ( !os ) + return os; + + const std::streamsize length = static_cast( sv.length() ); + + // Whether, and how, to pad: + const bool pad = ( length < os.width() ); + const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; + + if ( left_pad ) + write_padding( os, os.width() - length ); + + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); + + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); + + // Reset output stream width: + os.width( 0 ); + + return os; +} + +} // namespace detail + +template< class CharT, class Traits > +std::basic_ostream & +operator<<( + std::basic_ostream& os, + basic_string_view sv ) +{ + return detail::write_to_stream( os, sv ); +} + +// Several typedefs for common character types are provided: + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; +#if nssv_HAVE_WCHAR16_T +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; +#endif + +}} // namespace nonstd::sv_lite + +// +// 24.4.6 Suffix for basic_string_view literals: +// + +#if nssv_HAVE_USER_DEFINED_LITERALS + +namespace nonstd { +nssv_inline_ns namespace literals { +nssv_inline_ns namespace string_view_literals { + +#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +#if nssv_CONFIG_USR_SV_OPERATOR + +nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +}}} // namespace nonstd::literals::string_view_literals + +#endif + +// +// Extensions for std::string: +// + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { +namespace sv_lite { + +// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): + +#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 + +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +#else + +template< class CharT, class Traits > +std::basic_string +to_string( basic_string_view v ) +{ + return std::basic_string( v.begin(), v.end() ); +} + +template< class CharT, class Traits, class Allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +#endif // nssv_CPP11_OR_GREATER + +template< class CharT, class Traits, class Allocator > +basic_string_view +to_string_view( std::basic_string const & s ) +{ + return basic_string_view( s.data(), s.size() ); +} + +}} // namespace nonstd::sv_lite + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +// +// make types and algorithms available in namespace nonstd: +// + +namespace nonstd { + +using sv_lite::basic_string_view; +using sv_lite::string_view; +using sv_lite::wstring_view; + +#if nssv_HAVE_WCHAR16_T +using sv_lite::u16string_view; +#endif +#if nssv_HAVE_WCHAR32_T +using sv_lite::u32string_view; +#endif + +// literal "sv" + +using sv_lite::operator==; +using sv_lite::operator!=; +using sv_lite::operator<; +using sv_lite::operator<=; +using sv_lite::operator>; +using sv_lite::operator>=; + +using sv_lite::operator<<; + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +using sv_lite::to_string; +using sv_lite::to_string_view; +#endif + +} // namespace nonstd + +// 24.4.5 Hash support (C++11): + +// Note: The hash value of a string view object is equal to the hash value of +// the corresponding string object. + +#if nssv_HAVE_STD_HASH + +#include + +namespace std { + +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept + { + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept + { + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept + { + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept + { + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; + +} // namespace std + +#endif // nssv_HAVE_STD_HASH + +nssv_RESTORE_WARNINGS() + +#endif // nssv_HAVE_STD_STRING_VIEW +#endif // NONSTD_SV_LITE_H_INCLUDED + + + // If there is another version of Hedley, then the newer one + // takes precedence. + // See: https://github.com/nemequ/hedley +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < 9) +#if defined(HEDLEY_VERSION) +# undef HEDLEY_VERSION +#endif +#define HEDLEY_VERSION 9 + +#if defined(HEDLEY_STRINGIFY_EX) +# undef HEDLEY_STRINGIFY_EX +#endif +#define HEDLEY_STRINGIFY_EX(x) #x + +#if defined(HEDLEY_STRINGIFY) +# undef HEDLEY_STRINGIFY +#endif +#define HEDLEY_STRINGIFY(x) HEDLEY_STRINGIFY_EX(x) + +#if defined(HEDLEY_CONCAT_EX) +# undef HEDLEY_CONCAT_EX +#endif +#define HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(HEDLEY_CONCAT) +# undef HEDLEY_CONCAT +#endif +#define HEDLEY_CONCAT(a,b) HEDLEY_CONCAT_EX(a,b) + +#if defined(HEDLEY_VERSION_ENCODE) +# undef HEDLEY_VERSION_ENCODE +#endif +#define HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(HEDLEY_VERSION_DECODE_MAJOR) +# undef HEDLEY_VERSION_DECODE_MAJOR +#endif +#define HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(HEDLEY_VERSION_DECODE_MINOR) +# undef HEDLEY_VERSION_DECODE_MINOR +#endif +#define HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(HEDLEY_VERSION_DECODE_REVISION) +# undef HEDLEY_VERSION_DECODE_REVISION +#endif +#define HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(HEDLEY_GNUC_VERSION) +# undef HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +# define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +# define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(HEDLEY_GNUC_VERSION_CHECK) +# undef HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(HEDLEY_GNUC_VERSION) +# define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (HEDLEY_GNUC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_MSVC_VERSION) +# undef HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(HEDLEY_MSVC_VERSION_CHECK) +# undef HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(HEDLEY_INTEL_VERSION) +# undef HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +# define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) +# define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(HEDLEY_INTEL_VERSION_CHECK) +# undef HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(HEDLEY_INTEL_VERSION) +# define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_PGI_VERSION) +# undef HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) +# define HEDLEY_PGI_VERSION HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(HEDLEY_PGI_VERSION_CHECK) +# undef HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(HEDLEY_PGI_VERSION) +# define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (HEDLEY_PGI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_SUNPRO_VERSION) +# undef HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(HEDLEY_SUNPRO_VERSION_CHECK) +# undef HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(HEDLEY_SUNPRO_VERSION) +# define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (HEDLEY_SUNPRO_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_EMSCRIPTEN_VERSION) +# undef HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) +# define HEDLEY_EMSCRIPTEN_VERSION HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(HEDLEY_EMSCRIPTEN_VERSION_CHECK) +# undef HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(HEDLEY_EMSCRIPTEN_VERSION) +# define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (HEDLEY_EMSCRIPTEN_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_ARM_VERSION) +# undef HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) +# define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) +# define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(HEDLEY_ARM_VERSION_CHECK) +# undef HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(HEDLEY_ARM_VERSION) +# define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (HEDLEY_ARM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_IBM_VERSION) +# undef HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(HEDLEY_IBM_VERSION_CHECK) +# undef HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(HEDLEY_IBM_VERSION) +# define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (HEDLEY_IBM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_TI_VERSION) +# undef HEDLEY_TI_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) +# define HEDLEY_TI_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(HEDLEY_TI_VERSION_CHECK) +# undef HEDLEY_TI_VERSION_CHECK +#endif +#if defined(HEDLEY_TI_VERSION) +# define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_CRAY_VERSION) +# undef HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) +# if defined(_RELEASE_PATCHLEVEL) +# define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) +# else +# define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) +# endif +#endif + +#if defined(HEDLEY_CRAY_VERSION_CHECK) +# undef HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(HEDLEY_CRAY_VERSION) +# define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (HEDLEY_CRAY_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_IAR_VERSION) +# undef HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) +# if __VER__ > 1000 +# define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) +# else +# define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) +# endif +#endif + +#if defined(HEDLEY_IAR_VERSION_CHECK) +# undef HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(HEDLEY_IAR_VERSION) +# define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (HEDLEY_IAR_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_TINYC_VERSION) +# undef HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) +# define HEDLEY_TINYC_VERSION HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(HEDLEY_TINYC_VERSION_CHECK) +# undef HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(HEDLEY_TINYC_VERSION) +# define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (HEDLEY_TINYC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_DMC_VERSION) +# undef HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) +# define HEDLEY_DMC_VERSION HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(HEDLEY_DMC_VERSION_CHECK) +# undef HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(HEDLEY_DMC_VERSION) +# define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (HEDLEY_DMC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_COMPCERT_VERSION) +# undef HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) +# define HEDLEY_COMPCERT_VERSION HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(HEDLEY_COMPCERT_VERSION_CHECK) +# undef HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(HEDLEY_COMPCERT_VERSION) +# define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (HEDLEY_COMPCERT_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_PELLES_VERSION) +# undef HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) +# define HEDLEY_PELLES_VERSION HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(HEDLEY_PELLES_VERSION_CHECK) +# undef HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(HEDLEY_PELLES_VERSION) +# define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (HEDLEY_PELLES_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_GCC_VERSION) +# undef HEDLEY_GCC_VERSION +#endif +#if \ + defined(HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(HEDLEY_INTEL_VERSION) && \ + !defined(HEDLEY_PGI_VERSION) && \ + !defined(HEDLEY_ARM_VERSION) && \ + !defined(HEDLEY_TI_VERSION) && \ + !defined(__COMPCERT__) +# define HEDLEY_GCC_VERSION HEDLEY_GNUC_VERSION +#endif + +#if defined(HEDLEY_GCC_VERSION_CHECK) +# undef HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(HEDLEY_GCC_VERSION) +# define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (HEDLEY_GCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_HAS_ATTRIBUTE) +# undef HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else +# define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_BUILTIN) +# undef HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +# define HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_BUILTIN) +# undef HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else +# define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_BUILTIN) +# undef HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else +# define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_FEATURE) +# undef HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else +# define HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_FEATURE) +# undef HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else +# define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_FEATURE) +# undef HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else +# define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_EXTENSION) +# undef HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else +# define HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_EXTENSION) +# undef HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else +# define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_EXTENSION) +# undef HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else +# define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else +# define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_WARNING) +# undef HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else +# define HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_WARNING) +# undef HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else +# define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_WARNING) +# undef HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else +# define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,0,0) || \ + HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) +# define HEDLEY_PRAGMA(value) _Pragma(#value) +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_PRAGMA(value) __pragma(value) +#else +# define HEDLEY_PRAGMA(value) +#endif + +#if defined(HEDLEY_DIAGNOSTIC_PUSH) +# undef HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(HEDLEY_DIAGNOSTIC_POP) +# undef HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif HEDLEY_GCC_VERSION_CHECK(4,6,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) +# define HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif HEDLEY_ARM_VERSION_CHECK(5,6,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif HEDLEY_TI_VERSION_CHECK(8,1,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else +# define HEDLEY_DIAGNOSTIC_PUSH +# define HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) +# undef HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if HEDLEY_HAS_WARNING("-Wdeprecated-declarations") +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif HEDLEY_GCC_VERSION_CHECK(4,3,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) +# undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif HEDLEY_GCC_VERSION_CHECK(4,3,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) +# undef HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if HEDLEY_HAS_WARNING("-Wcast-qual") +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif HEDLEY_GCC_VERSION_CHECK(3,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(HEDLEY_DEPRECATED) +# undef HEDLEY_DEPRECATED +#endif +#if defined(HEDLEY_DEPRECATED_FOR) +# undef HEDLEY_DEPRECATED_FOR +#endif +#if defined(__cplusplus) && (__cplusplus >= 201402L) +# define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]] +# define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] +#elif \ + HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + HEDLEY_TI_VERSION_CHECK(8,3,0) +# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif HEDLEY_MSVC_VERSION_CHECK(14,0,0) +# define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + HEDLEY_PELLES_VERSION_CHECK(6,50,0) +# define HEDLEY_DEPRECATED(since) _declspec(deprecated) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DEPRECATED(since) _Pragma("deprecated") +# define HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else +# define HEDLEY_DEPRECATED(since) +# define HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(HEDLEY_UNAVAILABLE) +# undef HEDLEY_UNAVAILABLE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(warning) || \ + HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else +# define HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(HEDLEY_WARN_UNUSED_RESULT) +# undef HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(__cplusplus) && (__cplusplus >= 201703L) +# define HEDLEY_WARN_UNUSED_RESULT [[nodiscard]] +#elif \ + HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ +# define HEDLEY_WARN_UNUSED_RESULT _Check_return_ +#else +# define HEDLEY_WARN_UNUSED_RESULT +#endif + +#if defined(HEDLEY_SENTINEL) +# undef HEDLEY_SENTINEL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,4,0) +# define HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else +# define HEDLEY_SENTINEL(position) +#endif + +#if defined(HEDLEY_NO_RETURN) +# undef HEDLEY_NO_RETURN +#endif +#if HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_NO_RETURN __noreturn +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +# define HEDLEY_NO_RETURN [[noreturn]] +#elif \ + HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(18,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_NO_RETURN __declspec(noreturn) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) +# define HEDLEY_NO_RETURN __attribute((noreturn)) +#elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) +# define HEDLEY_NO_RETURN __declspec(noreturn) +#else +# define HEDLEY_NO_RETURN +#endif + +#if defined(HEDLEY_UNREACHABLE) +# undef HEDLEY_UNREACHABLE +#endif +#if defined(HEDLEY_UNREACHABLE_RETURN) +# undef HEDLEY_UNREACHABLE_RETURN +#endif +#if \ + (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(HEDLEY_ARM_VERSION))) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,5) +# define HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_UNREACHABLE() __assume(0) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) +# if defined(__cplusplus) +# define HEDLEY_UNREACHABLE() std::_nassert(0) +# else +# define HEDLEY_UNREACHABLE() _nassert(0) +# endif +# define HEDLEY_UNREACHABLE_RETURN(value) return value +#elif defined(EXIT_FAILURE) +# define HEDLEY_UNREACHABLE() abort() +#else +# define HEDLEY_UNREACHABLE() +# define HEDLEY_UNREACHABLE_RETURN(value) return value +#endif +#if !defined(HEDLEY_UNREACHABLE_RETURN) +# define HEDLEY_UNREACHABLE_RETURN(value) HEDLEY_UNREACHABLE() +#endif + +#if defined(HEDLEY_ASSUME) +# undef HEDLEY_ASSUME +#endif +#if \ + HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_ASSUME(expr) __assume(expr) +#elif HEDLEY_HAS_BUILTIN(__builtin_assume) +# define HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) +# if defined(__cplusplus) +# define HEDLEY_ASSUME(expr) std::_nassert(expr) +# else +# define HEDLEY_ASSUME(expr) _nassert(expr) +# endif +#elif \ + (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(HEDLEY_ARM_VERSION)) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,5) +# define HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) +#else +# define HEDLEY_ASSUME(expr) ((void) (expr)) +#endif + + +HEDLEY_DIAGNOSTIC_PUSH +#if \ + HEDLEY_HAS_WARNING("-Wvariadic-macros") || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) +# if defined(__clang__) +# pragma clang diagnostic ignored "-Wvariadic-macros" +# elif defined(HEDLEY_GCC_VERSION) +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# endif +#endif +#if defined(HEDLEY_NON_NULL) +# undef HEDLEY_NON_NULL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) +# define HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +# define HEDLEY_NON_NULL(...) +#endif +HEDLEY_DIAGNOSTIC_POP + +#if defined(HEDLEY_PRINTF_FORMAT) +# undef HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + HEDLEY_HAS_ATTRIBUTE(format) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif HEDLEY_PELLES_VERSION_CHECK(6,0,0) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(HEDLEY_CONSTEXPR) +# undef HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define HEDLEY_CONSTEXPR constexpr +# endif +#endif +#if !defined(HEDLEY_CONSTEXPR) +# define HEDLEY_CONSTEXPR +#endif + +#if defined(HEDLEY_PREDICT) +# undef HEDLEY_PREDICT +#endif +#if defined(HEDLEY_LIKELY) +# undef HEDLEY_LIKELY +#endif +#if defined(HEDLEY_UNLIKELY) +# undef HEDLEY_UNLIKELY +#endif +#if defined(HEDLEY_UNPREDICTABLE) +# undef HEDLEY_UNPREDICTABLE +#endif +#if HEDLEY_HAS_BUILTIN(__builtin_unpredictable) +# define HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) +#endif +#if \ + HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) +# define HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) +# define HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) +# define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +# if !defined(HEDLEY_BUILTIN_UNPREDICTABLE) +# define HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) +# endif +#elif \ + HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,27) +# define HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) +# define HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) +# define HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define HEDLEY_LIKELY(expr) (!!(expr)) +# define HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(HEDLEY_UNPREDICTABLE) +# define HEDLEY_UNPREDICTABLE(expr) HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(HEDLEY_MALLOC) +# undef HEDLEY_MALLOC +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(malloc) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_MALLOC __attribute__((__malloc__)) +#elif HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +# define HEDLEY_MALLOC __declspec(restrict) +#else +# define HEDLEY_MALLOC +#endif + +#if defined(HEDLEY_PURE) +# undef HEDLEY_PURE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(pure) || \ + HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_PURE __attribute__((__pure__)) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define HEDLEY_PURE +#endif + +#if defined(HEDLEY_CONST) +# undef HEDLEY_CONST +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(const) || \ + HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_CONST __attribute__((__const__)) +#else +# define HEDLEY_CONST HEDLEY_PURE +#endif + +#if defined(HEDLEY_RESTRICT) +# undef HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) +# define HEDLEY_RESTRICT restrict +#elif \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) +# define HEDLEY_RESTRICT __restrict +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) +# define HEDLEY_RESTRICT _Restrict +#else +# define HEDLEY_RESTRICT +#endif + +#if defined(HEDLEY_INLINE) +# undef HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) +# define HEDLEY_INLINE inline +#elif \ + defined(HEDLEY_GCC_VERSION) || \ + HEDLEY_ARM_VERSION_CHECK(6,2,0) +# define HEDLEY_INLINE __inline__ +#elif \ + HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_INLINE __inline +#else +# define HEDLEY_INLINE +#endif + +#if defined(HEDLEY_ALWAYS_INLINE) +# undef HEDLEY_ALWAYS_INLINE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) HEDLEY_INLINE +#elif HEDLEY_MSVC_VERSION_CHECK(12,0,0) +# define HEDLEY_ALWAYS_INLINE __forceinline +#elif HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) +# define HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define HEDLEY_ALWAYS_INLINE HEDLEY_INLINE +#endif + +#if defined(HEDLEY_NEVER_INLINE) +# undef HEDLEY_NEVER_INLINE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(noinline) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_NEVER_INLINE __declspec(noinline) +#elif HEDLEY_PGI_VERSION_CHECK(10,2,0) +# define HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) +# define HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) +# define HEDLEY_NEVER_INLINE __declspec(noinline) +#else +# define HEDLEY_NEVER_INLINE +#endif + +#if defined(HEDLEY_PRIVATE) +# undef HEDLEY_PRIVATE +#endif +#if defined(HEDLEY_PUBLIC) +# undef HEDLEY_PUBLIC +#endif +#if defined(HEDLEY_IMPORT) +# undef HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define HEDLEY_PRIVATE +# define HEDLEY_PUBLIC __declspec(dllexport) +# define HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + HEDLEY_HAS_ATTRIBUTE(visibility) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define HEDLEY_PRIVATE +# define HEDLEY_PUBLIC +# endif +# define HEDLEY_IMPORT extern +#endif + +#if defined(HEDLEY_NO_THROW) +# undef HEDLEY_NO_THROW +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) +# define HEDLEY_NO_THROW __declspec(nothrow) +#else +# define HEDLEY_NO_THROW +#endif + +#if defined(HEDLEY_FALL_THROUGH) +# undef HEDLEY_FALL_THROUGH +#endif +#if \ + defined(__cplusplus) && \ + (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + !defined(HEDLEY_PGI_VERSION) +# if \ + (__cplusplus >= 201703L) || \ + ((__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)) +# define HEDLEY_FALL_THROUGH [[fallthrough]] +# elif (__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough) +# define HEDLEY_FALL_THROUGH [[clang::fallthrough]] +# elif (__cplusplus >= 201103L) && HEDLEY_GCC_VERSION_CHECK(7,0,0) +# define HEDLEY_FALL_THROUGH [[gnu::fallthrough]] +# endif +#endif +#if !defined(HEDLEY_FALL_THROUGH) +# if HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(HEDLEY_PGI_VERSION) +# define HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +# elif defined(__fallthrough) /* SAL */ +# define HEDLEY_FALL_THROUGH __fallthrough +# else +# define HEDLEY_FALL_THROUGH +# endif +#endif + +#if defined(HEDLEY_RETURNS_NON_NULL) +# undef HEDLEY_RETURNS_NON_NULL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + HEDLEY_GCC_VERSION_CHECK(4,9,0) +# define HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ +# define HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else +# define HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(HEDLEY_ARRAY_PARAM) +# undef HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(HEDLEY_PGI_VERSION) && \ + !defined(HEDLEY_TINYC_VERSION) +# define HEDLEY_ARRAY_PARAM(name) (name) +#else +# define HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(HEDLEY_IS_CONSTANT) +# undef HEDLEY_IS_CONSTANT +#endif +#if defined(HEDLEY_REQUIRE_CONSTEXPR) +# undef HEDLEY_REQUIRE_CONSTEXPR +#endif +/* Note the double-underscore. For internal use only; no API + * guarantees! */ +#if defined(HEDLEY__IS_CONSTEXPR) +# undef HEDLEY__IS_CONSTEXPR +#endif + +#if \ + HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) || \ + HEDLEY_CRAY_VERSION_CHECK(8,1,0) +# define HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,24) +# if defined(__INTPTR_TYPE__) +# define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +# else +# include +# define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +# endif +# elif \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(HEDLEY_SUNPRO_VERSION) && !defined(HEDLEY_PGI_VERSION)) || \ + HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,3,0) +# if defined(__INTPTR_TYPE__) +# define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +# else +# include +# define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +# endif +# elif \ + defined(HEDLEY_GCC_VERSION) || \ + defined(HEDLEY_INTEL_VERSION) || \ + defined(HEDLEY_TINYC_VERSION) || \ + defined(HEDLEY_TI_VERSION) || \ + defined(__clang__) +# define HEDLEY__IS_CONSTEXPR(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ + ((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(HEDLEY__IS_CONSTEXPR) +# if !defined(HEDLEY_IS_CONSTANT) +# define HEDLEY_IS_CONSTANT(expr) HEDLEY__IS_CONSTEXPR(expr) +# endif +# define HEDLEY_REQUIRE_CONSTEXPR(expr) (HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1)) +#else +# if !defined(HEDLEY_IS_CONSTANT) +# define HEDLEY_IS_CONSTANT(expr) (0) +# endif +# define HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(HEDLEY_BEGIN_C_DECLS) +# undef HEDLEY_BEGIN_C_DECLS +#endif +#if defined(HEDLEY_END_C_DECLS) +# undef HEDLEY_END_C_DECLS +#endif +#if defined(HEDLEY_C_DECL) +# undef HEDLEY_C_DECL +#endif +#if defined(__cplusplus) +# define HEDLEY_BEGIN_C_DECLS extern "C" { +# define HEDLEY_END_C_DECLS } +# define HEDLEY_C_DECL extern "C" +#else +# define HEDLEY_BEGIN_C_DECLS +# define HEDLEY_END_C_DECLS +# define HEDLEY_C_DECL +#endif + +#if defined(HEDLEY_STATIC_ASSERT) +# undef HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + HEDLEY_HAS_FEATURE(c_static_assert) || \ + HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201703L)) || \ + HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + (defined(__cplusplus) && HEDLEY_TI_VERSION_CHECK(8,3,0)) +# define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +# define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr) +#else +# define HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(HEDLEY_CONST_CAST) +# undef HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + HEDLEY_HAS_WARNING("-Wcast-qual") || \ + HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(HEDLEY_REINTERPRET_CAST) +# undef HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else +# define HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) +#endif + +#if defined(HEDLEY_STATIC_CAST) +# undef HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else +# define HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(HEDLEY_CPP_CAST) +# undef HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_CPP_CAST(T, expr) static_cast(expr) +#else +# define HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(HEDLEY_MESSAGE) +# undef HEDLEY_MESSAGE +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_MESSAGE(msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + HEDLEY_PRAGMA(message msg) \ + HEDLEY_DIAGNOSTIC_POP +#elif \ + HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message msg) +#elif HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(_CRI message msg) +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) +#elif HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) +#else +# define HEDLEY_MESSAGE(msg) +#endif + +#if defined(HEDLEY_WARNING) +# undef HEDLEY_WARNING +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_WARNING(msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + HEDLEY_PRAGMA(clang warning msg) \ + HEDLEY_DIAGNOSTIC_POP +#elif \ + HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + HEDLEY_PGI_VERSION_CHECK(18,4,0) +# define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(GCC warning msg) +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(message(msg)) +#else +# define HEDLEY_WARNING(msg) HEDLEY_MESSAGE(msg) +#endif + +#if defined(HEDLEY_REQUIRE_MSG) +# undef HEDLEY_REQUIRE_MSG +#endif +#if HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if HEDLEY_HAS_WARNING("-Wgcc-compat") +# define HEDLEY_REQUIRE_MSG(expr, msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((__diagnose_if__(!(expr), msg, "error"))) \ + HEDLEY_DIAGNOSTIC_POP +# else +# define HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error"))) +# endif +#else +# define HEDLEY_REQUIRE_MSG(expr, msg) +#endif + +#if defined(HEDLEY_REQUIRE) +# undef HEDLEY_REQUIRE +#endif +#define HEDLEY_REQUIRE(expr) HEDLEY_REQUIRE_MSG(expr, #expr) + +#if defined(HEDLEY_FLAGS) +# undef HEDLEY_FLAGS +#endif +#if HEDLEY_HAS_ATTRIBUTE(flag_enum) +# define HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(HEDLEY_FLAGS_CAST) +# undef HEDLEY_FLAGS_CAST +#endif +#if HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define HEDLEY_FLAGS_CAST(T, expr) HEDLEY_STATIC_CAST(T, expr) +#endif + +/* Remaining macros are deprecated. */ + +#if defined(HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) +# undef HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) +# define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else +# define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_CLANG_HAS_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_BUILTIN) +# undef HEDLEY_CLANG_HAS_BUILTIN +#endif +#define HEDLEY_CLANG_HAS_BUILTIN(builtin) HEDLEY_HAS_BUILTIN(builtin) + +#if defined(HEDLEY_CLANG_HAS_FEATURE) +# undef HEDLEY_CLANG_HAS_FEATURE +#endif +#define HEDLEY_CLANG_HAS_FEATURE(feature) HEDLEY_HAS_FEATURE(feature) + +#if defined(HEDLEY_CLANG_HAS_EXTENSION) +# undef HEDLEY_CLANG_HAS_EXTENSION +#endif +#define HEDLEY_CLANG_HAS_EXTENSION(extension) HEDLEY_HAS_EXTENSION(extension) + +#if defined(HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_WARNING) +# undef HEDLEY_CLANG_HAS_WARNING +#endif +#define HEDLEY_CLANG_HAS_WARNING(warning) HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < X) */ + + +namespace csv { +#ifdef _MSC_VER +#pragma region Compatibility Macros +#endif + /** + * @def IF_CONSTEXPR + * Expands to `if constexpr` in C++17 and `if` otherwise + * + * @def CONSTEXPR_VALUE + * Expands to `constexpr` in C++17 and `const` otherwise. + * Mainly used for global variables. + * + * @def CONSTEXPR + * Expands to `constexpr` in decent compilers and `inline` otherwise. + * Intended for functions and methods. + */ + +#if CMAKE_CXX_STANDARD == 17 || __cplusplus >= 201703L +#define CSV_HAS_CXX17 +#endif + +#if CMAKE_CXX_STANDARD >= 14 || __cplusplus >= 201402L +#define CSV_HAS_CXX14 +#endif + +#ifdef CSV_HAS_CXX17 +#include + /** @typedef string_view + * The string_view class used by this library. + */ + using string_view = std::string_view; +#else + /** @typedef string_view + * The string_view class used by this library. + */ + using string_view = nonstd::string_view; +#endif + +#ifdef CSV_HAS_CXX17 +#define IF_CONSTEXPR if constexpr +#define CONSTEXPR_VALUE constexpr +#else +#define IF_CONSTEXPR if +#define CONSTEXPR_VALUE const +#endif + +#ifdef CSV_HAS_CXX14 + template + using enable_if_t = std::enable_if_t; +#else + template + using enable_if_t = typename std::enable_if::type; +#endif + + // Resolves g++ bug with regard to constexpr methods + // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d +#if defined __GNUC__ && !defined __clang__ +#if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8) +#define CONSTEXPR constexpr +#endif +#else +#ifdef CSV_HAS_CXX17 +#define CONSTEXPR constexpr +#endif +#endif + +#ifndef CONSTEXPR +#define CONSTEXPR inline +#endif + +#ifdef _MSC_VER +#pragma endregion +#endif + + namespace internals { + // PAGE_SIZE macro could be already defined by the host system. +#if defined(PAGE_SIZE) +#undef PAGE_SIZE +#endif + +// Get operating system specific details +#if defined(_WIN32) + inline int getpagesize() { + _SYSTEM_INFO sys_info = {}; + GetSystemInfo(&sys_info); + return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity); + } + + const int PAGE_SIZE = getpagesize(); +#elif defined(__linux__) + const int PAGE_SIZE = getpagesize(); +#else + /** Size of a memory page in bytes. Used by + * csv::internals::CSVFieldArray when allocating blocks. + */ + const int PAGE_SIZE = 4096; +#endif + + /** For functions that lazy load a large CSV, this determines how + * many bytes are read at a time + */ + constexpr size_t ITERATION_CHUNK_SIZE = 10000000; // 10MB + + template + inline bool is_equal(T a, T b, T epsilon = 0.001) { + /** Returns true if two floating point values are about the same */ + static_assert(std::is_floating_point::value, "T must be a floating point type."); + return std::abs(a - b) < epsilon; + } + + /** @typedef ParseFlags + * An enum used for describing the significance of each character + * with respect to CSV parsing + * + * @see quote_escape_flag + */ + enum class ParseFlags { + QUOTE_ESCAPE_QUOTE = 0, /**< A quote inside or terminating a quote_escaped field */ + QUOTE = 2 | 1, /**< Characters which may signify a quote escape */ + NOT_SPECIAL = 4, /**< Characters with no special meaning or escaped delimiters and newlines */ + DELIMITER = 4 | 2, /**< Characters which signify a new field */ + NEWLINE = 4 | 2 | 1 /**< Characters which signify a new row */ + }; + + /** Transform the ParseFlags given the context of whether or not the current + * field is quote escaped */ + constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept { + return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape)); + } + + // Assumed to be true by parsing functions: allows for testing + // if an item is DELIMITER or NEWLINE with a >= statement + static_assert(ParseFlags::DELIMITER < ParseFlags::NEWLINE); + + /** Optimizations for reducing branching in parsing loop + * + * Idea: The meaning of all non-quote characters changes depending + * on whether or not the parser is in a quote-escaped mode (0 or 1) + */ + static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL); + static_assert(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE); + static_assert(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER); + static_assert(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE); + + static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL); + static_assert(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE); + static_assert(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL); + static_assert(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL); + + /** An array which maps ASCII chars to a parsing flag */ + using ParseFlagMap = std::array; + + /** An array which maps ASCII chars to a flag indicating if it is whitespace */ + using WhitespaceMap = std::array; + } + + /** Integer indicating a requested column wasn't found. */ + constexpr int CSV_NOT_FOUND = -1; +} + + +namespace csv { + namespace internals { + struct ColNames; + using ColNamesPtr = std::shared_ptr; + + /** @struct ColNames + * A data structure for handling column name information. + * + * These are created by CSVReader and passed (via smart pointer) + * to CSVRow objects it creates, thus + * allowing for indexing by column name. + */ + struct ColNames { + public: + ColNames() = default; + ColNames(const std::vector& names) { + set_col_names(names); + } + + std::vector get_col_names() const; + void set_col_names(const std::vector&); + int index_of(csv::string_view) const; + + bool empty() const noexcept { return this->col_names.empty(); } + size_t size() const noexcept; + + private: + std::vector col_names; + std::unordered_map col_pos; + }; + } +} +/** @file + * Defines an object used to store CSV format settings + */ + +#include +#include +#include +#include + + +namespace csv { + namespace internals { + class IBasicCSVParser; + } + + class CSVReader; + + /** Determines how to handle rows that are shorter or longer than the majority */ + enum class VariableColumnPolicy { + THROW = -1, + IGNORE_ROW = 0, + KEEP = 1 + }; + + /** Stores the inferred format of a CSV file. */ + struct CSVGuessResult { + char delim; + int header_row; + }; + + /** Stores information about how to parse a CSV file. + * Can be used to construct a csv::CSVReader. + */ + class CSVFormat { + public: + /** Settings for parsing a RFC 4180 CSV file */ + CSVFormat() = default; + + /** Sets the delimiter of the CSV file + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + */ + CSVFormat& delimiter(char delim); + + /** Sets a list of potential delimiters + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + * @param[in] delim An array of possible delimiters to try parsing the CSV with + */ + CSVFormat& delimiter(const std::vector & delim); + + /** Sets the whitespace characters to be trimmed + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + * @param[in] ws An array of whitespace characters that should be trimmed + */ + CSVFormat& trim(const std::vector & ws); + + /** Sets the quote character + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + */ + CSVFormat& quote(char quote); + + /** Sets the column names. + * + * @note Unsets any values set by header_row() + */ + CSVFormat& column_names(const std::vector& names); + + /** Sets the header row + * + * @note Unsets any values set by column_names() + */ + CSVFormat& header_row(int row); + + /** Tells the parser that this CSV has no header row + * + * @note Equivalent to `header_row(-1)` + * + */ + CSVFormat& no_header() { + this->header_row(-1); + return *this; + } + + /** Turn quoting on or off */ + CSVFormat& quote(bool use_quote) { + this->no_quote = !use_quote; + return *this; + } + + /** Tells the parser how to handle columns of a different length than the others */ + CONSTEXPR CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) { + this->variable_column_policy = policy; + return *this; + } + + /** Tells the parser how to handle columns of a different length than the others */ + CONSTEXPR CSVFormat& variable_columns(bool policy) { + this->variable_column_policy = (VariableColumnPolicy)policy; + return *this; + } + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + char get_delim() const { + // This error should never be received by end users. + if (this->possible_delimiters.size() > 1) { + throw std::runtime_error("There is more than one possible delimiter."); + } + + return this->possible_delimiters.at(0); + } + + CONSTEXPR bool is_quoting_enabled() const { return !this->no_quote; } + CONSTEXPR char get_quote_char() const { return this->quote_char; } + CONSTEXPR int get_header() const { return this->header; } + std::vector get_possible_delims() const { return this->possible_delimiters; } + std::vector get_trim_chars() const { return this->trim_chars; } + CONSTEXPR VariableColumnPolicy get_variable_column_policy() const { return this->variable_column_policy; } + #endif + + /** CSVFormat for guessing the delimiter */ + CSV_INLINE static CSVFormat guess_csv() { + CSVFormat format; + format.delimiter({ ',', '|', '\t', ';', '^' }) + .quote('"') + .header_row(0); + + return format; + } + + bool guess_delim() { + return this->possible_delimiters.size() > 1; + } + + friend CSVReader; + friend internals::IBasicCSVParser; + + private: + /**< Throws an error if delimiters and trim characters overlap */ + void assert_no_char_overlap(); + + /**< Set of possible delimiters */ + std::vector possible_delimiters = { ',' }; + + /**< Set of whitespace characters to trim */ + std::vector trim_chars = {}; + + /**< Row number with columns (ignored if col_names is non-empty) */ + int header = 0; + + /**< Whether or not to use quoting */ + bool no_quote = false; + + /**< Quote character */ + char quote_char = '"'; + + /**< Should be left empty unless file doesn't include header */ + std::vector col_names = {}; + + /**< Allow variable length columns? */ + VariableColumnPolicy variable_column_policy = VariableColumnPolicy::IGNORE_ROW; + }; +} +/** @file + * Defines the data type used for storing information about a CSV row + */ + +#include +#include +#include // For CSVField +#include // For CSVField +#include +#include +#include +#include +#include + +/** @file + * @brief Implements data type parsing functionality + */ + +#include +#include +#include +#include + + +namespace csv { + /** Enumerates the different CSV field types that are + * recognized by this library + * + * @note Overflowing integers will be stored and classified as doubles. + * @note Unlike previous releases, integer enums here are platform agnostic. + */ + enum class DataType { + UNKNOWN = -1, + CSV_NULL, /**< Empty string */ + CSV_STRING, /**< Non-numeric string */ + CSV_INT8, /**< 8-bit integer */ + CSV_INT16, /**< 16-bit integer (short on MSVC/GCC) */ + CSV_INT32, /**< 32-bit integer (int on MSVC/GCC) */ + CSV_INT64, /**< 64-bit integer (long long on MSVC/GCC) */ + CSV_DOUBLE /**< Floating point value */ + }; + + static_assert(DataType::CSV_STRING < DataType::CSV_INT8, "String type should come before numeric types."); + static_assert(DataType::CSV_INT8 < DataType::CSV_INT64, "Smaller integer types should come before larger integer types."); + static_assert(DataType::CSV_INT64 < DataType::CSV_DOUBLE, "Integer types should come before floating point value types."); + + namespace internals { + /** Compute 10 to the power of n */ + template + HEDLEY_CONST CONSTEXPR + long double pow10(const T& n) noexcept { + long double multiplicand = n > 0 ? 10 : 0.1, + ret = 1; + + // Make all numbers positive + T iterations = n > 0 ? n : -n; + + for (T i = 0; i < iterations; i++) { + ret *= multiplicand; + } + + return ret; + } + + /** Compute 10 to the power of n */ + template<> + HEDLEY_CONST CONSTEXPR + long double pow10(const unsigned& n) noexcept { + long double multiplicand = n > 0 ? 10 : 0.1, + ret = 1; + + for (unsigned i = 0; i < n; i++) { + ret *= multiplicand; + } + + return ret; + } + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + /** Private site-indexed array mapping byte sizes to an integer size enum */ + constexpr DataType int_type_arr[8] = { + DataType::CSV_INT8, // 1 + DataType::CSV_INT16, // 2 + DataType::UNKNOWN, + DataType::CSV_INT32, // 4 + DataType::UNKNOWN, + DataType::UNKNOWN, + DataType::UNKNOWN, + DataType::CSV_INT64 // 8 + }; + + template + inline DataType type_num() { + static_assert(std::is_integral::value, "T should be an integral type."); + static_assert(sizeof(T) <= 8, "Byte size must be no greater than 8."); + return int_type_arr[sizeof(T) - 1]; + } + + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_NULL; } + template<> inline DataType type_num() { return DataType::CSV_STRING; } + + CONSTEXPR DataType data_type(csv::string_view in, long double* const out = nullptr); +#endif + + /** Given a byte size, return the largest number than can be stored in + * an integer of that size + * + * Note: Provides a platform-agnostic way of mapping names like "long int" to + * byte sizes + */ + template + CONSTEXPR long double get_int_max() { + static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, + "Bytes must be a power of 2 below 8."); + + IF_CONSTEXPR (sizeof(signed char) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(short) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(long long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + HEDLEY_UNREACHABLE(); + } + + /** Given a byte size, return the largest number than can be stored in + * an unsigned integer of that size + */ + template + CONSTEXPR long double get_uint_max() { + static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, + "Bytes must be a power of 2 below 8."); + + IF_CONSTEXPR(sizeof(unsigned char) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned short) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned long long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + HEDLEY_UNREACHABLE(); + } + + /** Largest number that can be stored in a 8-bit integer */ + CONSTEXPR_VALUE long double CSV_INT8_MAX = get_int_max<1>(); + + /** Largest number that can be stored in a 16-bit integer */ + CONSTEXPR_VALUE long double CSV_INT16_MAX = get_int_max<2>(); + + /** Largest number that can be stored in a 32-bit integer */ + CONSTEXPR_VALUE long double CSV_INT32_MAX = get_int_max<4>(); + + /** Largest number that can be stored in a 64-bit integer */ + CONSTEXPR_VALUE long double CSV_INT64_MAX = get_int_max<8>(); + + /** Largest number that can be stored in a 8-bit ungisned integer */ + CONSTEXPR_VALUE long double CSV_UINT8_MAX = get_uint_max<1>(); + + /** Largest number that can be stored in a 16-bit unsigned integer */ + CONSTEXPR_VALUE long double CSV_UINT16_MAX = get_uint_max<2>(); + + /** Largest number that can be stored in a 32-bit unsigned integer */ + CONSTEXPR_VALUE long double CSV_UINT32_MAX = get_uint_max<4>(); + + /** Largest number that can be stored in a 64-bit unsigned integer */ + CONSTEXPR_VALUE long double CSV_UINT64_MAX = get_uint_max<8>(); + + /** Given a pointer to the start of what is start of + * the exponential part of a number written (possibly) in scientific notation + * parse the exponent + */ + HEDLEY_PRIVATE CONSTEXPR + DataType _process_potential_exponential( + csv::string_view exponential_part, + const long double& coeff, + long double * const out) { + long double exponent = 0; + auto result = data_type(exponential_part, &exponent); + + // Exponents in scientific notation should not be decimal numbers + if (result >= DataType::CSV_INT8 && result < DataType::CSV_DOUBLE) { + if (out) *out = coeff * pow10(exponent); + return DataType::CSV_DOUBLE; + } + + return DataType::CSV_STRING; + } + + /** Given the absolute value of an integer, determine what numeric type + * it fits in + */ + HEDLEY_PRIVATE HEDLEY_PURE CONSTEXPR + DataType _determine_integral_type(const long double& number) noexcept { + // We can assume number is always non-negative + assert(number >= 0); + + if (number <= internals::CSV_INT8_MAX) + return DataType::CSV_INT8; + else if (number <= internals::CSV_INT16_MAX) + return DataType::CSV_INT16; + else if (number <= internals::CSV_INT32_MAX) + return DataType::CSV_INT32; + else if (number <= internals::CSV_INT64_MAX) + return DataType::CSV_INT64; + else // Conversion to long long will cause an overflow + return DataType::CSV_DOUBLE; + } + + /** Distinguishes numeric from other text values. Used by various + * type casting functions, like csv_parser::CSVReader::read_row() + * + * #### Rules + * - Leading and trailing whitespace ("padding") ignored + * - A string of just whitespace is NULL + * + * @param[in] in String value to be examined + * @param[out] out Pointer to long double where results of numeric parsing + * get stored + */ + CONSTEXPR + DataType data_type(csv::string_view in, long double* const out) { + // Empty string --> NULL + if (in.size() == 0) + return DataType::CSV_NULL; + + bool ws_allowed = true, + neg_allowed = true, + dot_allowed = true, + digit_allowed = true, + has_digit = false, + prob_float = false; + + unsigned places_after_decimal = 0; + long double integral_part = 0, + decimal_part = 0; + + for (size_t i = 0, ilen = in.size(); i < ilen; i++) { + const char& current = in[i]; + + switch (current) { + case ' ': + if (!ws_allowed) { + if (isdigit(in[i - 1])) { + digit_allowed = false; + ws_allowed = true; + } + else { + // Ex: '510 123 4567' + return DataType::CSV_STRING; + } + } + break; + case '-': + if (!neg_allowed) { + // Ex: '510-123-4567' + return DataType::CSV_STRING; + } + + neg_allowed = false; + break; + case '.': + if (!dot_allowed) { + return DataType::CSV_STRING; + } + + dot_allowed = false; + prob_float = true; + break; + case 'e': + case 'E': + // Process scientific notation + if (prob_float || (i && i + 1 < ilen && isdigit(in[i - 1]))) { + size_t exponent_start_idx = i + 1; + prob_float = true; + + // Strip out plus sign + if (in[i + 1] == '+') { + exponent_start_idx++; + } + + return _process_potential_exponential( + in.substr(exponent_start_idx), + neg_allowed ? integral_part + decimal_part : -(integral_part + decimal_part), + out + ); + } + + return DataType::CSV_STRING; + break; + default: + short digit = current - '0'; + if (digit >= 0 && digit <= 9) { + // Process digit + has_digit = true; + + if (!digit_allowed) + return DataType::CSV_STRING; + else if (ws_allowed) // Ex: '510 456' + ws_allowed = false; + + // Build current number + if (prob_float) + decimal_part += digit / pow10(++places_after_decimal); + else + integral_part = (integral_part * 10) + digit; + } + else { + return DataType::CSV_STRING; + } + } + } + + // No non-numeric/non-whitespace characters found + if (has_digit) { + long double number = integral_part + decimal_part; + if (out) { + *out = neg_allowed ? number : -number; + } + + return prob_float ? DataType::CSV_DOUBLE : _determine_integral_type(number); + } + + // Just whitespace + return DataType::CSV_NULL; + } + } +} + +namespace csv { + namespace internals { + class IBasicCSVParser; + + static const std::string ERROR_NAN = "Not a number."; + static const std::string ERROR_OVERFLOW = "Overflow error."; + static const std::string ERROR_FLOAT_TO_INT = + "Attempted to convert a floating point value to an integral type."; + static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types."; + + std::string json_escape_string(csv::string_view s) noexcept; + + /** A barebones class used for describing CSV fields */ + struct RawCSVField { + RawCSVField() = default; + RawCSVField(size_t _start, size_t _length, bool _double_quote = false) { + start = _start; + length = _length; + has_double_quote = _double_quote; + } + + /** The start of the field, relative to the beginning of the row */ + size_t start; + + /** The length of the row, ignoring quote escape characters */ + size_t length; + + /** Whether or not the field contains an escaped quote */ + bool has_double_quote; + }; + + /** A class used for efficiently storing RawCSVField objects and expanding as necessary + * + * @par Implementation + * This data structure stores RawCSVField in continguous blocks. When more capacity + * is needed, a new block is allocated, but previous data stays put. + * + * @par Thread Safety + * This class may be safely read from multiple threads and written to from one, + * as long as the writing thread does not actively touch fields which are being + * read. + */ + class CSVFieldList { + public: + /** Construct a CSVFieldList which allocates blocks of a certain size */ + CSVFieldList(size_t single_buffer_capacity = (size_t)(internals::PAGE_SIZE / sizeof(RawCSVField))) : + _single_buffer_capacity(single_buffer_capacity) { + this->allocate(); + } + + // No copy constructor + CSVFieldList(const CSVFieldList& other) = delete; + + // CSVFieldArrays may be moved + CSVFieldList(CSVFieldList&& other) : + _single_buffer_capacity(other._single_buffer_capacity) { + buffers = std::move(other.buffers); + _current_buffer_size = other._current_buffer_size; + _back = other._back; + } + + ~CSVFieldList() { + for (auto& buffer : buffers) + delete[] buffer; + } + + template + void emplace_back(Args&&... args) { + if (this->_current_buffer_size == this->_single_buffer_capacity) { + this->allocate(); + } + + *(_back++) = RawCSVField(std::forward(args)...); + _current_buffer_size++; + } + + size_t size() const noexcept { + return this->_current_buffer_size + ((this->buffers.size() - 1) * this->_single_buffer_capacity); + } + + RawCSVField& operator[](size_t n) const; + + private: + const size_t _single_buffer_capacity; + + std::vector buffers = {}; + + /** Number of items in the current buffer */ + size_t _current_buffer_size = 0; + + /** Pointer to the current empty field */ + RawCSVField* _back = nullptr; + + /** Allocate a new page of memory */ + void allocate(); + }; + + + /** A class for storing raw CSV data and associated metadata */ + struct RawCSVData { + std::shared_ptr _data = nullptr; + csv::string_view data = ""; + + internals::CSVFieldList fields; + + std::unordered_set has_double_quotes = {}; + + // TODO: Consider replacing with a more thread-safe structure + std::unordered_map double_quote_fields = {}; + + internals::ColNamesPtr col_names = nullptr; + internals::ParseFlagMap parse_flags; + internals::WhitespaceMap ws_flags; + }; + + using RawCSVDataPtr = std::shared_ptr; + } + + /** + * @class CSVField + * @brief Data type representing individual CSV values. + * CSVFields can be obtained by using CSVRow::operator[] + */ + class CSVField { + public: + /** Constructs a CSVField from a string_view */ + constexpr explicit CSVField(csv::string_view _sv) noexcept : sv(_sv) { }; + + operator std::string() const { + return std::string(" ") + std::string(this->sv); + } + + /** Returns the value casted to the requested type, performing type checking before. + * + * \par Valid options for T + * - std::string or csv::string_view + * - signed integral types (signed char, short, int, long int, long long int) + * - floating point types (float, double, long double) + * - unsigned integers are not supported at this time, but may be in a later release + * + * \par Invalid conversions + * - Converting non-numeric values to any numeric type + * - Converting floating point values to integers + * - Converting a large integer to a smaller type that will not hold it + * + * @note This method is capable of parsing scientific E-notation. + * See [this page](md_docs_source_scientific_notation.html) + * for more details. + * + * @throws std::runtime_error Thrown if an invalid conversion is performed. + * + * @warning Currently, conversions to floating point types are not + * checked for loss of precision + * + * @warning Any string_views returned are only guaranteed to be valid + * if the parent CSVRow is still alive. If you are concerned + * about object lifetimes, then grab a std::string or a + * numeric value. + * + */ + template T get() { + IF_CONSTEXPR(std::is_arithmetic::value) { + // Note: this->type() also converts the CSV value to float + if (this->type() <= DataType::CSV_STRING) { + throw std::runtime_error(internals::ERROR_NAN); + } + } + + IF_CONSTEXPR(std::is_integral::value) { + // Note: this->is_float() also converts the CSV value to float + if (this->is_float()) { + throw std::runtime_error(internals::ERROR_FLOAT_TO_INT); + } + + IF_CONSTEXPR(std::is_unsigned::value) { + if (this->value < 0) { + throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED); + } + } + } + + // Allow fallthrough from previous if branch + IF_CONSTEXPR(!std::is_floating_point::value) { + IF_CONSTEXPR(std::is_unsigned::value) { + // Quick hack to perform correct unsigned integer boundary checks + if (this->value > internals::get_uint_max()) { + throw std::runtime_error(internals::ERROR_OVERFLOW); + } + } + else if (internals::type_num() < this->_type) { + throw std::runtime_error(internals::ERROR_OVERFLOW); + } + } + + return static_cast(this->value); + } + + /** Compares the contents of this field to a numeric value. If this + * field does not contain a numeric value, then all comparisons return + * false. + * + * @note Floating point values are considered equal if they are within + * `0.000001` of each other. + * + * @warning Multiple numeric comparisons involving the same field can + * be done more efficiently by calling the CSVField::get<>() method. + * + * @sa csv::CSVField::operator==(const char * other) + * @sa csv::CSVField::operator==(csv::string_view other) + */ + template + CONSTEXPR bool operator==(T other) const noexcept + { + static_assert(std::is_arithmetic::value, + "T should be a numeric value."); + + if (this->_type != DataType::UNKNOWN) { + if (this->_type == DataType::CSV_STRING) { + return false; + } + + return internals::is_equal(value, static_cast(other), 0.000001L); + } + + long double out = 0; + if (internals::data_type(this->sv, &out) == DataType::CSV_STRING) { + return false; + } + + return internals::is_equal(out, static_cast(other), 0.000001L); + } + + /** Return a string view over the field's contents */ + CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; } + + /** Returns true if field is an empty string or string of whitespace characters */ + CONSTEXPR bool is_null() noexcept { return type() == DataType::CSV_NULL; } + + /** Returns true if field is a non-numeric, non-empty string */ + CONSTEXPR bool is_str() noexcept { return type() == DataType::CSV_STRING; } + + /** Returns true if field is an integer or float */ + CONSTEXPR bool is_num() noexcept { return type() >= DataType::CSV_INT8; } + + /** Returns true if field is an integer */ + CONSTEXPR bool is_int() noexcept { + return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64); + } + + /** Returns true if field is a floating point value */ + CONSTEXPR bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; }; + + /** Return the type of the underlying CSV data */ + CONSTEXPR DataType type() noexcept { + this->get_value(); + return _type; + } + + private: + long double value = 0; /**< Cached numeric value */ + csv::string_view sv = ""; /**< A pointer to this field's text */ + DataType _type = DataType::UNKNOWN; /**< Cached data type value */ + CONSTEXPR void get_value() noexcept { + /* Check to see if value has been cached previously, if not + * evaluate it + */ + if ((int)_type < 0) { + this->_type = internals::data_type(this->sv, &this->value); + } + } + }; + + /** Data structure for representing CSV rows */ + class CSVRow { + public: + friend internals::IBasicCSVParser; + + CSVRow() = default; + + /** Construct a CSVRow from a RawCSVDataPtr */ + CSVRow(internals::RawCSVDataPtr _data) : data(_data) {} + CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds) + : data(_data), data_start(_data_start), fields_start(_field_bounds) {} + + /** Indicates whether row is empty or not */ + CONSTEXPR bool empty() const noexcept { return this->size() == 0; } + + /** Return the number of fields in this row */ + CONSTEXPR size_t size() const noexcept { return row_length; } + + /** @name Value Retrieval */ + ///@{ + CSVField operator[](size_t n) const; + CSVField operator[](const std::string&) const; + std::string to_json(const std::vector& subset = {}) const; + std::string to_json_array(const std::vector& subset = {}) const; + + /** Retrieve this row's associated column names */ + std::vector get_col_names() const { + return this->data->col_names->get_col_names(); + } + + /** Convert this CSVRow into a vector of strings. + * **Note**: This is a less efficient method of + * accessing data than using the [] operator. + */ + operator std::vector() const; + ///@} + + /** A random access iterator over the contents of a CSV row. + * Each iterator points to a CSVField. + */ + class iterator { + public: +#ifndef DOXYGEN_SHOULD_SKIP_THIS + using value_type = CSVField; + using difference_type = int; + + // Using CSVField * as pointer type causes segfaults in MSVC debug builds + // but using shared_ptr as pointer type won't compile in g++ +#ifdef _MSC_BUILD + using pointer = std::shared_ptr; +#else + using pointer = CSVField * ; +#endif + + using reference = CSVField & ; + using iterator_category = std::random_access_iterator_tag; +#endif + iterator(const CSVRow*, int i); + + reference operator*() const; + pointer operator->() const; + + iterator operator++(int); + iterator& operator++(); + iterator operator--(int); + iterator& operator--(); + iterator operator+(difference_type n) const; + iterator operator-(difference_type n) const; + + /** Two iterators are equal if they point to the same field */ + CONSTEXPR bool operator==(const iterator& other) const noexcept { + return this->i == other.i; + }; + + CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } + +#ifndef NDEBUG + friend CSVRow; +#endif + + private: + const CSVRow * daddy = nullptr; // Pointer to parent + std::shared_ptr field = nullptr; // Current field pointed at + int i = 0; // Index of current field + }; + + /** A reverse iterator over the contents of a CSVRow. */ + using reverse_iterator = std::reverse_iterator; + + /** @name Iterators + * @brief Each iterator points to a CSVField object. + */ + ///@{ + iterator begin() const; + iterator end() const noexcept; + reverse_iterator rbegin() const noexcept; + reverse_iterator rend() const; + ///@} + + private: + /** Retrieve a string view corresponding to the specified index */ + csv::string_view get_field(size_t index) const; + + internals::RawCSVDataPtr data; + + /** Where in RawCSVData.data we start */ + size_t data_start = 0; + + /** Where in the RawCSVDataPtr.fields array we start */ + size_t fields_start = 0; + + /** How many columns this row spans */ + size_t row_length = 0; + }; + +#ifdef _MSC_VER +#pragma region CSVField::get Specializations +#endif + /** Retrieve this field's original string */ + template<> + inline std::string CSVField::get() { + return std::string(this->sv); + } + + /** Retrieve a view over this field's string + * + * @warning This string_view is only guaranteed to be valid as long as this + * CSVRow is still alive. + */ + template<> + CONSTEXPR csv::string_view CSVField::get() { + return this->sv; + } + + /** Retrieve this field's value as a long double */ + template<> + CONSTEXPR long double CSVField::get() { + if (!is_num()) + throw std::runtime_error(internals::ERROR_NAN); + + return this->value; + } +#ifdef _MSC_VER +#pragma endregion CSVField::get Specializations +#endif + + /** Compares the contents of this field to a string */ + template<> + CONSTEXPR bool CSVField::operator==(const char * other) const noexcept + { + return this->sv == other; + } + + /** Compares the contents of this field to a string */ + template<> + CONSTEXPR bool CSVField::operator==(csv::string_view other) const noexcept + { + return this->sv == other; + } +} + +inline std::ostream& operator << (std::ostream& os, csv::CSVField const& value) { + os << std::string(value); + return os; +} + + +namespace csv { + namespace internals { + /** Create a vector v where each index i corresponds to the + * ASCII number for a character and, v[i + 128] labels it according to + * the CSVReader::ParseFlags enum + */ + HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter) { + std::array ret = {}; + for (int i = -128; i < 128; i++) { + const int arr_idx = i + 128; + char ch = char(i); + + if (ch == delimiter) + ret[arr_idx] = ParseFlags::DELIMITER; + else if (ch == '\r' || ch == '\n') + ret[arr_idx] = ParseFlags::NEWLINE; + else + ret[arr_idx] = ParseFlags::NOT_SPECIAL; + } + + return ret; + } + + /** Create a vector v where each index i corresponds to the + * ASCII number for a character and, v[i + 128] labels it according to + * the CSVReader::ParseFlags enum + */ + HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter, char quote_char) { + std::array ret = make_parse_flags(delimiter); + ret[(size_t)quote_char + 128] = ParseFlags::QUOTE; + return ret; + } + + /** Create a vector v where each index i corresponds to the + * ASCII number for a character c and, v[i + 128] is true if + * c is a whitespace character + */ + HEDLEY_CONST CONSTEXPR WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) { + std::array ret = {}; + for (int i = -128; i < 128; i++) { + const int arr_idx = i + 128; + char ch = char(i); + ret[arr_idx] = false; + + for (size_t j = 0; j < n_chars; j++) { + if (ws_chars[j] == ch) { + ret[arr_idx] = true; + } + } + } + + return ret; + } + + inline WhitespaceMap make_ws_flags(const std::vector& flags) { + return make_ws_flags(flags.data(), flags.size()); + } + + CSV_INLINE size_t get_file_size(csv::string_view filename); + + CSV_INLINE std::string get_csv_head(csv::string_view filename); + + /** Read the first 500KB of a CSV file */ + CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size); + + /** A std::deque wrapper which allows multiple read and write threads to concurrently + * access it along with providing read threads the ability to wait for the deque + * to become populated + */ + template + class ThreadSafeDeque { + public: + ThreadSafeDeque(size_t notify_size = 100) : _notify_size(notify_size) {}; + ThreadSafeDeque(const ThreadSafeDeque& other) { + this->data = other.data; + this->_notify_size = other._notify_size; + } + + ThreadSafeDeque(const std::deque& source) : ThreadSafeDeque() { + this->data = source; + } + + void clear() noexcept { this->data.clear(); } + + bool empty() const noexcept { + return this->data.empty(); + } + + T& front() noexcept { + return this->data.front(); + } + + T& operator[](size_t n) { + return this->data[n]; + } + + void push_back(T&& item) { + std::lock_guard lock{ this->_lock }; + this->data.push_back(std::move(item)); + + if (this->size() >= _notify_size) { + this->_cond.notify_all(); + } + } + + T pop_front() noexcept { + std::lock_guard lock{ this->_lock }; + T item = std::move(data.front()); + data.pop_front(); + return item; + } + + size_t size() const noexcept { return this->data.size(); } + + /** Returns true if a thread is actively pushing items to this deque */ + constexpr bool is_waitable() const noexcept { return this->_is_waitable; } + + /** Wait for an item to become available */ + void wait() { + if (!is_waitable()) { + return; + } + + std::unique_lock lock{ this->_lock }; + this->_cond.wait(lock, [this] { return this->size() >= _notify_size || !this->is_waitable(); }); + lock.unlock(); + } + + typename std::deque::iterator begin() noexcept { + return this->data.begin(); + } + + typename std::deque::iterator end() noexcept { + return this->data.end(); + } + + /** Tell listeners that this deque is actively being pushed to */ + void notify_all() { + std::unique_lock lock{ this->_lock }; + this->_is_waitable = true; + this->_cond.notify_all(); + } + + /** Tell all listeners to stop */ + void kill_all() { + std::unique_lock lock{ this->_lock }; + this->_is_waitable = false; + this->_cond.notify_all(); + } + + private: + bool _is_waitable = false; + size_t _notify_size; + std::mutex _lock; + std::condition_variable _cond; + std::deque data; + }; + + constexpr const int UNINITIALIZED_FIELD = -1; + } + + /** Standard type for storing collection of rows */ + using RowCollection = internals::ThreadSafeDeque; + + namespace internals { + /** Abstract base class which provides CSV parsing logic. + * + * Concrete implementations may customize this logic across + * different input sources, such as memory mapped files, stringstreams, + * etc... + */ + class IBasicCSVParser { + public: + IBasicCSVParser() = default; + IBasicCSVParser(const CSVFormat&, const ColNamesPtr&); + IBasicCSVParser(const ParseFlagMap& parse_flags, const WhitespaceMap& ws_flags + ) : _parse_flags(parse_flags), _ws_flags(ws_flags) {}; + + virtual ~IBasicCSVParser() {} + + /** Whether or not we have reached the end of source */ + bool eof() { return this->_eof; } + + /** Parse the next block of data */ + virtual void next(size_t bytes) = 0; + + /** Indicate the last block of data has been parsed */ + void end_feed(); + + CONSTEXPR ParseFlags parse_flag(const char ch) const noexcept { + return _parse_flags.data()[ch + 128]; + } + + CONSTEXPR ParseFlags compound_parse_flag(const char ch) const noexcept { + return quote_escape_flag(parse_flag(ch), this->quote_escape); + } + + /** Whether or not this CSV has a UTF-8 byte order mark */ + CONSTEXPR bool utf8_bom() const { return this->_utf8_bom; } + + void set_output(RowCollection& rows) { this->_records = &rows; } + + protected: + /** @name Current Parser State */ + ///@{ + CSVRow current_row; + RawCSVDataPtr data_ptr = nullptr; + ColNamesPtr _col_names = nullptr; + CSVFieldList* fields = nullptr; + int field_start = UNINITIALIZED_FIELD; + size_t field_length = 0; + + /** An array where the (i + 128)th slot gives the ParseFlags for ASCII character i */ + ParseFlagMap _parse_flags; + ///@} + + /** @name Current Stream/File State */ + ///@{ + bool _eof = false; + + /** The size of the incoming CSV */ + size_t source_size = 0; + ///@} + + /** Whether or not source needs to be read in chunks */ + CONSTEXPR bool no_chunk() const { return this->source_size < ITERATION_CHUNK_SIZE; } + + /** Parse the current chunk of data * + * + * @returns How many character were read that are part of complete rows + */ + size_t parse(); + + /** Create a new RawCSVDataPtr for a new chunk of data */ + void reset_data_ptr(); + private: + /** An array where the (i + 128)th slot determines whether ASCII character i should + * be trimmed + */ + WhitespaceMap _ws_flags; + bool quote_escape = false; + bool field_has_double_quote = false; + + /** Where we are in the current data block */ + size_t data_pos = 0; + + /** Whether or not an attempt to find Unicode BOM has been made */ + bool unicode_bom_scan = false; + bool _utf8_bom = false; + + /** Where complete rows should be pushed to */ + RowCollection* _records = nullptr; + + CONSTEXPR bool ws_flag(const char ch) const noexcept { + return _ws_flags.data()[ch + 128]; + } + + size_t& current_row_start() { + return this->current_row.data_start; + } + + void parse_field() noexcept; + + /** Finish parsing the current field */ + void push_field(); + + /** Finish parsing the current row */ + void push_row(); + + /** Handle possible Unicode byte order mark */ + void trim_utf8_bom(); + }; + + /** A class for parsing CSV data from a `std::stringstream` + * or an `std::ifstream` + */ + template + class StreamParser: public IBasicCSVParser { + using RowCollection = ThreadSafeDeque; + + public: + StreamParser(TStream& source, + const CSVFormat& format, + const ColNamesPtr& col_names = nullptr + ) : _source(std::move(source)), IBasicCSVParser(format, col_names) {}; + + StreamParser( + TStream& source, + internals::ParseFlagMap parse_flags, + internals::WhitespaceMap ws_flags) : + IBasicCSVParser(parse_flags, ws_flags), + _source(std::move(source)) + {}; + + ~StreamParser() {} + + void next(size_t bytes = ITERATION_CHUNK_SIZE) override { + if (this->eof()) return; + + this->reset_data_ptr(); + this->data_ptr->_data = std::make_shared(); + + if (source_size == 0) { + const auto start = _source.tellg(); + _source.seekg(0, std::ios::end); + const auto end = _source.tellg(); + _source.seekg(0, std::ios::beg); + + source_size = end - start; + } + + // Read data into buffer + size_t length = std::min(source_size - stream_pos, bytes); + std::unique_ptr buff(new char[length]); + _source.seekg(stream_pos, std::ios::beg); + _source.read(buff.get(), length); + stream_pos = _source.tellg(); + ((std::string*)(this->data_ptr->_data.get()))->assign(buff.get(), length); + + // Create string_view + this->data_ptr->data = *((std::string*)this->data_ptr->_data.get()); + + // Parse + this->current_row = CSVRow(this->data_ptr); + size_t remainder = this->parse(); + + if (stream_pos == source_size || no_chunk()) { + this->_eof = true; + this->end_feed(); + } + else { + this->stream_pos -= (length - remainder); + } + } + + private: + TStream _source; + size_t stream_pos = 0; + }; + + /** Parser for memory-mapped files + * + * @par Implementation + * This class constructs moving windows over a file to avoid + * creating massive memory maps which may require more RAM + * than the user has available. It contains logic to automatically + * re-align each memory map to the beginning of a CSV row. + * + */ + class MmapParser : public IBasicCSVParser { + public: + MmapParser(csv::string_view filename, + const CSVFormat& format, + const ColNamesPtr& col_names = nullptr + ) : IBasicCSVParser(format, col_names) { + this->_filename = filename.data(); + this->source_size = get_file_size(filename); + }; + + ~MmapParser() {} + + void next(size_t bytes) override; + + private: + std::string _filename; + size_t mmap_pos = 0; + }; + } +} + +/** The all encompassing namespace */ +namespace csv { + /** Stuff that is generally not of interest to end-users */ + namespace internals { + std::string format_row(const std::vector& row, csv::string_view delim = ", "); + + std::vector _get_col_names( csv::string_view head, const CSVFormat format = CSVFormat::guess_csv()); + + struct GuessScore { + double score; + size_t header; + }; + + CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format); + + CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); + } + + std::vector get_col_names( + csv::string_view filename, + const CSVFormat format = CSVFormat::guess_csv()); + + /** Guess the delimiter used by a delimiter-separated values file */ + CSVGuessResult guess_format(csv::string_view filename, + const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); + + /** @class CSVReader + * @brief Main class for parsing CSVs from files and in-memory sources + * + * All rows are compared to the column names for length consistency + * - By default, rows that are too short or too long are dropped + * - Custom behavior can be defined by overriding bad_row_handler in a subclass + */ + class CSVReader { + public: + /** + * An input iterator capable of handling large files. + * @note Created by CSVReader::begin() and CSVReader::end(). + * + * @par Iterating over a file + * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 1 + * + * @par Using with `` library + * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 2 + */ + class iterator { + public: + #ifndef DOXYGEN_SHOULD_SKIP_THIS + using value_type = CSVRow; + using difference_type = std::ptrdiff_t; + using pointer = CSVRow * ; + using reference = CSVRow & ; + using iterator_category = std::input_iterator_tag; + #endif + + iterator() = default; + iterator(CSVReader* reader) : daddy(reader) {}; + iterator(CSVReader*, CSVRow&&); + + /** Access the CSVRow held by the iterator */ + CONSTEXPR reference operator*() { return this->row; } + + /** Return a pointer to the CSVRow the iterator has stopped at */ + CONSTEXPR pointer operator->() { return &(this->row); } + + iterator& operator++(); /**< Pre-increment iterator */ + iterator operator++(int); /**< Post-increment ierator */ + iterator& operator--(); + + /** Returns true if iterators were constructed from the same CSVReader + * and point to the same row + */ + CONSTEXPR bool operator==(const iterator& other) const noexcept { + return (this->daddy == other.daddy) && (this->i == other.i); + } + + CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } + private: + CSVReader * daddy = nullptr; // Pointer to parent + CSVRow row; // Current row + size_t i = 0; // Index of current row + }; + + /** @name Constructors + * Constructors for iterating over large files and parsing in-memory sources. + */ + ///@{ + CSVReader(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); + + /** Allows parsing stream sources such as `std::stringstream` or `std::ifstream` + * + * @tparam TStream An input stream deriving from `std::istream` + * @note Currently this constructor requires special CSV dialects to be manually + * specified. + */ + template::value, int> = 0> + CSVReader(TStream& source, CSVFormat format = CSVFormat()) : _format(format) { + using Parser = internals::StreamParser; + + if (!format.col_names.empty()) + this->set_col_names(format.col_names); + + this->parser = std::unique_ptr( + new Parser(source, format, col_names)); // For C++11 + this->initial_read(); + } + ///@} + + CSVReader(const CSVReader&) = delete; // No copy constructor + CSVReader(CSVReader&&) = default; // Move constructor + CSVReader& operator=(const CSVReader&) = delete; // No copy assignment + CSVReader& operator=(CSVReader&& other) = default; + ~CSVReader() { + if (this->read_csv_worker.joinable()) { + this->read_csv_worker.join(); + } + } + + /** @name Retrieving CSV Rows */ + ///@{ + bool read_row(CSVRow &row); + iterator begin(); + HEDLEY_CONST iterator end() const noexcept; + + /** Returns true if we have reached end of file */ + bool eof() const noexcept { return this->parser->eof(); }; + ///@} + + /** @name CSV Metadata */ + ///@{ + CSVFormat get_format() const; + std::vector get_col_names() const; + int index_of(csv::string_view col_name) const; + ///@} + + /** @name CSV Metadata: Attributes */ + ///@{ + /** Whether or not the file or stream contains valid CSV rows, + * not including the header. + * + * @note Gives an accurate answer regardless of when it is called. + * + */ + CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; } + + /** Retrieves the number of rows that have been read so far */ + CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; } + + /** Whether or not CSV was prefixed with a UTF-8 bom */ + bool utf8_bom() const noexcept { return this->parser->utf8_bom(); } + ///@} + + protected: + /** + * \defgroup csv_internal CSV Parser Internals + * @brief Internals of CSVReader. Only maintainers and those looking to + * extend the parser should read this. + * @{ + */ + + /** Sets this reader's column names and associated data */ + void set_col_names(const std::vector&); + + /** @name CSV Settings **/ + ///@{ + CSVFormat _format; + ///@} + + /** @name Parser State */ + ///@{ + /** Pointer to a object containing column information */ + internals::ColNamesPtr col_names = std::make_shared(); + + /** Helper class which actually does the parsing */ + std::unique_ptr parser = nullptr; + + /** Queue of parsed CSV rows */ + RowCollection records = RowCollection(100); + + size_t n_cols = 0; /**< The number of columns in this CSV */ + size_t _n_rows = 0; /**< How many rows (minus header) have been read so far */ + + /** @name Multi-Threaded File Reading Functions */ + ///@{ + bool read_csv(size_t bytes = internals::ITERATION_CHUNK_SIZE); + ///@} + + /**@}*/ + + private: + /** Whether or not rows before header were trimmed */ + bool header_trimmed = false; + + /** @name Multi-Threaded File Reading: Flags and State */ + ///@{ + std::thread read_csv_worker; /**< Worker thread for read_csv() */ + ///@} + + /** Read initial chunk to get metadata */ + void initial_read() { + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + this->read_csv_worker.join(); + } + + void trim_header(); + }; +} +/** @file + * Calculates statistics from CSV files + */ + +#include +#include +#include + +namespace csv { + /** Class for calculating statistics from CSV files and in-memory sources + * + * **Example** + * \include programs/csv_stats.cpp + * + */ + class CSVStat { + public: + using FreqCount = std::unordered_map; + using TypeCount = std::unordered_map; + + std::vector get_mean() const; + std::vector get_variance() const; + std::vector get_mins() const; + std::vector get_maxes() const; + std::vector get_counts() const; + std::vector get_dtypes() const; + + std::vector get_col_names() const { + return this->reader.get_col_names(); + } + + CSVStat(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); + CSVStat(std::stringstream& source, CSVFormat format = CSVFormat()); + private: + // An array of rolling averages + // Each index corresponds to the rolling mean for the column at said index + std::vector rolling_means; + std::vector rolling_vars; + std::vector mins; + std::vector maxes; + std::vector counts; + std::vector dtypes; + std::vector n; + + // Statistic calculators + void variance(const long double&, const size_t&); + void count(CSVField&, const size_t&); + void min_max(const long double&, const size_t&); + void dtype(CSVField&, const size_t&); + + void calc(); + void calc_worker(const size_t&); + + CSVReader reader; + std::deque records = {}; + }; +} + +#include +#include +#include + +namespace csv { + /** Returned by get_file_info() */ + struct CSVFileInfo { + std::string filename; /**< Filename */ + std::vector col_names; /**< CSV column names */ + char delim; /**< Delimiting character */ + size_t n_rows; /**< Number of rows in a file */ + size_t n_cols; /**< Number of columns in a CSV */ + }; + + /** @name Shorthand Parsing Functions + * @brief Convienience functions for parsing small strings + */ + ///@{ + CSVReader operator ""_csv(const char*, size_t); + CSVReader operator ""_csv_no_header(const char*, size_t); + CSVReader parse(csv::string_view in, CSVFormat format = CSVFormat()); + CSVReader parse_no_header(csv::string_view in); + ///@} + + /** @name Utility Functions */ + ///@{ + std::unordered_map csv_data_types(const std::string&); + CSVFileInfo get_file_info(const std::string& filename); + int get_col_pos(csv::string_view filename, csv::string_view col_name, + const CSVFormat& format = CSVFormat::guess_csv()); + ///@} +} +/** @file + * A standalone header file for writing delimiter-separated files + */ + +#include +#include +#include +#include +#include +#include + + +namespace csv { + namespace internals { + /** to_string() for unsigned integers */ + template::value, int> = 0> + inline std::string to_string(T value) { + std::string digits_reverse = ""; + + if (value == 0) return "0"; + + while (value > 0) { + digits_reverse += (char)('0' + (value % 10)); + value /= 10; + } + + return std::string(digits_reverse.rbegin(), digits_reverse.rend()); + } + + /** to_string() for signed integers */ + template< + typename T, + csv::enable_if_t::value && std::is_signed::value, int> = 0 + > + inline std::string to_string(T value) { + if (value >= 0) + return to_string((size_t)value); + + return "-" + to_string((size_t)(value * -1)); + } + + /** to_string() for floating point numbers */ + template< + typename T, + csv::enable_if_t::value, int> = 0 + > + inline std::string to_string(T value) { + std::string result; + + if (value < 0) result = "-"; + + // Integral part + size_t integral = (size_t)(std::abs(value)); + result += (integral == 0) ? "0" : to_string(integral); + + // Decimal part + size_t decimal = (size_t)(((double)std::abs(value) - (double)integral) * 100000); + + result += "."; + result += (decimal == 0) ? "0" : to_string(decimal); + + return result; + } + } + + /** @name CSV Writing */ + ///@{ + /** + * Class for writing delimiter separated values files + * + * To write formatted strings, one should + * -# Initialize a DelimWriter with respect to some output stream + * -# Call write_row() on std::vectors of unformatted text + * + * @tparam OutputStream The output stream, e.g. `std::ofstream`, `std::stringstream` + * @tparam Delim The delimiter character + * @tparam Quote The quote character + * @tparam Flush True: flush after every writing function, + * false: you need to flush explicitly if needed. + * In both cases the destructor will flush. + * + * @par Hint + * Use the aliases csv::CSVWriter to write CSV + * formatted strings and csv::TSVWriter + * to write tab separated strings + * + * @par Example w/ std::vector, std::deque, std::list + * @snippet test_write_csv.cpp CSV Writer Example + * + * @par Example w/ std::tuple + * @snippet test_write_csv.cpp CSV Writer Tuple Example + */ + template + class DelimWriter { + public: + /** Construct a DelimWriter over the specified output stream + * + * @param _out Stream to write to + * @param _quote_minimal Limit field quoting to only when necessary + */ + DelimWriter(OutputStream& _out, bool _quote_minimal = true) + : out(_out), quote_minimal(_quote_minimal) {}; + + /** Construct a DelimWriter over the file + * + * @param[out] filename File to write to + */ + DelimWriter(const std::string& filename) : DelimWriter(std::ifstream(filename)) {}; + + /** Destructor will flush remaining data + * + */ + ~DelimWriter() { + out.flush(); + } + + /** Format a sequence of strings and write to CSV according to RFC 4180 + * + * @warning This does not check to make sure row lengths are consistent + * + * @param[in] record Sequence of strings to be formatted + * + * @return The current DelimWriter instance (allowing for operator chaining) + */ + template + DelimWriter& operator<<(const std::array& record) { + for (size_t i = 0; i < Size; i++) { + out << csv_escape(record[i]); + if (i + 1 != Size) out << Delim; + } + + end_out(); + return *this; + } + + /** @copydoc operator<< */ + template + DelimWriter& operator<<(const std::tuple& record) { + this->write_tuple<0, T...>(record); + return *this; + } + + /** + * @tparam T A container such as std::vector, std::deque, or std::list + * + * @copydoc operator<< + */ + template< + typename T, typename Alloc, template class Container, + + // Avoid conflicting with tuples with two elements + csv::enable_if_t::value, int> = 0 + > + DelimWriter& operator<<(const Container& record) { + const size_t ilen = record.size(); + size_t i = 0; + for (const auto& field : record) { + out << csv_escape(field); + if (i + 1 != ilen) out << Delim; + i++; + } + + end_out(); + return *this; + } + + /** Flushes the written data + * + */ + void flush() { + out.flush(); + } + + private: + template< + typename T, + csv::enable_if_t< + !std::is_convertible::value + && !std::is_convertible::value + , int> = 0 + > + std::string csv_escape(T in) { + return internals::to_string(in); + } + + template< + typename T, + csv::enable_if_t< + std::is_convertible::value + || std::is_convertible::value + , int> = 0 + > + std::string csv_escape(T in) { + IF_CONSTEXPR(std::is_convertible::value) { + return _csv_escape(in); + } + + return _csv_escape(std::string(in)); + } + + std::string _csv_escape(csv::string_view in) { + /** Format a string to be RFC 4180-compliant + * @param[in] in String to be CSV-formatted + * @param[out] quote_minimal Only quote fields if necessary. + * If False, everything is quoted. + */ + + // Do we need a quote escape + bool quote_escape = false; + + for (auto ch : in) { + if (ch == Quote || ch == Delim) { + quote_escape = true; + break; + } + } + + if (!quote_escape) { + if (quote_minimal) return std::string(in); + else { + std::string ret(Quote, 1); + ret += in.data(); + ret += Quote; + } + } + + // Start initial quote escape sequence + std::string ret(1, Quote); + for (auto ch: in) { + if (ch == Quote) ret += std::string(2, Quote); + else ret += ch; + } + + // Finish off quote escape + ret += Quote; + return ret; + } + + /** Recurisve template for writing std::tuples */ + template + typename std::enable_if::type write_tuple(const std::tuple& record) { + out << csv_escape(std::get(record)); + + IF_CONSTEXPR (Index + 1 < sizeof...(T)) out << Delim; + + this->write_tuple(record); + } + + /** Base case for writing std::tuples */ + template + typename std::enable_if::type write_tuple(const std::tuple& record) { + (void)record; + end_out(); + } + + /** Ends a line in 'out' and flushes, if Flush is true.*/ + void end_out() { + out << '\n'; + IF_CONSTEXPR(Flush) out.flush(); + } + + OutputStream & out; + bool quote_minimal; + }; + + /** An alias for csv::DelimWriter for writing standard CSV files + * + * @sa csv::DelimWriter::operator<<() + * + * @note Use `csv::make_csv_writer()` to in instatiate this class over + * an actual output stream. + */ + template + using CSVWriter = DelimWriter; + + /** Class for writing tab-separated values files + * + * @sa csv::DelimWriter::write_row() + * @sa csv::DelimWriter::operator<<() + * + * @note Use `csv::make_tsv_writer()` to in instatiate this class over + * an actual output stream. + */ + template + using TSVWriter = DelimWriter; + + /** Return a csv::CSVWriter over the output stream */ + template + inline CSVWriter make_csv_writer(OutputStream& out, bool quote_minimal=true) { + return CSVWriter(out, quote_minimal); + } + + /** Return a buffered csv::CSVWriter over the output stream (does not auto flush) */ + template + inline CSVWriter make_csv_writer_buffered(OutputStream& out, bool quote_minimal=true) { + return CSVWriter(out, quote_minimal); + } + + /** Return a csv::TSVWriter over the output stream */ + template + inline TSVWriter make_tsv_writer(OutputStream& out, bool quote_minimal=true) { + return TSVWriter(out, quote_minimal); + } + + /** Return a buffered csv::TSVWriter over the output stream (does not auto flush) */ + template + inline TSVWriter make_tsv_writer_buffered(OutputStream& out, bool quote_minimal=true) { + return TSVWriter(out, quote_minimal); + } + ///@} +} + +/** @file + * Defines an input iterator for csv::CSVReader + */ + + +namespace csv { + /** Return an iterator to the first row in the reader */ + CSV_INLINE CSVReader::iterator CSVReader::begin() { + if (this->records.empty()) { + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + this->read_csv_worker.join(); + + // Still empty => return end iterator + if (this->records.empty()) return this->end(); + } + + CSVReader::iterator ret(this, std::move(this->records.pop_front())); + return ret; + } + + /** A placeholder for the imaginary past the end row in a CSV. + * Attempting to deference this will lead to bad things. + */ + CSV_INLINE HEDLEY_CONST CSVReader::iterator CSVReader::end() const noexcept { + return CSVReader::iterator(); + } + + ///////////////////////// + // CSVReader::iterator // + ///////////////////////// + + CSV_INLINE CSVReader::iterator::iterator(CSVReader* _daddy, CSVRow&& _row) : + daddy(_daddy) { + row = std::move(_row); + } + + /** Advance the iterator by one row. If this CSVReader has an + * associated file, then the iterator will lazily pull more data from + * that file until the end of file is reached. + * + * @note This iterator does **not** block the thread responsible for parsing CSV. + * + */ + CSV_INLINE CSVReader::iterator& CSVReader::iterator::operator++() { + if (!daddy->read_row(this->row)) { + this->daddy = nullptr; // this == end() + } + + return *this; + } + + /** Post-increment iterator */ + CSV_INLINE CSVReader::iterator CSVReader::iterator::operator++(int) { + auto temp = *this; + if (!daddy->read_row(this->row)) { + this->daddy = nullptr; // this == end() + } + + return temp; + } +} +/** @file + * Defines an object used to store CSV format settings + */ + +#include +#include + + +namespace csv { + CSV_INLINE CSVFormat& CSVFormat::delimiter(char delim) { + this->possible_delimiters = { delim }; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::delimiter(const std::vector & delim) { + this->possible_delimiters = delim; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::quote(char quote) { + this->no_quote = false; + this->quote_char = quote; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::trim(const std::vector & chars) { + this->trim_chars = chars; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::column_names(const std::vector& names) { + this->col_names = names; + this->header = -1; + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::header_row(int row) { + if (row < 0) this->variable_column_policy = VariableColumnPolicy::KEEP; + + this->header = row; + this->col_names = {}; + return *this; + } + + CSV_INLINE void CSVFormat::assert_no_char_overlap() + { + auto delims = std::set( + this->possible_delimiters.begin(), this->possible_delimiters.end()), + trims = std::set( + this->trim_chars.begin(), this->trim_chars.end()); + + // Stores intersection of possible delimiters and trim characters + std::vector intersection = {}; + + // Find which characters overlap, if any + std::set_intersection( + delims.begin(), delims.end(), + trims.begin(), trims.end(), + std::back_inserter(intersection)); + + // Make sure quote character is not contained in possible delimiters + // or whitespace characters + if (delims.find(this->quote_char) != delims.end() || + trims.find(this->quote_char) != trims.end()) { + intersection.push_back(this->quote_char); + } + + if (!intersection.empty()) { + std::string err_msg = "There should be no overlap between the quote character, " + "the set of possible delimiters " + "and the set of whitespace characters. Offending characters: "; + + // Create a pretty error message with the list of overlapping + // characters + for (size_t i = 0; i < intersection.size(); i++) { + err_msg += "'"; + err_msg += intersection[i]; + err_msg += "'"; + + if (i + 1 < intersection.size()) + err_msg += ", "; + } + + throw std::runtime_error(err_msg + '.'); + } + } +} + +namespace csv { + namespace internals { + CSV_INLINE std::vector ColNames::get_col_names() const { + return this->col_names; + } + + CSV_INLINE void ColNames::set_col_names(const std::vector& cnames) { + this->col_names = cnames; + + for (size_t i = 0; i < cnames.size(); i++) { + this->col_pos[cnames[i]] = i; + } + } + + CSV_INLINE int ColNames::index_of(csv::string_view col_name) const { + auto pos = this->col_pos.find(col_name.data()); + if (pos != this->col_pos.end()) + return (int)pos->second; + + return CSV_NOT_FOUND; + } + + CSV_INLINE size_t ColNames::size() const noexcept { + return this->col_names.size(); + } + + } +} +#include +#include + + +namespace csv { + /** Shorthand function for parsing an in-memory CSV string + * + * @return A collection of CSVRow objects + * + * @par Example + * @snippet tests/test_read_csv.cpp Parse Example + */ + CSV_INLINE CSVReader parse(csv::string_view in, CSVFormat format) { + std::stringstream stream(in.data()); + return CSVReader(stream, format); + } + + /** Parses a CSV string with no headers + * + * @return A collection of CSVRow objects + */ + CSV_INLINE CSVReader parse_no_header(csv::string_view in) { + CSVFormat format; + format.header_row(-1); + + return parse(in, format); + } + + /** Parse a RFC 4180 CSV string, returning a collection + * of CSVRow objects + * + * @par Example + * @snippet tests/test_read_csv.cpp Escaped Comma + * + */ + CSV_INLINE CSVReader operator ""_csv(const char* in, size_t n) { + return parse(csv::string_view(in, n)); + } + + /** A shorthand for csv::parse_no_header() */ + CSV_INLINE CSVReader operator ""_csv_no_header(const char* in, size_t n) { + return parse_no_header(csv::string_view(in, n)); + } + + /** + * Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise + * + * @param[in] filename Path to CSV file + * @param[in] col_name Column whose position we should resolve + * @param[in] format Format of the CSV file + */ + CSV_INLINE int get_col_pos( + csv::string_view filename, + csv::string_view col_name, + const CSVFormat& format) { + CSVReader reader(filename, format); + return reader.index_of(col_name); + } + + /** Get basic information about a CSV file + * @include programs/csv_info.cpp + */ + CSV_INLINE CSVFileInfo get_file_info(const std::string& filename) { + CSVReader reader(filename); + CSVFormat format = reader.get_format(); + for (auto it = reader.begin(); it != reader.end(); ++it); + + CSVFileInfo info = { + filename, + reader.get_col_names(), + format.get_delim(), + reader.n_rows(), + reader.get_col_names().size() + }; + + return info; + } +} +/** @file + * @brief Defines functionality needed for basic CSV parsing + */ + + +namespace csv { + namespace internals { + CSV_INLINE std::string format_row(const std::vector& row, csv::string_view delim) { + /** Print a CSV row */ + std::stringstream ret; + for (size_t i = 0; i < row.size(); i++) { + ret << row[i]; + if (i + 1 < row.size()) ret << delim; + else ret << '\n'; + } + ret.flush(); + + return ret.str(); + } + + /** Return a CSV's column names + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + */ + CSV_INLINE std::vector _get_col_names(csv::string_view head, CSVFormat format) { + // Parse the CSV + auto trim_chars = format.get_trim_chars(); + std::stringstream source(head.data()); + RowCollection rows; + + StreamParser parser(source, format); + parser.set_output(rows); + parser.next(); + + return CSVRow(std::move(rows[format.get_header()])); + } + + CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format) { + // Frequency counter of row length + std::unordered_map row_tally = { { 0, 0 } }; + + // Map row lengths to row num where they first occurred + std::unordered_map row_when = { { 0, 0 } }; + + // Parse the CSV + std::stringstream source(head.data()); + RowCollection rows; + + StreamParser parser(source, format); + parser.set_output(rows); + parser.next(); + + for (size_t i = 0; i < rows.size(); i++) { + auto& row = rows[i]; + + // Ignore zero-length rows + if (row.size() > 0) { + if (row_tally.find(row.size()) != row_tally.end()) { + row_tally[row.size()]++; + } + else { + row_tally[row.size()] = 1; + row_when[row.size()] = i; + } + } + } + + double final_score = 0; + size_t header_row = 0; + + // Final score is equal to the largest + // row size times rows of that size + for (auto& pair : row_tally) { + auto row_size = pair.first; + auto row_count = pair.second; + double score = (double)(row_size * row_count); + if (score > final_score) { + final_score = score; + header_row = row_when[row_size]; + } + } + + return { + final_score, + header_row + }; + } + + /** Guess the delimiter used by a delimiter-separated values file */ + CSV_INLINE CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims) { + /** For each delimiter, find out which row length was most common. + * The delimiter with the longest mode row length wins. + * Then, the line number of the header row is the first row with + * the mode row length. + */ + + CSVFormat format; + size_t max_score = 0, + header = 0; + char current_delim = delims[0]; + + for (char cand_delim : delims) { + auto result = calculate_score(head, format.delimiter(cand_delim)); + + if (result.score > max_score) { + max_score = (size_t)result.score; + current_delim = cand_delim; + header = result.header; + } + } + + return { current_delim, (int)header }; + } + } + + /** Return a CSV's column names + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + */ + CSV_INLINE std::vector get_col_names(csv::string_view filename, CSVFormat format) { + auto head = internals::get_csv_head(filename); + + /** Guess delimiter and header row */ + if (format.guess_delim()) { + auto guess_result = guess_format(filename, format.get_possible_delims()); + format.delimiter(guess_result.delim).header_row(guess_result.header_row); + } + + return internals::_get_col_names(head, format); + } + + /** Guess the delimiter used by a delimiter-separated values file */ + CSV_INLINE CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims) { + auto head = internals::get_csv_head(filename); + return internals::_guess_format(head, delims); + } + + /** Reads an arbitrarily large CSV file using memory-mapped IO. + * + * **Details:** Reads the first block of a CSV file synchronously to get information + * such as column names and delimiting character. + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + * \snippet tests/test_read_csv.cpp CSVField Example + * + */ + CSV_INLINE CSVReader::CSVReader(csv::string_view filename, CSVFormat format) : _format(format) { + auto head = internals::get_csv_head(filename); + using Parser = internals::MmapParser; + + /** Guess delimiter and header row */ + if (format.guess_delim()) { + auto guess_result = internals::_guess_format(head, format.possible_delimiters); + format.delimiter(guess_result.delim); + format.header = guess_result.header_row; + this->_format = format; + } + + if (!format.col_names.empty()) + this->set_col_names(format.col_names); + + this->parser = std::unique_ptr(new Parser(filename, format, this->col_names)); // For C++11 + this->initial_read(); + } + + /** Return the format of the original raw CSV */ + CSV_INLINE CSVFormat CSVReader::get_format() const { + CSVFormat new_format = this->_format; + + // Since users are normally not allowed to set + // column names and header row simulatenously, + // we will set the backing variables directly here + new_format.col_names = this->col_names->get_col_names(); + new_format.header = this->_format.header; + + return new_format; + } + + /** Return the CSV's column names as a vector of strings. */ + CSV_INLINE std::vector CSVReader::get_col_names() const { + if (this->col_names) { + return this->col_names->get_col_names(); + } + + return std::vector(); + } + + /** Return the index of the column name if found or + * csv::CSV_NOT_FOUND otherwise. + */ + CSV_INLINE int CSVReader::index_of(csv::string_view col_name) const { + auto _col_names = this->get_col_names(); + for (size_t i = 0; i < _col_names.size(); i++) + if (_col_names[i] == col_name) return (int)i; + + return CSV_NOT_FOUND; + } + + CSV_INLINE void CSVReader::trim_header() { + if (!this->header_trimmed) { + for (int i = 0; i <= this->_format.header && !this->records.empty(); i++) { + if (i == this->_format.header && this->col_names->empty()) { + this->set_col_names(this->records.pop_front()); + } + else { + this->records.pop_front(); + } + } + + this->header_trimmed = true; + } + } + + /** + * @param[in] names Column names + */ + CSV_INLINE void CSVReader::set_col_names(const std::vector& names) + { + this->col_names->set_col_names(names); + this->n_cols = names.size(); + } + + /** + * Read a chunk of CSV data. + * + * @note This method is meant to be run on its own thread. Only one `read_csv()` thread + * should be active at a time. + * + * @param[in] bytes Number of bytes to read. + * + * @see CSVReader::read_csv_worker + * @see CSVReader::read_row() + */ + CSV_INLINE bool CSVReader::read_csv(size_t bytes) { + // Tell read_row() to listen for CSV rows + this->records.notify_all(); + + this->parser->set_output(this->records); + this->parser->next(bytes); + + if (!this->header_trimmed) { + this->trim_header(); + } + + // Tell read_row() to stop waiting + this->records.kill_all(); + + return true; + } + + /** + * Retrieve rows as CSVRow objects, returning true if more rows are available. + * + * @par Performance Notes + * - Reads chunks of data that are csv::internals::ITERATION_CHUNK_SIZE bytes large at a time + * - For performance details, read the documentation for CSVRow and CSVField. + * + * @param[out] row The variable where the parsed row will be stored + * @see CSVRow, CSVField + * + * **Example:** + * \snippet tests/test_read_csv.cpp CSVField Example + * + */ + CSV_INLINE bool CSVReader::read_row(CSVRow &row) { + while (true) { + if (this->records.empty()) { + if (this->records.is_waitable()) + // Reading thread is currently active => wait for it to populate records + this->records.wait(); + else if (this->parser->eof()) + // End of file and no more records + return false; + else { + // Reading thread is not active => start another one + if (this->read_csv_worker.joinable()) + this->read_csv_worker.join(); + + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + } + } + else if (this->records.front().size() != this->n_cols && + this->_format.variable_column_policy != VariableColumnPolicy::KEEP) { + auto errored_row = this->records.pop_front(); + + if (this->_format.variable_column_policy == VariableColumnPolicy::THROW) { + if (errored_row.size() < this->n_cols) + throw std::runtime_error("Line too short " + internals::format_row(errored_row)); + + throw std::runtime_error("Line too long " + internals::format_row(errored_row)); + } + } + else { + row = std::move(this->records.pop_front()); + this->_n_rows++; + return true; + } + } + + return false; + } +} +/** @file + * Calculates statistics from CSV files + */ + +#include + +namespace csv { + /** Calculate statistics for an arbitrarily large file. When this constructor + * is called, CSVStat will process the entire file iteratively. Once finished, + * methods like get_mean(), get_counts(), etc... can be used to retrieve statistics. + */ + CSV_INLINE CSVStat::CSVStat(csv::string_view filename, CSVFormat format) : + reader(filename, format) { + this->calc(); + } + + /** Calculate statistics for a CSV stored in a std::stringstream */ + CSV_INLINE CSVStat::CSVStat(std::stringstream& stream, CSVFormat format) : + reader(stream, format) { + this->calc(); + } + + /** Return current means */ + CSV_INLINE std::vector CSVStat::get_mean() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->rolling_means[i]); + } + return ret; + } + + /** Return current variances */ + CSV_INLINE std::vector CSVStat::get_variance() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->rolling_vars[i]/(this->n[i] - 1)); + } + return ret; + } + + /** Return current mins */ + CSV_INLINE std::vector CSVStat::get_mins() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->mins[i]); + } + return ret; + } + + /** Return current maxes */ + CSV_INLINE std::vector CSVStat::get_maxes() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->maxes[i]); + } + return ret; + } + + /** Get counts for each column */ + CSV_INLINE std::vector CSVStat::get_counts() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->counts[i]); + } + return ret; + } + + /** Get data type counts for each column */ + CSV_INLINE std::vector CSVStat::get_dtypes() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->dtypes[i]); + } + return ret; + } + + CSV_INLINE void CSVStat::calc() { + constexpr size_t CALC_CHUNK_SIZE = 5000; + + while (true) { + /** Chunk rows */ + for (auto& row : reader) { + if (this->records.size() < CALC_CHUNK_SIZE) { + this->records.push_back(std::move(row)); + } + } + + /** Go through all records and calculate specified statistics */ + for (size_t i = 0; i < this->get_col_names().size(); i++) { + dtypes.push_back({}); + counts.push_back({}); + rolling_means.push_back(0); + rolling_vars.push_back(0); + mins.push_back(NAN); + maxes.push_back(NAN); + n.push_back(0); + } + + std::vector pool; + + // Start threads + for (size_t i = 0; i < this->get_col_names().size(); i++) + pool.push_back(std::thread(&CSVStat::calc_worker, this, i)); + + // Block until done + for (auto& th : pool) + th.join(); + + if (reader.eof()) break; + } + } + + CSV_INLINE void CSVStat::calc_worker(const size_t &i) { + /** Worker thread for CSVStat::calc() which calculates statistics for one column. + * + * @param[in] i Column index + */ + + auto current_record = this->records.begin(); + + for (size_t processed = 0; current_record != this->records.end(); processed++) { + if (current_record->size() == this->get_col_names().size()) { + auto current_field = (*current_record)[i]; + + // Optimization: Don't count() if there's too many distinct values in the first 1000 rows + if (processed < 1000 || this->counts[i].size() <= 500) + this->count(current_field, i); + + this->dtype(current_field, i); + + // Numeric Stuff + if (current_field.is_num()) { + long double x_n = current_field.get(); + + // This actually calculates mean AND variance + this->variance(x_n, i); + this->min_max(x_n, i); + } + } + else if (this->reader.get_format().get_variable_column_policy() == VariableColumnPolicy::THROW) { + throw std::runtime_error("Line has different length than the others " + internals::format_row(*current_record)); + } + + ++current_record; + } + } + + CSV_INLINE void CSVStat::dtype(CSVField& data, const size_t &i) { + /** Given a record update the type counter + * @param[in] record Data observation + * @param[out] i The column index that should be updated + */ + + auto type = data.type(); + if (this->dtypes[i].find(type) != + this->dtypes[i].end()) { + // Increment count + this->dtypes[i][type]++; + } else { + // Initialize count + this->dtypes[i].insert(std::make_pair(type, 1)); + } + } + + CSV_INLINE void CSVStat::count(CSVField& data, const size_t &i) { + /** Given a record update the frequency counter + * @param[in] record Data observation + * @param[out] i The column index that should be updated + */ + + auto item = data.get(); + + if (this->counts[i].find(item) != + this->counts[i].end()) { + // Increment count + this->counts[i][item]++; + } else { + // Initialize count + this->counts[i].insert(std::make_pair(item, 1)); + } + } + + CSV_INLINE void CSVStat::min_max(const long double &x_n, const size_t &i) { + /** Update current minimum and maximum + * @param[in] x_n Data observation + * @param[out] i The column index that should be updated + */ + if (std::isnan(this->mins[i])) + this->mins[i] = x_n; + if (std::isnan(this->maxes[i])) + this->maxes[i] = x_n; + + if (x_n < this->mins[i]) + this->mins[i] = x_n; + else if (x_n > this->maxes[i]) + this->maxes[i] = x_n; + } + + CSV_INLINE void CSVStat::variance(const long double &x_n, const size_t &i) { + /** Given a record update rolling mean and variance for all columns + * using Welford's Algorithm + * @param[in] x_n Data observation + * @param[out] i The column index that should be updated + */ + long double& current_rolling_mean = this->rolling_means[i]; + long double& current_rolling_var = this->rolling_vars[i]; + long double& current_n = this->n[i]; + long double delta; + long double delta2; + + current_n++; + + if (current_n == 1) { + current_rolling_mean = x_n; + } else { + delta = x_n - current_rolling_mean; + current_rolling_mean += delta/current_n; + delta2 = x_n - current_rolling_mean; + current_rolling_var += delta*delta2; + } + } + + /** Useful for uploading CSV files to SQL databases. + * + * Return a data type for each column such that every value in a column can be + * converted to the corresponding data type without data loss. + * @param[in] filename The CSV file + * + * \return A mapping of column names to csv::DataType enums + */ + CSV_INLINE std::unordered_map csv_data_types(const std::string& filename) { + CSVStat stat(filename); + std::unordered_map csv_dtypes; + + auto col_names = stat.get_col_names(); + auto temp = stat.get_dtypes(); + + for (size_t i = 0; i < stat.get_col_names().size(); i++) { + auto& col = temp[i]; + auto& col_name = col_names[i]; + + if (col[DataType::CSV_STRING]) + csv_dtypes[col_name] = DataType::CSV_STRING; + else if (col[DataType::CSV_INT64]) + csv_dtypes[col_name] = DataType::CSV_INT64; + else if (col[DataType::CSV_INT32]) + csv_dtypes[col_name] = DataType::CSV_INT32; + else if (col[DataType::CSV_INT16]) + csv_dtypes[col_name] = DataType::CSV_INT16; + else if (col[DataType::CSV_INT8]) + csv_dtypes[col_name] = DataType::CSV_INT8; + else + csv_dtypes[col_name] = DataType::CSV_DOUBLE; + } + + return csv_dtypes; + } +} +/** @file + * Defines the data type used for storing information about a CSV row + */ + +#include +#include + +namespace csv { + namespace internals { + CSV_INLINE RawCSVField& CSVFieldList::operator[](size_t n) const { + const size_t page_no = n / _single_buffer_capacity; + const size_t buffer_idx = (page_no < 1) ? n : n % _single_buffer_capacity; + return this->buffers[page_no][buffer_idx]; + } + + CSV_INLINE void CSVFieldList::allocate() { + RawCSVField * buffer = new RawCSVField[_single_buffer_capacity]; + buffers.push_back(buffer); + _current_buffer_size = 0; + _back = &(buffers.back()[0]); + } + } + + /** Return a CSVField object corrsponding to the nth value in the row. + * + * @note This method performs bounds checking, and will throw an + * `std::runtime_error` if n is invalid. + * + * @complexity + * Constant, by calling csv::CSVRow::get_csv::string_view() + * + */ + CSV_INLINE CSVField CSVRow::operator[](size_t n) const { + return CSVField(this->get_field(n)); + } + + /** Retrieve a value by its associated column name. If the column + * specified can't be round, a runtime error is thrown. + * + * @complexity + * Constant. This calls the other CSVRow::operator[]() after + * converting column names into indices using a hash table. + * + * @param[in] col_name The column to look for + */ + CSV_INLINE CSVField CSVRow::operator[](const std::string& col_name) const { + auto & col_names = this->data->col_names; + auto col_pos = col_names->index_of(col_name); + if (col_pos > -1) { + return this->operator[](col_pos); + } + + throw std::runtime_error("Can't find a column named " + col_name); + } + + CSV_INLINE CSVRow::operator std::vector() const { + std::vector ret; + for (size_t i = 0; i < size(); i++) + ret.push_back(std::string(this->get_field(i))); + + return ret; + } + + CSV_INLINE csv::string_view CSVRow::get_field(size_t index) const + { + using internals::ParseFlags; + + if (index >= this->size()) + throw std::runtime_error("Index out of bounds."); + + const size_t field_index = this->fields_start + index; + auto& field = this->data->fields[field_index]; + auto field_str = csv::string_view(this->data->data).substr(this->data_start + field.start); + + if (field.has_double_quote) { + auto& value = this->data->double_quote_fields[field_index]; + if (value.empty()) { + bool prev_ch_quote = false; + for (size_t i = 0; i < field.length; i++) { + if (this->data->parse_flags[field_str[i] + 128] == ParseFlags::QUOTE) { + if (prev_ch_quote) { + prev_ch_quote = false; + continue; + } + else { + prev_ch_quote = true; + } + } + + value += field_str[i]; + } + } + + return csv::string_view(value); + } + + return field_str.substr(0, field.length); + } + +#ifdef _MSC_VER +#pragma region CSVRow Iterator +#endif + /** Return an iterator pointing to the first field. */ + CSV_INLINE CSVRow::iterator CSVRow::begin() const { + return CSVRow::iterator(this, 0); + } + + /** Return an iterator pointing to just after the end of the CSVRow. + * + * @warning Attempting to dereference the end iterator results + * in dereferencing a null pointer. + */ + CSV_INLINE CSVRow::iterator CSVRow::end() const noexcept { + return CSVRow::iterator(this, (int)this->size()); + } + + CSV_INLINE CSVRow::reverse_iterator CSVRow::rbegin() const noexcept { + return std::reverse_iterator(this->end()); + } + + CSV_INLINE CSVRow::reverse_iterator CSVRow::rend() const { + return std::reverse_iterator(this->begin()); + } + + CSV_INLINE HEDLEY_NON_NULL(2) + CSVRow::iterator::iterator(const CSVRow* _reader, int _i) + : daddy(_reader), i(_i) { + if (_i < (int)this->daddy->size()) + this->field = std::make_shared( + this->daddy->operator[](_i)); + else + this->field = nullptr; + } + + CSV_INLINE CSVRow::iterator::reference CSVRow::iterator::operator*() const { + return *(this->field.get()); + } + + CSV_INLINE CSVRow::iterator::pointer CSVRow::iterator::operator->() const { + // Using CSVField * as pointer type causes segfaults in MSVC debug builds + #ifdef _MSC_BUILD + return this->field; + #else + return this->field.get(); + #endif + } + + CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator++() { + // Pre-increment operator + this->i++; + if (this->i < (int)this->daddy->size()) + this->field = std::make_shared( + this->daddy->operator[](i)); + else // Reached the end of row + this->field = nullptr; + return *this; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator++(int) { + // Post-increment operator + auto temp = *this; + this->operator++(); + return temp; + } + + CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator--() { + // Pre-decrement operator + this->i--; + this->field = std::make_shared( + this->daddy->operator[](this->i)); + return *this; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator--(int) { + // Post-decrement operator + auto temp = *this; + this->operator--(); + return temp; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const { + // Allows for iterator arithmetic + return CSVRow::iterator(this->daddy, i + (int)n); + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const { + // Allows for iterator arithmetic + return CSVRow::iterator::operator+(-n); + } +#ifdef _MSC_VER +#pragma endregion CSVRow Iterator +#endif +} + +/** @file + * Implements JSON serialization abilities + */ + + +namespace csv { + /* + The implementations for json_extra_space() and json_escape_string() + were modified from source code for JSON for Modern C++. + + The respective license is below: + + The code is licensed under the [MIT + License](http://opensource.org/licenses/MIT): + + Copyright © 2013-2015 Niels Lohmann. + + 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. + */ + + namespace internals { + /*! + @brief calculates the extra space to escape a JSON string + + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. + */ + static std::size_t json_extra_space(csv::string_view& s) noexcept + { + std::size_t result = 0; + + + for (const auto& c : s) + { + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + { + // from c (1 byte) to \x (2 bytes) + result += 1; + break; + } + + + default: + { + if (c >= 0x00 && c <= 0x1f) + { + // from c (1 byte) to \uxxxx (6 bytes) + result += 5; + } + break; + } + } + } + + + return result; + } + + CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept + { + const auto space = json_extra_space(s); + if (space == 0) + { + return std::string(s); + } + + // create a result string of necessary size + std::string result(s.size() + space, '\\'); + std::size_t pos = 0; + + for (const auto& c : s) + { + switch (c) + { + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } + + + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } + + + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } + + + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } + + + // newline (0x0a) + case '\n': + { + result[pos + 1] = 'n'; + pos += 2; + break; + } + + + // carriage return (0x0d) + case '\r': + { + result[pos + 1] = 'r'; + pos += 2; + break; + } + + + // horizontal tab (0x09) + case '\t': + { + result[pos + 1] = 't'; + pos += 2; + break; + } + + + default: + { + if (c >= 0x00 && c <= 0x1f) + { + // print character c as \uxxxx + sprintf(&result[pos + 1], "u%04x", int(c)); + pos += 6; + // overwrite trailing null character + result[pos] = '\\'; + } + else + { + // all other characters are added as-is + result[pos++] = c; + } + break; + } + } + } + + return result; + } + } + + /** Convert a CSV row to a JSON object, i.e. + * `{"col1":"value1","col2":"value2"}` + * + * @note All strings are properly escaped. Numeric values are not quoted. + * @param[in] subset A subset of columns to contain in the JSON. + * Leave empty for original columns. + */ + CSV_INLINE std::string CSVRow::to_json(const std::vector& subset) const { + std::vector col_names = subset; + if (subset.empty()) { + col_names = this->data ? this->get_col_names() : std::vector({}); + } + + const size_t _n_cols = col_names.size(); + std::string ret = "{"; + + for (size_t i = 0; i < _n_cols; i++) { + auto& col = col_names[i]; + auto field = this->operator[](col); + + // TODO: Possible performance enhancements by caching escaped column names + ret += '"' + internals::json_escape_string(col) + "\":"; + + // Add quotes around strings but not numbers + if (field.is_num()) + ret += internals::json_escape_string(field.get()); + else + ret += '"' + internals::json_escape_string(field.get()) + '"'; + + // Do not add comma after last string + if (i + 1 < _n_cols) + ret += ','; + } + + ret += '}'; + return ret; + } + + /** Convert a CSV row to a JSON array, i.e. + * `["value1","value2",...]` + * + * @note All strings are properly escaped. Numeric values are not quoted. + * @param[in] subset A subset of columns to contain in the JSON. + * Leave empty for all columns. + */ + CSV_INLINE std::string CSVRow::to_json_array(const std::vector& subset) const { + std::vector col_names = subset; + if (subset.empty()) + col_names = this->data ? this->get_col_names() : std::vector({}); + + const size_t _n_cols = col_names.size(); + std::string ret = "["; + + for (size_t i = 0; i < _n_cols; i++) { + auto field = this->operator[](col_names[i]); + + // Add quotes around strings but not numbers + if (field.is_num()) + ret += internals::json_escape_string(field.get()); + else + ret += '"' + internals::json_escape_string(field.get()) + '"'; + + // Do not add comma after last string + if (i + 1 < _n_cols) + ret += ','; + } + + ret += ']'; + return ret; + } +} + +namespace csv { + namespace internals { + CSV_INLINE size_t get_file_size(csv::string_view filename) { + std::ifstream infile(std::string(filename), std::ios::binary); + const auto start = infile.tellg(); + infile.seekg(0, std::ios::end); + const auto end = infile.tellg(); + + return end - start; + } + + CSV_INLINE std::string get_csv_head(csv::string_view filename) { + return get_csv_head(filename, get_file_size(filename)); + } + + CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size) { + const size_t bytes = 500000; + + std::error_code error; + size_t length = std::min((size_t)file_size, bytes); + auto mmap = mio::make_mmap_source(std::string(filename), 0, length, error); + + if (error) { + throw std::runtime_error("Cannot open file " + std::string(filename)); + } + + return std::string(mmap.begin(), mmap.end()); + } + +#ifdef _MSC_VER +#pragma region IBasicCVParser +#endif + CSV_INLINE IBasicCSVParser::IBasicCSVParser( + const CSVFormat& format, + const ColNamesPtr& col_names + ) : _col_names(col_names) { + if (format.no_quote) { + _parse_flags = internals::make_parse_flags(format.get_delim()); + } + else { + _parse_flags = internals::make_parse_flags(format.get_delim(), format.quote_char); + } + + _ws_flags = internals::make_ws_flags( + format.trim_chars.data(), format.trim_chars.size() + ); + } + + CSV_INLINE void IBasicCSVParser::end_feed() { + using internals::ParseFlags; + + bool empty_last_field = this->data_ptr + && this->data_ptr->_data + && !this->data_ptr->data.empty() + && parse_flag(this->data_ptr->data.back()) == ParseFlags::DELIMITER; + + // Push field + if (this->field_length > 0 || empty_last_field) { + this->push_field(); + } + + // Push row + if (this->current_row.size() > 0) + this->push_row(); + } + + CSV_INLINE void IBasicCSVParser::parse_field() noexcept { + using internals::ParseFlags; + auto& in = this->data_ptr->data; + + // Trim off leading whitespace + while (data_pos < in.size() && ws_flag(in[data_pos])) + data_pos++; + + if (field_start == UNINITIALIZED_FIELD) + field_start = (int)(data_pos - current_row_start()); + + // Optimization: Since NOT_SPECIAL characters tend to occur in contiguous + // sequences, use the loop below to avoid having to go through the outer + // switch statement as much as possible + while (data_pos < in.size() && compound_parse_flag(in[data_pos]) == ParseFlags::NOT_SPECIAL) + data_pos++; + + field_length = data_pos - (field_start + current_row_start()); + + // Trim off trailing whitespace, this->field_length constraint matters + // when field is entirely whitespace + for (size_t j = data_pos - 1; ws_flag(in[j]) && this->field_length > 0; j--) + this->field_length--; + } + + CSV_INLINE void IBasicCSVParser::push_field() + { + // Update + if (field_has_double_quote) { + fields->emplace_back( + field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, + field_length, + true + ); + field_has_double_quote = false; + + } + else { + fields->emplace_back( + field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, + field_length + ); + } + + current_row.row_length++; + + // Reset field state + field_start = UNINITIALIZED_FIELD; + field_length = 0; + } + + /** @return The number of characters parsed that belong to complete rows */ + CSV_INLINE size_t IBasicCSVParser::parse() + { + using internals::ParseFlags; + + this->quote_escape = false; + this->data_pos = 0; + this->current_row_start() = 0; + this->trim_utf8_bom(); + + auto& in = this->data_ptr->data; + while (this->data_pos < in.size()) { + switch (compound_parse_flag(in[this->data_pos])) { + case ParseFlags::DELIMITER: + this->push_field(); + this->data_pos++; + break; + + case ParseFlags::NEWLINE: + this->data_pos++; + + // Catches CRLF (or LFLF) + if (this->data_pos < in.size() && parse_flag(in[this->data_pos]) == ParseFlags::NEWLINE) + this->data_pos++; + + // End of record -> Write record + this->push_field(); + this->push_row(); + + // Reset + this->current_row = CSVRow(data_ptr, this->data_pos, fields->size()); + break; + + case ParseFlags::NOT_SPECIAL: + this->parse_field(); + break; + + case ParseFlags::QUOTE_ESCAPE_QUOTE: + if (data_pos + 1 == in.size()) return this->current_row_start(); + else if (data_pos + 1 < in.size()) { + auto next_ch = parse_flag(in[data_pos + 1]); + if (next_ch >= ParseFlags::DELIMITER) { + quote_escape = false; + data_pos++; + break; + } + else if (next_ch == ParseFlags::QUOTE) { + // Case: Escaped quote + data_pos += 2; + this->field_length += 2; + this->field_has_double_quote = true; + break; + } + } + + // Case: Unescaped single quote => not strictly valid but we'll keep it + this->field_length++; + data_pos++; + + break; + + default: // Quote (currently not quote escaped) + if (this->field_length == 0) { + quote_escape = true; + data_pos++; + break; + } + + // Case: Unescaped quote + this->field_length++; + data_pos++; + + break; + } + } + + return this->current_row_start(); + } + + CSV_INLINE void IBasicCSVParser::push_row() { + current_row.row_length = fields->size() - current_row.fields_start; + this->_records->push_back(std::move(current_row)); + } + + CSV_INLINE void IBasicCSVParser::reset_data_ptr() { + this->data_ptr = std::make_shared(); + this->data_ptr->parse_flags = this->_parse_flags; + this->data_ptr->col_names = this->_col_names; + this->fields = &(this->data_ptr->fields); + } + + CSV_INLINE void IBasicCSVParser::trim_utf8_bom() { + auto& data = this->data_ptr->data; + + if (!this->unicode_bom_scan && data.size() >= 3) { + if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') { + this->data_pos += 3; // Remove BOM from input string + this->_utf8_bom = true; + } + + this->unicode_bom_scan = true; + } + } +#ifdef _MSC_VER +#pragma endregion +#endif + +#ifdef _MSC_VER +#pragma region Specializations +#endif + CSV_INLINE void MmapParser::next(size_t bytes = ITERATION_CHUNK_SIZE) { + // Reset parser state + this->field_start = UNINITIALIZED_FIELD; + this->field_length = 0; + this->reset_data_ptr(); + + // Create memory map + size_t length = std::min(this->source_size - this->mmap_pos, bytes); + std::error_code error; + this->data_ptr->_data = std::make_shared>(mio::make_mmap_source(this->_filename, this->mmap_pos, length, error)); + this->mmap_pos += length; + if (error) throw error; + + auto mmap_ptr = (mio::basic_mmap_source*)(this->data_ptr->_data.get()); + + // Create string view + this->data_ptr->data = csv::string_view(mmap_ptr->data(), mmap_ptr->length()); + + // Parse + this->current_row = CSVRow(this->data_ptr); + size_t remainder = this->parse(); + + if (this->mmap_pos == this->source_size || no_chunk()) { + this->_eof = true; + this->end_feed(); + } + + this->mmap_pos -= (length - remainder); + } +#ifdef _MSC_VER +#pragma endregion +#endif + } +} + + +#endif diff --git a/thirdparty/cxxopts/.gitignore b/thirdparty/cxxopts/.gitignore deleted file mode 100644 index e91cd408..00000000 --- a/thirdparty/cxxopts/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.swp -build* -CMakeCache.txt -Makefile -CMakeFiles/ -Testing/ -CTestTestfile.cmake -cmake_install.cmake diff --git a/thirdparty/cxxopts/.travis.yml b/thirdparty/cxxopts/.travis.yml deleted file mode 100644 index 87c78a7e..00000000 --- a/thirdparty/cxxopts/.travis.yml +++ /dev/null @@ -1,70 +0,0 @@ -sudo: required -dist: trusty -language: cpp -os: - - linux -matrix: - include: - - os: linux - env: COMPILER=g++-4.9 - addons: - apt: - packages: - - g++-4.9 - sources: &sources - - llvm-toolchain-trusty-3.8 - - llvm-toolchain-trusty-5.0 - - ubuntu-toolchain-r-test - - os: linux - env: COMPILER=g++-4.9 UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes - addons: - apt: - packages: - - g++-4.9 - sources: *sources - - os: linux - env: COMPILER=g++-5 - addons: - apt: - packages: - - g++-5 - sources: *sources - - os: linux - env: COMPILER=g++-5 UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes - addons: - apt: - packages: - - g++-5 - sources: *sources - - os: linux - env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++ - addons: - apt: - packages: - - clang-3.8 - - libc++-dev - sources: *sources - - os: linux - env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++ UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes - addons: - apt: - packages: - - clang-3.8 - - libc++-dev - sources: *sources - - os: linux - env: COMPILER=clang++-5.0 CMAKE_OPTIONS=-DCXXOPTS_CXX_STANDARD=17 - addons: - apt: - packages: - - clang-5.0 - - g++-5 - sources: *sources -script: > - cmake -DCXXOPTS_BUILD_TESTS=ON -DCMAKE_CXX_COMPILER=$COMPILER - -DCMAKE_CXX_FLAGS=$CXXFLAGS $UNICODE_OPTIONS $CMAKE_OPTIONS . - && make && make ARGS=--output-on-failure test - -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install icu4c; fi diff --git a/thirdparty/cxxopts/CHANGELOG.md b/thirdparty/cxxopts/CHANGELOG.md deleted file mode 100644 index 7bcbf72a..00000000 --- a/thirdparty/cxxopts/CHANGELOG.md +++ /dev/null @@ -1,82 +0,0 @@ -# Changelog - -This is the changelog for `cxxopts`, a C++11 library for parsing command line -options. The project adheres to semantic versioning. - -## 2.2 - -### Changed - -* Allow integers to have leading zeroes. -* Build the tests by default. - -### Added - -* Iterator inputs to `parse_positional`. - -### Bug Fixes - -* Fix a warning about possible loss of data. -* Fix version numbering in CMakeLists.txt -* Remove unused declaration of the undefined `ParseResult::get_option`. -* Throw on invalid option syntax when beginning with a `-`. -* Throw in `as` when option wasn't present. -* Fix catching exceptions by reference. - -## 2.1.1 - -### Bug Fixes - -* Revert the change adding `const` type for `argv`, because most users expect - to pass a non-const `argv` from `main`. - -## 2.1 - -### Changed - -* Options with implicit arguments now require the `--option=value` form if - they are to be specified with an option. This is to remove the ambiguity - when a positional argument could follow an option with an implicit value. - For example, `--foo value`, where `foo` has an implicit value, will be - parsed as `--foo=implicit` and a positional argument `value`. -* Boolean values are no longer special, but are just an option with a default - and implicit value. - -### Added - -* Added support for `std::optional` as a storage type. -* Allow the help string to be customised. -* Use `const` for the type in the `argv` parameter, since the contents of the - arguments is never modified. - -### Bug Fixes - -* Building against GCC 4.9 was broken due to overly strict shadow warnings. -* Fixed an ambiguous overload in the `parse_positional` function when an - `initializer_list` was directly passed. -* Fixed precedence in the Boolean value regex. - -## 2.0 - -### Changed - -* `Options::parse` returns a ParseResult rather than storing the parse - result internally. -* Options with default values now get counted as appearing once if they - were not specified by the user. - -### Added - -* A new `ParseResult` object that is the immutable result of parsing. It - responds to the same `count` and `operator[]` as `Options` of 1.x did. -* The function `ParseResult::arguments` returns a vector of the parsed - arguments to iterate through in the order they were provided. -* The symbol `cxxopts::version` for the version of the library. -* Booleans can be specified with various strings and explicitly set false. - -## 1.x - -The 1.x series was the first major version of the library, with release numbers -starting to follow semantic versioning, after 0.x being unstable. It never had -a changelog maintained for it. Releases mostly contained bug fixes, with the -occasional feature added. diff --git a/thirdparty/cxxopts/CMakeLists.txt b/thirdparty/cxxopts/CMakeLists.txt deleted file mode 100644 index 440490ba..00000000 --- a/thirdparty/cxxopts/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2014 Jarryd Beck -# -# 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.1) - -# parse the current version from the cxxopts header -file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/cxxopts.hpp" cxxopts_version_defines - REGEX "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH)") -foreach(ver ${cxxopts_version_defines}) - if(ver MATCHES "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") - set(CXXOPTS__VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") - endif() -endforeach() -set(VERSION ${CXXOPTS__VERSION_MAJOR}.${CXXOPTS__VERSION_MINOR}.${CXXOPTS__VERSION_PATCH}) -message(STATUS "cxxopts version ${VERSION}") - -project(cxxopts VERSION "${VERSION}") - -enable_testing() - -option(CXXOPTS_BUILD_EXAMPLES "Set to ON to build examples" ON) -option(CXXOPTS_BUILD_TESTS "Set to ON to build tests" ON) - -# request c++11 without gnu extension for the whole project and enable more warnings -if (CXXOPTS_CXX_STANDARD) - set(CMAKE_CXX_STANDARD ${CXXOPTS_CXX_STANDARD}) -else() - set(CMAKE_CXX_STANDARD 11) -endif() - -set(CMAKE_CXX_EXTENSIONS OFF) - -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W2") -elseif(CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wextra -Wshadow") -endif() - -add_library(cxxopts INTERFACE) - -# optionally, enable unicode support using the ICU library -set(CXXOPTS_USE_UNICODE_HELP FALSE CACHE BOOL "Use ICU Unicode library") -if(CXXOPTS_USE_UNICODE_HELP) - find_package(PkgConfig) - pkg_check_modules(ICU REQUIRED icu-uc) - - target_link_libraries(cxxopts INTERFACE ${ICU_LDFLAGS}) - target_compile_options(cxxopts INTERFACE ${ICU_CFLAGS}) - target_compile_definitions(cxxopts INTERFACE CXXOPTS_USE_UNICODE) -endif() - -target_include_directories(cxxopts INTERFACE - $ - $ - ) - -include(CMakePackageConfigHelpers) -set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING - "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") -set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") -set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") -set(targets_export_name cxxopts-targets) - -# Generate the version, config and target files into the build directory. -write_basic_package_version_file( - ${version_config} - VERSION ${VERSION} - COMPATIBILITY AnyNewerVersion) -configure_package_config_file( - ${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in - ${project_config} - INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) -export(TARGETS cxxopts NAMESPACE cxxopts:: - FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) - -# Install version, config and target files. -install( - FILES ${project_config} ${version_config} - DESTINATION ${CXXOPTS_CMAKE_DIR}) -install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} - NAMESPACE cxxopts::) - -# Install the header file and export the target -install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) -install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) - -add_subdirectory(src) -add_subdirectory(test) diff --git a/thirdparty/cxxopts/INSTALL b/thirdparty/cxxopts/INSTALL deleted file mode 100644 index 3e1c6f3d..00000000 --- a/thirdparty/cxxopts/INSTALL +++ /dev/null @@ -1,23 +0,0 @@ -== System installation == - -This library is header only. So you can either copy `include/cxxopts.hpp` to `/usr/include` or `/usr/local/include`, or add `include` to your search path. - -== Building the examples and tests == - -It is preferable to build out of source. Make a build directory somewhere, and then -do the following, where `${CXXOPTS_DIR}` is the path that you checked out `cxxopts` -to: - - cmake ${CXXOPTS_DIR} - make - -You can use another build tool, such as ninja. - - cmake -G Ninja ${CXXOPTS_DIR} - ninja - - -To run the tests, you have to configure `cxxopts` with another flag: - cmake -D CXXOPTS_BUILD_TESTS=On ${CXXOPTS_DIR} - make - make test diff --git a/thirdparty/cxxopts/README.md b/thirdparty/cxxopts/README.md deleted file mode 100644 index e575e61f..00000000 --- a/thirdparty/cxxopts/README.md +++ /dev/null @@ -1,134 +0,0 @@ -[![Build Status](https://travis-ci.org/jarro2783/cxxopts.svg?branch=master)](https://travis-ci.org/jarro2783/cxxopts) - -# Release versions - -Note that `master` is generally a work in progress, and you probably want to use a -tagged release version. - -# Quick start - -This is a lightweight C++ option parser library, supporting the standard GNU -style syntax for options. - -Options can be given as: - - --long - --long=argument - --long argument - -a - -ab - -abc argument - -where c takes an argument, but a and b do not. - -Additionally, anything after `--` will be parsed as a positional argument. - -## Basics - - #include - -Create a cxxopts::Options instance. - - cxxopts::Options options("MyProgram", "One line description of MyProgram"); - -Then use `add_options`. - - options.add_options() - ("d,debug", "Enable debugging") - ("f,file", "File name", cxxopts::value()) - ; - -Options are declared with a long and an optional short option. A description -must be provided. The third argument is the value, if omitted it is boolean. -Any type can be given as long as it can be parsed, with operator>>. - -To parse the command line do: - - auto result = options.parse(argc, argv); - -To retrieve an option use `result.count("option")` to get the number of times -it appeared, and - - result["opt"].as() - -to get its value. If "opt" doesn't exist, or isn't of the right type, then an -exception will be thrown. - -Note that the result of `options.parse` should only be used as long as the -`options` object that created it is in scope. - -## Exceptions - -Exceptional situations throw C++ exceptions. There are two types of -exceptions: errors defining the options, and errors when parsing a list of -arguments. All exceptions derive from `cxxopts::OptionException`. Errors -defining options derive from `cxxopts::OptionSpecException` and errors -parsing arguments derive from `cxxopts::OptionParseException`. - -All exceptions define a `what()` function to get a printable string -explaining the error. - -## Help groups - -Options can be placed into groups for the purposes of displaying help messages. -To place options in a group, pass the group as a string to `add_options`. Then, -when displaying the help, pass the groups that you would like displayed as a -vector to the `help` function. - -## Positional Arguments - -Positional arguments can be optionally parsed into one or more options. -To set up positional arguments, call - - options.parse_positional({"first", "second", "last"}) - -where "last" should be the name of an option with a container type, and the -others should have a single value. - -## Default and implicit values - -An option can be declared with a default or an implicit value, or both. - -A default value is the value that an option takes when it is not specified -on the command line. The following specifies a default value for an option: - - cxxopts::value()->default_value("value") - -An implicit value is the value that an option takes when it is given on the -command line without an argument. The following specifies an implicit value: - - cxxopts::value()->implicit_value("implicit") - -If an option had both, then not specifying it would give the value `"value"`, -writing it on the command line as `--option` would give the value `"implicit"`, -and writing `--option=another` would give it the value `"another"`. - -Note that the default and implicit value is always stored as a string, -regardless of the type that you want to store it in. It will be parsed as -though it was given on the command line. - -## Boolean values - -Boolean options have a default implicit value of `"true"`, which can be -overridden. The effect is that writing `-o` by itself will set option `o` to -`true`. However, they can also be written with various strings using either -`=value` or the next argument. - -## Custom help - -The string after the program name on the first line of the help can be -completely replaced by calling `options.custom_help`. Note that you might -also want to override the positional help by calling `options.positional_help`. - -# Linking - -This is a header only library. - -# Requirements - -The only build requirement is a C++ compiler that supports C++11 regular -expressions. For example GCC >= 4.9 or clang with libc++. - -# TODO list - -* Allow unrecognised options. diff --git a/thirdparty/cxxopts/cxxopts-config.cmake.in b/thirdparty/cxxopts/cxxopts-config.cmake.in deleted file mode 100644 index c9efaf14..00000000 --- a/thirdparty/cxxopts/cxxopts-config.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ -@PACKAGE_INIT@ - -include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) -check_required_components(cxxopts) diff --git a/thirdparty/cxxopts/include/cxxopts.hpp b/thirdparty/cxxopts/include/cxxopts.hpp deleted file mode 100644 index e4ff2377..00000000 --- a/thirdparty/cxxopts/include/cxxopts.hpp +++ /dev/null @@ -1,1430 +0,0 @@ -/* - -Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - -#ifndef CXXOPTS_HPP_INCLUDED -#define CXXOPTS_HPP_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cpp_lib_optional - -#include - -#define CXXOPTS_HAS_OPTIONAL -#endif - -#define CXXOPTS__VERSION_MAJOR 2 -#define CXXOPTS__VERSION_MINOR 2 -#define CXXOPTS__VERSION_PATCH 0 - -namespace cxxopts { - static constexpr struct { - uint8_t major, minor, patch; - } version = {CXXOPTS__VERSION_MAJOR, CXXOPTS__VERSION_MINOR, - CXXOPTS__VERSION_PATCH}; -} // namespace cxxopts - -// when we ask cxxopts to use Unicode, help strings are processed using ICU, -// which results in the correct lengths being computed for strings when they -// are formatted for the help output -// it is necessary to make sure that can be found by the -// compiler, and that icu-uc is linked in to the binary. - -#ifdef CXXOPTS_USE_UNICODE -#include - -namespace cxxopts { -typedef icu::UnicodeString String; - -inline String toLocalString(std::string s) { - return icu::UnicodeString::fromUTF8(std::move(s)); -} - -class UnicodeStringIterator - : public std::iterator { -public: - UnicodeStringIterator(const icu::UnicodeString *string, int32_t pos) - : s(string), i(pos) {} - - value_type operator*() const { return s->char32At(i); } - - bool operator==(const UnicodeStringIterator &rhs) const { - return s == rhs.s && i == rhs.i; - } - - bool operator!=(const UnicodeStringIterator &rhs) const { - return !(*this == rhs); - } - - UnicodeStringIterator &operator++() { - ++i; - return *this; - } - - UnicodeStringIterator operator+(int32_t v) { - return UnicodeStringIterator(s, i + v); - } - -private: - const icu::UnicodeString *s; - int32_t i; -}; - -inline String &stringAppend(String &s, String a) { - return s.append(std::move(a)); -} - -inline String &stringAppend(String &s, int n, UChar32 c) { - for (int i = 0; i != n; ++i) { - s.append(c); - } - - return s; -} - -template -String &stringAppend(String &s, Iterator begin, Iterator end) { - while (begin != end) { - s.append(*begin); - ++begin; - } - - return s; -} - -inline size_t stringLength(const String &s) { return s.length(); } - -inline std::string toUTF8String(const String &s) { - std::string result; - s.toUTF8String(result); - - return result; -} - -inline bool empty(const String &s) { return s.isEmpty(); } -} // namespace cxxopts - -namespace std { -inline cxxopts::UnicodeStringIterator begin(const icu::UnicodeString &s) { - return cxxopts::UnicodeStringIterator(&s, 0); -} - -inline cxxopts::UnicodeStringIterator end(const icu::UnicodeString &s) { - return cxxopts::UnicodeStringIterator(&s, s.length()); -} -} // namespace std - -// ifdef CXXOPTS_USE_UNICODE -#else - -namespace cxxopts { - typedef std::string String; - - template - T toLocalString(T &&t) { return std::forward(t); } - - inline size_t stringLength(const String &s) { return s.length(); } - - inline String &stringAppend(String &s, String a) { - return s.append(std::move(a)); - } - - inline String &stringAppend(String &s, size_t n, char c) { - return s.append(n, c); - } - - template - String &stringAppend(String &s, Iterator begin, Iterator end) { - return s.append(begin, end); - } - - template - std::string toUTF8String(T &&t) { - return std::forward(t); - } - - inline bool empty(const std::string &s) { return s.empty(); } -} // namespace cxxopts - -// ifdef CXXOPTS_USE_UNICODE -#endif - -namespace cxxopts { - namespace { -#ifdef _WIN32 - const std::string LQUOTE("\'"); -const std::string RQUOTE("\'"); -#else - const std::string LQUOTE("‘"); - const std::string RQUOTE("’"); -#endif - } // namespace - - class Value : public std::enable_shared_from_this { - public: - virtual ~Value() = default; - - virtual std::shared_ptr clone() const = 0; - - virtual void parse(const std::string &text) const = 0; - - virtual void parse() const = 0; - - virtual bool has_default() const = 0; - - virtual bool is_container() const = 0; - - virtual bool has_implicit() const = 0; - - virtual std::string get_default_value() const = 0; - - virtual std::string get_implicit_value() const = 0; - - virtual std::shared_ptr default_value(const std::string &value) = 0; - - virtual std::shared_ptr implicit_value(const std::string &value) = 0; - - virtual bool is_boolean() const = 0; - }; - - class OptionException : public std::exception { - public: - OptionException(const std::string &message) : m_message(message) {} - - virtual const char *what() const noexcept { return m_message.c_str(); } - - private: - std::string m_message; - }; - - class OptionSpecException : public OptionException { - public: - OptionSpecException(const std::string &message) : OptionException(message) {} - }; - - class OptionParseException : public OptionException { - public: - OptionParseException(const std::string &message) : OptionException(message) {} - }; - - class option_exists_error : public OptionSpecException { - public: - option_exists_error(const std::string &option) - : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + - u8" already exists") {} - }; - - class invalid_option_format_error : public OptionSpecException { - public: - invalid_option_format_error(const std::string &format) - : OptionSpecException(u8"Invalid option format " + LQUOTE + format + - RQUOTE) {} - }; - - class option_syntax_exception : public OptionParseException { - public: - option_syntax_exception(const std::string &text) - : OptionParseException(u8"Argument " + LQUOTE + text + RQUOTE + - u8" starts with a - but has incorrect syntax") {} - }; - - class option_not_exists_exception : public OptionParseException { - public: - option_not_exists_exception(const std::string &option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" does not exist") {} - }; - - class missing_argument_exception : public OptionParseException { - public: - missing_argument_exception(const std::string &option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" is missing an argument") {} - }; - - class option_requires_argument_exception : public OptionParseException { - public: - option_requires_argument_exception(const std::string &option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" requires an argument") {} - }; - - class option_not_has_argument_exception : public OptionParseException { - public: - option_not_has_argument_exception(const std::string &option, - const std::string &arg) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" does not take an argument, but argument " + - LQUOTE + arg + RQUOTE + " given") {} - }; - - class option_not_present_exception : public OptionParseException { - public: - option_not_present_exception(const std::string &option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" not present") {} - }; - - class argument_incorrect_type : public OptionParseException { - public: - argument_incorrect_type(const std::string &arg) - : OptionParseException(u8"Argument " + LQUOTE + arg + RQUOTE + - u8" failed to parse") {} - }; - - class option_required_exception : public OptionParseException { - public: - option_required_exception(const std::string &option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + - u8" is required but not present") {} - }; - - namespace values { - namespace { - std::basic_regex integer_pattern("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); - std::basic_regex truthy_pattern("(t|T)(rue)?"); - std::basic_regex falsy_pattern("((f|F)(alse)?)?"); - } // namespace - - namespace detail { - template - struct SignedCheck; - - template - struct SignedCheck { - template - void operator()(bool negative, U u, const std::string &text) { - if (negative) { - if (u > static_cast(-std::numeric_limits::min())) { - throw argument_incorrect_type(text); - } - } else { - if (u > static_cast(std::numeric_limits::max())) { - throw argument_incorrect_type(text); - } - } - } - }; - - template - struct SignedCheck { - template - void operator()(bool, U, const std::string &) {} - }; - - template - void check_signed_range(bool negative, U value, const std::string &text) { - SignedCheck::is_signed>()(negative, value, text); - } - } // namespace detail - - template - R checked_negate(T &&t, const std::string &, std::true_type) { - // if we got to here, then `t` is a positive number that fits into - // `R`. So to avoid MSVC C4146, we first cast it to `R`. - // See https://github.com/jarro2783/cxxopts/issues/62 for more details. - return -static_cast(t); - } - - template - T checked_negate(T &&, const std::string &text, std::false_type) { - throw argument_incorrect_type(text); - } - - template - void integer_parser(const std::string &text, T &value) { - std::smatch match; - std::regex_match(text, match, integer_pattern); - - if (match.length() == 0) { - throw argument_incorrect_type(text); - } - - if (match.length(4) > 0) { - value = 0; - return; - } - - using US = typename std::make_unsigned::type; - - constexpr auto umax = std::numeric_limits::max(); - constexpr bool is_signed = std::numeric_limits::is_signed; - const bool negative = match.length(1) > 0; - const uint8_t base = match.length(2) > 0 ? 16 : 10; - - auto value_match = match[3]; - - US result = 0; - - for (auto iter = value_match.first; iter != value_match.second; ++iter) { - US digit = 0; - - if (*iter >= '0' && *iter <= '9') { - digit = *iter - '0'; - } else if (base == 16 && *iter >= 'a' && *iter <= 'f') { - digit = *iter - 'a' + 10; - } else if (base == 16 && *iter >= 'A' && *iter <= 'F') { - digit = *iter - 'A' + 10; - } else { - throw argument_incorrect_type(text); - } - - if (umax - digit < result * base) { - throw argument_incorrect_type(text); - } - - result = result * base + digit; - } - - detail::check_signed_range(negative, result, text); - - if (negative) { - value = checked_negate(result, text, - std::integral_constant()); - } else { - value = result; - } - } - - template - void stringstream_parser(const std::string &text, T &value) { - std::stringstream in(text); - in >> value; - if (!in) { - throw argument_incorrect_type(text); - } - } - - inline void parse_value(const std::string &text, uint8_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, int8_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, uint16_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, int16_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, uint32_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, int32_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, uint64_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, int64_t &value) { - integer_parser(text, value); - } - - inline void parse_value(const std::string &text, bool &value) { - std::smatch result; - std::regex_match(text, result, truthy_pattern); - - if (!result.empty()) { - value = true; - return; - } - - std::regex_match(text, result, falsy_pattern); - if (!result.empty()) { - value = false; - return; - } - - throw argument_incorrect_type(text); - } - - inline void parse_value(const std::string &text, std::string &value) { - value = text; - } - -// The fallback parser. It uses the stringstream parser to parse all types -// that have not been overloaded explicitly. It has to be placed in the -// source code before all other more specialized templates. - template - void parse_value(const std::string &text, T &value) { - stringstream_parser(text, value); - } - - template - void parse_value(const std::string &text, std::vector &value) { - T v; - parse_value(text, v); - value.push_back(v); - } - -#ifdef CXXOPTS_HAS_OPTIONAL - - template - void parse_value(const std::string &text, std::optional &value) { - T result; - parse_value(text, result); - value = std::move(result); - } - -#endif - - template - struct type_is_container { - static constexpr bool value = false; - }; - - template - struct type_is_container> { - static constexpr bool value = true; - }; - - template - class abstract_value : public Value { - using Self = abstract_value; - - public: - abstract_value() : m_result(std::make_shared()), m_store(m_result.get()) {} - - abstract_value(T *t) : m_store(t) {} - - virtual ~abstract_value() = default; - - abstract_value(const abstract_value &rhs) { - if (rhs.m_result) { - m_result = std::make_shared(); - m_store = m_result.get(); - } else { - m_store = rhs.m_store; - } - - m_default = rhs.m_default; - m_implicit = rhs.m_implicit; - m_default_value = rhs.m_default_value; - m_implicit_value = rhs.m_implicit_value; - } - - void parse(const std::string &text) const { parse_value(text, *m_store); } - - bool is_container() const { return type_is_container::value; } - - void parse() const { parse_value(m_default_value, *m_store); } - - bool has_default() const { return m_default; } - - bool has_implicit() const { return m_implicit; } - - std::shared_ptr default_value(const std::string &value) { - m_default = true; - m_default_value = value; - return shared_from_this(); - } - - std::shared_ptr implicit_value(const std::string &value) { - m_implicit = true; - m_implicit_value = value; - return shared_from_this(); - } - - std::string get_default_value() const { return m_default_value; } - - std::string get_implicit_value() const { return m_implicit_value; } - - bool is_boolean() const { return std::is_same::value; } - - const T &get() const { - if (m_store == nullptr) { - return *m_result; - } else { - return *m_store; - } - } - - protected: - std::shared_ptr m_result; - T *m_store; - - bool m_default = false; - bool m_implicit = false; - - std::string m_default_value; - std::string m_implicit_value; - }; - - template - class standard_value : public abstract_value { - public: - using abstract_value::abstract_value; - - std::shared_ptr clone() const { - return std::make_shared>(*this); - } - }; - - template<> - class standard_value : public abstract_value { - public: - ~standard_value() = default; - - standard_value() { set_default_and_implicit(); } - - standard_value(bool *b) : abstract_value(b) { set_default_and_implicit(); } - - std::shared_ptr clone() const { - return std::make_shared>(*this); - } - - private: - void set_default_and_implicit() { - m_default = true; - m_default_value = "false"; - m_implicit = true; - m_implicit_value = "true"; - } - }; - } // namespace values - - template - std::shared_ptr value() { - return std::make_shared>(); - } - - template - std::shared_ptr value(T &t) { - return std::make_shared>(&t); - } - - class OptionAdder; - - class OptionDetails { - public: - OptionDetails(const std::string &short_, const std::string &long_, - const String &desc, std::shared_ptr val) - : m_short(short_), m_long(long_), m_desc(desc), m_value(val), m_count(0) { - } - - OptionDetails(const OptionDetails &rhs) - : m_desc(rhs.m_desc), m_count(rhs.m_count) { - m_value = rhs.m_value->clone(); - } - - OptionDetails(OptionDetails &&rhs) = default; - - const String &description() const { return m_desc; } - - const Value &value() const { return *m_value; } - - std::shared_ptr make_storage() const { return m_value->clone(); } - - const std::string &short_name() const { return m_short; } - - const std::string &long_name() const { return m_long; } - - private: - std::string m_short; - std::string m_long; - String m_desc; - std::shared_ptr m_value; - int m_count; - }; - - struct HelpOptionDetails { - std::string s; - std::string l; - String desc; - bool has_default; - std::string default_value; - bool has_implicit; - std::string implicit_value; - std::string arg_help; - bool is_container; - bool is_boolean; - }; - - struct HelpGroupDetails { - std::string name; - std::string description; - std::vector options; - }; - - class OptionValue { - public: - void parse(std::shared_ptr details, - const std::string &text) { - ensure_value(details); - ++m_count; - m_value->parse(text); - } - - void parse_default(std::shared_ptr details) { - ensure_value(details); - m_value->parse(); - } - - size_t count() const { return m_count; } - - template - const T &as() const { - if (m_value == nullptr) { - throw std::domain_error("No value"); - } - -#ifdef CXXOPTS_NO_RTTI - return static_cast &>(*m_value).get(); -#else - return dynamic_cast &>(*m_value).get(); -#endif - } - - private: - void ensure_value(std::shared_ptr details) { - if (m_value == nullptr) { - m_value = details->make_storage(); - } - } - - std::shared_ptr m_value; - size_t m_count = 0; - }; - - class KeyValue { - public: - KeyValue(std::string key_, std::string value_) - : m_key(std::move(key_)), m_value(std::move(value_)) {} - - const std::string &key() const { return m_key; } - - const std::string value() const { return m_value; } - - template - T as() const { - T result; - values::parse_value(m_value, result); - return result; - } - - private: - std::string m_key; - std::string m_value; - }; - - class ParseResult { - public: - ParseResult( - const std::shared_ptr< - std::unordered_map>>, - std::vector, bool allow_unrecognised, int &, char **&); - - size_t count(const std::string &o) const { - auto iter = m_options->find(o); - if (iter == m_options->end()) { - return 0; - } - - auto riter = m_results.find(iter->second); - - return riter->second.count(); - } - - const OptionValue &operator[](const std::string &option) const { - auto iter = m_options->find(option); - - if (iter == m_options->end()) { - throw option_not_present_exception(option); - } - - auto riter = m_results.find(iter->second); - - return riter->second; - } - - const std::vector &arguments() const { return m_sequential; } - - private: - void parse(int &argc, char **&argv); - - void add_to_option(const std::string &option, const std::string &arg); - - bool consume_positional(std::string a); - - void parse_option(std::shared_ptr value, - const std::string &name, const std::string &arg = ""); - - void parse_default(std::shared_ptr details); - - void checked_parse_arg(int argc, char *argv[], int ¤t, - std::shared_ptr value, - const std::string &name); - - const std::shared_ptr< - std::unordered_map>> - m_options; - std::vector m_positional; - std::vector::iterator m_next_positional; - std::unordered_set m_positional_set; - std::unordered_map, OptionValue> m_results; - - bool m_allow_unrecognised; - - std::vector m_sequential; - }; - - class Options { - typedef std::unordered_map> - OptionMap; - - public: - Options(std::string program, std::string help_string = "") - : m_program(std::move(program)), - m_help_string(toLocalString(std::move(help_string))), - m_custom_help("[OPTION...]"), - m_positional_help("positional parameters"), m_show_positional(false), - m_allow_unrecognised(false), m_options(std::make_shared()), - m_next_positional(m_positional.end()) {} - - Options &positional_help(std::string help_text) { - m_positional_help = std::move(help_text); - return *this; - } - - Options &custom_help(std::string help_text) { - m_custom_help = std::move(help_text); - return *this; - } - - Options &show_positional_help() { - m_show_positional = true; - return *this; - } - - Options &allow_unrecognised_options() { - m_allow_unrecognised = true; - return *this; - } - - ParseResult parse(int &argc, char **&argv); - - OptionAdder add_options(std::string group = ""); - - void add_option(const std::string &group, const std::string &s, - const std::string &l, std::string desc, - std::shared_ptr value, std::string arg_help); - - // parse positional arguments into the given option - void parse_positional(std::string option); - - void parse_positional(std::vector options); - - void parse_positional(std::initializer_list options); - - template - void parse_positional(Iterator begin, Iterator end) { - parse_positional(std::vector{begin, end}); - } - - std::string help(const std::vector &groups = {""}) const; - - const std::vector groups() const; - - const HelpGroupDetails &group_help(const std::string &group) const; - - private: - void add_one_option(const std::string &option, - std::shared_ptr details); - - String help_one_group(const std::string &group) const; - - void generate_group_help(String &result, - const std::vector &groups) const; - - void generate_all_groups_help(String &result) const; - - std::string m_program; - String m_help_string; - std::string m_custom_help; - std::string m_positional_help; - bool m_show_positional; - bool m_allow_unrecognised; - - std::shared_ptr m_options; - std::vector m_positional; - std::vector::iterator m_next_positional; - std::unordered_set m_positional_set; - - // mapping from groups to help options - std::map m_help; - }; - - class OptionAdder { - public: - OptionAdder(Options &options, std::string group) - : m_options(options), m_group(std::move(group)) {} - - OptionAdder & - operator()(const std::string &opts, const std::string &desc, - std::shared_ptr value = ::cxxopts::value(), - std::string arg_help = ""); - - private: - Options &m_options; - std::string m_group; - }; - - namespace { - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; - - std::basic_regex - option_matcher("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - - std::basic_regex - option_specifier("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); - - String format_option(const HelpOptionDetails &o) { - auto &s = o.s; - auto &l = o.l; - - String result = " "; - - if (s.size() > 0) { - result += "-" + toLocalString(s) + ","; - } else { - result += " "; - } - - if (l.size() > 0) { - result += " --" + toLocalString(l); - } - - auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; - - if (!o.is_boolean) { - if (o.has_implicit) { - result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; - } else { - result += " " + arg; - } - } - - return result; - } - - String format_description(const HelpOptionDetails &o, size_t start, - size_t width) { - auto desc = o.desc; - - if (o.has_default && (!o.is_boolean || o.default_value != "false")) { - desc += toLocalString(" (default: " + o.default_value + ")"); - } - - String result; - - auto current = std::begin(desc); - auto startLine = current; - auto lastSpace = current; - - auto size = size_t{}; - - while (current != std::end(desc)) { - if (*current == ' ') { - lastSpace = current; - } - - if (*current == '\n') { - startLine = current + 1; - lastSpace = startLine; - } else if (size > width) { - if (lastSpace == startLine) { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; - } else { - stringAppend(result, startLine, lastSpace); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - } - size = 0; - } else { - ++size; - } - - ++current; - } - - // append whatever is left - stringAppend(result, startLine, current); - - return result; - } - } // namespace - - inline ParseResult::ParseResult( - const std::shared_ptr< - std::unordered_map>> - options, - std::vector positional, bool allow_unrecognised, int &argc, - char **&argv) - : m_options(options), m_positional(std::move(positional)), - m_next_positional(m_positional.begin()), - m_allow_unrecognised(allow_unrecognised) { - parse(argc, argv); - } - - inline OptionAdder Options::add_options(std::string group) { - return OptionAdder(*this, std::move(group)); - } - - inline OptionAdder &OptionAdder::operator()(const std::string &opts, - const std::string &desc, - std::shared_ptr value, - std::string arg_help) { - std::match_results result; - std::regex_match(opts.c_str(), result, option_specifier); - - if (result.empty()) { - throw invalid_option_format_error(opts); - } - - const auto &short_match = result[2]; - const auto &long_match = result[3]; - - if (!short_match.length() && !long_match.length()) { - throw invalid_option_format_error(opts); - } else if (long_match.length() == 1 && short_match.length()) { - throw invalid_option_format_error(opts); - } - - auto option_names = [](const std::sub_match &short_, - const std::sub_match &long_) { - if (long_.length() == 1) { - return std::make_tuple(long_.str(), short_.str()); - } else { - return std::make_tuple(short_.str(), long_.str()); - } - }(short_match, long_match); - - m_options.add_option(m_group, std::get<0>(option_names), - std::get<1>(option_names), desc, value, - std::move(arg_help)); - - return *this; - } - - inline void ParseResult::parse_default(std::shared_ptr details) { - m_results[details].parse_default(details); - } - - inline void ParseResult::parse_option(std::shared_ptr value, - const std::string & /*name*/, - const std::string &arg) { - auto &result = m_results[value]; - result.parse(value, arg); - - m_sequential.emplace_back(value->long_name(), arg); - } - - inline void ParseResult::checked_parse_arg(int argc, char *argv[], int ¤t, - std::shared_ptr value, - const std::string &name) { - if (current + 1 >= argc) { - if (value->value().has_implicit()) { - parse_option(value, name, value->value().get_implicit_value()); - } else { - throw missing_argument_exception(name); - } - } else { - if (value->value().has_implicit()) { - parse_option(value, name, value->value().get_implicit_value()); - } else { - parse_option(value, name, argv[current + 1]); - ++current; - } - } - } - - inline void ParseResult::add_to_option(const std::string &option, - const std::string &arg) { - auto iter = m_options->find(option); - - if (iter == m_options->end()) { - throw option_not_exists_exception(option); - } - - parse_option(iter->second, option, arg); - } - - inline bool ParseResult::consume_positional(std::string a) { - while (m_next_positional != m_positional.end()) { - auto iter = m_options->find(*m_next_positional); - if (iter != m_options->end()) { - auto &result = m_results[iter->second]; - if (!iter->second->value().is_container()) { - if (result.count() == 0) { - add_to_option(*m_next_positional, a); - ++m_next_positional; - return true; - } else { - ++m_next_positional; - continue; - } - } else { - add_to_option(*m_next_positional, a); - return true; - } - } - ++m_next_positional; - } - - return false; - } - - inline void Options::parse_positional(std::string option) { - parse_positional(std::vector{std::move(option)}); - } - - inline void Options::parse_positional(std::vector options) { - m_positional = std::move(options); - m_next_positional = m_positional.begin(); - - m_positional_set.insert(m_positional.begin(), m_positional.end()); - } - - inline void - Options::parse_positional(std::initializer_list options) { - parse_positional(std::vector(std::move(options))); - } - - inline ParseResult Options::parse(int &argc, char **&argv) { - ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); - return result; - } - - inline void ParseResult::parse(int &argc, char **&argv) { - int current = 1; - - int nextKeep = 1; - - bool consume_remaining = false; - - while (current != argc) { - if (strcmp(argv[current], "--") == 0) { - consume_remaining = true; - ++current; - break; - } - - std::match_results result; - std::regex_match(argv[current], result, option_matcher); - - if (result.empty()) { - // not a flag - - // but if it starts with a `-`, then it's an error - if (argv[current][0] == '-') { - throw option_syntax_exception(argv[current]); - } - - // if true is returned here then it was consumed, otherwise it is - // ignored - if (consume_positional(argv[current])) { - } else { - argv[nextKeep] = argv[current]; - ++nextKeep; - } - // if we return from here then it was parsed successfully, so continue - } else { - // short or long option? - if (result[4].length() != 0) { - const std::string &s = result[4]; - - for (std::size_t i = 0; i != s.size(); ++i) { - std::string name(1, s[i]); - auto iter = m_options->find(name); - - if (iter == m_options->end()) { - if (m_allow_unrecognised) { - continue; - } else { - // error - throw option_not_exists_exception(name); - } - } - - auto value = iter->second; - - if (i + 1 == s.size()) { - // it must be the last argument - checked_parse_arg(argc, argv, current, value, name); - } else if (value->value().has_implicit()) { - parse_option(value, name, value->value().get_implicit_value()); - } else { - // error - throw option_requires_argument_exception(name); - } - } - } else if (result[1].length() != 0) { - const std::string &name = result[1]; - - auto iter = m_options->find(name); - - if (iter == m_options->end()) { - if (m_allow_unrecognised) { - // keep unrecognised options in argument list, skip to next argument - argv[nextKeep] = argv[current]; - ++nextKeep; - ++current; - continue; - } else { - // error - throw option_not_exists_exception(name); - } - } - - auto opt = iter->second; - - // equals provided for long option? - if (result[2].length() != 0) { - // parse the option given - - parse_option(opt, name, result[3]); - } else { - // parse the next argument - checked_parse_arg(argc, argv, current, opt, name); - } - } - } - - ++current; - } - - for (auto &opt : *m_options) { - auto &detail = opt.second; - auto &value = detail->value(); - - auto &store = m_results[detail]; - - if (!store.count() && value.has_default()) { - parse_default(detail); - } - } - - if (consume_remaining) { - while (current < argc) { - if (!consume_positional(argv[current])) { - break; - } - ++current; - } - - // adjust argv for any that couldn't be swallowed - while (current != argc) { - argv[nextKeep] = argv[current]; - ++nextKeep; - ++current; - } - } - - argc = nextKeep; - } - - inline void Options::add_option(const std::string &group, const std::string &s, - const std::string &l, std::string desc, - std::shared_ptr value, - std::string arg_help) { - auto stringDesc = toLocalString(std::move(desc)); - auto option = std::make_shared(s, l, stringDesc, value); - - if (s.size() > 0) { - add_one_option(s, option); - } - - if (l.size() > 0) { - add_one_option(l, option); - } - - // add the help details - auto &options = m_help[group]; - - options.options.emplace_back(HelpOptionDetails{ - s, l, stringDesc, value->has_default(), value->get_default_value(), - value->has_implicit(), value->get_implicit_value(), std::move(arg_help), - value->is_container(), value->is_boolean()}); - } - - inline void Options::add_one_option(const std::string &option, - std::shared_ptr details) { - auto in = m_options->emplace(option, details); - - if (!in.second) { - throw option_exists_error(option); - } - } - - inline String Options::help_one_group(const std::string &g) const { - typedef std::vector> OptionHelp; - - auto group = m_help.find(g); - if (group == m_help.end()) { - return ""; - } - - OptionHelp format; - - size_t longest = 0; - - String result; - - if (!g.empty()) { - result += toLocalString(" " + g + " options:\n"); - } - - for (const auto &o : group->second.options) { - if (o.is_container && - m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) { - continue; - } - - auto s = format_option(o); - longest = std::max(longest, stringLength(s)); - format.push_back(std::make_pair(s, String())); - } - - longest = std::min(longest, static_cast(OPTION_LONGEST)); - - // widest allowed description - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; - - auto fiter = format.begin(); - for (const auto &o : group->second.options) { - if (o.is_container && - m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) { - continue; - } - - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); - - result += fiter->first; - if (stringLength(fiter->first) > longest) { - result += '\n'; - result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); - } else { - result += toLocalString(std::string( - longest + OPTION_DESC_GAP - stringLength(fiter->first), ' ')); - } - result += d; - result += '\n'; - - ++fiter; - } - - return result; - } - - inline void Options::generate_group_help( - String &result, const std::vector &print_groups) const { - for (size_t i = 0; i != print_groups.size(); ++i) { - const String &group_help_text = help_one_group(print_groups[i]); - if (empty(group_help_text)) { - continue; - } - result += group_help_text; - if (i < print_groups.size() - 1) { - result += '\n'; - } - } - } - - inline void Options::generate_all_groups_help(String &result) const { - std::vector all_groups; - all_groups.reserve(m_help.size()); - - for (auto &group : m_help) { - all_groups.push_back(group.first); - } - - generate_group_help(result, all_groups); - } - - inline std::string - Options::help(const std::vector &help_groups) const { - String result = m_help_string + "\nUsage:\n " + toLocalString(m_program) + - " " + toLocalString(m_custom_help); - - if (m_positional.size() > 0 && m_positional_help.size() > 0) { - result += " " + toLocalString(m_positional_help); - } - - result += "\n\n"; - - if (help_groups.size() == 0) { - generate_all_groups_help(result); - } else { - generate_group_help(result, help_groups); - } - - return toUTF8String(result); - } - - inline const std::vector Options::groups() const { - std::vector g; - - std::transform( - m_help.begin(), m_help.end(), std::back_inserter(g), - [](const std::map::value_type &pair) { - return pair.first; - }); - - return g; - } - - inline const HelpGroupDetails & - Options::group_help(const std::string &group) const { - return m_help.at(group); - } - -} // namespace cxxopts - -#endif // CXXOPTS_HPP_INCLUDED diff --git a/thirdparty/cxxopts/src/.gitignore b/thirdparty/cxxopts/src/.gitignore deleted file mode 100644 index 33a9488b..00000000 --- a/thirdparty/cxxopts/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -example diff --git a/thirdparty/cxxopts/src/CMakeLists.txt b/thirdparty/cxxopts/src/CMakeLists.txt deleted file mode 100644 index eec97b74..00000000 --- a/thirdparty/cxxopts/src/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2014 Jarryd Beck -# -# 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. - -if(CXXOPTS_BUILD_EXAMPLES) - add_executable(example example.cpp) - target_link_libraries(example cxxopts) -endif() diff --git a/thirdparty/cxxopts/src/example.cpp b/thirdparty/cxxopts/src/example.cpp deleted file mode 100644 index f2457060..00000000 --- a/thirdparty/cxxopts/src/example.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - -Copyright (c) 2014 Jarryd Beck - -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 - -#include "cxxopts.hpp" - -cxxopts::ParseResult -parse(int argc, char* argv[]) -{ - try - { - cxxopts::Options options(argv[0], " - example command line options"); - options - .positional_help("[optional args]") - .show_positional_help(); - - bool apple = false; - - options - .allow_unrecognised_options() - .add_options() - ("a,apple", "an apple", cxxopts::value(apple)) - ("b,bob", "Bob") - ("t,true", "True", cxxopts::value()->default_value("true")) - ("f, file", "File", cxxopts::value>(), "FILE") - ("i,input", "Input", cxxopts::value()) - ("o,output", "Output file", cxxopts::value() - ->default_value("a.out")->implicit_value("b.def"), "BIN") - ("positional", - "Positional arguments: these are the arguments that are entered " - "without an option", cxxopts::value>()) - ("long-description", - "thisisareallylongwordthattakesupthewholelineandcannotbebrokenataspace") - ("help", "Print help") - ("int", "An integer", cxxopts::value(), "N") - ("float", "A floating point number", cxxopts::value()) - ("option_that_is_too_long_for_the_help", "A very long option") - #ifdef CXXOPTS_USE_UNICODE - ("unicode", u8"A help option with non-ascii: à. Here the size of the" - " string should be correct") - #endif - ; - - options.add_options("Group") - ("c,compile", "compile") - ("d,drop", "drop", cxxopts::value>()); - - options.parse_positional({"input", "output", "positional"}); - - auto result = options.parse(argc, argv); - - if (result.count("help")) - { - std::cout << options.help({"", "Group"}) << std::endl; - exit(0); - } - - if (apple) - { - std::cout << "Saw option ‘a’ " << result.count("a") << " times " << - std::endl; - } - - if (result.count("b")) - { - std::cout << "Saw option ‘b’" << std::endl; - } - - if (result.count("f")) - { - auto& ff = result["f"].as>(); - std::cout << "Files" << std::endl; - for (const auto& f : ff) - { - std::cout << f << std::endl; - } - } - - if (result.count("input")) - { - std::cout << "Input = " << result["input"].as() - << std::endl; - } - - if (result.count("output")) - { - std::cout << "Output = " << result["output"].as() - << std::endl; - } - - if (result.count("positional")) - { - std::cout << "Positional = {"; - auto& v = result["positional"].as>(); - for (const auto& s : v) { - std::cout << s << ", "; - } - std::cout << "}" << std::endl; - } - - if (result.count("int")) - { - std::cout << "int = " << result["int"].as() << std::endl; - } - - if (result.count("float")) - { - std::cout << "float = " << result["float"].as() << std::endl; - } - - std::cout << "Arguments remain = " << argc << std::endl; - - return result; - - } catch (const cxxopts::OptionException& e) - { - std::cout << "error parsing options: " << e.what() << std::endl; - exit(1); - } -} - -int main(int argc, char* argv[]) -{ - auto result = parse(argc, argv); - auto arguments = result.arguments(); - std::cout << "Saw " << arguments.size() << " arguments" << std::endl; - - return 0; -} diff --git a/thirdparty/cxxopts/test/.gitignore b/thirdparty/cxxopts/test/.gitignore deleted file mode 100644 index d2cad1ae..00000000 --- a/thirdparty/cxxopts/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -options_test diff --git a/thirdparty/cxxopts/test/CMakeLists.txt b/thirdparty/cxxopts/test/CMakeLists.txt deleted file mode 100644 index 19695457..00000000 --- a/thirdparty/cxxopts/test/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -if (CXXOPTS_BUILD_TESTS) - add_executable(options_test main.cpp options.cpp) - target_link_libraries(options_test cxxopts) - add_test(options options_test) - - # test if the targets are findable from the build directory - add_test(find-package-test ${CMAKE_CTEST_COMMAND} - -C ${CMAKE_BUILD_TYPE} - --build-and-test - "${CMAKE_CURRENT_SOURCE_DIR}/find-package-test" - "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" - --build-generator ${CMAKE_GENERATOR} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-options - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" - "-Dcxxopts_DIR=${PROJECT_BINARY_DIR}" - ) - - # test if the targets are findable when add_subdirectory is used - add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND} - -C ${CMAKE_BUILD_TYPE} - --build-and-test - "${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test" - "${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test" - --build-generator ${CMAKE_GENERATOR} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-options - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" - ) - - add_executable(link_test link_a.cpp link_b.cpp) - target_link_libraries(link_test cxxopts) -endif() diff --git a/thirdparty/cxxopts/test/add-subdirectory-test/CMakeLists.txt b/thirdparty/cxxopts/test/add-subdirectory-test/CMakeLists.txt deleted file mode 100644 index 6c922992..00000000 --- a/thirdparty/cxxopts/test/add-subdirectory-test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(cxxopts-test) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_EXTENSIONS OFF) - -add_subdirectory(../.. cxxopts EXCLUDE_FROM_ALL) - -add_executable(library-test "../../src/example.cpp") -target_link_libraries(library-test cxxopts) diff --git a/thirdparty/cxxopts/test/catch.hpp b/thirdparty/cxxopts/test/catch.hpp deleted file mode 100644 index ef9a4632..00000000 --- a/thirdparty/cxxopts/test/catch.hpp +++ /dev/null @@ -1,10460 +0,0 @@ -/* - * Catch v1.5.0 - * Generated: 2016-04-25 18:56:14.308559 - * ---------------------------------------------------------- - * 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 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// #included from: internal/catch_suppress_warnings.h - -#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" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# 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 ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#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 - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - -#if defined(__cplusplus) && __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER -#endif - -#ifdef __clang__ - -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#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 ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - -#endif - -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -#define CATCH_INTERNAL_CONFIG_COUNTER - -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#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 - -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr -#endif - -namespace Catch { - - struct IConfig; - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - 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_CONFIG_CPP11_GENERATED_METHODS - 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 ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); - - // 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( CATCH_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 = CATCH_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() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_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 std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -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; -}; - -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - -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 ); - } - - ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); -}; - -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ - static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #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_METHOD2( TestName, ClassName, ... )\ - namespace{ \ - struct TestName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void TestName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); - -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #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_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); -#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 = 0x01, - - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // 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_CONFIG_CPP11_GENERATED_METHODS - 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 - -// #included from: catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } - - 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; - - Generic::AllOf operator && ( Matcher const& other ) const; - Generic::AnyOf operator || ( Matcher const& other ) const; - Generic::Not operator ! () const; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - template - class Not : public MatcherImpl, ExpressionT> { - public: - explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} - - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); - } - - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } - private: - Ptr< Matcher > m_matcher; - }; - - 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(); - } - - AllOf operator && ( Matcher const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - 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(); - } - - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - } // namespace Generic - - template - Generic::AllOf Matcher::operator && ( Matcher const& other ) const { - Generic::AllOf allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - template - Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { - Generic::AnyOf anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - template - Generic::Not Matcher::operator ! () const { - return Generic::Not( *this ); - } - - 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 CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - - } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; - } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct Equals : MatcherImpl { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} - - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - } // namespace StdString - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template - inline Impl::Generic::Not Not( Impl::Matcher const& m ) { - return Impl::Generic::Not( m ); - } - - 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, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); - } - 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 - -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, - char const* secondArg = "" ); - - 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 captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher const& matcher ); - void handleResult( AssertionResult const& result ); - 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 bool( opCast( lhs ) == opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) != opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) < opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) > opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) >= opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( 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_LONG_LONG - // long long to unsigned X - template bool compare( long long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // unsigned long long to X - template bool compare( unsigned long long lhs, int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long long (when comparing against NULL) - template bool compare( long long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } -#endif // CATCH_CONFIG_CPP11_LONG_LONG - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template bool compare( std::nullptr_t, T* rhs ) { - return Evaluator::evaluate( nullptr, rhs ); - } - template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, nullptr ); - } -#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 - -#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_CONFIG_CPP11_TUPLE -#include -#endif - -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#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_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif - -#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 const std::string unprintableString; - - struct BorgType { - template BorgType( T const& ); - }; - - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; - - 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 ) }; - }; - -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - 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_CONFIG_CPP11_IS_ENUM) - 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 "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return "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_CONFIG_CPP11_TUPLE - -// 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 // CATCH_CONFIG_CPP11_TUPLE - -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_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif - -public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - 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 SectionEndInfo; - 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( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 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 { \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast(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, matcher, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ - if( __catchResult.allowThrows() ) \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ - } \ - 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 = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (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 - -// #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; - }; -} - -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; - }; - - struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; - -} // end namespace Catch - -// #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 -#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, Ptr const& factory ) = 0; - virtual void registerListener( Ptr const& 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; - typedef std::vector ExceptionTranslators; - - struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) 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( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { - try { - if( it == itEnd ) - throw; - else - return (*it)->translate( it+1, itEnd ); - } - 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_EXCEPTION2( translatorName, signature ) \ - static std::string translatorName( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ - static std::string translatorName( signature ) - -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( 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_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( CATCH_NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_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 = CATCH_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 != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } - - bool operator !() const { return nullableValue == CATCH_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 ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); - - 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( CATCH_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_session.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 - -// #included from: catch_wildcard_pattern.hpp -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -namespace Catch -{ - class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, "*" ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, "*" ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } - -#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 adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; - std::string m_pattern; - }; -} - -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern : SharedImpl<> { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - class NamePattern : public Pattern { - public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } - private: - WildcardPattern m_wildcardPattern; - }; - - 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 - }; }; - struct UseColour { enum YesOrNo { - Auto, - Yes, - No - }; }; - - 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 UseColour::YesOrNo useColour() const = 0; - }; -} - -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_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 { - - std::ostream& cout(); - std::ostream& cerr(); - - struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; - virtual std::ostream& stream() const = 0; - }; - - class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class CoutStream : public IStream { - mutable std::ostream m_os; - public: - CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class DebugOutStream : public IStream { - std::auto_ptr m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; -} - -#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 ), - filenamesAsTags( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) - {} - - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; - - std::string outputFilename; - std::string name; - std::string processName; - - std::vector reporterNames; - std::vector testsOrTags; - }; - - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: - - Config() - {} - - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - 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() { - } - - 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; } - - std::vector getReporterNames() const { return m_data.reporterNames; } - - 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_stream->stream(); } - 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 UseColour::YesOrNo useColour() const { return m_data.useColour; } - - private: - - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } - ConfigData m_data; - - std::auto_ptr m_stream; - 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 - -// Version 0.0.2.3 - -// 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 -#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 clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -// ----------- #included from clara_compilers.h ----------- - -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// In general each macro has a _NO_ form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 - -#ifdef __clang__ - -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -#define CLARA_CPP11_OR_GREATER - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif - -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif - -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif - -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr -#endif - -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h - -#include -#include -#include - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif - -// 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 + "'" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -#endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) 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 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 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 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 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 bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - inline std::vector argsToVector( int argc, char const* const* const argv ) { - std::vector args( static_cast( argc ) ); - for( std::size_t i = 0; i < static_cast( argc ); ++i ) - args[i] = argv[i]; - - return args; - } - - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - Parser() : mode( None ), from( 0 ), inQuotes( false ){} - - void parseIntoTokens( std::vector const& args, std::vector& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } - - void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } - - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; -#endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( std::string( " \t:=\0", 5 ).find( c ) == std::string::npos ) - return mode; - - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( inQuotes || std::string( " \t\0", 3 ).find( c ) == std::string::npos ) - return mode; - - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; - - 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(); - } - }; - - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; - - 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( std::vector const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[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( args, 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.set( config, "true" ); - } - 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 addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } - - 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 setUseColour( ConfigData& config, std::string const& value ) { - std::string mode = toLower( value ); - - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); - } - inline void forceColour( ConfigData& config ) { - config.useColour = UseColour::Yes; - } - 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( &addReporterName, "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" ); - - cli["-#"]["--filenames-as-tags"] - .describe( "adds a tag for the filename" ) - .bind( &ConfigData::filenamesAsTags ); - - // 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 (deprecated)" ) - .bind( &forceColour ); - - cli["--use-colour"] - .describe( "should output be colourised" ) - .bind( &setUseColour, "yes|no" ); - - 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_CONFIG_CPP11_GENERATED_METHODS - 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_CONFIG_CPP11_GENERATED_METHODS - 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_CONFIG_CPP11_GENERATED_METHODS - 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_CONFIG_CPP11_GENERATED_METHODS - 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_CONFIG_CPP11_GENERATED_METHODS - 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 : IShared { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map > FactoryMap; - typedef std::vector > Listeners; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - - Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); - -} - -#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 = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - 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 = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - 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 = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - 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_run_context.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 -#include - -namespace Catch { -namespace TestCaseTracking { - - struct ITracker : SharedImpl<> { - virtual ~ITracker(); - - // static queries - virtual std::string name() const = 0; - - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; - - virtual ITracker& parent() = 0; - - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; - - virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; - virtual void openChild() = 0; - }; - - class TrackerContext { - - enum RunState { - NotStarted, - Executing, - CompletedCycle - }; - - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; - - public: - - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} - - ITracker& startRun(); - - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } - - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } - }; - - class TrackerBase : public ITracker { - protected: - enum CycleState { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed - }; - class TrackerHasName { - std::string m_name; - public: - TrackerHasName( std::string const& name ) : m_name( name ) {} - bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; - } - }; - typedef std::vector > Children; - std::string m_name; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; - CycleState m_runState; - public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); - - virtual std::string name() const CATCH_OVERRIDE { - return m_name; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } - - virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } - - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } - - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } - - virtual void close() CATCH_OVERRIDE { - - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); - - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } - private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } - }; - - class SectionTracker : public TrackerBase { - public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} - virtual ~SectionTracker(); - - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { - SectionTracker* section = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { - section = dynamic_cast( childTracker ); - assert( section ); - } - else { - section = new SectionTracker( name, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() && !section->isComplete() ) { - - section->open(); - } - return *section; - } - }; - - class IndexTracker : public TrackerBase { - int m_size; - int m_index; - public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); - - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { - IndexTracker* tracker = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { - tracker = dynamic_cast( childTracker ); - assert( tracker ); - } - else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } - - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } - }; - - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; - -} // 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( CATCH_NULL ), - m_config( _config ), - m_reporter( reporter ) - { - 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() ) ); - } - - 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; - - do { - m_trackerContext.startRun(); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); - } - // !TBD: deprecated - this will be replaced by indexed trackers - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - - Totals deltaTotals = m_totals.delta( prevTotals ); - if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; - - 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; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); - if( !sectionTracker.isOpen() ) - return false; - m_activeSections.push_back( §ionTracker ); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 ) - return false; - if( !m_config->warnAboutMissingAssertions() ) - return false; - if( m_trackerContext.currentTracker().hasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionEndInfo const& endInfo ) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( !m_activeSections.empty() ) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } - - m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { - if( m_unfinishedSections.empty() ) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); - - m_unfinishedSections.push_back( endInfo ); - } - - 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 ); - - seedRng( *m_config ); - - 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(); - } - m_testCaseTracker->close(); - 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 ); - m_unfinishedSections.clear(); - } - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; - }; - - 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 _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ); - - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; - - // buildNumber is only used if branchName is not null - std::string const branchName; - unsigned int const buildNumber; - - friend std::ostream& operator << ( std::ostream& os, Version const& version ); - - private: - void operator=( Version const& ); - }; - - extern Version libraryVersion; -} - -#include -#include -#include - -namespace Catch { - - Ptr createReporter( std::string const& reporterName, Ptr const& config ) { - Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); - if( !reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - return reporter; - } - - Ptr makeReporter( Ptr const& config ) { - std::vector reporters = config->getReporterNames(); - if( reporters.empty() ) - reporters.push_back( "console" ); - - Ptr reporter; - for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it ) - reporter = addReporter( reporter, createReporter( *it, config ) ); - return reporter; - } - Ptr addListeners( Ptr const& config, Ptr reporters ) { - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it ) - reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); - return reporters; - } - - Totals runTests( Ptr const& config ) { - - Ptr iconfig = config.get(); - - Ptr reporter = makeReporter( config ); - reporter = addListeners( iconfig, reporter ); - - RunContext context( iconfig, reporter ); - - Totals totals; - - context.testGroupStarting( config->name(), 1, 1 ); - - TestSpec testSpec = config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); - for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) - totals += context.runTest( *it ); - else - reporter->skipTest( *it ); - } - - context.testGroupEnded( iconfig->name(), totals, 1, 1 ); - return totals; - } - - void applyFilenamesAsTags( IConfig const& config ) { - std::vector const& tests = getAllTestCasesSorted( config ); - for(std::size_t i = 0; i < tests.size(); ++i ) { - TestCase& test = const_cast( tests[i] ); - std::set tags = test.tags; - - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of( "\\/" ); - if( lastSlash != std::string::npos ) - filename = filename.substr( lastSlash+1 ); - - std::string::size_type lastDot = filename.find_last_of( "." ); - if( lastDot != std::string::npos ) - filename = filename.substr( 0, lastDot ); - - tags.insert( "#" + filename ); - setTags( test, tags ); - } - } - - 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 << "\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* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( Clara::argsToVector( 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* const* 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 - - seedRng( *m_config ); - - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runTests( m_config ).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 { - - struct LexSort { - bool operator() (TestCase i,TestCase j) const { return (i sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - - std::vector sorted = unsortedTestCases; - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end(), LexSort() ); - break; - case RunTests::InRandomOrder: - { - seedRng( config ); - - RandomNumberGenerator rng; - std::random_shuffle( sorted.begin(), sorted.end(), rng ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } - - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set seenFunctions; - for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it ) { - std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ){ - Catch::cerr() - << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - exit(1); - } - } - } - - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) - if( matchTest( *it, testSpec, config ) ) - filtered.push_back( *it ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } - - class TestRegistry : public ITestCaseRegistry { - public: - TestRegistry() - : m_currentSortOrder( RunTests::InDeclarationOrder ), - m_unnamedCount( 0 ) - {} - virtual ~TestRegistry(); - - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); - } - - virtual std::vector const& getAllTests() const { - return m_functions; - } - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); - - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; - } - - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised - }; - - /////////////////////////////////////////////////////////////////////////// - - 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; - } - - void registerTestCase - ( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase - ( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCaseFunction( function, lineInfo, nameAndDesc ); - } - - AutoReg::~AutoReg() {} - -} // 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() CATCH_OVERRIDE {} - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, Ptr const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - void registerListener( Ptr const& factory ) { - m_listeners.push_back( factory ); - } - - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; - } - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} - -// #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 { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - return tryTranslators(); -#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 "Unknown exception"; - } - } - - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); - } - - 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 CATCH_OVERRIDE { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerListener( factory ); - } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } - } - - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_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 - -#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; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - FileStream::FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; - throw std::domain_error( oss.str() ); - } - } - - std::ostream& FileStream::stream() const { - return m_ofs; - } - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - DebugOutStream::DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - m_os( m_streamBuf.get() ) - {} - - std::ostream& DebugOutStream::stream() const { - return m_os; - } - - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream::CoutStream() - : m_os( Catch::cout().rdbuf() ) - {} - - std::ostream& CoutStream::stream() const { - return m_os; - } - -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { - return std::cout; - } - std::ostream& cerr() { - return std::cerr; - } -#endif -} - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_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 - : CATCH_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 = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_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 ); - originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); - originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); - } - - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalForegroundAttributes ); - 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 | originalBackgroundAttributes ); - } - HANDLE stdoutHandle; - WORD originalForegroundAttributes; - WORD originalBackgroundAttributes; - }; - - IColourImpl* platformColourInstance() { - static Win32ColourImpl s_instance; - - Ptr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? &s_instance - : NoColourImpl::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(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? 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 = 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 ); - } - - void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) - { - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); - - std::ostringstream oss; - for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; - std::string lcaseTag = toLower( *it ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); - } - testCaseInfo.tagsAsString = oss.str(); - } - - 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 ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); - } - - 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 { - - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} - - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." - << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; - } - return os; - } - - Version libraryVersion( 1, 5, 0, "", 0 ); - -} - -// #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,CATCH_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 ); - } - - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); - } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - - 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 ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); - } - } - - // 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, CATCH_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 { - - const std::string unprintableString = "{?}"; - - namespace { - const int hexThreshold = 255; - - 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; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << 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_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} -#endif - -#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 { - - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; - } - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), 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::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); - else - captureExpectedException( Matchers::Equals( expectedMessage ) ); - } - - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { - - assert( m_exprComponents.testFalse == false ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; - - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; - } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); - } - - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); - } - void ResultBuilder::handleResult( AssertionResult const& result ) - { - 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_multi.hpp -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED - -namespace Catch { - -class MultipleReporters : public SharedImpl { - typedef std::vector > Reporters; - Reporters m_reporters; - -public: - void add( Ptr const& reporter ) { - m_reporters.push_back( reporter ); - } - -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); - } - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); - } - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); - } - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); - } - - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); - } -}; - -Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { - Ptr resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = dynamic_cast( existingReporter.get() ); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr( multi ); - if( existingReporter ) - multi->add( existingReporter ); - } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); - } - else - resultingReporter = additionalReporter; - - return resultingReporter; -} - -} // 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() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual ~StreamingReporterBase() CATCH_OVERRIDE; - - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { - // 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; - ReporterPreferences m_reporterPrefs; - }; - - 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() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} - - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - 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& ) CATCH_OVERRIDE {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - 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& ) CATCH_OVERRIDE {} - - 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; - ReporterPreferences m_reporterPrefs; - - }; - - 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; - } - - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; - } - }; - -} // 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 SharedImpl { - - // *** 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() ); - } - }; - - template - class ListenerRegistrar { - - class ListenerFactory : public SharedImpl { - - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - virtual std::string getDescription() const { - return ""; - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); - } - }; -} - -#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 ); } - -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } - -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; - - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} - - void encodeTo( std::ostream& os ) const { - - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 - if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << static_cast( c ); - else - os << c; - } - } - } - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } - - private: - std::string m_str; - ForWhat m_forWhat; - }; - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_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(); - } - - 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 << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; - 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 ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( 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; - } - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream* m_os; - }; - -} -// #included from: 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 - - -namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_sectionDepth( 0 ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~XmlReporter() CATCH_OVERRIDE; - - static std::string getDescription() { - return "Reports test results as an XML document"; - } - - public: // StreamingReporterBase - - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { - StreamingReporterBase::noMatchingTestCases( s ); - } - - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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& ) CATCH_OVERRIDE { } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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() ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~JunitReporter() CATCH_OVERRIDE; - - 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*/ ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() CATCH_OVERRIDE { - 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() CATCH_OVERRIDE; - static std::string getDescription() { - return "Reports test results as plain lines of text"; - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { - 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 ) CATCH_OVERRIDE { - 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 << " 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.testCases.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 { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - 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() {} - WildcardPattern::~WildcardPattern() {} - 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() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } -} - -#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 * 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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#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_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __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_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, 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( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " 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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#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 REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __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 REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( 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( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) - -using Catch::Detail::Approx; - -#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - diff --git a/thirdparty/cxxopts/test/find-package-test/CMakeLists.txt b/thirdparty/cxxopts/test/find-package-test/CMakeLists.txt deleted file mode 100644 index 77d58316..00000000 --- a/thirdparty/cxxopts/test/find-package-test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(cxxopts-test) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_EXTENSIONS OFF) - -find_package(cxxopts REQUIRED) - -add_executable(library-test "../../src/example.cpp") -target_link_libraries(library-test cxxopts::cxxopts) diff --git a/thirdparty/cxxopts/test/link_a.cpp b/thirdparty/cxxopts/test/link_a.cpp deleted file mode 100644 index 3611692e..00000000 --- a/thirdparty/cxxopts/test/link_a.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "cxxopts.hpp" - -int main(int, char**) -{ - return 0; -} diff --git a/thirdparty/cxxopts/test/link_b.cpp b/thirdparty/cxxopts/test/link_b.cpp deleted file mode 100644 index e48e22a9..00000000 --- a/thirdparty/cxxopts/test/link_b.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/thirdparty/cxxopts/test/main.cpp b/thirdparty/cxxopts/test/main.cpp deleted file mode 100644 index 0c7c351f..00000000 --- a/thirdparty/cxxopts/test/main.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch.hpp" diff --git a/thirdparty/cxxopts/test/options.cpp b/thirdparty/cxxopts/test/options.cpp deleted file mode 100644 index 1c02053b..00000000 --- a/thirdparty/cxxopts/test/options.cpp +++ /dev/null @@ -1,550 +0,0 @@ -#include "catch.hpp" - -#include - -#include "cxxopts.hpp" - -class Argv { - public: - - Argv(std::initializer_list args) - : m_argv(new char*[args.size()]) - , m_argc(args.size()) - { - int i = 0; - auto iter = args.begin(); - while (iter != args.end()) { - auto len = strlen(*iter) + 1; - auto ptr = std::unique_ptr(new char[len]); - - strcpy(ptr.get(), *iter); - m_args.push_back(std::move(ptr)); - m_argv.get()[i] = m_args.back().get(); - - ++iter; - ++i; - } - } - - char** argv() const { - return m_argv.get(); - } - - int argc() const { - return m_argc; - } - - private: - - std::vector> m_args; - std::unique_ptr m_argv; - int m_argc; -}; - -TEST_CASE("Basic options", "[options]") -{ - - cxxopts::Options options("tester", " - test basic options"); - - options.add_options() - ("long", "a long option") - ("s,short", "a short option") - ("value", "an option with a value", cxxopts::value()) - ("a,av", "a short option with a value", cxxopts::value()) - ("6,six", "a short number option") - ("p, space", "an option with space between short and long") - ("nothing", "won't exist", cxxopts::value()) - ; - - Argv argv({ - "tester", - "--long", - "-s", - "--value", - "value", - "-a", - "b", - "-6", - "-p", - "--space", - }); - - char** actual_argv = argv.argv(); - auto argc = argv.argc(); - - auto result = options.parse(argc, actual_argv); - - CHECK(result.count("long") == 1); - CHECK(result.count("s") == 1); - CHECK(result.count("value") == 1); - CHECK(result.count("a") == 1); - CHECK(result["value"].as() == "value"); - CHECK(result["a"].as() == "b"); - CHECK(result.count("6") == 1); - CHECK(result.count("p") == 2); - CHECK(result.count("space") == 2); - - auto& arguments = result.arguments(); - REQUIRE(arguments.size() == 7); - CHECK(arguments[0].key() == "long"); - CHECK(arguments[0].value() == "true"); - CHECK(arguments[0].as() == true); - - CHECK(arguments[1].key() == "short"); - CHECK(arguments[2].key() == "value"); - CHECK(arguments[3].key() == "av"); - - CHECK_THROWS_AS(result["nothing"].as(), std::domain_error&); -} - -TEST_CASE("Short options", "[options]") -{ - cxxopts::Options options("test_short", " - test short options"); - - options.add_options() - ("a", "a short option", cxxopts::value()); - - Argv argv({"test_short", "-a", "value"}); - - auto actual_argv = argv.argv(); - auto argc = argv.argc(); - - auto result = options.parse(argc, actual_argv); - - CHECK(result.count("a") == 1); - CHECK(result["a"].as() == "value"); - - REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), - cxxopts::invalid_option_format_error&); -} - -TEST_CASE("No positional", "[positional]") -{ - cxxopts::Options options("test_no_positional", - " - test no positional options"); - - Argv av({"tester", "a", "b", "def"}); - - char** argv = av.argv(); - auto argc = av.argc(); - auto result = options.parse(argc, argv); - - REQUIRE(argc == 4); - CHECK(strcmp(argv[1], "a") == 0); -} - -TEST_CASE("All positional", "[positional]") -{ - std::vector positional; - - cxxopts::Options options("test_all_positional", " - test all positional"); - options.add_options() - ("positional", "Positional parameters", - cxxopts::value>(positional)) - ; - - Argv av({"tester", "a", "b", "c"}); - - auto argc = av.argc(); - auto argv = av.argv(); - - std::vector pos_names = {"positional"}; - - options.parse_positional(pos_names.begin(), pos_names.end()); - - auto result = options.parse(argc, argv); - - REQUIRE(argc == 1); - REQUIRE(positional.size() == 3); - - CHECK(positional[0] == "a"); - CHECK(positional[1] == "b"); - CHECK(positional[2] == "c"); -} - -TEST_CASE("Some positional explicit", "[positional]") -{ - cxxopts::Options options("positional_explicit", " - test positional"); - - options.add_options() - ("input", "Input file", cxxopts::value()) - ("output", "Output file", cxxopts::value()) - ("positional", "Positional parameters", - cxxopts::value>()) - ; - - options.parse_positional({"input", "output", "positional"}); - - Argv av({"tester", "--output", "a", "b", "c", "d"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - - CHECK(argc == 1); - CHECK(result.count("output")); - CHECK(result["input"].as() == "b"); - CHECK(result["output"].as() == "a"); - - auto& positional = result["positional"].as>(); - - REQUIRE(positional.size() == 2); - CHECK(positional[0] == "c"); - CHECK(positional[1] == "d"); -} - -TEST_CASE("No positional with extras", "[positional]") -{ - cxxopts::Options options("posargmaster", "shows incorrect handling"); - options.add_options() - ("dummy", "oh no", cxxopts::value()) - ; - - Argv av({"extras", "--", "a", "b", "c", "d"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto old_argv = argv; - auto old_argc = argc; - - options.parse(argc, argv); - - REQUIRE(argc == old_argc - 1); - CHECK(argv[0] == std::string("extras")); - CHECK(argv[1] == std::string("a")); -} - -TEST_CASE("Empty with implicit value", "[implicit]") -{ - cxxopts::Options options("empty_implicit", "doesn't handle empty"); - options.add_options() - ("implicit", "Has implicit", cxxopts::value() - ->implicit_value("foo")); - - Argv av({"implicit", "--implicit="}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - - REQUIRE(result.count("implicit") == 1); - REQUIRE(result["implicit"].as() == ""); -} - -TEST_CASE("Default values", "[default]") -{ - cxxopts::Options options("defaults", "has defaults"); - options.add_options() - ("default", "Has implicit", cxxopts::value() - ->default_value("42")); - - SECTION("Sets defaults") { - Argv av({"implicit"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - CHECK(result.count("default") == 0); - CHECK(result["default"].as() == 42); - } - - SECTION("When values provided") { - Argv av({"implicit", "--default", "5"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - CHECK(result.count("default") == 1); - CHECK(result["default"].as() == 5); - } -} - -TEST_CASE("Parse into a reference", "[reference]") -{ - int value = 0; - - cxxopts::Options options("into_reference", "parses into a reference"); - options.add_options() - ("ref", "A reference", cxxopts::value(value)); - - Argv av({"into_reference", "--ref", "42"}); - - auto argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - CHECK(result.count("ref") == 1); - CHECK(value == 42); -} - -TEST_CASE("Integers", "[options]") -{ - cxxopts::Options options("parses_integers", "parses integers correctly"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf", "0x0"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - auto result = options.parse(argc, argv); - - REQUIRE(result.count("positional") == 7); - - auto& positional = result["positional"].as>(); - REQUIRE(positional.size() == 7); - CHECK(positional[0] == 5); - CHECK(positional[1] == 6); - CHECK(positional[2] == -6); - CHECK(positional[3] == 0); - CHECK(positional[4] == 0xab); - CHECK(positional[5] == 0xaf); - CHECK(positional[6] == 0x0); -} - -TEST_CASE("Leading zero integers", "[options]") -{ - cxxopts::Options options("parses_integers", "parses integers correctly"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - Argv av({"ints", "--", "05", "06", "0x0ab", "0x0001"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - auto result = options.parse(argc, argv); - - REQUIRE(result.count("positional") == 4); - - auto& positional = result["positional"].as>(); - REQUIRE(positional.size() == 4); - CHECK(positional[0] == 5); - CHECK(positional[1] == 6); - CHECK(positional[2] == 0xab); - CHECK(positional[3] == 0x1); -} - -TEST_CASE("Unsigned integers", "[options]") -{ - cxxopts::Options options("parses_unsigned", "detects unsigned errors"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - Argv av({"ints", "--", "-2"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); -} - -TEST_CASE("Integer bounds", "[integer]") -{ - cxxopts::Options options("integer_boundaries", "check min/max integer"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - SECTION("No overflow") - { - Argv av({"ints", "--", "127", "-128", "0x7f", "-0x80", "0x7e"}); - - auto argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - auto result = options.parse(argc, argv); - - REQUIRE(result.count("positional") == 5); - - auto& positional = result["positional"].as>(); - CHECK(positional[0] == 127); - CHECK(positional[1] == -128); - CHECK(positional[2] == 0x7f); - CHECK(positional[3] == -0x80); - CHECK(positional[4] == 0x7e); - } -} - -TEST_CASE("Overflow on boundary", "[integer]") -{ - using namespace cxxopts::values; - - int8_t si; - uint8_t ui; - - CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::argument_incorrect_type&); - CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::argument_incorrect_type&); - CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::argument_incorrect_type&); - CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::argument_incorrect_type&); - CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::argument_incorrect_type&); - CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::argument_incorrect_type&); -} - -TEST_CASE("Integer overflow", "[options]") -{ - cxxopts::Options options("reject_overflow", "rejects overflowing integers"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - Argv av({"ints", "--", "128"}); - - auto argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); -} - -TEST_CASE("Floats", "[options]") -{ - cxxopts::Options options("parses_floats", "parses floats correctly"); - options.add_options() - ("double", "Double precision", cxxopts::value()) - ("positional", "Floats", cxxopts::value>()); - - Argv av({"floats", "--double", "0.5", "--", "4", "-4", "1.5e6", "-1.5e6"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - auto result = options.parse(argc, argv); - - REQUIRE(result.count("double") == 1); - REQUIRE(result.count("positional") == 4); - - CHECK(result["double"].as() == 0.5); - - auto& positional = result["positional"].as>(); - CHECK(positional[0] == 4); - CHECK(positional[1] == -4); - CHECK(positional[2] == 1.5e6); - CHECK(positional[3] == -1.5e6); -} - -TEST_CASE("Invalid integers", "[integer]") { - cxxopts::Options options("invalid_integers", "rejects invalid integers"); - options.add_options() - ("positional", "Integers", cxxopts::value>()); - - Argv av({"ints", "--", "Ae"}); - - char **argv = av.argv(); - auto argc = av.argc(); - - options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); -} - -TEST_CASE("Booleans", "[boolean]") { - cxxopts::Options options("parses_floats", "parses floats correctly"); - options.add_options() - ("bool", "A Boolean", cxxopts::value()) - ("debug", "Debugging", cxxopts::value()) - ("timing", "Timing", cxxopts::value()) - ("noExplicitDefault", "No Explicit Default", cxxopts::value()) - ("defaultTrue", "Timing", cxxopts::value()->default_value("true")) - ("defaultFalse", "Timing", cxxopts::value()->default_value("false")) - ("others", "Other arguments", cxxopts::value>()) - ; - - options.parse_positional("others"); - - Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "extra"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - auto result = options.parse(argc, argv); - - REQUIRE(result.count("bool") == 1); - REQUIRE(result.count("debug") == 1); - REQUIRE(result.count("timing") == 1); - REQUIRE(result.count("noExplicitDefault") == 0); - REQUIRE(result.count("defaultTrue") == 0); - REQUIRE(result.count("defaultFalse") == 0); - - CHECK(result["bool"].as() == false); - CHECK(result["debug"].as() == true); - CHECK(result["timing"].as() == true); - CHECK(result["noExplicitDefault"].as() == false); - CHECK(result["defaultTrue"].as() == true); - CHECK(result["defaultFalse"].as() == false); - - REQUIRE(result.count("others") == 1); -} - -#ifdef CXXOPTS_HAS_OPTIONAL -TEST_CASE("std::optional", "[optional]") { - std::optional optional; - cxxopts::Options options("optional", " - tests optional"); - options.add_options() - ("optional", "an optional option", cxxopts::value>(optional)); - - Argv av({"optional", "--optional", "foo"}); - - char** argv = av.argv(); - auto argc = av.argc(); - - options.parse(argc, argv); - - REQUIRE(optional.has_value()); - CHECK(*optional == "foo"); -} -#endif - -TEST_CASE("Unrecognised options", "[options]") { - cxxopts::Options options("unknown_options", " - test unknown options"); - - options.add_options() - ("long", "a long option") - ("s,short", "a short option"); - - Argv av({ - "unknown_options", - "--unknown", - "--long", - "-su", - "--another_unknown", - }); - - char** argv = av.argv(); - auto argc = av.argc(); - - SECTION("Default behaviour") { - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); - } - - SECTION("After allowing unrecognised options") { - options.allow_unrecognised_options(); - CHECK_NOTHROW(options.parse(argc, argv)); - REQUIRE(argc == 3); - CHECK_THAT(argv[1], Catch::Equals("--unknown")); - } -} - -TEST_CASE("Invalid option syntax", "[options]") { - cxxopts::Options options("invalid_syntax", " - test invalid syntax"); - - Argv av({ - "invalid_syntax", - "--a", - }); - - char** argv = av.argv(); - auto argc = av.argc(); - - SECTION("Default behaviour") { - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); - } -}