diff --git a/.cncc.style b/.cncc.style new file mode 100644 index 000000000..bdece58d9 --- /dev/null +++ b/.cncc.style @@ -0,0 +1,14 @@ +# Kind-specific patterns to check AST nodes against. Both python-clang and +# libclang docs explain CursorKind, with differences in detail. See also: +# - https://github.com/llvm-mirror/clang/blob/aca4fe314a55cacae29e1548cb7bfd2119c6df4c/bindings/python/clang/cindex.py#L599 +# - http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013 +# - https://docs.python.org/2/library/re.html#regular-expression-syntax + +class_decl: '^([A-Z]+[a-z]+)+$' +struct_decl: '^([A-Z]+[a-z]+)+$' +field_decl: '^[a-z_]+$' +var_decl: '^[a-z]+[a-z0-9_]*$' +parm_decl: '^[a-z]*[a-z0-9_]*$' +namespace: '^[a-z_]*$' +cxx_method: '^([A-Z]+[a-z]+)+$' +function_decl: '^[a-z]+([A-Z]+[a-z]+)*$' diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..d6d517dcc --- /dev/null +++ b/.eslintrc @@ -0,0 +1,28 @@ +{ + "rules": { + "indent": [ + 2, + 4 + ], + "quotes": [ + 1, + "single" + ], + "linebreak-style": [ + 2, + "unix" + ], + "semi": [ + 2, + "always" + ], + "no-console": [ + 1 + ] + }, + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended" +} diff --git a/.gitignore b/.gitignore index 21608e35b..dd46b9f0e 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ Thumbs.db # build related files # ####################### /build/ +/example/build/ +/test/data/monaco* /cmake/postinst # Eclipse related files # @@ -73,8 +75,16 @@ stxxl.errlog ################### /sandbox/ +# Test related files # +###################### /test/profile.lua +/test/cache +/test/speeds.csv +/test/data/monaco.* +node_modules # Deprecated config file # ########################## /server.ini + +*.swp diff --git a/.travis.yml b/.travis.yml index c0659495b..df8df0a6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ -language: cpp +#language: cpp +# This makes travis use the thin image which boots faster +language: generic + + +# sudo:required is needed for trusty images sudo: required dist: trusty @@ -11,136 +16,151 @@ branches: - develop matrix: + fast_finish: true + include: - # 1/ Linux Clang Builds - + # Debug Builds - os: linux - compiler: clang - addons: &clang38 + compiler: gcc + addons: &gcc5 apt: - sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test'] - packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'rubygems-integration', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] - env: COMPILER='clang++-3.8' BUILD_TYPE='Release' + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'pip'] + env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug' COVERAGE=ON - - os: linux - compiler: clang - addons: &clang38 - apt: - sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test'] - packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'rubygems-integration', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] - env: COMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON - - - os: linux - compiler: clang - addons: *clang38 - env: COMPILER='clang++-3.8' BUILD_TYPE='Debug' - - - # 2/ Linux GCC Builds - os: linux compiler: gcc addons: &gcc48 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'rubygems-integration', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] - env: COMPILER='g++-4.8' BUILD_TYPE='Release' + packages: ['g++-4.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Debug' - os: linux - compiler: gcc - addons: *gcc48 - env: COMPILER='g++-4.8' BUILD_TYPE='Debug' + compiler: clang + addons: &clang38 + apt: + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test'] + packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Debug' RUN_CLANG_FORMAT=ON + - os: osx + osx_image: xcode7.3 + compiler: clang + env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Debug' + # Release Builds - os: linux compiler: gcc addons: &gcc5 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'rubygems-integration', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] - env: COMPILER='g++-5' BUILD_TYPE='Release' + packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release' + - os: linux + compiler: gcc + addons: &gcc48 + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='gcc-4.8' CXXCOMPILER='g++-4.8' BUILD_TYPE='Release' + + - os: linux + compiler: clang + addons: &clang38 + apt: + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test'] + packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' + + - os: osx + osx_image: xcode7.3 + compiler: clang + env: CCOMPILER='clang' CXXCOMPILER='clang++' BUILD_TYPE='Release' + + # Shared Library - os: linux compiler: gcc addons: &gcc5 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'rubygems-integration', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] - env: COMPILER='g++-5' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON + packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON - os: linux - compiler: gcc - addons: *gcc5 - env: COMPILER='g++-5' BUILD_TYPE='Debug' - - - # Disabled until tests all pass on OSX: - # - # 3/ OSX Clang Builds - #- os: osx - # osx_image: xcode6.4 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Debug' - - #- os: osx - # osx_image: xcode6.4 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Release' - - #- os: osx - # osx_image: xcode6.4 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON - - #- os: osx - # osx_image: xcode7 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Debug' - - #- os: osx - # osx_image: xcode7 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Release' - - #- os: osx - # osx_image: xcode7 - # compiler: clang - # env: COMPILER='clang++' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON + compiler: clang + addons: &clang38 + apt: + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test'] + packages: ['clang-3.8', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev'] + env: CCOMPILER='clang-3.8' CXXCOMPILER='clang++-3.8' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON +before_install: + - source ./scripts/install_node.sh 4 install: + - npm install - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - | + if [[ -n "${COVERAGE}" ]]; then + pip install --user cpp-coveralls + fi - | if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then - CMAKE_URL="http://www.cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz" + CMAKE_URL="http://www.cmake.org/files/v3.5/cmake-3.5.1-Linux-x86_64.tar.gz" mkdir cmake && travis_retry wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake export PATH=${DEPS_DIR}/cmake/bin:${PATH} - - OSMOSIS_URL="http://bretth.dev.openstreetmap.org/osmosis-build/osmosis-latest.tgz" - mkdir osmosis && travis_retry wget --quiet -O - ${OSMOSIS_URL} | tar -xz -C osmosis - export PATH=${DEPS_DIR}/osmosis/bin:${PATH} - elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - brew install cmake boost libzip libstxxl libxml2 lua51 luabind tbb GDAL osmosis + # implicit deps, but seem to be installed by default with recent images: libxml2 GDAL boost + brew install cmake libzip libstxxl lua51 luabind tbb md5sha1sum fi before_script: - cd ${TRAVIS_BUILD_DIR} - - rvm use 1.9.3 - - gem install bundler - - bundle install - - mkdir build && cd build - - export CXX=${COMPILER} - - export OSRM_PORT=5000 OSRM_TIMEOUT=60 - - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DBUILD_TOOLS=1 + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + ./scripts/check_taginfo.py taginfo.json profiles/car.lua + fi + - mkdir build && pushd build + - export CC=${CCOMPILER} CXX=${CXXCOMPILER} + - export OSRM_PORT=5000 OSRM_TIMEOUT=6000 + - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DCOVERAGE=${COVERAGE:-OFF} -DBUILD_TOOLS=1 -DENABLE_CCACHE=0 script: - make --jobs=2 - make tests --jobs=2 - make benchmarks - - ./algorithm-tests - - ./datastructure-tests - - ./util-tests - - cd .. - - cucumber -p verify + - sudo make install + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + sudo ldconfig + fi + - echo "travis_fold:start:UNIT_TESTS" + - ./unit_tests/extractor-tests + - ./unit_tests/engine-tests + - ./unit_tests/util-tests + - ./unit_tests/server-tests + - echo "travis_fold:end:UNIT_TESTS" + - popd + - echo "travis_fold:start:CUCUMBER" + - npm test + - echo "travis_fold:end:CUCUMBER" + - echo "travis_fold:start:BENCHMARK" + - make -C test/data benchmark + - echo "travis_fold:end:BENCHMARK" + - ./build/unit_tests/library-tests test/data/monaco.osrm + - mkdir example/build && pushd example/build + - cmake .. + - make + - ./osrm-example ../../test/data/monaco.osrm + - popd + +after_success: + - | + if [ -n "$RUN_CLANG_FORMAT" ]; then + ./scripts/format.sh || true # we don't want to fail just yet + fi + - coveralls --build-root build --exclude unit_tests --exclude third_party --exclude node_modules --gcov-options '\-lp' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b324d1575 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,88 @@ +# 5.0.0 + Changes with regard 5.0.0 RC2: + - API: + - if `geometry=geojson` is passed the resulting geometry can be a LineString or Point + depending on how many coordinates are present. + - the removal of the summary field was revered. for `steps=flase` the field will always be an empty string. + + Changes with regard to 4.9.1: + - API: + - BREAKING: Complete rewrite of the HTTP and library API. See detailed documentation in the wiki. + - BREAKING: The default coordinate order is now `longitude, latidue`. Exception: Polyline geometry + which follow the original Google specification of `latitdue, longitude`. + - BREAKING: Polyline geometries now use precision 5, instead of previously 6 + - BREAKING: Removed GPX support + - New service `tile` which serves debug vector tiles of the road network + - Completely new engine for guidance generation: + - Support for highway ramps + - Support for different intersection types (end of street, forks, merges) + - Instruction post-processing to merge unimportant instructions + - Improved handling of roundabouts + + - Tools: + - BREAKING: Renamed osrm-prepare to osrm-contract + - BREAKING: Removes profiles from osrm-contract, only needed in osrm-extract. + - Abort processing in osrm-extract if there are no snappable edges remaining. + - Added .properties file to osrm-extract ouput. + - Enables the use of multiple segment-speed-files on the osrm-contract command line + + - Profile changes: + - Remove movable bridge mode + - Add `maxspeed=none` tag to car profile. + - A `side_road` tag support for the OSRM car profile. + + - Fixes: + - Issue #2150: Prevents routing over delivery ways and nodes + - Issue #1972: Provide uninstall target + - Issue #2072: Disable alternatives by default and if core factor < 1.0 + - Issue #1999: Fix unpacking for self-loop nodes not in core. + + - Infrastructure: + - Cucumber test suit is now based on cucumber-js, removes Ruby as dependency + - Updated to mapbox/variant v1.1 + - Updated to libosmium v2.6.1 + - Remove GeoJSON based debugging output, replaced by debug tiles + + +# 5.0.0 RC2 + - Profiles: + - `properties.allow_uturns_at_via` -> `properties.continue_straight_at_waypoint` (value is inverted!) + - API: + - Removed summary from legs property + - Disable steps and alternatives by default + - Fix `code` field: 'ok' -> 'Ok' + - Allow 4.json and 4.3.json format + - Conform to v5 spec and support "unlimited" as radiuses value. + - `uturns` parameter was replaced by `continue_straight` (value is inverted!) + - Features: + - Report progress for gennerating edge expanded edges in the edge based graph factory + - Add maxspeed=none tag to car profile. + - Optimize StaticRTree code: speedup 2x (to RC1) + - Optimize DouglasPeucker code: speedup 10x (to RC1) + - Optimize WebMercator projection: speedup 2x (to RC1) + - Bugs: + - #2195: Resolves issues with multiple includedirs in pkg-config file + - #2219: Internal server error when using the match plugin + - #2027: basename -> filename + - #2168: Report correct position where parsing failed + - #2036: Add license to storage and storage config exposed in public API + - Fix uturn detection in match plugin + - Add missing -lz to fix linking of server-tests + +# 5.0.0 RC1 + - Renamed osrm-prepare into osrm-contract + - osrm-contract does not need a profile parameter anymore + - New public HTTP API, find documentation [here](https://github.com/Project-OSRM/osrm-backend/wiki/New-Server-api) + - POST support is discontinued, please use library bindings for more complex requests + - Removed timestamp plugin + - Coordinate order is now Longitude,Latitude + - Cucumber tests now based on Javascript (run with `npm test`) + - Profile API changed: + - `forward_mode` and `backward_mode` now need to be selected from a pre-defined list + - Global profile properties are now stored in a global `properties` element. This includes: + - `properties.traffic_signal_penalty` + - `properties.use_turn_restrictions` + - `properties.u_turn_penalty` + - `properties.allow_u_turn_at_via` + + diff --git a/CMakeLists.txt b/CMakeLists.txt index b8fecc577..dd3d188b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. P endif() project(OSRM C CXX) -set(OSRM_VERSION_MAJOR 4) -set(OSRM_VERSION_MINOR 9) +set(OSRM_VERSION_MAJOR 5) +set(OSRM_VERSION_MINOR 0) set(OSRM_VERSION_PATCH 0) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -29,15 +29,17 @@ if(WIN32 AND MSVC_VERSION LESS 1800) message(FATAL_ERROR "Building with Microsoft compiler needs Visual Studio 2013 or later (Express version works too)") endif() +option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON) option(ENABLE_JSON_LOGGING "Adds additional JSON debug logging to the response" OFF) -option(DEBUG_GEOMETRY "Enables an option to dump GeoJSON of the final routing graph" OFF) option(BUILD_TOOLS "Build OSRM tools" OFF) +option(BUILD_COMPONENTS "Build osrm-components" ON) +option(ENABLE_ASSERTIONS OFF) +option(COVERAGE OFF) +option(SANITIZER OFF) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/) +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/) +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/) include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/) -include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include/) add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND} "-DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}" @@ -46,74 +48,37 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND} COMMENT "Configuring revision fingerprint" VERBATIM) -add_custom_target(tests DEPENDS datastructure-tests algorithm-tests util-tests) -add_custom_target(benchmarks DEPENDS rtree-bench) - -set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework) +set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread) configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/util/version.hpp.in - ${CMAKE_CURRENT_BINARY_DIR}/util/version.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/util/version.hpp.in + ${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp ) -file(GLOB ExtractorGlob extractor/*.cpp data_structures/hilbert_value.cpp) -file(GLOB ImporterGlob data_structures/import_edge.cpp data_structures/external_memory_node.cpp data_structures/raster_source.cpp) -add_library(IMPORT OBJECT ${ImporterGlob}) -add_library(LOGGER OBJECT util/simple_logger.cpp) -add_library(PHANTOMNODE OBJECT data_structures/phantom_node.cpp) -add_library(RASTERSOURCE OBJECT data_structures/raster_source.cpp) -add_library(EXCEPTION OBJECT util/osrm_exception.cpp) -add_library(MERCATOR OBJECT util/mercator.cpp) -add_library(ANGLE OBJECT util/compute_angle.cpp) +file(GLOB UtilGlob src/util/*.cpp) +file(GLOB ExtractorGlob src/extractor/*.cpp src/extractor/*/*.cpp) +file(GLOB ContractorGlob src/contractor/*.cpp) +file(GLOB StorageGlob src/storage/*.cpp) +file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp) +file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp) -set(ExtractorSources extract.cpp ${ExtractorGlob}) -add_executable(osrm-extract ${ExtractorSources} $ $ $ $ $ $ $ $ $ $) +add_library(UTIL OBJECT ${UtilGlob}) +add_library(EXTRACTOR OBJECT ${ExtractorGlob}) +add_library(CONTRACTOR OBJECT ${ContractorGlob}) +add_library(STORAGE OBJECT ${StorageGlob}) +add_library(ENGINE OBJECT ${EngineGlob}) +add_library(SERVER OBJECT ${ServerGlob}) -add_library(RESTRICTION OBJECT data_structures/restriction_map.cpp) -add_library(COMPRESSEDEDGE OBJECT data_structures/compressed_edge_container.cpp) -add_library(GRAPHCOMPRESSOR OBJECT algorithms/graph_compressor.cpp) +add_dependencies(UTIL FingerPrintConfigure) +set_target_properties(UTIL PROPERTIES LINKER_LANGUAGE CXX) -file(GLOB PrepareGlob contractor/*.cpp data_structures/hilbert_value.cpp {RestrictionMapGlob}) -set(PrepareSources prepare.cpp ${PrepareGlob}) -add_executable(osrm-prepare ${PrepareSources} $ $ $ $ $ $ $ $ $ $) - -file(GLOB ServerGlob server/*.cpp) -file(GLOB DescriptorGlob descriptors/*.cpp) -file(GLOB DatastructureGlob data_structures/search_engine_data.cpp data_structures/route_parameters.cpp util/bearing.cpp) -file(GLOB CoordinateGlob data_structures/coordinate.cpp algorithms/coordinate_calculation.cpp) -file(GLOB AlgorithmGlob algorithms/polyline_compressor.cpp algorithms/polyline_formatter.cpp algorithms/douglas_peucker.cpp) -file(GLOB HttpGlob server/http/*.cpp) -file(GLOB LibOSRMGlob library/*.cpp) -file(GLOB DataStructureTestsGlob unit_tests/data_structures/*.cpp data_structures/hilbert_value.cpp) -file(GLOB AlgorithmTestsGlob unit_tests/algorithms/*.cpp algorithms/graph_compressor.cpp) -file(GLOB UtilTestsGlob unit_tests/util/*.cpp) - -set( - OSRMSources - ${LibOSRMGlob} - ${DescriptorGlob} - ${DatastructureGlob} - ${AlgorithmGlob} - ${HttpGlob} -) - -add_library(COORDINATE OBJECT ${CoordinateGlob}) -add_library(OSRM ${OSRMSources} $ $ $ $ $ $ $ $ $) - -add_library(FINGERPRINT OBJECT util/fingerprint.cpp) -add_dependencies(FINGERPRINT FingerPrintConfigure) -add_dependencies(OSRM FingerPrintConfigure) -set_target_properties(FINGERPRINT PROPERTIES LINKER_LANGUAGE CXX) - -add_executable(osrm-routed routed.cpp ${ServerGlob} $) -add_executable(osrm-datastore datastore.cpp $ $ $ $ $) - -# Unit tests -add_executable(datastructure-tests EXCLUDE_FROM_ALL unit_tests/datastructure_tests.cpp ${DataStructureTestsGlob} $ $ $ $ $ $ $ $ $) -add_executable(algorithm-tests EXCLUDE_FROM_ALL unit_tests/algorithm_tests.cpp ${AlgorithmTestsGlob} $ $ $ $ $ $) -add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob}) - -# Benchmarks -add_executable(rtree-bench EXCLUDE_FROM_ALL benchmarks/static_rtree.cpp $ $ $ $ $) +add_executable(osrm-extract src/tools/extract.cpp) +add_executable(osrm-contract src/tools/contract.cpp) +add_executable(osrm-routed src/tools/routed.cpp $ $) +add_executable(osrm-datastore src/tools/store.cpp $) +add_library(osrm src/osrm/osrm.cpp $ $ $) +add_library(osrm_extract $ $) +add_library(osrm_contract $ $) +add_library(osrm_store $ $) # Check the release mode if(NOT CMAKE_BUILD_TYPE MATCHES Debug) @@ -121,6 +86,7 @@ if(NOT CMAKE_BUILD_TYPE MATCHES Debug) endif() if(CMAKE_BUILD_TYPE MATCHES Debug) message(STATUS "Configuring OSRM in debug mode") + set(ENABLE_ASSERTIONS ON) if(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline -fno-omit-frame-pointer") @@ -169,13 +135,24 @@ if(CMAKE_BUILD_TYPE MATCHES Release) endif() endif() -if(NOT WIN32) - add_definitions(-DBOOST_TEST_DYN_LINK) +set(MAYBE_COVERAGE_LIBRARIES "") +if (COVERAGE) + if (NOT CMAKE_BUILD_TYPE MATCHES "Debug") + message(ERROR "COVERAGE=ON only make sense with a Debug build") + endif() + set(MAYBE_COVERAGE_LIBRARIES "gcov") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs") +endif() +if (SANITIZER) + if (NOT CMAKE_BUILD_TYPE MATCHES "Debug") + message(ERROR "SANITIZER=ON only make sense with a Debug build") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") endif() # Configuring compilers if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=2 -D_FORTIFY_SOURCE=2 -fPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=2 -D_FORTIFY_SOURCE=2 -fPIC -fcolor-diagnostics") elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") set(COLOR_FLAG "-fdiagnostics-color=auto") check_cxx_compiler_flag("-fdiagnostics-color=auto" HAS_COLOR_FLAG) @@ -185,7 +162,6 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") # using GCC set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=1 -D_FORTIFY_SOURCE=2 ${COLOR_FLAG} -fPIC") if(WIN32) # using mingw - add_definitions(-D_USE_MATH_DEFINES) # define M_PI, M_1_PI etc. add_definitions(-DWIN32) set(OPTIONAL_SOCKET_LIBS ws2_32 wsock32) endif() @@ -195,9 +171,10 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel") elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") # using Visual Studio C++ set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time chrono zlib) + add_definitions(-DBOOST_LIB_DIAGNOSTIC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-DNOMINMAX) # avoid min and max macros that can break compilation - add_definitions(-D_USE_MATH_DEFINES) # define M_PI + add_definitions(-D_USE_MATH_DEFINES) #needed for M_PI with cmath.h add_definitions(-D_WIN32_WINNT=0x0501) add_definitions(-DXML_STATIC) find_library(ws2_32_LIBRARY_PATH ws2_32) @@ -245,76 +222,47 @@ if(APPLE) endif() if(UNIX AND NOT APPLE) - target_link_libraries(osrm-prepare rt) - target_link_libraries(osrm-datastore rt) - target_link_libraries(OSRM rt) + set(MAYBE_RT_LIBRARY rt) endif() -#Check Boost -find_package(Boost 1.49.0 COMPONENTS ${BOOST_COMPONENTS} REQUIRED) -if(NOT Boost_FOUND) - message(FATAL_ERROR "Fatal error: Boost (version >= 1.49.0) required.\n") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/cmake") +set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include") +find_package(Osmium REQUIRED COMPONENTS io) +include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) + + +find_package(Boost 1.49.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +if(NOT WIN32) + add_definitions(-DBOOST_TEST_DYN_LINK) endif() +add_definitions(-DBOOST_SPIRIT_USE_PHOENIX_V3) +add_definitions(-DBOOST_RESULT_OF_USE_DECLTYPE) +add_definitions(-DBOOST_FILESYSTEM_NO_DEPRECATED) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) -target_link_libraries(OSRM ${Boost_LIBRARIES}) -target_link_libraries(osrm-extract ${Boost_LIBRARIES}) -target_link_libraries(osrm-prepare ${Boost_LIBRARIES}) -target_link_libraries(osrm-routed ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} OSRM) -target_link_libraries(osrm-datastore ${Boost_LIBRARIES}) -target_link_libraries(datastructure-tests ${Boost_LIBRARIES}) -target_link_libraries(algorithm-tests ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} OSRM) -target_link_libraries(util-tests ${Boost_LIBRARIES}) -target_link_libraries(rtree-bench ${Boost_LIBRARIES}) - find_package(Threads REQUIRED) -target_link_libraries(osrm-extract ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(osrm-datastore ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(osrm-prepare ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(OSRM ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(datastructure-tests ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(algorithm-tests ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(rtree-bench ${CMAKE_THREAD_LIBS_INIT}) find_package(TBB REQUIRED) +include_directories(SYSTEM ${TBB_INCLUDE_DIR}) if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Debug) set(TBB_LIBRARIES ${TBB_DEBUG_LIBRARIES}) endif() -target_link_libraries(osrm-datastore ${TBB_LIBRARIES}) -target_link_libraries(osrm-extract ${TBB_LIBRARIES}) -target_link_libraries(osrm-prepare ${TBB_LIBRARIES}) -target_link_libraries(osrm-routed ${TBB_LIBRARIES}) -target_link_libraries(datastructure-tests ${TBB_LIBRARIES}) -target_link_libraries(algorithm-tests ${TBB_LIBRARIES}) -target_link_libraries(rtree-bench ${TBB_LIBRARIES}) -include_directories(SYSTEM ${TBB_INCLUDE_DIR}) -find_package( Luabind REQUIRED ) +find_package(Luabind REQUIRED) include(check_luabind) - include_directories(SYSTEM ${LUABIND_INCLUDE_DIR}) -target_link_libraries(osrm-extract ${LUABIND_LIBRARY}) -target_link_libraries(osrm-prepare ${LUABIND_LIBRARY}) +set(USED_LUA_LIBRARIES ${LUA_LIBRARY}) if(LUAJIT_FOUND) - target_link_libraries(osrm-extract ${LUAJIT_LIBRARIES}) - target_link_libraries(osrm-prepare ${LUAJIT_LIBRARIES}) -else() - target_link_libraries(osrm-extract ${LUA_LIBRARY}) - target_link_libraries(osrm-prepare ${LUA_LIBRARY}) + set(USED_LUA_LIBRARIES, LUAJIT_LIBRARIES) endif() include_directories(SYSTEM ${LUA_INCLUDE_DIR}) find_package(EXPAT REQUIRED) include_directories(SYSTEM ${EXPAT_INCLUDE_DIRS}) -target_link_libraries(osrm-extract ${EXPAT_LIBRARIES}) find_package(STXXL REQUIRED) include_directories(SYSTEM ${STXXL_INCLUDE_DIR}) -target_link_libraries(OSRM ${STXXL_LIBRARY}) -target_link_libraries(osrm-extract ${STXXL_LIBRARY}) -target_link_libraries(osrm-prepare ${STXXL_LIBRARY}) -target_link_libraries(datastructure-tests ${STXXL_LIBRARY}) set(OpenMP_FIND_QUIETLY ON) find_package(OpenMP) @@ -325,91 +273,142 @@ endif() find_package(BZip2 REQUIRED) include_directories(SYSTEM ${BZIP_INCLUDE_DIRS}) -target_link_libraries(osrm-extract ${BZIP2_LIBRARIES}) find_package(ZLIB REQUIRED) include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS}) -target_link_libraries(osrm-extract ${ZLIB_LIBRARY}) -target_link_libraries(osrm-routed ${ZLIB_LIBRARY}) if (ENABLE_JSON_LOGGING) message(STATUS "Enabling json logging") add_definitions(-DENABLE_JSON_LOGGING) endif() -if (DEBUG_GEOMETRY) - message(STATUS "Enabling final edge weight GeoJSON output option") - add_definitions(-DDEBUG_GEOMETRY) -endif() +# Binaries +target_link_libraries(osrm-datastore osrm_store ${Boost_LIBRARIES}) +target_link_libraries(osrm-extract osrm_extract ${Boost_LIBRARIES}) +target_link_libraries(osrm-contract osrm_contract ${Boost_LIBRARIES}) +target_link_libraries(osrm-routed osrm ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} ${ZLIB_LIBRARY}) -if(BUILD_TOOLS) - message(STATUS "Activating OSRM internal tools") +set(EXTRACTOR_LIBRARIES + ${BZIP2_LIBRARIES} + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${EXPAT_LIBRARIES} + ${LUABIND_LIBRARY} + ${USED_LUA_LIBRARIES} + ${OSMIUM_LIBRARIES} + ${STXXL_LIBRARY} + ${TBB_LIBRARIES} + ${ZLIB_LIBRARY} + ${MAYBE_COVERAGE_LIBRARIES}) +set(CONTRACTOR_LIBRARIES + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${LUABIND_LIBRARY} + ${USED_LUA_LIBRARIES} + ${STXXL_LIBRARY} + ${TBB_LIBRARIES} + ${MAYBE_RT_LIBRARY} + ${MAYBE_COVERAGE_LIBRARIES}) +set(ENGINE_LIBRARIES + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${STXXL_LIBRARY} + ${TBB_LIBRARIES} + ${MAYBE_RT_LIBRARY} + ${MAYBE_COVERAGE_LIBRARIES}) +set(STORAGE_LIBRARIES + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${TBB_LIBRARIES} + ${MAYBE_RT_LIBRARY} + ${MAYBE_COVERAGE_LIBRARIES}) +set(UTIL_LIBRARIES + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${STXXL_LIBRARY} + ${TBB_LIBRARIES} + ${MAYBE_COVERAGE_LIBRARIES}) +# Libraries +target_link_libraries(osrm ${ENGINE_LIBRARIES}) +target_link_libraries(osrm_contract ${CONTRACTOR_LIBRARIES}) +target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES}) +target_link_libraries(osrm_store ${STORAGE_LIBRARIES}) + +if(BUILD_COMPONENTS) find_package(GDAL) if(GDAL_FOUND) - add_executable(osrm-components tools/components.cpp $ $ $ $ $ $ $) + add_executable(osrm-components src/tools/components.cpp $) target_link_libraries(osrm-components ${TBB_LIBRARIES}) include_directories(SYSTEM ${GDAL_INCLUDE_DIR}) target_link_libraries(osrm-components ${GDAL_LIBRARIES} ${Boost_LIBRARIES}) install(TARGETS osrm-components DESTINATION bin) else() - message(FATAL_ERROR "libgdal and/or development headers not found") + message(WARNING "libgdal and/or development headers not found") endif() - add_executable(osrm-cli tools/simpleclient.cpp $ $ $) - target_link_libraries(osrm-cli ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} OSRM) - target_link_libraries(osrm-cli ${TBB_LIBRARIES}) - add_executable(osrm-io-benchmark tools/io-benchmark.cpp $ $) +endif() + +if(BUILD_TOOLS) + message(STATUS "Activating OSRM internal tools") + add_executable(osrm-io-benchmark src/tools/io-benchmark.cpp $) target_link_libraries(osrm-io-benchmark ${Boost_LIBRARIES}) - add_executable(osrm-unlock-all tools/unlock_all_mutexes.cpp $ $) + add_executable(osrm-unlock-all src/tools/unlock_all_mutexes.cpp $) target_link_libraries(osrm-unlock-all ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) if(UNIX AND NOT APPLE) target_link_libraries(osrm-unlock-all rt) endif() - add_executable(osrm-check-hsgr tools/check-hsgr.cpp $ $ $ $) - target_link_libraries(osrm-check-hsgr ${Boost_LIBRARIES} ${TBB_LIBRARIES}) - add_executable(osrm-springclean tools/springclean.cpp $ $ $) + add_executable(osrm-springclean src/tools/springclean.cpp $) target_link_libraries(osrm-springclean ${Boost_LIBRARIES}) - install(TARGETS osrm-cli DESTINATION bin) install(TARGETS osrm-io-benchmark DESTINATION bin) install(TARGETS osrm-unlock-all DESTINATION bin) - install(TARGETS osrm-check-hsgr DESTINATION bin) install(TARGETS osrm-springclean DESTINATION bin) endif() -file(GLOB InstallGlob include/osrm/*.hpp) -file(GLOB VariantGlob third_party/variant/*.hpp) +if (ENABLE_ASSERTIONS) + message(STATUS "Enabling assertions") + add_definitions(-DBOOST_ENABLE_ASSERT_HANDLER) +endif() # Add RPATH info to executables so that when they are run after being installed # (i.e., from /usr/local/bin/) the linker can find library dependencies. For # more info see http://www.cmake.org/Wiki/CMake_RPATH_handling set_property(TARGET osrm-extract PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) -set_property(TARGET osrm-prepare PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) +set_property(TARGET osrm-contract PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) set_property(TARGET osrm-datastore PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) set_property(TARGET osrm-routed PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) -install(FILES ${InstallGlob} DESTINATION include/osrm) +file(GLOB VariantGlob third_party/variant/*.hpp) +file(GLOB LibraryGlob include/osrm/*.hpp) +file(GLOB ParametersGlob include/engine/api/*_parameters.hpp) +set(EngineHeader include/engine/status.hpp include/engine/engine_config.hpp include/engine/hint.hpp include/engine/bearing.hpp include/engine/phantom_node.hpp) +set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp include/util/typedefs.hpp include/util/strong_typedef.hpp) +set(ExtractorHeader include/extractor/extractor.hpp include/extractor/extractor_config.hpp include/extractor/travel_mode.hpp) +set(ContractorHeader include/contractor/contractor.hpp include/contractor/contractor_config.hpp) +set(StorageHeader include/storage/storage.hpp include/storage/storage_config.hpp) +install(FILES ${EngineHeader} DESTINATION include/osrm/engine) +install(FILES ${UtilHeader} DESTINATION include/osrm/util) +install(FILES ${StorageHeader} DESTINATION include/osrm/storage) +install(FILES ${ExtractorHeader} DESTINATION include/osrm/extractor) +install(FILES ${ContractorHeader} DESTINATION include/osrm/contractor) +install(FILES ${LibraryGlob} DESTINATION include/osrm) +install(FILES ${ParametersGlob} DESTINATION include/osrm/engine/api) install(FILES ${VariantGlob} DESTINATION include/variant) install(TARGETS osrm-extract DESTINATION bin) -install(TARGETS osrm-prepare DESTINATION bin) +install(TARGETS osrm-contract DESTINATION bin) install(TARGETS osrm-datastore DESTINATION bin) install(TARGETS osrm-routed DESTINATION bin) -install(TARGETS OSRM DESTINATION lib) +install(TARGETS osrm DESTINATION lib) +install(TARGETS osrm_extract DESTINATION lib) +install(TARGETS osrm_contract DESTINATION lib) +install(TARGETS osrm_store DESTINATION lib) -list(GET Boost_LIBRARIES 1 BOOST_LIBRARY_FIRST) -get_filename_component(BOOST_LIBRARY_LISTING "${BOOST_LIBRARY_FIRST}" PATH) -set(BOOST_LIBRARY_LISTING "-L${BOOST_LIBRARY_LISTING}") -foreach(lib ${Boost_LIBRARIES}) - get_filename_component(BOOST_LIBRARY_NAME "${lib}" NAME_WE) - string(REPLACE "lib" "" BOOST_LIBRARY_NAME ${BOOST_LIBRARY_NAME}) - set(BOOST_LIBRARY_LISTING "${BOOST_LIBRARY_LISTING} -l${BOOST_LIBRARY_NAME}") -endforeach() -list(GET TBB_LIBRARIES 1 TBB_LIBRARY_FIRST) -get_filename_component(TBB_LIBRARY_LISTING "${TBB_LIBRARY_FIRST}" PATH) -set(TBB_LIBRARY_LISTING "-L${TBB_LIBRARY_LISTING}") -foreach(lib ${TBB_LIBRARIES}) - get_filename_component(TBB_LIBRARY_NAME "${lib}" NAME_WE) - string(REPLACE "lib" "" TBB_LIBRARY_NAME ${TBB_LIBRARY_NAME}) - set(TBB_LIBRARY_LISTING "${TBB_LIBRARY_LISTING} -l${TBB_LIBRARY_NAME}") +list(GET ENGINE_LIBRARIES 1 ENGINE_LIBRARY_FIRST) +foreach(lib ${ENGINE_LIBRARIES}) + get_filename_component(ENGINE_LIBRARY_PATH "${ENGINE_LIBRARY_FIRST}" PATH) + get_filename_component(ENGINE_LIBRARY_NAME "${lib}" NAME_WE) + string(REPLACE "lib" "" ENGINE_LIBRARY_NAME ${ENGINE_LIBRARY_NAME}) + string(REPLACE "-l" "" ENGINE_LIBRARY_NAME ${ENGINE_LIBRARY_NAME}) + set(ENGINE_LIBRARY_LISTING "${ENGINE_LIBRARY_LISTING} -L${ENGINE_LIBRARY_PATH} -l${ENGINE_LIBRARY_NAME}") endforeach() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkgconfig.in libosrm.pc @ONLY) @@ -432,11 +431,26 @@ COMMENT "Generating API documentation with Doxygen" VERBATIM endif() # prefix compilation with ccache by default if available and on clang or gcc -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") +if(ENABLE_CCACHE AND (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) + message(STATUS "Using ccache to speed up incremental builds") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) set(ENV{CCACHE_CPP2} "true") endif() endif() + +# uninstall target +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake) + + +# Modular build system: each directory registered here provides its own CMakeLists.txt +add_subdirectory(unit_tests) +add_subdirectory(src/benchmarks) diff --git a/Doxyfile.in b/Doxyfile.in index 5842f6bd2..dd51b16d7 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -18,6 +18,7 @@ RECURSIVE = YES EXCLUDE = @CMAKE_CURRENT_SOURCE_DIR@/third_party \ @CMAKE_CURRENT_SOURCE_DIR@/build \ + @CMAKE_CURRENT_SOURCE_DIR@/node_modules \ @CMAKE_CURRENT_SOURCE_DIR@/unit_tests \ @CMAKE_CURRENT_SOURCE_DIR@/benchmarks \ @CMAKE_CURRENT_SOURCE_DIR@/features diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 31d044bd6..000000000 --- a/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source "http://rubygems.org" - -gem "cucumber" -gem "rake" -gem "osmlib-base" -gem "sys-proctable" -gem "rspec-expectations" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 3363e9218..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,35 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - builder (3.2.2) - cucumber (2.0.0) - builder (>= 2.1.2) - cucumber-core (~> 1.1.3) - diff-lcs (>= 1.1.3) - gherkin (~> 2.12) - multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.1.2) - cucumber-core (1.1.3) - gherkin (~> 2.12.0) - diff-lcs (1.2.5) - gherkin (2.12.2) - multi_json (~> 1.3) - multi_json (1.11.0) - multi_test (0.1.2) - osmlib-base (0.1.4) - rake (10.4.2) - rspec-expectations (3.2.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.2.0) - rspec-support (3.2.2) - sys-proctable (0.9.8) - -PLATFORMS - ruby - -DEPENDENCIES - cucumber - osmlib-base - rake - rspec-expectations - sys-proctable diff --git a/LICENCE.TXT b/LICENCE.TXT index c77e5cd1b..143580ec6 100644 --- a/LICENCE.TXT +++ b/LICENCE.TXT @@ -1,4 +1,4 @@ -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/README.md b/README.md index d319d26bc..084cfba24 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ The Open Source Routing Machine is a high performance routing engine written in | Linux | develop | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=develop)](https://travis-ci.org/Project-OSRM/osrm-backend) | | Windows | master/develop | [![Build status](https://ci.appveyor.com/api/projects/status/4iuo3s9gxprmcjjh)](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) | | LUAbind fork | master | [![Build Status](https://travis-ci.org/DennisOSRM/luabind.png?branch=master)](https://travis-ci.org/DennisOSRM/luabind) | +| Coverage | develop | [![Coverage Status](https://coveralls.io/repos/github/Project-OSRM/osrm-backend/badge.svg?branch=develop)](https://coveralls.io/github/Project-OSRM/osrm-backend?branch=develop) | ## Building diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 1c848bb07..000000000 --- a/Rakefile +++ /dev/null @@ -1,190 +0,0 @@ -require 'OSM/StreamParser' -require 'socket' -require 'digest/sha1' -require 'cucumber/rake/task' -require 'sys/proctable' - -BUILD_FOLDER = 'build' -DATA_FOLDER = 'sandbox' -PROFILE = 'bicycle' -OSRM_PORT = 5000 -PROFILES_FOLDER = '../profiles' - -Cucumber::Rake::Task.new do |t| - t.cucumber_opts = %w{--format pretty} -end - -areas = { - :kbh => { :country => 'denmark', :bbox => 'top=55.6972 left=12.5222 right=12.624 bottom=55.6376' }, - :frd => { :country => 'denmark', :bbox => 'top=55.7007 left=12.4765 bottom=55.6576 right=12.5698' }, - :regh => { :country => 'denmark', :bbox => 'top=56.164 left=11.792 bottom=55.403 right=12.731' }, - :denmark => { :country => 'denmark', :bbox => nil }, - :skaane => { :country => 'sweden', :bbox => 'top=56.55 left=12.4 bottom=55.3 right=14.6' } -} - - - -osm_data_area_name = ARGV[1] ? ARGV[1].to_s.to_sym : :kbh -raise "Unknown data area." unless areas[osm_data_area_name] -osm_data_country = areas[osm_data_area_name][:country] -osm_data_area_bbox = areas[osm_data_area_name][:bbox] - - -task osm_data_area_name.to_sym {} #define empty task to prevent rake from whining. will break if area has same name as a task - - -def each_process name, &block - Sys::ProcTable.ps do |process| - if process.comm.strip == name.strip && process.state != 'zombie' - yield process.pid.to_i, process.state.strip - end - end -end - -def up? - find_pid('osrm-routed') != nil -end - -def find_pid name - each_process(name) { |pid,state| return pid.to_i } - return nil -end - -def wait_for_shutdown name - timeout = 10 - (timeout*10).times do - return if find_pid(name) == nil - sleep 0.1 - end - raise "*** Could not terminate #{name}." -end - - -desc "Rebuild and run tests." -task :default => [:build] - -desc "Build using CMake." -task :build do - if Dir.exists? BUILD_FOLDER - Dir.chdir BUILD_FOLDER do - system "make" - end - else - system "mkdir build; cd build; cmake ..; make" - end -end - -desc "Setup config files." -task :setup do -end - -desc "Download OSM data." -task :download do - Dir.mkdir "#{DATA_FOLDER}" unless File.exist? "#{DATA_FOLDER}" - puts "Downloading..." - puts "curl http://download.geofabrik.de/europe/#{osm_data_country}-latest.osm.pbf -o #{DATA_FOLDER}/#{osm_data_country}.osm.pbf" - raise "Error while downloading data." unless system "curl http://download.geofabrik.de/europe/#{osm_data_country}-latest.osm.pbf -o #{DATA_FOLDER}/#{osm_data_country}.osm.pbf" - if osm_data_area_bbox - puts "Cropping and converting to protobuffer..." - raise "Error while cropping data." unless system "osmosis --read-pbf file=#{DATA_FOLDER}/#{osm_data_country}.osm.pbf --bounding-box #{osm_data_area_bbox} --write-pbf file=#{DATA_FOLDER}/#{osm_data_area_name}.osm.pbf omitmetadata=true" - end -end - -desc "Crop OSM data" -task :crop do - if osm_data_area_bbox - raise "Error while cropping data." unless system "osmosis --read-pbf file=#{DATA_FOLDER}/#{osm_data_country}.osm.pbf --bounding-box #{osm_data_area_bbox} --write-pbf file=#{DATA_FOLDER}/#{osm_data_area_name}.osm.pbf omitmetadata=true" - end -end - -desc "Reprocess OSM data." -task :process => [:extract,:prepare] do -end - -desc "Extract OSM data." -task :extract do - Dir.chdir DATA_FOLDER do - raise "Error while extracting data." unless system "../#{BUILD_FOLDER}/osrm-extract #{osm_data_area_name}.osm.pbf --profile ../profiles/#{PROFILE}.lua" - end -end - -desc "Prepare OSM data." -task :prepare do - Dir.chdir DATA_FOLDER do - raise "Error while preparing data." unless system "../#{BUILD_FOLDER}/osrm-prepare #{osm_data_area_name}.osrm --profile ../profiles/#{PROFILE}.lua" - end -end - -desc "Delete preprocessing files." -task :clean do - File.delete *Dir.glob("#{DATA_FOLDER}/*.osrm") - File.delete *Dir.glob("#{DATA_FOLDER}/*.osrm.*") -end - -desc "Run all cucumber test" -task :test do - system "cucumber" - puts -end - -desc "Run the routing server in the terminal. Press Ctrl-C to stop." -task :run do - Dir.chdir DATA_FOLDER do - system "../#{BUILD_FOLDER}/osrm-routed #{osm_data_area_name}.osrm --port #{OSRM_PORT}" - end -end - -desc "Launch the routing server in the background. Use rake:down to stop it." -task :up do - Dir.chdir DATA_FOLDER do - abort("Already up.") if up? - pipe = IO.popen("../#{BUILD_FOLDER}/osrm-routed #{osm_data_area_name}.osrm --port #{OSRM_PORT} 1>>osrm-routed.log 2>>osrm-routed.log") - timeout = 5 - (timeout*10).times do - begin - socket = TCPSocket.new('localhost', OSRM_PORT) - socket.puts 'ping' - rescue Errno::ECONNREFUSED - sleep 0.1 - end - end - end -end - -desc "Stop the routing server." -task :down do - pid = find_pid 'osrm-routed' - if pid - Process.kill 'TERM', pid - else - puts "Already down." - end -end - -desc "Kill all osrm-extract, osrm-prepare and osrm-routed processes." -task :kill do - each_process('osrm-routed') { |pid,state| Process.kill 'KILL', pid } - each_process('osrm-prepare') { |pid,state| Process.kill 'KILL', pid } - each_process('osrm-extract') { |pid,state| Process.kill 'KILL', pid } - wait_for_shutdown 'osrm-routed' - wait_for_shutdown 'osrm-prepare' - wait_for_shutdown 'osrm-extract' -end - -desc "Get PIDs of all osrm-extract, osrm-prepare and osrm-routed processes." -task :pid do - each_process 'osrm-routed' do |pid,state| - puts "#{pid}\t#{state}" - end -end - -desc "Stop, reprocess and restart." -task :update => [:down,:process,:up] do -end - - -desc "Remove test cache files." -task :sweep do - system "rm test/cache/*" -end - diff --git a/algorithms/bfs_components.hpp b/algorithms/bfs_components.hpp deleted file mode 100644 index b3f536401..000000000 --- a/algorithms/bfs_components.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef BFS_COMPONENTS_HPP_ -#define BFS_COMPONENTS_HPP_ - -#include "../typedefs.h" -#include "../data_structures/restriction_map.hpp" - -#include -#include - -// Explores the components of the given graph while respecting turn restrictions -// and barriers. -template class BFSComponentExplorer -{ - public: - BFSComponentExplorer(const GraphT &dynamic_graph, - const RestrictionMap &restrictions, - const std::unordered_set &barrier_nodes) - : m_graph(dynamic_graph), m_restriction_map(restrictions), m_barrier_nodes(barrier_nodes) - { - BOOST_ASSERT(m_graph.GetNumberOfNodes() > 0); - } - - /*! - * Returns the size of the component that the node belongs to. - */ - unsigned int GetComponentSize(const NodeID node) const - { - BOOST_ASSERT(node < m_component_index_list.size()); - - return m_component_index_size[m_component_index_list[node]]; - } - - unsigned int GetNumberOfComponents() { return m_component_index_size.size(); } - - /*! - * Computes the component sizes. - */ - void run() - { - std::queue> bfs_queue; - unsigned current_component = 0; - - BOOST_ASSERT(m_component_index_list.empty()); - BOOST_ASSERT(m_component_index_size.empty()); - - unsigned num_nodes = m_graph.GetNumberOfNodes(); - - m_component_index_list.resize(num_nodes, std::numeric_limits::max()); - - BOOST_ASSERT(num_nodes > 0); - - // put unexplorered node with parent pointer into queue - for (NodeID node = 0; node < num_nodes; ++node) - { - if (std::numeric_limits::max() == m_component_index_list[node]) - { - unsigned size = ExploreComponent(bfs_queue, node, current_component); - - // push size into vector - m_component_index_size.emplace_back(size); - ++current_component; - } - } - } - - private: - /*! - * Explores the current component that starts at node using BFS. - */ - unsigned ExploreComponent(std::queue> &bfs_queue, - NodeID node, - unsigned current_component) - { - /* - Graphical representation of variables: - - u v w - *---------->*---------->* - e2 - */ - - bfs_queue.emplace(node, node); - // mark node as read - m_component_index_list[node] = current_component; - - unsigned current_component_size = 1; - - while (!bfs_queue.empty()) - { - // fetch element from BFS queue - std::pair current_queue_item = bfs_queue.front(); - bfs_queue.pop(); - - const NodeID v = current_queue_item.first; // current node - const NodeID u = current_queue_item.second; // parent - // increment size counter of current component - ++current_component_size; - if (m_barrier_nodes.find(v) != m_barrier_nodes.end()) - { - continue; - } - const NodeID to_node_of_only_restriction = - m_restriction_map.CheckForEmanatingIsOnlyTurn(u, v); - - for (auto e2 : m_graph.GetAdjacentEdgeRange(v)) - { - const NodeID w = m_graph.GetTarget(e2); - - if (to_node_of_only_restriction != std::numeric_limits::max() && - w != to_node_of_only_restriction) - { - // At an only_-restriction but not at the right turn - continue; - } - - if (u != w) - { - // only add an edge if turn is not a U-turn except - // when it is at the end of a dead-end street. - if (!m_restriction_map.CheckIfTurnIsRestricted(u, v, w)) - { - // only add an edge if turn is not prohibited - if (std::numeric_limits::max() == m_component_index_list[w]) - { - // insert next (node, parent) only if w has - // not yet been explored - // mark node as read - m_component_index_list[w] = current_component; - bfs_queue.emplace(w, v); - } - } - } - } - } - - return current_component_size; - } - - std::vector m_component_index_list; - std::vector m_component_index_size; - - const GraphT &m_graph; - const RestrictionMap &m_restriction_map; - const std::unordered_set &m_barrier_nodes; -}; - -#endif // BFS_COMPONENTS_HPP_ diff --git a/algorithms/coordinate_calculation.cpp b/algorithms/coordinate_calculation.cpp deleted file mode 100644 index ff7626e90..000000000 --- a/algorithms/coordinate_calculation.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "coordinate_calculation.hpp" - -#include "../util/mercator.hpp" -#include "../util/string_util.hpp" - -#include - -#include - -#include - -#include - -namespace -{ -constexpr static const float RAD = 0.017453292519943295769236907684886f; -// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) -// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) -constexpr static const float earth_radius = 6372797.560856f; -} - -namespace coordinate_calculation -{ - -double haversine_distance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - const double lt1 = lat1 / COORDINATE_PRECISION; - const double ln1 = lon1 / COORDINATE_PRECISION; - const double lt2 = lat2 / COORDINATE_PRECISION; - const double ln2 = lon2 / COORDINATE_PRECISION; - const double dlat1 = lt1 * (RAD); - - const double dlong1 = ln1 * (RAD); - const double dlat2 = lt2 * (RAD); - const double dlong2 = ln2 * (RAD); - - const double dLong = dlong1 - dlong2; - const double dLat = dlat1 - dlat2; - - const double aHarv = std::pow(std::sin(dLat / 2.0), 2.0) + - std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dLong / 2.), 2); - const double cHarv = 2. * std::atan2(std::sqrt(aHarv), std::sqrt(1.0 - aHarv)); - return earth_radius * cHarv; -} - -double haversine_distance(const FixedPointCoordinate &coordinate_1, - const FixedPointCoordinate &coordinate_2) -{ - return haversine_distance(coordinate_1.lat, coordinate_1.lon, coordinate_2.lat, - coordinate_2.lon); -} - -float great_circle_distance(const FixedPointCoordinate &coordinate_1, - const FixedPointCoordinate &coordinate_2) -{ - return great_circle_distance(coordinate_1.lat, coordinate_1.lon, coordinate_2.lat, - coordinate_2.lon); -} - -float great_circle_distance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - - const float float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; - const float float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; - const float float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; - const float float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; - - const float x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.f); - const float y_value = float_lat2 - float_lat1; - return std::hypot(x_value, y_value) * earth_radius; -} - -float perpendicular_distance(const FixedPointCoordinate &source_coordinate, - const FixedPointCoordinate &target_coordinate, - const FixedPointCoordinate &query_location) -{ - float ratio; - FixedPointCoordinate nearest_location; - - return perpendicular_distance(source_coordinate, target_coordinate, query_location, - nearest_location, ratio); -} - -float perpendicular_distance(const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location, - FixedPointCoordinate &nearest_location, - float &ratio) -{ - return perpendicular_distance_from_projected_coordinate( - segment_source, segment_target, query_location, - {mercator::lat2y(query_location.lat / COORDINATE_PRECISION), - query_location.lon / COORDINATE_PRECISION}, - nearest_location, ratio); -} - -float perpendicular_distance_from_projected_coordinate( - const FixedPointCoordinate &source_coordinate, - const FixedPointCoordinate &target_coordinate, - const FixedPointCoordinate &query_location, - const std::pair &projected_coordinate) -{ - float ratio; - FixedPointCoordinate nearest_location; - - return perpendicular_distance_from_projected_coordinate(source_coordinate, target_coordinate, - query_location, projected_coordinate, - nearest_location, ratio); -} - -float perpendicular_distance_from_projected_coordinate( - const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location, - const std::pair &projected_coordinate, - FixedPointCoordinate &nearest_location, - float &ratio) -{ - BOOST_ASSERT(query_location.is_valid()); - - // initialize values - const double x = projected_coordinate.first; - const double y = projected_coordinate.second; - const double a = mercator::lat2y(segment_source.lat / COORDINATE_PRECISION); - const double b = segment_source.lon / COORDINATE_PRECISION; - const double c = mercator::lat2y(segment_target.lat / COORDINATE_PRECISION); - const double d = segment_target.lon / COORDINATE_PRECISION; - double p, q /*,mX*/, nY; - if (std::abs(a - c) > std::numeric_limits::epsilon()) - { - const double m = (d - b) / (c - a); // slope - // Projection of (x,y) on line joining (a,b) and (c,d) - p = ((x + (m * y)) + (m * m * a - m * b)) / (1.f + m * m); - q = b + m * (p - a); - } - else - { - p = c; - q = y; - } - nY = (d * p - c * q) / (a * d - b * c); - - // discretize the result to coordinate precision. it's a hack! - if (std::abs(nY) < (1.f / COORDINATE_PRECISION)) - { - nY = 0.f; - } - - // compute ratio - ratio = - static_cast((p - nY * a) / c); // These values are actually n/m+n and m/m+n , we need - // not calculate the explicit values of m an n as we - // are just interested in the ratio - if (std::isnan(ratio)) - { - ratio = (segment_target == query_location ? 1.f : 0.f); - } - else if (std::abs(ratio) <= std::numeric_limits::epsilon()) - { - ratio = 0.f; - } - else if (std::abs(ratio - 1.f) <= std::numeric_limits::epsilon()) - { - ratio = 1.f; - } - - // compute nearest location - BOOST_ASSERT(!std::isnan(ratio)); - if (ratio <= 0.f) - { - nearest_location = segment_source; - } - else if (ratio >= 1.f) - { - nearest_location = segment_target; - } - else - { - // point lies in between - nearest_location.lat = static_cast(mercator::y2lat(p) * COORDINATE_PRECISION); - nearest_location.lon = static_cast(q * COORDINATE_PRECISION); - } - BOOST_ASSERT(nearest_location.is_valid()); - - const float approximate_distance = - great_circle_distance(query_location, nearest_location); - BOOST_ASSERT(0.f <= approximate_distance); - return approximate_distance; -} - -void lat_or_lon_to_string(const int value, std::string &output) -{ - char buffer[12]; - buffer[11] = 0; // zero termination - output = printInt<11, 6>(buffer, value); -} - -float deg_to_rad(const float degree) -{ - return degree * (static_cast(M_PI) / 180.f); -} - -float rad_to_deg(const float radian) -{ - return radian * (180.f * static_cast(M_1_PI)); -} - -float bearing(const FixedPointCoordinate &first_coordinate, - const FixedPointCoordinate &second_coordinate) -{ - const float lon_diff = - second_coordinate.lon / COORDINATE_PRECISION - first_coordinate.lon / COORDINATE_PRECISION; - const float lon_delta = deg_to_rad(lon_diff); - const float lat1 = deg_to_rad(first_coordinate.lat / COORDINATE_PRECISION); - const float lat2 = deg_to_rad(second_coordinate.lat / COORDINATE_PRECISION); - const float y = std::sin(lon_delta) * std::cos(lat2); - const float x = - std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(lon_delta); - float result = rad_to_deg(std::atan2(y, x)); - while (result < 0.f) - { - result += 360.f; - } - - while (result >= 360.f) - { - result -= 360.f; - } - return result; -} - -} diff --git a/algorithms/coordinate_calculation.hpp b/algorithms/coordinate_calculation.hpp deleted file mode 100644 index 80ec7c289..000000000 --- a/algorithms/coordinate_calculation.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef COORDINATE_CALCULATION -#define COORDINATE_CALCULATION - -struct FixedPointCoordinate; - -#include -#include - -namespace coordinate_calculation -{ - double - haversine_distance(const int lat1, const int lon1, const int lat2, const int lon2); - - double haversine_distance(const FixedPointCoordinate &first_coordinate, - const FixedPointCoordinate &second_coordinate); - - float great_circle_distance(const FixedPointCoordinate &first_coordinate, - const FixedPointCoordinate &second_coordinate); - - float great_circle_distance(const int lat1, const int lon1, const int lat2, const int lon2); - - void lat_or_lon_to_string(const int value, std::string &output); - - float perpendicular_distance(const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location); - - float perpendicular_distance(const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location, - FixedPointCoordinate &nearest_location, - float &ratio); - - float perpendicular_distance_from_projected_coordinate( - const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location, - const std::pair &projected_coordinate); - - float perpendicular_distance_from_projected_coordinate( - const FixedPointCoordinate &segment_source, - const FixedPointCoordinate &segment_target, - const FixedPointCoordinate &query_location, - const std::pair &projected_coordinate, - FixedPointCoordinate &nearest_location, - float &ratio); - - float deg_to_rad(const float degree); - float rad_to_deg(const float radian); - - float bearing(const FixedPointCoordinate &first_coordinate, - const FixedPointCoordinate &second_coordinate); -} - -#endif // COORDINATE_CALCULATION diff --git a/algorithms/douglas_peucker.cpp b/algorithms/douglas_peucker.cpp deleted file mode 100644 index 280c90fa8..000000000 --- a/algorithms/douglas_peucker.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "douglas_peucker.hpp" - -#include "../data_structures/segment_information.hpp" - -#include -#include - -#include -#include -#include - -namespace -{ -struct CoordinatePairCalculator -{ - CoordinatePairCalculator() = delete; - CoordinatePairCalculator(const FixedPointCoordinate &coordinate_a, - const FixedPointCoordinate &coordinate_b) - { - // initialize distance calculator with two fixed coordinates a, b - const float RAD = 0.017453292519943295769236907684886f; - first_lat = (coordinate_a.lat / COORDINATE_PRECISION) * RAD; - first_lon = (coordinate_a.lon / COORDINATE_PRECISION) * RAD; - second_lat = (coordinate_b.lat / COORDINATE_PRECISION) * RAD; - second_lon = (coordinate_b.lon / COORDINATE_PRECISION) * RAD; - } - - int operator()(FixedPointCoordinate &other) const - { - // set third coordinate c - const float RAD = 0.017453292519943295769236907684886f; - const float earth_radius = 6372797.560856f; - const float float_lat1 = (other.lat / COORDINATE_PRECISION) * RAD; - const float float_lon1 = (other.lon / COORDINATE_PRECISION) * RAD; - - // compute distance (a,c) - const float x_value_1 = (first_lon - float_lon1) * cos((float_lat1 + first_lat) / 2.f); - const float y_value_1 = first_lat - float_lat1; - const float dist1 = std::hypot(x_value_1, y_value_1) * earth_radius; - - // compute distance (b,c) - const float x_value_2 = (second_lon - float_lon1) * cos((float_lat1 + second_lat) / 2.f); - const float y_value_2 = second_lat - float_lat1; - const float dist2 = std::hypot(x_value_2, y_value_2) * earth_radius; - - // return the minimum - return static_cast(std::min(dist1, dist2)); - } - - float first_lat; - float first_lon; - float second_lat; - float second_lon; -}; -} - -void DouglasPeucker::Run(std::vector &input_geometry, const unsigned zoom_level) -{ - Run(std::begin(input_geometry), std::end(input_geometry), zoom_level); -} - -void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level) -{ - const auto size = std::distance(begin, end); - if (size < 2) - { - return; - } - - begin->necessary = true; - std::prev(end)->necessary = true; - - { - BOOST_ASSERT_MSG(zoom_level < DOUGLAS_PEUCKER_THRESHOLDS.size(), "unsupported zoom level"); - auto left_border = begin; - auto right_border = std::next(begin); - // Sweep over array and identify those ranges that need to be checked - do - { - // traverse list until new border element found - if (right_border->necessary) - { - // sanity checks - BOOST_ASSERT(left_border->necessary); - BOOST_ASSERT(right_border->necessary); - recursion_stack.emplace(left_border, right_border); - left_border = right_border; - } - ++right_border; - } while (right_border != end); - } - - // mark locations as 'necessary' by divide-and-conquer - while (!recursion_stack.empty()) - { - // pop next element - const GeometryRange pair = recursion_stack.top(); - recursion_stack.pop(); - // sanity checks - BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary"); - BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary"); - BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry"); - BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0, - "left border on the wrong side"); - - int max_int_distance = 0; - auto farthest_entry_it = pair.second; - const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location); - - // sweep over range to find the maximum - for (auto it = std::next(pair.first); it != pair.second; ++it) - { - const int distance = dist_calc(it->location); - // found new feasible maximum? - if (distance > max_int_distance && distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) - { - farthest_entry_it = it; - max_int_distance = distance; - } - } - - // check if maximum violates a zoom level dependent threshold - if (max_int_distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) - { - // mark idx as necessary - farthest_entry_it->necessary = true; - if (1 < std::distance(pair.first, farthest_entry_it)) - { - recursion_stack.emplace(pair.first, farthest_entry_it); - } - if (1 < std::distance(farthest_entry_it, pair.second)) - { - recursion_stack.emplace(farthest_entry_it, pair.second); - } - } - } -} diff --git a/algorithms/douglas_peucker.hpp b/algorithms/douglas_peucker.hpp deleted file mode 100644 index da02982f3..000000000 --- a/algorithms/douglas_peucker.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef DOUGLAS_PEUCKER_HPP_ -#define DOUGLAS_PEUCKER_HPP_ - -#include "../data_structures/segment_information.hpp" - -#include -#include -#include -#include - -/* This class object computes the bitvector of indicating generalized input - * points according to the (Ramer-)Douglas-Peucker algorithm. - * - * Input is vector of pairs. Each pair consists of the point information and a - * bit indicating if the points is present in the generalization. - * Note: points may also be pre-selected*/ - -static const std::array DOUGLAS_PEUCKER_THRESHOLDS{{ - 512440, // z0 - 256720, // z1 - 122560, // z2 - 56780, // z3 - 28800, // z4 - 14400, // z5 - 7200, // z6 - 3200, // z7 - 2400, // z8 - 1000, // z9 - 600, // z10 - 120, // z11 - 60, // z12 - 45, // z13 - 36, // z14 - 20, // z15 - 8, // z16 - 6, // z17 - 4 // z18 -}}; - -class DouglasPeucker -{ - public: - using RandomAccessIt = std::vector::iterator; - - using GeometryRange = std::pair; - // Stack to simulate the recursion - std::stack recursion_stack; - - public: - void Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level); - void Run(std::vector &input_geometry, const unsigned zoom_level); -}; - -#endif /* DOUGLAS_PEUCKER_HPP_ */ diff --git a/algorithms/geospatial_query.hpp b/algorithms/geospatial_query.hpp deleted file mode 100644 index 96b4fc5a2..000000000 --- a/algorithms/geospatial_query.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef GEOSPATIAL_QUERY_HPP -#define GEOSPATIAL_QUERY_HPP - -#include "coordinate_calculation.hpp" -#include "../typedefs.h" -#include "../data_structures/phantom_node.hpp" -#include "../util/bearing.hpp" - -#include - -#include -#include -#include - -// Implements complex queries on top of an RTree and builds PhantomNodes from it. -// -// Only holds a weak reference on the RTree! -template class GeospatialQuery -{ - using EdgeData = typename RTreeT::EdgeData; - using CoordinateList = typename RTreeT::CoordinateList; - - public: - GeospatialQuery(RTreeT &rtree_, std::shared_ptr coordinates_) - : rtree(rtree_), coordinates(coordinates_) - { - } - - // Returns nearest PhantomNodes in the given bearing range within max_distance. - // Does not filter by small/big component! - std::vector - NearestPhantomNodesInRange(const FixedPointCoordinate &input_coordinate, - const float max_distance, - const int bearing = 0, - const int bearing_range = 180) - { - auto results = - rtree.Nearest(input_coordinate, - [this, bearing, bearing_range, max_distance](const EdgeData &data) - { - return checkSegmentBearing(data, bearing, bearing_range); - }, - [max_distance](const std::size_t, const float min_dist) - { - return min_dist > max_distance; - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns max_results nearest PhantomNodes in the given bearing range. - // Does not filter by small/big component! - std::vector - NearestPhantomNodes(const FixedPointCoordinate &input_coordinate, - const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) - { - auto results = rtree.Nearest(input_coordinate, - [this, bearing, bearing_range](const EdgeData &data) - { - return checkSegmentBearing(data, bearing, bearing_range); - }, - [max_results](const std::size_t num_results, const float) - { - return num_results >= max_results; - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns the nearest phantom node. If this phantom node is not from a big component - // a second phantom node is return that is the nearest coordinate in a big component. - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const FixedPointCoordinate &input_coordinate, - const int bearing = 0, - const int bearing_range = 180) - { - bool has_small_component = false; - bool has_big_component = false; - auto results = rtree.Nearest( - input_coordinate, - [this, bearing, bearing_range, &has_big_component, - &has_small_component](const EdgeData &data) - { - auto use_segment = - (!has_small_component || (!has_big_component && !data.component.is_tiny)); - auto use_directions = std::make_pair(use_segment, use_segment); - - if (use_segment) - { - use_directions = checkSegmentBearing(data, bearing, bearing_range); - if (use_directions.first || use_directions.second) - { - has_big_component = has_big_component || !data.component.is_tiny; - has_small_component = has_small_component || data.component.is_tiny; - } - } - - return use_directions; - }, - [&has_big_component](const std::size_t num_results, const float) - { - return num_results > 0 && has_big_component; - }); - - if (results.size() == 0) - { - return std::make_pair(PhantomNode{}, PhantomNode{}); - } - - BOOST_ASSERT(results.size() > 0); - return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, - MakePhantomNode(input_coordinate, results.back()).phantom_node); - } - - private: - std::vector - MakePhantomNodes(const FixedPointCoordinate &input_coordinate, - const std::vector &results) const - { - std::vector distance_and_phantoms(results.size()); - std::transform(results.begin(), results.end(), distance_and_phantoms.begin(), - [this, &input_coordinate](const EdgeData &data) - { - return MakePhantomNode(input_coordinate, data); - }); - return distance_and_phantoms; - } - - PhantomNodeWithDistance MakePhantomNode(const FixedPointCoordinate &input_coordinate, - const EdgeData &data) const - { - FixedPointCoordinate point_on_segment; - float ratio; - const auto current_perpendicular_distance = coordinate_calculation::perpendicular_distance( - coordinates->at(data.u), coordinates->at(data.v), input_coordinate, point_on_segment, - ratio); - - auto transformed = - PhantomNodeWithDistance { PhantomNode{data, point_on_segment}, current_perpendicular_distance }; - - ratio = std::min(1.f, std::max(0.f, ratio)); - - if (SPECIAL_NODEID != transformed.phantom_node.forward_node_id) - { - transformed.phantom_node.forward_weight *= ratio; - } - if (SPECIAL_NODEID != transformed.phantom_node.reverse_node_id) - { - transformed.phantom_node.reverse_weight *= 1.f - ratio; - } - return transformed; - } - - std::pair checkSegmentBearing(const EdgeData &segment, - const float filter_bearing, - const float filter_bearing_range) - { - const float forward_edge_bearing = - coordinate_calculation::bearing(coordinates->at(segment.u), coordinates->at(segment.v)); - - const float backward_edge_bearing = (forward_edge_bearing + 180) > 360 - ? (forward_edge_bearing - 180) - : (forward_edge_bearing + 180); - - const bool forward_bearing_valid = - bearing::CheckInBounds(forward_edge_bearing, filter_bearing, filter_bearing_range) && - segment.forward_edge_based_node_id != SPECIAL_NODEID; - const bool backward_bearing_valid = - bearing::CheckInBounds(backward_edge_bearing, filter_bearing, filter_bearing_range) && - segment.reverse_edge_based_node_id != SPECIAL_NODEID; - return std::make_pair(forward_bearing_valid, backward_bearing_valid); - } - - RTreeT &rtree; - const std::shared_ptr coordinates; -}; - -#endif diff --git a/algorithms/graph_compressor.hpp b/algorithms/graph_compressor.hpp deleted file mode 100644 index 8096a92d6..000000000 --- a/algorithms/graph_compressor.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ -#ifndef GEOMETRY_COMPRESSOR_HPP -#define GEOMETRY_COMPRESSOR_HPP - -#include "../typedefs.h" - -#include "../extractor/speed_profile.hpp" -#include "../data_structures/node_based_graph.hpp" - -#include -#include - -class CompressedEdgeContainer; -class RestrictionMap; - -class GraphCompressor -{ - using EdgeData = NodeBasedDynamicGraph::EdgeData; - -public: - GraphCompressor(SpeedProfileProperties speed_profile); - - void Compress(const std::unordered_set& barrier_nodes, - const std::unordered_set& traffic_lights, - RestrictionMap& restriction_map, - NodeBasedDynamicGraph& graph, - CompressedEdgeContainer& geometry_compressor); -private: - - void PrintStatistics(unsigned original_number_of_nodes, - unsigned original_number_of_edges, - const NodeBasedDynamicGraph& graph) const; - - SpeedProfileProperties speed_profile; -}; - -#endif diff --git a/algorithms/object_encoder.hpp b/algorithms/object_encoder.hpp deleted file mode 100644 index 581b3967c..000000000 --- a/algorithms/object_encoder.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef OBJECT_ENCODER_HPP -#define OBJECT_ENCODER_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include - -struct ObjectEncoder -{ - using base64_t = boost::archive::iterators::base64_from_binary< - boost::archive::iterators::transform_width>; - - using binary_t = boost::archive::iterators::transform_width< - boost::archive::iterators::binary_from_base64, - 8, - 6>; - - template static void EncodeToBase64(const ObjectT &object, std::string &encoded) - { - const char *char_ptr_to_object = reinterpret_cast(&object); - std::vector data(sizeof(object)); - std::copy(char_ptr_to_object, char_ptr_to_object + sizeof(ObjectT), data.begin()); - - unsigned char number_of_padded_chars = 0; // is in {0,1,2}; - while (data.size() % 3 != 0) - { - ++number_of_padded_chars; - data.push_back(0x00); - } - - BOOST_ASSERT_MSG(0 == data.size() % 3, "base64 input data size is not a multiple of 3!"); - encoded.resize(sizeof(ObjectT)); - encoded.assign(base64_t(&data[0]), - base64_t(&data[0] + (data.size() - number_of_padded_chars))); - std::replace(begin(encoded), end(encoded), '+', '-'); - std::replace(begin(encoded), end(encoded), '/', '_'); - } - - template static void DecodeFromBase64(const std::string &input, ObjectT &object) - { - try - { - std::string encoded(input); - std::replace(begin(encoded), end(encoded), '-', '+'); - std::replace(begin(encoded), end(encoded), '_', '/'); - - std::copy(binary_t(encoded.begin()), binary_t(encoded.begin() + encoded.length()), - reinterpret_cast(&object)); - } - catch (...) - { - } - } -}; - -#endif /* OBJECT_ENCODER_HPP */ diff --git a/algorithms/polyline_compressor.cpp b/algorithms/polyline_compressor.cpp deleted file mode 100644 index 0db75dcc6..000000000 --- a/algorithms/polyline_compressor.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "polyline_compressor.hpp" -#include "../data_structures/segment_information.hpp" - -#include - -std::string PolylineCompressor::encode_vector(std::vector &numbers) const -{ - std::string output; - const auto end = numbers.size(); - for (std::size_t i = 0; i < end; ++i) - { - numbers[i] <<= 1; - if (numbers[i] < 0) - { - numbers[i] = ~(numbers[i]); - } - } - for (const int number : numbers) - { - output += encode_number(number); - } - return output; -} - -std::string PolylineCompressor::encode_number(int number_to_encode) const -{ - std::string output; - while (number_to_encode >= 0x20) - { - const int next_value = (0x20 | (number_to_encode & 0x1f)) + 63; - output += static_cast(next_value); - number_to_encode >>= 5; - } - - number_to_encode += 63; - output += static_cast(number_to_encode); - return output; -} - -std::string -PolylineCompressor::get_encoded_string(const std::vector &polyline) const -{ - if (polyline.empty()) - { - return {}; - } - - std::vector delta_numbers; - delta_numbers.reserve((polyline.size() - 1) * 2); - FixedPointCoordinate previous_coordinate = {0, 0}; - for (const auto &segment : polyline) - { - if (segment.necessary) - { - const int lat_diff = segment.location.lat - previous_coordinate.lat; - const int lon_diff = segment.location.lon - previous_coordinate.lon; - delta_numbers.emplace_back(lat_diff); - delta_numbers.emplace_back(lon_diff); - previous_coordinate = segment.location; - } - } - return encode_vector(delta_numbers); -} - -std::vector PolylineCompressor::decode_string(const std::string &geometry_string) const -{ - std::vector new_coordinates; - int index = 0, len = geometry_string.size(); - int lat = 0, lng = 0; - - while (index < len) - { - int b, shift = 0, result = 0; - do - { - b = geometry_string.at(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); - lat += dlat; - - shift = 0; - result = 0; - do - { - b = geometry_string.at(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); - lng += dlng; - - FixedPointCoordinate p; - p.lat = COORDINATE_PRECISION * (((double) lat / 1E6)); - p.lon = COORDINATE_PRECISION * (((double) lng / 1E6)); - new_coordinates.push_back(p); - } - - return new_coordinates; -} diff --git a/algorithms/polyline_compressor.hpp b/algorithms/polyline_compressor.hpp deleted file mode 100644 index a148200ca..000000000 --- a/algorithms/polyline_compressor.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef POLYLINECOMPRESSOR_H_ -#define POLYLINECOMPRESSOR_H_ - -struct SegmentInformation; - -#include - -#include -#include - -class PolylineCompressor -{ - private: - std::string encode_vector(std::vector &numbers) const; - - std::string encode_number(const int number_to_encode) const; - - public: - std::string get_encoded_string(const std::vector &polyline) const; - - std::vector decode_string(const std::string &geometry_string) const; -}; - -#endif /* POLYLINECOMPRESSOR_H_ */ diff --git a/algorithms/polyline_formatter.cpp b/algorithms/polyline_formatter.cpp deleted file mode 100644 index 670a6121f..000000000 --- a/algorithms/polyline_formatter.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "polyline_formatter.hpp" - -#include "polyline_compressor.hpp" -#include "../data_structures/segment_information.hpp" - -#include - -osrm::json::String -PolylineFormatter::printEncodedString(const std::vector &polyline) const -{ - return osrm::json::String(PolylineCompressor().get_encoded_string(polyline)); -} - -osrm::json::Array -PolylineFormatter::printUnencodedString(const std::vector &polyline) const -{ - osrm::json::Array json_geometry_array; - for (const auto &segment : polyline) - { - if (segment.necessary) - { - osrm::json::Array json_coordinate; - json_coordinate.values.push_back(segment.location.lat / COORDINATE_PRECISION); - json_coordinate.values.push_back(segment.location.lon / COORDINATE_PRECISION); - json_geometry_array.values.push_back(json_coordinate); - } - } - return json_geometry_array; -} diff --git a/algorithms/route_name_extraction.hpp b/algorithms/route_name_extraction.hpp deleted file mode 100644 index 00dae89c4..000000000 --- a/algorithms/route_name_extraction.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef EXTRACT_ROUTE_NAMES_H -#define EXTRACT_ROUTE_NAMES_H - -#include - -#include -#include -#include - -struct RouteNames -{ - std::string shortest_path_name_1; - std::string shortest_path_name_2; - std::string alternative_path_name_1; - std::string alternative_path_name_2; -}; - -// construct routes names -template struct ExtractRouteNames -{ - private: - SegmentT PickNextLongestSegment(const std::vector &segment_list, - const unsigned blocked_name_id) const - { - SegmentT result_segment; - result_segment.length = 0; - - for (const SegmentT &segment : segment_list) - { - if (segment.name_id != blocked_name_id && segment.length > result_segment.length && - segment.name_id != 0) - { - result_segment = segment; - } - } - return result_segment; - } - - public: - RouteNames operator()(std::vector &shortest_path_segments, - std::vector &alternative_path_segments, - const DataFacadeT *facade) const - { - RouteNames route_names; - - SegmentT shortest_segment_1, shortest_segment_2; - SegmentT alternative_segment_1, alternative_segment_2; - - auto length_comperator = [](const SegmentT &a, const SegmentT &b) - { - return a.length > b.length; - }; - auto name_id_comperator = [](const SegmentT &a, const SegmentT &b) - { - return a.name_id < b.name_id; - }; - - if (shortest_path_segments.empty()) - { - return route_names; - } - - // pick the longest segment for the shortest path. - std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator); - shortest_segment_1 = shortest_path_segments[0]; - if (!alternative_path_segments.empty()) - { - std::sort(alternative_path_segments.begin(), alternative_path_segments.end(), - length_comperator); - - // also pick the longest segment for the alternative path - alternative_segment_1 = alternative_path_segments[0]; - } - - // compute the set difference (for shortest path) depending on names between shortest and - // alternative - std::vector shortest_path_set_difference(shortest_path_segments.size()); - std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), name_id_comperator); - std::sort(alternative_path_segments.begin(), alternative_path_segments.end(), - name_id_comperator); - std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(), - alternative_path_segments.begin(), alternative_path_segments.end(), - shortest_path_set_difference.begin(), name_id_comperator); - - std::sort(shortest_path_set_difference.begin(), shortest_path_set_difference.end(), - length_comperator); - shortest_segment_2 = - PickNextLongestSegment(shortest_path_set_difference, shortest_segment_1.name_id); - - // compute the set difference (for alternative path) depending on names between shortest and - // alternative - // vectors are still sorted, no need to do again - BOOST_ASSERT(std::is_sorted(shortest_path_segments.begin(), shortest_path_segments.end(), - name_id_comperator)); - BOOST_ASSERT(std::is_sorted(alternative_path_segments.begin(), - alternative_path_segments.end(), name_id_comperator)); - - std::vector alternative_path_set_difference(alternative_path_segments.size()); - std::set_difference(alternative_path_segments.begin(), alternative_path_segments.end(), - shortest_path_segments.begin(), shortest_path_segments.end(), - alternative_path_set_difference.begin(), name_id_comperator); - - std::sort(alternative_path_set_difference.begin(), alternative_path_set_difference.end(), - length_comperator); - - if (!alternative_path_segments.empty()) - { - alternative_segment_2 = PickNextLongestSegment(alternative_path_set_difference, - alternative_segment_1.name_id); - } - - // move the segments into the order in which they occur. - if (shortest_segment_1.position > shortest_segment_2.position) - { - std::swap(shortest_segment_1, shortest_segment_2); - } - if (alternative_segment_1.position > alternative_segment_2.position) - { - std::swap(alternative_segment_1, alternative_segment_2); - } - - // fetching names for the selected segments - route_names.shortest_path_name_1 = facade->get_name_for_id(shortest_segment_1.name_id); - route_names.shortest_path_name_2 = facade->get_name_for_id(shortest_segment_2.name_id); - - route_names.alternative_path_name_1 = - facade->get_name_for_id(alternative_segment_1.name_id); - route_names.alternative_path_name_2 = - facade->get_name_for_id(alternative_segment_2.name_id); - - return route_names; - } -}; - -#endif // EXTRACT_ROUTE_NAMES_H diff --git a/appveyor-build.bat b/appveyor-build.bat index a1e96031c..3ef305d32 100644 --- a/appveyor-build.bat +++ b/appveyor-build.bat @@ -8,16 +8,39 @@ SET PROJECT_DIR=%CD% ECHO PROJECT_DIR^: %PROJECT_DIR% ECHO NUMBER_OF_PROCESSORS^: %NUMBER_OF_PROCESSORS% ECHO cmake^: && cmake --version +IF %ERRORLEVEL% NEQ 0 ECHO CMAKE not found GOTO ERROR + +FOR /F %%G IN ("--version") DO cmake %%G 2>&1 | findstr /C:"3.5.0" > nul && goto CMAKE_NOT_OK +GOTO CMAKE_OK + +:CMAKE_NOT_OK +ECHO CMAKE NOT OK - downloading new CMake +IF NOT EXIST cm.zip powershell Invoke-WebRequest https://cmake.org/files/v3.5/cmake-3.5.1-win32-x86.zip -OutFile $env:PROJECT_DIR\cm.zip +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +IF NOT EXIST cmake-3.5.1-win32-x86 7z -y x cm.zip | %windir%\system32\FIND "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +SET PATH=%PROJECT_DIR%\cmake-3.5.1-win32-x86\bin;%PATH% + +:CMAKE_OK +ECHO CMAKE_OK +cmake --version ECHO activating VS command prompt ... SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 ECHO platform^: %platform% + +ECHO cl.exe version +cl +ECHO msbuild version +msbuild /version + :: HARDCODE "x64" as it is uppercase on AppVeyor and download from S3 is case sensitive SET DEPSPKG=osrm-deps-win-x64-14.0.7z :: local development +ECHO. ECHO LOCAL_DEV^: %LOCAL_DEV% IF NOT DEFINED LOCAL_DEV SET LOCAL_DEV=0 IF DEFINED LOCAL_DEV IF %LOCAL_DEV% EQU 1 IF EXIST %DEPSPKG% ECHO skipping deps download && GOTO SKIPDL @@ -33,27 +56,39 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR IF EXIST osrm-deps ECHO deleting osrm-deps... && RD /S /Q osrm-deps IF %ERRORLEVEL% NEQ 0 GOTO ERROR -IF EXIST build ECHO deletings build dir... && RD /S /Q build +IF EXIST build ECHO deleting build dir... && RD /S /Q build IF %ERRORLEVEL% NEQ 0 GOTO ERROR 7z -y x %DEPSPKG% | %windir%\system32\FIND "ing archive" IF %ERRORLEVEL% NEQ 0 GOTO ERROR +::tree osrm-deps + MKDIR build IF %ERRORLEVEL% NEQ 0 GOTO ERROR cd build IF %ERRORLEVEL% NEQ 0 GOTO ERROR -SET OSRMDEPSDIR=%PROJECT_DIR%\osrm-deps +SET OSRMDEPSDIR=%PROJECT_DIR%/osrm-deps set PREFIX=%OSRMDEPSDIR%/libs set BOOST_ROOT=%OSRMDEPSDIR%/boost +set BOOST_LIBRARYDIR=%BOOST_ROOT%/lib set TBB_INSTALL_DIR=%OSRMDEPSDIR%/tbb set TBB_ARCH_PLATFORM=intel64/vc14 +ECHO OSRMDEPSDIR ^: %OSRMDEPSDIR% +ECHO PREFIX ^: %PREFIX% +ECHO BOOST_ROOT ^: %BOOST_ROOT% +ECHO BOOST_LIBRARYDIR ^: %BOOST_LIBRARYDIR% +ECHO TBB_INSTALL_DIR ^: %TBB_INSTALL_DIR% +ECHO TBB_ARCH_PLATFORM ^: %TBB_ARCH_PLATFORM% + + ECHO calling cmake .... cmake .. ^ -G "Visual Studio 14 2015 Win64" ^ -DBOOST_ROOT=%BOOST_ROOT% ^ +-DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% ^ -DBoost_ADDITIONAL_VERSIONS=1.58 ^ -DBoost_USE_MULTITHREADED=ON ^ -DBoost_USE_STATIC_LIBS=ON ^ @@ -81,11 +116,17 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR SET PATH=%PROJECT_DIR%\osrm-deps\libs\bin;%PATH% -ECHO running datastructure-tests.exe ... -%Configuration%\datastructure-tests.exe +ECHO running extractor-tests.exe ... +unit_tests\%Configuration%\extractor-tests.exe IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ECHO running algorithm-tests.exe ... -%Configuration%\algorithm-tests.exe +ECHO running engine-tests.exe ... +unit_tests\%Configuration%\engine-tests.exe +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +ECHO running util-tests.exe ... +unit_tests\%Configuration%\util-tests.exe +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +ECHO running server-tests.exe ... +unit_tests\%Configuration%\server-tests.exe IF %ERRORLEVEL% NEQ 0 GOTO ERROR IF NOT "%APPVEYOR_REPO_BRANCH%"=="develop" GOTO DONE diff --git a/appveyor.yml b/appveyor.yml index b8fb9799f..b7e09ef14 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,6 +25,11 @@ artifacts: # - path: osrm_Debug.zip # name: osrm_Debug.zip +branches: + only: + - master + - develop + deploy: provider: FTP server: @@ -36,9 +41,3 @@ deploy: folder: / enable_ssl: true active_mode: false - -# notifications: -# - provider: HipChat -# auth_token: -# secure: boLE7BjcahdIUxv9jkN7U3F8iOASF+MkhtctlVoWJoo= -# room: Directions diff --git a/benchmarks/static_rtree.cpp b/benchmarks/static_rtree.cpp deleted file mode 100644 index ebe055cce..000000000 --- a/benchmarks/static_rtree.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "../data_structures/query_node.hpp" -#include "../data_structures/static_rtree.hpp" -#include "../data_structures/edge_based_node.hpp" -#include "../algorithms/geospatial_query.hpp" -#include "../util/timing_util.hpp" - -#include - -#include -#include - -// Choosen by a fair W20 dice roll (this value is completely arbitrary) -constexpr unsigned RANDOM_SEED = 13; -constexpr int32_t WORLD_MIN_LAT = -90 * COORDINATE_PRECISION; -constexpr int32_t WORLD_MAX_LAT = 90 * COORDINATE_PRECISION; -constexpr int32_t WORLD_MIN_LON = -180 * COORDINATE_PRECISION; -constexpr int32_t WORLD_MAX_LON = 180 * COORDINATE_PRECISION; - -using RTreeLeaf = EdgeBasedNode; -using FixedPointCoordinateListPtr = std::shared_ptr>; -using BenchStaticRTree = StaticRTree::vector, false>; -using BenchQuery = GeospatialQuery; - -FixedPointCoordinateListPtr LoadCoordinates(const boost::filesystem::path &nodes_file) -{ - boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary); - - QueryNode current_node; - unsigned coordinate_count = 0; - nodes_input_stream.read((char *)&coordinate_count, sizeof(unsigned)); - auto coords = std::make_shared>(coordinate_count); - for (unsigned i = 0; i < coordinate_count; ++i) - { - nodes_input_stream.read((char *)¤t_node, sizeof(QueryNode)); - coords->at(i) = FixedPointCoordinate(current_node.lat, current_node.lon); - BOOST_ASSERT((std::abs(coords->at(i).lat) >> 30) == 0); - BOOST_ASSERT((std::abs(coords->at(i).lon) >> 30) == 0); - } - nodes_input_stream.close(); - return coords; -} - -template -void BenchmarkQuery(const std::vector &queries, - const std::string& name, - QueryT query) -{ - std::cout << "Running " << name << " with " << queries.size() << " coordinates: " << std::flush; - - TIMER_START(query); - for (const auto &q : queries) - { - auto result = query(q); - } - TIMER_STOP(query); - - std::cout << "Took " << TIMER_SEC(query) << " seconds " - << "(" << TIMER_MSEC(query) << "ms" - << ") -> " << TIMER_MSEC(query) / queries.size() << " ms/query " - << "(" << TIMER_MSEC(query) << "ms" - << ")" << std::endl; -} - -void Benchmark(BenchStaticRTree &rtree, BenchQuery &geo_query, unsigned num_queries) -{ - std::mt19937 mt_rand(RANDOM_SEED); - std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT); - std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON); - std::vector queries; - for (unsigned i = 0; i < num_queries; i++) - { - queries.emplace_back(lat_udist(mt_rand), lon_udist(mt_rand)); - } - - BenchmarkQuery(queries, "raw RTree queries (1 result)", [&rtree](const FixedPointCoordinate &q) - { - return rtree.Nearest(q, 1); - }); - BenchmarkQuery(queries, "raw RTree queries (10 results)", - [&rtree](const FixedPointCoordinate &q) - { - return rtree.Nearest(q, 10); - }); - - BenchmarkQuery(queries, "big component alternative queries", - [&geo_query](const FixedPointCoordinate &q) - { - return geo_query.NearestPhantomNodeWithAlternativeFromBigComponent(q); - }); - BenchmarkQuery(queries, "max distance 1000", [&geo_query](const FixedPointCoordinate &q) - { - return geo_query.NearestPhantomNodesInRange(q, 1000); - }); - BenchmarkQuery(queries, "PhantomNode query (1 result)", [&geo_query](const FixedPointCoordinate &q) - { - return geo_query.NearestPhantomNodes(q, 1); - }); - BenchmarkQuery(queries, "PhantomNode query (10 result)", [&geo_query](const FixedPointCoordinate &q) - { - return geo_query.NearestPhantomNodes(q, 10); - }); -} - -int main(int argc, char **argv) -{ - if (argc < 4) - { - std::cout << "./rtree-bench file.ramIndex file.fileIndx file.nodes" - << "\n"; - return 1; - } - - const char *ramPath = argv[1]; - const char *filePath = argv[2]; - const char *nodesPath = argv[3]; - - auto coords = LoadCoordinates(nodesPath); - - BenchStaticRTree rtree(ramPath, filePath, coords); - BenchQuery query(rtree, coords); - - Benchmark(rtree, query, 10000); - - return 0; -} diff --git a/build-local.bat b/build-local.bat index b26c41522..a90c0aa74 100644 --- a/build-local.bat +++ b/build-local.bat @@ -11,7 +11,8 @@ SET CONFIGURATION=Release FOR /F "tokens=*" %%i in ('git rev-parse --abbrev-ref HEAD') do SET APPVEYOR_REPO_BRANCH=%%i ECHO APPVEYOR_REPO_BRANCH^: %APPVEYOR_REPO_BRANCH% -SET PATH=C:\mb\windows-builds-64\tmp-bin\cmake-3.4.0-win32-x86\bin;%PATH% +::SET PATH=C:\mb\windows-builds-64\tmp-bin\cmake-3.5.0-win32-x86\bin;%PATH% +SET PATH=C:\mb\windows-builds-64\tmp-bin\cmake-3.5.1-win32-x86\bin;%PATH% SET PATH=C:\Program Files\7-Zip;%PATH% powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force diff --git a/cmake/CPackDebianConfig.cmake b/cmake/CPackDebianConfig.cmake index bd434ee9e..13216dd2f 100644 --- a/cmake/CPackDebianConfig.cmake +++ b/cmake/CPackDebianConfig.cmake @@ -7,15 +7,13 @@ INCLUDE(FindDebArch) SET(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENCE.TXT") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CPACK_RESOURCE_FILE_README}") -SET(CPACK_PACKAGE_VERSION_MAJOR "0") -SET(CPACK_PACKAGE_VERSION_MINOR "4") -SET(CPACK_PACKAGE_VERSION_PATCH "3") - -SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") +SET(CPACK_PACKAGE_UPSTREAM_VERSION "${OSRM_VERSION_MAJOR}.${OSRM_VERSION_MINOR}.${OSRM_VERSION_PATCH}") +SET(CPACK_PACKAGE_DEBIAN_REVISION "1") +SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_UPSTREAM_VERSION}-${CPACK_PACKAGE_DEBIAN_REVISION}") string(TOLOWER "${CMAKE_PROJECT_NAME}" LOWER_PROJECT_NAME) SET(CPACK_PACKAGE_FILE_NAME "${LOWER_PROJECT_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") -SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${LOWER_PROJECT_NAME}_${CPACK_PACKAGE_VERSION}_orig") +SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${LOWER_PROJECT_NAME}_${CPACK_PACKAGE_UPSTREAM_VERSION}_orig") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open Source Routing Machine (OSRM).") SET(CPACK_PACKAGE_DESCRIPTION "Open Source Routing Machine (OSRM) is a routing engine.") @@ -27,7 +25,7 @@ SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY "FALSE") SET(CPACK_GENERATOR "DEB") SET(CPACK_DEBIAN_PACKAGE_NAME "${CPACK_PACKAGE_NAME}${VERSION_SUFFIX}") -SET(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}${CPACK_PACKAGE_REVISION}") +SET(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Dennis Luxen ") SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") SET(CPACK_DEBIAN_PACKAGE_SECTION "devel") @@ -38,7 +36,6 @@ SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6-dev, libbz2-1.0, libstxxl1, libxml2, file(GLOB_RECURSE ProfileGlob ${CMAKE_SOURCE_DIR}/profiles/*) install(FILES ${ProfileGlob} DESTINATION "share/doc/${LOWER_PROJECT_NAME}/profiles") -CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/cmake/postinst.in postinst) -set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_BINARY_DIR}/postinst;${CMAKE_CURRENT_BINARY_DIR}/copyright;") +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_BINARY_DIR}/copyright;") MESSAGE(STATUS "Debian Package: ${CPACK_DEBIAN_PACKAGE_NAME} (${CPACK_DEBIAN_PACKAGE_VERSION}) [${CPACK_PACKAGE_FILE_NAME}.deb]") diff --git a/cmake/FindLua52.cmake b/cmake/FindLua52.cmake index d17fbf61d..4aeaf94f9 100644 --- a/cmake/FindLua52.cmake +++ b/cmake/FindLua52.cmake @@ -14,7 +14,7 @@ #============================================================================= # Copyright 2007-2009 Kitware, Inc. -# Copyright 2013 for Project-OSRM, Lua5.1 => Lua5.2 +# Copyright 2016 for Project-OSRM, Lua5.1 => Lua5.2 # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. diff --git a/cmake/FindSTXXL.cmake b/cmake/FindSTXXL.cmake index 76a2722b0..473fb6a29 100644 --- a/cmake/FindSTXXL.cmake +++ b/cmake/FindSTXXL.cmake @@ -24,7 +24,7 @@ FIND_PATH(STXXL_INCLUDE_DIR stxxl.h ) FIND_LIBRARY(STXXL_LIBRARY - NAMES stxxl + NAMES stxxl stxxl_debug HINTS $ENV{STXXL_DIR} PATH_SUFFIXES lib64 lib diff --git a/cmake/FingerPrint-Config.cmake b/cmake/FingerPrint-Config.cmake index d36b62274..0672b3402 100644 --- a/cmake/FingerPrint-Config.cmake +++ b/cmake/FingerPrint-Config.cmake @@ -1,10 +1,10 @@ -set(OLDFILE ${OUTPUT_DIR}/util/fingerprint_impl.hpp) +set(OLDFILE ${OUTPUT_DIR}/include/util/fingerprint_impl.hpp) set(NEWFILE ${OLDFILE}.tmp) -set(INFILE ${SOURCE_DIR}/util/fingerprint_impl.hpp.in) -file(MD5 ${SOURCE_DIR}/prepare.cpp MD5PREPARE) -file(MD5 ${SOURCE_DIR}/data_structures/static_rtree.hpp MD5RTREE) -file(MD5 ${SOURCE_DIR}/util/graph_loader.hpp MD5GRAPH) -file(MD5 ${SOURCE_DIR}/server/data_structures/internal_datafacade.hpp MD5OBJECTS) +set(INFILE ${SOURCE_DIR}/include/util/fingerprint_impl.hpp.in) +file(MD5 ${SOURCE_DIR}/src/tools/contract.cpp MD5PREPARE) +file(MD5 ${SOURCE_DIR}/include/util/static_rtree.hpp MD5RTREE) +file(MD5 ${SOURCE_DIR}/include/util/graph_loader.hpp MD5GRAPH) +file(MD5 ${SOURCE_DIR}/include/engine/datafacade/internal_datafacade.hpp MD5OBJECTS) CONFIGURE_FILE(${INFILE} ${NEWFILE}) diff --git a/cmake/cmake_options_script.py b/cmake/cmake_options_script.py index 52e943e79..8d30604d5 100644 --- a/cmake/cmake_options_script.py +++ b/cmake/cmake_options_script.py @@ -32,13 +32,11 @@ cache_file = "%s/cached_options.txt" % (scriptpath) db = None if os.access(cache_file, os.R_OK) == 0: db = load_db(sys.argv[1]) - f = open(cache_file, "wb") - pickle.dump(db, f) - f.close() + with open(cache_file, "wb") as f: + pickle.dump(db, f) else: - f = open(cache_file) - db = pickle.load(f) - f.close() + with open(cache_file) as f: + db = pickle.load(f) if db and sys.argv[2] in db: for option in db[sys.argv[2]]: diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 000000000..2037e3653 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif(NOT "${rm_retval}" STREQUAL 0) + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/cmake/pkgconfig.in b/cmake/pkgconfig.in index 16263bfe0..c85a076ca 100644 --- a/cmake/pkgconfig.in +++ b/cmake/pkgconfig.in @@ -6,6 +6,6 @@ Name: libOSRM Description: Project OSRM library Version: v@OSRM_VERSION_MAJOR@.@OSRM_VERSION_MINOR@.@OSRM_VERSION_PATCH@ Requires: -Libs: -L${libdir} -lOSRM -Libs.private: @BOOST_LIBRARY_LISTING@ @TBB_LIBRARY_LISTING@ -Cflags: -I${includedir} +Libs: -L${libdir} -losrm +Libs.private: @ENGINE_LIBRARY_LISTING@ +Cflags: -I${includedir} -I${includedir}/osrm diff --git a/cmake/postinst.in b/cmake/postinst.in deleted file mode 100644 index 92f2fde4f..000000000 --- a/cmake/postinst.in +++ /dev/null @@ -1,2 +0,0 @@ -#/usr/bin/env bash -ln -s /usr/share/doc/@CMAKE_PROJECT_NAME@/profiles/car.lua @CMAKE_INSTALL_PREFIX@/profile.lua diff --git a/config/cucumber.yml b/config/cucumber.yml deleted file mode 100644 index 2cdea3688..000000000 --- a/config/cucumber.yml +++ /dev/null @@ -1,9 +0,0 @@ -# config/cucumber.yml -##YAML Template ---- -default: --require features --tags ~@todo --tags ~@bug --tag ~@stress -verify: --require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress -jenkins: --require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress -bugs: --require features --tags @bug -todo: --require features --tags @todo -all: --require features diff --git a/contractor/contractor_options.cpp b/contractor/contractor_options.cpp deleted file mode 100644 index d48346563..000000000 --- a/contractor/contractor_options.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "contractor_options.hpp" - -#include "util/version.hpp" -#include "../util/simple_logger.hpp" - -#include -#include - -#include - -return_code -ContractorOptions::ParseArguments(int argc, char *argv[], ContractorConfig &contractor_config) -{ - // declare a group of options that will be allowed only on command line - boost::program_options::options_description generic_options("Options"); - generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")( - "config,c", boost::program_options::value(&contractor_config.config_file_path) - ->default_value("contractor.ini"), - "Path to a configuration file."); - - // declare a group of options that will be allowed both on command line and in config file - boost::program_options::options_description config_options("Configuration"); - config_options.add_options()( - "profile,p", - boost::program_options::value(&contractor_config.profile_path) - ->default_value("profile.lua"), - "Path to LUA routing profile")( - "threads,t", - boost::program_options::value(&contractor_config.requested_num_threads) - ->default_value(tbb::task_scheduler_init::default_num_threads()), - "Number of threads to use")( - "core,k", boost::program_options::value(&contractor_config.core_factor) - ->default_value(1.0),"Percentage of the graph (in vertices) to contract [0..1]")( - "segment-speed-file", boost::program_options::value(&contractor_config.segment_speed_lookup_path), - "Lookup file containing nodeA,nodeB,speed data to adjust edge weights")( - "level-cache,o", - boost::program_options::value(&contractor_config.use_cached_priority)->default_value(false), - "Use .level file to retain the contaction level for each node from the last run."); - -#ifdef DEBUG_GEOMETRY - config_options.add_options()( - "debug-geometry", boost::program_options::value(&contractor_config.debug_geometry_path) - ,"Write out edge-weight debugging geometry data in GeoJSON format to this file"); -#endif - - // hidden options, will be allowed both on command line and in config file, but will not be - // shown to the user - boost::program_options::options_description hidden_options("Hidden options"); - hidden_options.add_options()( - "input,i", boost::program_options::value(&contractor_config.osrm_input_path), - "Input file in .osm, .osm.bz2 or .osm.pbf format"); - - // positional option - boost::program_options::positional_options_description positional_options; - positional_options.add("input", 1); - - // combine above options for parsing - boost::program_options::options_description cmdline_options; - cmdline_options.add(generic_options).add(config_options).add(hidden_options); - - boost::program_options::options_description config_file_options; - config_file_options.add(config_options).add(hidden_options); - - boost::program_options::options_description visible_options( - "Usage: " + boost::filesystem::basename(argv[0]) + " [options]"); - visible_options.add(generic_options).add(config_options); - - // parse command line options - boost::program_options::variables_map option_variables; - boost::program_options::store(boost::program_options::command_line_parser(argc, argv) - .options(cmdline_options) - .positional(positional_options) - .run(), - option_variables); - - const auto &temp_config_path = option_variables["config"].as(); - if (boost::filesystem::is_regular_file(temp_config_path)) - { - boost::program_options::store(boost::program_options::parse_config_file( - temp_config_path.string().c_str(), cmdline_options, true), - option_variables); - } - - if (option_variables.count("version")) - { - SimpleLogger().Write() << OSRM_VERSION; - return return_code::exit; - } - - if (option_variables.count("help")) - { - SimpleLogger().Write() << "\n" << visible_options; - return return_code::exit; - } - - boost::program_options::notify(option_variables); - - if (!option_variables.count("input")) - { - SimpleLogger().Write() << "\n" << visible_options; - return return_code::fail; - } - - return return_code::ok; -} - -void ContractorOptions::GenerateOutputFilesNames(ContractorConfig &contractor_config) -{ - contractor_config.level_output_path = contractor_config.osrm_input_path.string() + ".level"; - contractor_config.core_output_path = contractor_config.osrm_input_path.string() + ".core"; - contractor_config.graph_output_path = contractor_config.osrm_input_path.string() + ".hsgr"; - contractor_config.edge_based_graph_path = contractor_config.osrm_input_path.string() + ".ebg"; - contractor_config.edge_segment_lookup_path = contractor_config.osrm_input_path.string() + ".edge_segment_lookup"; - contractor_config.edge_penalty_path = contractor_config.osrm_input_path.string() + ".edge_penalties"; -} diff --git a/contractor/processing_chain.cpp b/contractor/processing_chain.cpp deleted file mode 100644 index 2bd535093..000000000 --- a/contractor/processing_chain.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "processing_chain.hpp" -#include "contractor.hpp" - -#include "contractor.hpp" - -#include "../data_structures/deallocating_vector.hpp" - -#include "../algorithms/crc32_processor.hpp" -#include "../util/graph_loader.hpp" -#include "../util/integer_range.hpp" -#include "../util/lua_util.hpp" -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" -#include "../util/string_util.hpp" -#include "../util/timing_util.hpp" -#include "../typedefs.h" - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "../util/debug_geometry.hpp" - -Prepare::~Prepare() {} - -int Prepare::Run() -{ -#ifdef WIN32 -#pragma message("Memory consumption on Windows can be higher due to different bit packing") -#else - static_assert(sizeof(NodeBasedEdge) == 20, - "changing NodeBasedEdge type has influence on memory consumption!"); - static_assert(sizeof(EdgeBasedEdge) == 16, - "changing EdgeBasedEdge type has influence on memory consumption!"); -#endif - - if (config.core_factor > 1.0 || config.core_factor < 0) - { - throw osrm::exception("Core factor must be between 0.0 to 1.0 (inclusive)"); - } - - TIMER_START(preparing); - - // Create a new lua state - - SimpleLogger().Write() << "Loading edge-expanded graph representation"; - - DeallocatingVector edge_based_edge_list; - - size_t max_edge_id = LoadEdgeExpandedGraph( - config.edge_based_graph_path, edge_based_edge_list, config.edge_segment_lookup_path, - config.edge_penalty_path, config.segment_speed_lookup_path); - - // Contracting the edge-expanded graph - - TIMER_START(contraction); - std::vector is_core_node; - std::vector node_levels; - if (config.use_cached_priority) - { - ReadNodeLevels(node_levels); - } - DeallocatingVector contracted_edge_list; - ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, is_core_node, - node_levels); - TIMER_STOP(contraction); - - SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec"; - - std::size_t number_of_used_edges = WriteContractedGraph(max_edge_id, contracted_edge_list); - WriteCoreNodeMarker(std::move(is_core_node)); - if (!config.use_cached_priority) - { - WriteNodeLevels(std::move(node_levels)); - } - - TIMER_STOP(preparing); - - SimpleLogger().Write() << "Preprocessing : " << TIMER_SEC(preparing) << " seconds"; - SimpleLogger().Write() << "Contraction: " << ((max_edge_id + 1) / TIMER_SEC(contraction)) - << " nodes/sec and " << number_of_used_edges / TIMER_SEC(contraction) - << " edges/sec"; - - SimpleLogger().Write() << "finished preprocessing"; - - return 0; -} - -namespace std -{ - -template <> struct hash> -{ - std::size_t operator()(const std::pair &k) const - { - return OSMNodeID_to_uint64_t(k.first) ^ (OSMNodeID_to_uint64_t(k.second) << 12); - } -}; -} - -std::size_t Prepare::LoadEdgeExpandedGraph(std::string const &edge_based_graph_filename, - DeallocatingVector &edge_based_edge_list, - const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, - const std::string &segment_speed_filename) -{ - SimpleLogger().Write() << "Opening " << edge_based_graph_filename; - boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary); - - const bool update_edge_weights = segment_speed_filename != ""; - - boost::filesystem::ifstream edge_segment_input_stream; - boost::filesystem::ifstream edge_fixed_penalties_input_stream; - - if (update_edge_weights) - { - edge_segment_input_stream.open(edge_segment_lookup_filename, std::ios::binary); - edge_fixed_penalties_input_stream.open(edge_penalty_filename, std::ios::binary); - if (!edge_segment_input_stream || !edge_fixed_penalties_input_stream) - { - throw osrm::exception("Could not load .edge_segment_lookup or .edge_penalties, did you " - "run osrm-extract with '--generate-edge-lookup'?"); - } - } - - const FingerPrint fingerprint_valid = FingerPrint::GetValid(); - FingerPrint fingerprint_loaded; - input_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); - fingerprint_loaded.TestPrepare(fingerprint_valid); - - size_t number_of_edges = 0; - size_t max_edge_id = SPECIAL_EDGEID; - input_stream.read((char *)&number_of_edges, sizeof(size_t)); - input_stream.read((char *)&max_edge_id, sizeof(size_t)); - - edge_based_edge_list.resize(number_of_edges); - SimpleLogger().Write() << "Reading " << number_of_edges << " edges from the edge based graph"; - - std::unordered_map, unsigned> segment_speed_lookup; - - if (update_edge_weights) - { - SimpleLogger().Write() << "Segment speed data supplied, will update edge weights from " - << segment_speed_filename; - io::CSVReader<3> csv_in(segment_speed_filename); - csv_in.set_header("from_node", "to_node", "speed"); - uint64_t from_node_id; - uint64_t to_node_id; - unsigned speed; - while (csv_in.read_row(from_node_id, to_node_id, speed)) - { - segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id), OSMNodeID(to_node_id))] = speed; - } - } - - DEBUG_GEOMETRY_START(config); - - // TODO: can we read this in bulk? DeallocatingVector isn't necessarily - // all stored contiguously - for (; number_of_edges > 0; --number_of_edges) - { - EdgeBasedEdge inbuffer; - input_stream.read((char *) &inbuffer, sizeof(EdgeBasedEdge)); - - if (update_edge_weights) - { - // Processing-time edge updates - unsigned fixed_penalty; - edge_fixed_penalties_input_stream.read(reinterpret_cast(&fixed_penalty), - sizeof(fixed_penalty)); - - int new_weight = 0; - - unsigned num_osm_nodes = 0; - edge_segment_input_stream.read(reinterpret_cast(&num_osm_nodes), - sizeof(num_osm_nodes)); - OSMNodeID previous_osm_node_id; - edge_segment_input_stream.read(reinterpret_cast(&previous_osm_node_id), - sizeof(previous_osm_node_id)); - OSMNodeID this_osm_node_id; - double segment_length; - int segment_weight; - --num_osm_nodes; - for (; num_osm_nodes != 0; --num_osm_nodes) - { - edge_segment_input_stream.read(reinterpret_cast(&this_osm_node_id), - sizeof(this_osm_node_id)); - edge_segment_input_stream.read(reinterpret_cast(&segment_length), - sizeof(segment_length)); - edge_segment_input_stream.read(reinterpret_cast(&segment_weight), - sizeof(segment_weight)); - - auto speed_iter = segment_speed_lookup.find( - std::make_pair(previous_osm_node_id, this_osm_node_id)); - if (speed_iter != segment_speed_lookup.end()) - { - // This sets the segment weight using the same formula as the - // EdgeBasedGraphFactory for consistency. The *why* of this formula - // is lost in the annals of time. - int new_segment_weight = - std::max(1, static_cast(std::floor( - (segment_length * 10.) / (speed_iter->second / 3.6) + .5))); - new_weight += new_segment_weight; - - DEBUG_GEOMETRY_EDGE( - new_segment_weight, - segment_length, - previous_osm_node_id, - this_osm_node_id); - } - else - { - // If no lookup found, use the original weight value for this segment - new_weight += segment_weight; - - DEBUG_GEOMETRY_EDGE( - segment_weight, - segment_length, - previous_osm_node_id, - this_osm_node_id); - } - - previous_osm_node_id = this_osm_node_id; - } - - inbuffer.weight = fixed_penalty + new_weight; - } - - edge_based_edge_list.emplace_back(std::move(inbuffer)); - } - - DEBUG_GEOMETRY_STOP(); - SimpleLogger().Write() << "Done reading edges"; - return max_edge_id; -} - -void Prepare::ReadNodeLevels(std::vector &node_levels) const -{ - boost::filesystem::ifstream order_input_stream(config.level_output_path, std::ios::binary); - - unsigned level_size; - order_input_stream.read((char *)&level_size, sizeof(unsigned)); - node_levels.resize(level_size); - order_input_stream.read((char *)node_levels.data(), sizeof(float) * node_levels.size()); -} - -void Prepare::WriteNodeLevels(std::vector &&in_node_levels) const -{ - std::vector node_levels(std::move(in_node_levels)); - - boost::filesystem::ofstream order_output_stream(config.level_output_path, std::ios::binary); - - unsigned level_size = node_levels.size(); - order_output_stream.write((char *)&level_size, sizeof(unsigned)); - order_output_stream.write((char *)node_levels.data(), sizeof(float) * node_levels.size()); -} - -void Prepare::WriteCoreNodeMarker(std::vector &&in_is_core_node) const -{ - std::vector is_core_node(std::move(in_is_core_node)); - std::vector unpacked_bool_flags(std::move(is_core_node.size())); - for (auto i = 0u; i < is_core_node.size(); ++i) - { - unpacked_bool_flags[i] = is_core_node[i] ? 1 : 0; - } - - boost::filesystem::ofstream core_marker_output_stream(config.core_output_path, - std::ios::binary); - unsigned size = unpacked_bool_flags.size(); - core_marker_output_stream.write((char *)&size, sizeof(unsigned)); - core_marker_output_stream.write((char *)unpacked_bool_flags.data(), - sizeof(char) * unpacked_bool_flags.size()); -} - -std::size_t Prepare::WriteContractedGraph(unsigned max_node_id, - const DeallocatingVector &contracted_edge_list) -{ - // Sorting contracted edges in a way that the static query graph can read some in in-place. - tbb::parallel_sort(contracted_edge_list.begin(), contracted_edge_list.end()); - const unsigned contracted_edge_count = contracted_edge_list.size(); - SimpleLogger().Write() << "Serializing compacted graph of " << contracted_edge_count - << " edges"; - - const FingerPrint fingerprint = FingerPrint::GetValid(); - boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary); - hsgr_output_stream.write((char *)&fingerprint, sizeof(FingerPrint)); - const unsigned max_used_node_id = [&contracted_edge_list] - { - unsigned tmp_max = 0; - for (const QueryEdge &edge : contracted_edge_list) - { - BOOST_ASSERT(SPECIAL_NODEID != edge.source); - BOOST_ASSERT(SPECIAL_NODEID != edge.target); - tmp_max = std::max(tmp_max, edge.source); - tmp_max = std::max(tmp_max, edge.target); - } - return tmp_max; - }(); - - SimpleLogger().Write(logDEBUG) << "input graph has " << (max_node_id + 1) << " nodes"; - SimpleLogger().Write(logDEBUG) << "contracted graph has " << (max_used_node_id + 1) << " nodes"; - - std::vector::NodeArrayEntry> node_array; - // make sure we have at least one sentinel - node_array.resize(max_node_id + 2); - - SimpleLogger().Write() << "Building node array"; - StaticGraph::EdgeIterator edge = 0; - StaticGraph::EdgeIterator position = 0; - StaticGraph::EdgeIterator last_edge; - - // initializing 'first_edge'-field of nodes: - for (const auto node : osrm::irange(0u, max_used_node_id + 1)) - { - last_edge = edge; - while ((edge < contracted_edge_count) && (contracted_edge_list[edge].source == node)) - { - ++edge; - } - node_array[node].first_edge = position; //=edge - position += edge - last_edge; // remove - } - - for (const auto sentinel_counter : - osrm::irange(max_used_node_id + 1, node_array.size())) - { - // sentinel element, guarded against underflow - node_array[sentinel_counter].first_edge = contracted_edge_count; - } - - SimpleLogger().Write() << "Serializing node array"; - - RangebasedCRC32 crc32_calculator; - const unsigned edges_crc32 = crc32_calculator(contracted_edge_list); - SimpleLogger().Write() << "Writing CRC32: " << edges_crc32; - - const unsigned node_array_size = node_array.size(); - // serialize crc32, aka checksum - hsgr_output_stream.write((char *)&edges_crc32, sizeof(unsigned)); - // serialize number of nodes - hsgr_output_stream.write((char *)&node_array_size, sizeof(unsigned)); - // serialize number of edges - hsgr_output_stream.write((char *)&contracted_edge_count, sizeof(unsigned)); - // serialize all nodes - if (node_array_size > 0) - { - hsgr_output_stream.write((char *)&node_array[0], - sizeof(StaticGraph::NodeArrayEntry) * node_array_size); - } - - // serialize all edges - SimpleLogger().Write() << "Building edge array"; - int number_of_used_edges = 0; - - StaticGraph::EdgeArrayEntry current_edge; - for (const auto edge : osrm::irange(0, contracted_edge_list.size())) - { - // no eigen loops - BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target); - current_edge.target = contracted_edge_list[edge].target; - current_edge.data = contracted_edge_list[edge].data; - - // every target needs to be valid - BOOST_ASSERT(current_edge.target <= max_used_node_id); -#ifndef NDEBUG - if (current_edge.data.distance <= 0) - { - SimpleLogger().Write(logWARNING) << "Edge: " << edge - << ",source: " << contracted_edge_list[edge].source - << ", target: " << contracted_edge_list[edge].target - << ", dist: " << current_edge.data.distance; - - SimpleLogger().Write(logWARNING) << "Failed at adjacency list of node " - << contracted_edge_list[edge].source << "/" - << node_array.size() - 1; - return 1; - } -#endif - hsgr_output_stream.write((char *)¤t_edge, - sizeof(StaticGraph::EdgeArrayEntry)); - - ++number_of_used_edges; - } - - return number_of_used_edges; -} - -/** - \brief Build contracted graph. - */ -void Prepare::ContractGraph(const unsigned max_edge_id, - DeallocatingVector &edge_based_edge_list, - DeallocatingVector &contracted_edge_list, - std::vector &is_core_node, - std::vector &inout_node_levels) const -{ - std::vector node_levels; - node_levels.swap(inout_node_levels); - - Contractor contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels)); - contractor.Run(config.core_factor); - contractor.GetEdges(contracted_edge_list); - contractor.GetCoreMarker(is_core_node); - contractor.GetNodeLevels(inout_node_levels); -} diff --git a/contractor/processing_chain.hpp b/contractor/processing_chain.hpp deleted file mode 100644 index 0eb65553d..000000000 --- a/contractor/processing_chain.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef PROCESSING_CHAIN_HPP -#define PROCESSING_CHAIN_HPP - -#include "contractor.hpp" -#include "contractor_options.hpp" -#include "../data_structures/query_edge.hpp" -#include "../data_structures/static_graph.hpp" -#include "../data_structures/deallocating_vector.hpp" -#include "../data_structures/node_based_graph.hpp" - -struct SpeedProfileProperties; -struct EdgeBasedNode; -struct lua_State; - -#include - -#include - -/** - \brief class of 'prepare' utility. - */ -class Prepare -{ - public: - using EdgeData = QueryEdge::EdgeData; - - explicit Prepare(ContractorConfig contractor_config) : config(std::move(contractor_config)) {} - Prepare(const Prepare &) = delete; - ~Prepare(); - - int Run(); - - protected: - void ContractGraph(const unsigned max_edge_id, - DeallocatingVector &edge_based_edge_list, - DeallocatingVector &contracted_edge_list, - std::vector &is_core_node, - std::vector &node_levels) const; - void WriteCoreNodeMarker(std::vector &&is_core_node) const; - void WriteNodeLevels(std::vector &&node_levels) const; - void ReadNodeLevels(std::vector &contraction_order) const; - std::size_t WriteContractedGraph(unsigned number_of_edge_based_nodes, - const DeallocatingVector &contracted_edge_list); - void FindComponents(unsigned max_edge_id, - const DeallocatingVector &edges, - std::vector &nodes) const; - - private: - ContractorConfig config; - std::size_t LoadEdgeExpandedGraph(const std::string &edge_based_graph_path, - DeallocatingVector &edge_based_edge_list, - const std::string &edge_segment_lookup_path, - const std::string &edge_penalty_path, - const std::string &segment_speed_path); -}; - -#endif // PROCESSING_CHAIN_HPP diff --git a/cucumber.js b/cucumber.js new file mode 100644 index 000000000..5a87dd7f1 --- /dev/null +++ b/cucumber.js @@ -0,0 +1,11 @@ +module.exports = { + default: '--require features --tags ~@stress --tags ~@todo', + verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress', + jenkins: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress', + bugs: '--require features --tags @bug', + todo: '--require features --tags @todo', + all: '--require features' +} + + + diff --git a/data_structures/compressed_edge_container.hpp b/data_structures/compressed_edge_container.hpp deleted file mode 100644 index 5d94ee6de..000000000 --- a/data_structures/compressed_edge_container.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef GEOMETRY_COMPRESSOR_HPP_ -#define GEOMETRY_COMPRESSOR_HPP_ - -#include "../typedefs.h" - -#include - -#include -#include - -class CompressedEdgeContainer -{ - public: - using CompressedNode = std::pair; - using EdgeBucket = std::vector; - - CompressedEdgeContainer(); - void CompressEdge(const EdgeID surviving_edge_id, - const EdgeID removed_edge_id, - const NodeID via_node_id, - const NodeID target_node, - const EdgeWeight weight1, - const EdgeWeight weight2); - - bool HasEntryForID(const EdgeID edge_id) const; - void PrintStatistics() const; - void SerializeInternalVector(const std::string &path) const; - unsigned GetPositionForID(const EdgeID edge_id) const; - const EdgeBucket& GetBucketReference(const EdgeID edge_id) const; - NodeID GetFirstEdgeTargetID(const EdgeID edge_id) const; - NodeID GetLastEdgeSourceID(const EdgeID edge_id) const; - - private: - int free_list_maximum = 0; - - void IncreaseFreeList(); - std::vector m_compressed_geometries; - std::vector m_free_list; - std::unordered_map m_edge_id_to_list_index_map; -}; - -#endif // GEOMETRY_COMPRESSOR_HPP_ diff --git a/data_structures/coordinate.cpp b/data_structures/coordinate.cpp deleted file mode 100644 index 208f3eb07..000000000 --- a/data_structures/coordinate.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "../algorithms/coordinate_calculation.hpp" - -#ifndef NDEBUG -#include "../util/simple_logger.hpp" -#endif -#include - -#ifndef NDEBUG -#include -#endif -#include -#include - -FixedPointCoordinate::FixedPointCoordinate() - : lat(std::numeric_limits::min()), lon(std::numeric_limits::min()) -{ -} - -FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon) -{ -#ifndef NDEBUG - if (0 != (std::abs(lat) >> 30)) - { - std::bitset<32> y_coordinate_vector(lat); - SimpleLogger().Write(logDEBUG) << "broken lat: " << lat - << ", bits: " << y_coordinate_vector; - } - if (0 != (std::abs(lon) >> 30)) - { - std::bitset<32> x_coordinate_vector(lon); - SimpleLogger().Write(logDEBUG) << "broken lon: " << lon - << ", bits: " << x_coordinate_vector; - } -#endif -} - -bool FixedPointCoordinate::is_valid() const -{ - if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION || - lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION) - { - return false; - } - return true; -} - -bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const -{ - return lat == other.lat && lon == other.lon; -} - -void FixedPointCoordinate::output(std::ostream &out) const -{ - out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; -} - -float FixedPointCoordinate::bearing(const FixedPointCoordinate &other) const -{ - return coordinate_calculation::bearing(other, *this); -} diff --git a/data_structures/edge_based_node.hpp b/data_structures/edge_based_node.hpp deleted file mode 100644 index 8efbf0175..000000000 --- a/data_structures/edge_based_node.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef EDGE_BASED_NODE_HPP -#define EDGE_BASED_NODE_HPP - -#include "../data_structures/travel_mode.hpp" -#include "../typedefs.h" - -#include - -#include - -#include - -/// This is what StaticRTree serialized and stores on disk -/// It is generated in EdgeBasedGraphFactory. -struct EdgeBasedNode -{ - EdgeBasedNode() - : forward_edge_based_node_id(SPECIAL_NODEID), reverse_edge_based_node_id(SPECIAL_NODEID), - u(SPECIAL_NODEID), v(SPECIAL_NODEID), name_id(0), - forward_weight(INVALID_EDGE_WEIGHT >> 1), reverse_weight(INVALID_EDGE_WEIGHT >> 1), - forward_offset(0), reverse_offset(0), packed_geometry_id(SPECIAL_EDGEID), - component{INVALID_COMPONENTID, false}, fwd_segment_position(std::numeric_limits::max()), - forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), - backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) - { - } - - explicit EdgeBasedNode(NodeID forward_edge_based_node_id, - NodeID reverse_edge_based_node_id, - NodeID u, - NodeID v, - unsigned name_id, - int forward_weight, - int reverse_weight, - int forward_offset, - int reverse_offset, - unsigned packed_geometry_id, - bool is_tiny_component, - unsigned component_id, - unsigned short fwd_segment_position, - TravelMode forward_travel_mode, - TravelMode backward_travel_mode) - : forward_edge_based_node_id(forward_edge_based_node_id), - reverse_edge_based_node_id(reverse_edge_based_node_id), u(u), v(v), name_id(name_id), - forward_weight(forward_weight), reverse_weight(reverse_weight), - forward_offset(forward_offset), reverse_offset(reverse_offset), - packed_geometry_id(packed_geometry_id), component{component_id, is_tiny_component}, - fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode), - backward_travel_mode(backward_travel_mode) - { - BOOST_ASSERT((forward_edge_based_node_id != SPECIAL_NODEID) || - (reverse_edge_based_node_id != SPECIAL_NODEID)); - } - - static inline FixedPointCoordinate Centroid(const FixedPointCoordinate &a, - const FixedPointCoordinate &b) - { - FixedPointCoordinate centroid; - // The coordinates of the midpoint are given by: - centroid.lat = (a.lat + b.lat) / 2; - centroid.lon = (a.lon + b.lon) / 2; - return centroid; - } - - bool IsCompressed() const { return packed_geometry_id != SPECIAL_EDGEID; } - - NodeID forward_edge_based_node_id; // needed for edge-expanded graph - NodeID reverse_edge_based_node_id; // needed for edge-expanded graph - NodeID u; // indices into the coordinates array - NodeID v; // indices into the coordinates array - unsigned name_id; // id of the edge name - int forward_weight; // weight of the edge - int reverse_weight; // weight in the other direction (may be different) - int forward_offset; // prefix sum of the weight up the edge TODO: short must suffice - int reverse_offset; // prefix sum of the weight from the edge TODO: short must suffice - unsigned packed_geometry_id; // if set, then the edge represents a packed geometry - struct { - unsigned id : 31; - bool is_tiny : 1; - } component; - unsigned short fwd_segment_position; // segment id in a compressed geometry - TravelMode forward_travel_mode : 4; - TravelMode backward_travel_mode : 4; -}; - -#endif // EDGE_BASED_NODE_HPP diff --git a/data_structures/external_memory_node.cpp b/data_structures/external_memory_node.cpp deleted file mode 100644 index d144f52a9..000000000 --- a/data_structures/external_memory_node.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "external_memory_node.hpp" -#include "query_node.hpp" - -#include - -ExternalMemoryNode::ExternalMemoryNode( - int lat, int lon, OSMNodeID node_id, bool barrier, bool traffic_lights) - : QueryNode(lat, lon, node_id), barrier(barrier), traffic_lights(traffic_lights) -{ -} - -ExternalMemoryNode::ExternalMemoryNode() : barrier(false), traffic_lights(false) {} - -ExternalMemoryNode ExternalMemoryNode::min_value() -{ - return ExternalMemoryNode(0, 0, MIN_OSM_NODEID, false, false); -} - -ExternalMemoryNode ExternalMemoryNode::max_value() -{ - return ExternalMemoryNode(std::numeric_limits::max(), std::numeric_limits::max(), - MAX_OSM_NODEID, false, false); -} - -bool ExternalMemoryNodeSTXXLCompare::operator()(const ExternalMemoryNode &left, - const ExternalMemoryNode &right) const -{ - return left.node_id < right.node_id; -} - -ExternalMemoryNodeSTXXLCompare::value_type ExternalMemoryNodeSTXXLCompare::max_value() -{ - return ExternalMemoryNode::max_value(); -} - -ExternalMemoryNodeSTXXLCompare::value_type ExternalMemoryNodeSTXXLCompare::min_value() -{ - return ExternalMemoryNode::min_value(); -} diff --git a/data_structures/external_memory_node.hpp b/data_structures/external_memory_node.hpp deleted file mode 100644 index a48d1a13a..000000000 --- a/data_structures/external_memory_node.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef EXTERNAL_MEMORY_NODE_HPP_ -#define EXTERNAL_MEMORY_NODE_HPP_ - -#include "query_node.hpp" - -#include "../typedefs.h" - -struct ExternalMemoryNode : QueryNode -{ - ExternalMemoryNode(int lat, int lon, OSMNodeID id, bool barrier, bool traffic_light); - - ExternalMemoryNode(); - - static ExternalMemoryNode min_value(); - - static ExternalMemoryNode max_value(); - - bool barrier; - bool traffic_lights; -}; - -struct ExternalMemoryNodeSTXXLCompare -{ - using value_type = ExternalMemoryNode; - bool operator()(const ExternalMemoryNode &left, const ExternalMemoryNode &right) const; - value_type max_value(); - value_type min_value(); -}; - -#endif /* EXTERNAL_MEMORY_NODE_HPP_ */ diff --git a/data_structures/fixed_point_number.hpp b/data_structures/fixed_point_number.hpp deleted file mode 100644 index c7ed257e5..000000000 --- a/data_structures/fixed_point_number.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef FIXED_POINT_NUMBER_HPP -#define FIXED_POINT_NUMBER_HPP - -#include -#include - -#include -#include -#include -#include - -namespace osrm -{ - -// implements an binary based fixed point number type -template -class FixedPointNumber -{ - static_assert(FractionalBitSize > 0, "FractionalBitSize must be greater than 0"); - static_assert(FractionalBitSize <= 32, "FractionalBitSize must at most 32"); - - typename std::conditional::type m_fixed_point_state; - constexpr static const decltype(m_fixed_point_state) PRECISION = 1 << FractionalBitSize; - - // state signage encapsulates whether the state should either represent a - // signed or an unsigned floating point number - using state_signage = - typename std::conditional::type, - decltype(m_fixed_point_state)>::type; - - public: - FixedPointNumber() : m_fixed_point_state(0) {} - - // the type is either initialized with a floating point value or an - // integral state. Anything else will throw at compile-time. - template - constexpr FixedPointNumber(const T &&input) noexcept - : m_fixed_point_state(static_cast( - std::round(std::forward(input) * PRECISION))) - { - static_assert( - std::is_floating_point::value || std::is_integral::value, - "FixedPointNumber needs to be initialized with floating point or integral value"); - } - - // get max value - template ::value>::type * = nullptr> - constexpr static auto max() noexcept -> T - { - return static_cast(std::numeric_limits::max()) / PRECISION; - } - - // get min value - template ::value>::type * = nullptr> - constexpr static auto min() noexcept -> T - { - return static_cast(1) / PRECISION; - } - - // get lowest value - template ::value>::type * = nullptr> - constexpr static auto lowest() noexcept -> T - { - return static_cast(std::numeric_limits::min()) / PRECISION; - } - - // cast to floating point type T, return value - template ::value>::type * = nullptr> - explicit operator const T() const noexcept - { - // casts to external type (signed or unsigned) and then to float - return static_cast(static_cast(m_fixed_point_state)) / PRECISION; - } - - // warn about cast to integral type T, its disabled for good reason - template ::value>::type * = nullptr> - explicit operator T() const - { - static_assert(std::is_integral::value, - "casts to integral types have been disabled on purpose"); - } - - // compare, ie. sort fixed-point numbers - bool operator<(const FixedPointNumber &other) const noexcept - { - return m_fixed_point_state < other.m_fixed_point_state; - } - - // equality, ie. sort fixed-point numbers - bool operator==(const FixedPointNumber &other) const noexcept - { - return m_fixed_point_state == other.m_fixed_point_state; - } - - bool operator!=(const FixedPointNumber &other) const { return !(*this == other); } - bool operator>(const FixedPointNumber &other) const { return other < *this; } - bool operator<=(const FixedPointNumber &other) const { return !(other < *this); } - bool operator>=(const FixedPointNumber &other) const { return !(*this < other); } - - // arithmetic operators - FixedPointNumber operator+(const FixedPointNumber &other) const noexcept - { - FixedPointNumber tmp = *this; - tmp.m_fixed_point_state += other.m_fixed_point_state; - return tmp; - } - - FixedPointNumber &operator+=(const FixedPointNumber &other) noexcept - { - this->m_fixed_point_state += other.m_fixed_point_state; - return *this; - } - - FixedPointNumber operator-(const FixedPointNumber &other) const noexcept - { - FixedPointNumber tmp = *this; - tmp.m_fixed_point_state -= other.m_fixed_point_state; - return tmp; - } - - FixedPointNumber &operator-=(const FixedPointNumber &other) noexcept - { - this->m_fixed_point_state -= other.m_fixed_point_state; - return *this; - } - - FixedPointNumber operator*(const FixedPointNumber &other) const noexcept - { - int64_t temp = this->m_fixed_point_state; - temp *= other.m_fixed_point_state; - - // rounding! - if (!truncate_results) - { - temp = temp + ((temp & 1 << (FractionalBitSize - 1)) << 1); - } - temp >>= FractionalBitSize; - FixedPointNumber tmp; - tmp.m_fixed_point_state = static_cast(temp); - return tmp; - } - - FixedPointNumber &operator*=(const FixedPointNumber &other) noexcept - { - int64_t temp = this->m_fixed_point_state; - temp *= other.m_fixed_point_state; - - // rounding! - if (!truncate_results) - { - temp = temp + ((temp & 1 << (FractionalBitSize - 1)) << 1); - } - temp >>= FractionalBitSize; - this->m_fixed_point_state = static_cast(temp); - return *this; - } - - FixedPointNumber operator/(const FixedPointNumber &other) const noexcept - { - int64_t temp = this->m_fixed_point_state; - temp <<= FractionalBitSize; - temp /= static_cast(other.m_fixed_point_state); - FixedPointNumber tmp; - tmp.m_fixed_point_state = static_cast(temp); - return tmp; - } - - FixedPointNumber &operator/=(const FixedPointNumber &other) noexcept - { - int64_t temp = this->m_fixed_point_state; - temp <<= FractionalBitSize; - temp /= static_cast(other.m_fixed_point_state); - FixedPointNumber tmp; - this->m_fixed_point_state = static_cast(temp); - return *this; - } -}; - -static_assert(4 == sizeof(FixedPointNumber<1>), "FP19 has wrong size != 4"); -} -#endif // FIXED_POINT_NUMBER_HPP diff --git a/data_structures/hidden_markov_model.hpp b/data_structures/hidden_markov_model.hpp deleted file mode 100644 index e3efcea08..000000000 --- a/data_structures/hidden_markov_model.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef HIDDEN_MARKOV_MODEL -#define HIDDEN_MARKOV_MODEL - -#include "../util/integer_range.hpp" - -#include - -#include - -#include -#include - -namespace osrm -{ -namespace matching -{ -static const double log_2_pi = std::log(2. * M_PI); -static const double IMPOSSIBLE_LOG_PROB = -std::numeric_limits::infinity(); -static const double MINIMAL_LOG_PROB = std::numeric_limits::lowest(); -static const std::size_t INVALID_STATE = std::numeric_limits::max(); -} // namespace matching -} // namespace osrm - -// closures to precompute log -> only simple floating point operations -struct EmissionLogProbability -{ - double sigma_z; - double log_sigma_z; - - EmissionLogProbability(const double sigma_z) : sigma_z(sigma_z), log_sigma_z(std::log(sigma_z)) - { - } - - double operator()(const double distance) const - { - return -0.5 * (osrm::matching::log_2_pi + (distance / sigma_z) * (distance / sigma_z)) - - log_sigma_z; - } -}; - -struct TransitionLogProbability -{ - double beta; - double log_beta; - TransitionLogProbability(const double beta) : beta(beta), log_beta(std::log(beta)) {} - - double operator()(const double d_t) const { return -log_beta - d_t / beta; } -}; - -template struct HiddenMarkovModel -{ - std::vector> viterbi; - std::vector>> parents; - std::vector> path_lengths; - std::vector> pruned; - std::vector> suspicious; - std::vector breakage; - - const CandidateLists &candidates_list; - const EmissionLogProbability &emission_log_probability; - - HiddenMarkovModel(const CandidateLists &candidates_list, - const EmissionLogProbability &emission_log_probability) - : breakage(candidates_list.size()), candidates_list(candidates_list), - emission_log_probability(emission_log_probability) - { - viterbi.resize(candidates_list.size()); - parents.resize(candidates_list.size()); - path_lengths.resize(candidates_list.size()); - suspicious.resize(candidates_list.size()); - pruned.resize(candidates_list.size()); - breakage.resize(candidates_list.size()); - for (const auto i : osrm::irange(0u, candidates_list.size())) - { - const auto& num_candidates = candidates_list[i].size(); - // add empty vectors - if (num_candidates > 0) - { - viterbi[i].resize(num_candidates); - parents[i].resize(num_candidates); - path_lengths[i].resize(num_candidates); - suspicious[i].resize(num_candidates); - pruned[i].resize(num_candidates); - } - } - - clear(0); - } - - void clear(std::size_t initial_timestamp) - { - BOOST_ASSERT(viterbi.size() == parents.size() && parents.size() == path_lengths.size() && - path_lengths.size() == pruned.size() && pruned.size() == breakage.size()); - - for (const auto t : osrm::irange(initial_timestamp, viterbi.size())) - { - std::fill(viterbi[t].begin(), viterbi[t].end(), osrm::matching::IMPOSSIBLE_LOG_PROB); - std::fill(parents[t].begin(), parents[t].end(), std::make_pair(0u, 0u)); - std::fill(path_lengths[t].begin(), path_lengths[t].end(), 0); - std::fill(suspicious[t].begin(), suspicious[t].end(), true); - std::fill(pruned[t].begin(), pruned[t].end(), true); - } - std::fill(breakage.begin() + initial_timestamp, breakage.end(), true); - } - - std::size_t initialize(std::size_t initial_timestamp) - { - auto num_points = candidates_list.size(); - do - { - BOOST_ASSERT(initial_timestamp < num_points); - - for (const auto s : osrm::irange(0u, viterbi[initial_timestamp].size())) - { - viterbi[initial_timestamp][s] = - emission_log_probability(candidates_list[initial_timestamp][s].distance); - parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s); - pruned[initial_timestamp][s] = - viterbi[initial_timestamp][s] < osrm::matching::MINIMAL_LOG_PROB; - suspicious[initial_timestamp][s] = false; - - breakage[initial_timestamp] = - breakage[initial_timestamp] && pruned[initial_timestamp][s]; - } - - ++initial_timestamp; - } while (initial_timestamp < num_points && breakage[initial_timestamp - 1]); - - if (initial_timestamp >= num_points) - { - return osrm::matching::INVALID_STATE; - } - - BOOST_ASSERT(initial_timestamp > 0); - --initial_timestamp; - - BOOST_ASSERT(breakage[initial_timestamp] == false); - - return initial_timestamp; - } -}; - -#endif // HIDDEN_MARKOV_MODEL diff --git a/data_structures/hilbert_value.cpp b/data_structures/hilbert_value.cpp deleted file mode 100644 index d0d61e60a..000000000 --- a/data_structures/hilbert_value.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "hilbert_value.hpp" - -#include - -uint64_t HilbertCode::operator()(const FixedPointCoordinate ¤t_coordinate) const -{ - unsigned location[2]; - location[0] = current_coordinate.lat + static_cast(90 * COORDINATE_PRECISION); - location[1] = current_coordinate.lon + static_cast(180 * COORDINATE_PRECISION); - - TransposeCoordinate(location); - return BitInterleaving(location[0], location[1]); -} - -uint64_t HilbertCode::BitInterleaving(const uint32_t latitude, const uint32_t longitude) const -{ - uint64_t result = 0; - for (int8_t index = 31; index >= 0; --index) - { - result |= (latitude >> index) & 1; - result <<= 1; - result |= (longitude >> index) & 1; - if (0 != index) - { - result <<= 1; - } - } - return result; -} - -void HilbertCode::TransposeCoordinate(uint32_t *X) const -{ - uint32_t M = 1u << (32 - 1), P, Q, t; - int i; - // Inverse undo - for (Q = M; Q > 1; Q >>= 1) - { - P = Q - 1; - for (i = 0; i < 2; ++i) - { - - const bool condition = (X[i] & Q); - if (condition) - { - X[0] ^= P; // invert - } - else - { - t = (X[0] ^ X[i]) & P; - X[0] ^= t; - X[i] ^= t; - } - } // exchange - } - // Gray encode - for (i = 1; i < 2; ++i) - { - X[i] ^= X[i - 1]; - } - t = 0; - for (Q = M; Q > 1; Q >>= 1) - { - const bool condition = (X[2 - 1] & Q); - if (condition) - { - t ^= Q - 1; - } - } // check if this for loop is wrong - for (i = 0; i < 2; ++i) - { - X[i] ^= t; - } -} diff --git a/data_structures/hilbert_value.hpp b/data_structures/hilbert_value.hpp deleted file mode 100644 index 7b8bffa93..000000000 --- a/data_structures/hilbert_value.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef HILBERT_VALUE_HPP -#define HILBERT_VALUE_HPP - -#include - -// computes a 64 bit value that corresponds to the hilbert space filling curve - -struct FixedPointCoordinate; - -class HilbertCode -{ - public: - uint64_t operator()(const FixedPointCoordinate ¤t_coordinate) const; - HilbertCode() {} - HilbertCode(const HilbertCode &) = delete; - - private: - inline uint64_t BitInterleaving(const uint32_t a, const uint32_t b) const; - inline void TransposeCoordinate(uint32_t *X) const; -}; - -#endif /* HILBERT_VALUE_HPP */ diff --git a/data_structures/import_edge.cpp b/data_structures/import_edge.cpp deleted file mode 100644 index bf2829d4e..000000000 --- a/data_structures/import_edge.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "import_edge.hpp" - -#include "travel_mode.hpp" -#include "../typedefs.h" - -bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const -{ - if (source == other.source) - { - if (target == other.target) - { - if (weight == other.weight) - { - return forward && backward && ((!other.forward) || (!other.backward)); - } - return weight < other.weight; - } - return target < other.target; - } - return source < other.source; -} - -NodeBasedEdge::NodeBasedEdge() - : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false), - backward(false), roundabout(false), - access_restricted(false), startpoint(true), is_split(false), travel_mode(false) -{ -} - -NodeBasedEdge::NodeBasedEdge(NodeID source, - NodeID target, - NodeID name_id, - EdgeWeight weight, - bool forward, - bool backward, - bool roundabout, - bool access_restricted, - bool startpoint, - TravelMode travel_mode, - bool is_split) - : source(source), target(target), name_id(name_id), weight(weight), forward(forward), - backward(backward), roundabout(roundabout), - access_restricted(access_restricted), startpoint(startpoint), is_split(is_split), travel_mode(travel_mode) -{ -} - -bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const -{ - if (source == other.source) - { - if (target == other.target) - { - if (weight == other.weight) - { - return forward && backward && ((!other.forward) || (!other.backward)); - } - return weight < other.weight; - } - return target < other.target; - } - return source < other.source; -} - -template -EdgeBasedEdge::EdgeBasedEdge(const EdgeT &other) - : source(other.source), target(other.target), edge_id(other.data.via), - weight(other.data.distance), forward(other.data.forward), backward(other.data.backward) -{ -} - -/** Default constructor. target and weight are set to 0.*/ -EdgeBasedEdge::EdgeBasedEdge() - : source(0), target(0), edge_id(0), weight(0), forward(false), backward(false) -{ -} - -EdgeBasedEdge::EdgeBasedEdge(const NodeID source, - const NodeID target, - const NodeID edge_id, - const EdgeWeight weight, - const bool forward, - const bool backward) - : source(source), target(target), edge_id(edge_id), weight(weight), forward(forward), - backward(backward) -{ -} diff --git a/data_structures/import_edge.hpp b/data_structures/import_edge.hpp deleted file mode 100644 index 449ded2a1..000000000 --- a/data_structures/import_edge.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef IMPORT_EDGE_HPP -#define IMPORT_EDGE_HPP - -#include "../data_structures/travel_mode.hpp" -#include "../typedefs.h" - -struct NodeBasedEdge -{ - bool operator<(const NodeBasedEdge &e) const; - - NodeBasedEdge(); - explicit NodeBasedEdge(NodeID source, - NodeID target, - NodeID name_id, - EdgeWeight weight, - bool forward, - bool backward, - bool roundabout, - bool access_restricted, - bool startpoint, - TravelMode travel_mode, - bool is_split); - - NodeID source; - NodeID target; - NodeID name_id; - EdgeWeight weight; - bool forward : 1; - bool backward : 1; - bool roundabout : 1; - bool access_restricted : 1; - bool startpoint : 1; - bool is_split : 1; - TravelMode travel_mode : 4; -}; - -struct NodeBasedEdgeWithOSM : NodeBasedEdge -{ - explicit NodeBasedEdgeWithOSM(OSMNodeID source, - OSMNodeID target, - NodeID name_id, - EdgeWeight weight, - bool forward, - bool backward, - bool roundabout, - bool access_restricted, - bool startpoint, - TravelMode travel_mode, - bool is_split) - : NodeBasedEdge(SPECIAL_NODEID, SPECIAL_NODEID, name_id, weight, forward, backward, roundabout, access_restricted, startpoint, travel_mode, is_split), - osm_source_id(source), osm_target_id(target) {} - - OSMNodeID osm_source_id; - OSMNodeID osm_target_id; -}; - -struct EdgeBasedEdge -{ - - public: - bool operator<(const EdgeBasedEdge &e) const; - - template explicit EdgeBasedEdge(const EdgeT &myEdge); - - EdgeBasedEdge(); - - explicit EdgeBasedEdge(const NodeID source, - const NodeID target, - const NodeID edge_id, - const EdgeWeight weight, - const bool forward, - const bool backward); - NodeID source; - NodeID target; - NodeID edge_id; - EdgeWeight weight : 30; - bool forward : 1; - bool backward : 1; -}; - -#endif /* IMPORT_EDGE_HPP */ diff --git a/data_structures/internal_route_result.hpp b/data_structures/internal_route_result.hpp deleted file mode 100644 index 068b63a80..000000000 --- a/data_structures/internal_route_result.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef RAW_ROUTE_DATA_H -#define RAW_ROUTE_DATA_H - -#include "../data_structures/phantom_node.hpp" -#include "../data_structures/travel_mode.hpp" -#include "../data_structures/turn_instructions.hpp" -#include "../typedefs.h" - -#include - -#include - -struct PathData -{ - PathData() - : node(SPECIAL_NODEID), name_id(INVALID_EDGE_WEIGHT), segment_duration(INVALID_EDGE_WEIGHT), - turn_instruction(TurnInstruction::NoTurn), travel_mode(TRAVEL_MODE_INACCESSIBLE) - { - } - - PathData(NodeID node, - unsigned name_id, - TurnInstruction turn_instruction, - EdgeWeight segment_duration, - TravelMode travel_mode) - : node(node), name_id(name_id), segment_duration(segment_duration), - turn_instruction(turn_instruction), travel_mode(travel_mode) - { - } - NodeID node; - unsigned name_id; - EdgeWeight segment_duration; - TurnInstruction turn_instruction; - TravelMode travel_mode : 4; -}; - -struct InternalRouteResult -{ - std::vector> unpacked_path_segments; - std::vector unpacked_alternative; - std::vector segment_end_coordinates; - std::vector source_traversed_in_reverse; - std::vector target_traversed_in_reverse; - std::vector alt_source_traversed_in_reverse; - std::vector alt_target_traversed_in_reverse; - int shortest_path_length; - int alternative_path_length; - - bool is_via_leg(const std::size_t leg) const - { - return (leg != unpacked_path_segments.size() - 1); - } - - InternalRouteResult() - : shortest_path_length(INVALID_EDGE_WEIGHT), alternative_path_length(INVALID_EDGE_WEIGHT) - { - } -}; - -#endif // RAW_ROUTE_DATA_H diff --git a/data_structures/lru_cache.hpp b/data_structures/lru_cache.hpp deleted file mode 100644 index 155ab1ef4..000000000 --- a/data_structures/lru_cache.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef LRUCACHE_HPP -#define LRUCACHE_HPP - -#include -#include - -template class LRUCache -{ - private: - struct CacheEntry - { - CacheEntry(KeyT k, ValueT v) : key(k), value(v) {} - KeyT key; - ValueT value; - }; - unsigned capacity; - std::list itemsInCache; - std::unordered_map::iterator> positionMap; - - public: - explicit LRUCache(unsigned c) : capacity(c) {} - - bool Holds(KeyT key) - { - if (positionMap.find(key) != positionMap.end()) - { - return true; - } - return false; - } - - void Insert(const KeyT key, ValueT &value) - { - itemsInCache.push_front(CacheEntry(key, value)); - positionMap.insert(std::make_pair(key, itemsInCache.begin())); - if (itemsInCache.size() > capacity) - { - positionMap.erase(itemsInCache.back().key); - itemsInCache.pop_back(); - } - } - - void Insert(const KeyT key, ValueT value) - { - itemsInCache.push_front(CacheEntry(key, value)); - positionMap.insert(std::make_pair(key, itemsInCache.begin())); - if (itemsInCache.size() > capacity) - { - positionMap.erase(itemsInCache.back().key); - itemsInCache.pop_back(); - } - } - - bool Fetch(const KeyT key, ValueT &result) - { - if (Holds(key)) - { - CacheEntry e = *(positionMap.find(key)->second); - result = e.value; - - // move to front - itemsInCache.splice(itemsInCache.begin(), itemsInCache, positionMap.find(key)->second); - positionMap.find(key)->second = itemsInCache.begin(); - return true; - } - return false; - } - unsigned Size() const { return itemsInCache.size(); } -}; -#endif // LRUCACHE_HPP diff --git a/data_structures/matrix_graph_wrapper.hpp b/data_structures/matrix_graph_wrapper.hpp deleted file mode 100644 index f8b3e65bb..000000000 --- a/data_structures/matrix_graph_wrapper.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef MATRIX_GRAPH_WRAPPER_H -#define MATRIX_GRAPH_WRAPPER_H - -#include -#include -#include - -#include "../typedefs.h" - -// This Wrapper provides all methods that are needed for TarjanSCC, when the graph is given in a -// matrix representation (e.g. as output from a distance table call) - -template class MatrixGraphWrapper -{ - public: - MatrixGraphWrapper(std::vector table, const std::size_t number_of_nodes) - : table_(std::move(table)), number_of_nodes_(number_of_nodes){}; - - std::size_t GetNumberOfNodes() const { return number_of_nodes_; } - - std::vector GetAdjacentEdgeRange(const NodeID node) const - { - - std::vector edges; - // find all valid adjacent edges and move to vector `edges` - for (std::size_t i = 0; i < number_of_nodes_; ++i) - { - if (*(std::begin(table_) + node * number_of_nodes_ + i) != INVALID_EDGE_WEIGHT) - { - edges.push_back(i); - } - } - return edges; - } - - EdgeWeight GetTarget(const EdgeWeight edge) const { return edge; } - - private: - const std::vector table_; - const std::size_t number_of_nodes_; -}; - -#endif // MATRIX_GRAPH_WRAPPER_H diff --git a/data_structures/node_based_graph.hpp b/data_structures/node_based_graph.hpp deleted file mode 100644 index e58cfceb4..000000000 --- a/data_structures/node_based_graph.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef NODE_BASED_GRAPH_HPP -#define NODE_BASED_GRAPH_HPP - -#include "dynamic_graph.hpp" -#include "import_edge.hpp" -#include "../util/graph_utils.hpp" - -#include - -#include - -struct NodeBasedEdgeData -{ - NodeBasedEdgeData() - : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), - name_id(std::numeric_limits::max()), access_restricted(false), - reversed(false), roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE) - { - } - - NodeBasedEdgeData(int distance, unsigned edge_id, unsigned name_id, - bool access_restricted, bool reversed, - bool roundabout, bool startpoint, TravelMode travel_mode) - : distance(distance), edge_id(edge_id), name_id(name_id), - access_restricted(access_restricted), reversed(reversed), - roundabout(roundabout), startpoint(startpoint), travel_mode(travel_mode) - { - } - - int distance; - unsigned edge_id; - unsigned name_id; - bool access_restricted : 1; - bool reversed : 1; - bool roundabout : 1; - bool startpoint : 1; - TravelMode travel_mode : 4; - - bool IsCompatibleTo(const NodeBasedEdgeData &other) const - { - return (reversed == other.reversed) && (name_id == other.name_id) && - (travel_mode == other.travel_mode); - } -}; - -using NodeBasedDynamicGraph = DynamicGraph; - -/// Factory method to create NodeBasedDynamicGraph from NodeBasedEdges -/// Since DynamicGraph expects directed edges, we need to insert -/// two edges for undirected edges. -inline std::shared_ptr -NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes, const std::vector &input_edge_list) -{ - auto edges_list = directedEdgesFromCompressed(input_edge_list, - [](NodeBasedDynamicGraph::InputEdge& output_edge, const NodeBasedEdge& input_edge) - { - output_edge.data.distance = static_cast(input_edge.weight); - BOOST_ASSERT(output_edge.data.distance > 0); - - output_edge.data.roundabout = input_edge.roundabout; - output_edge.data.name_id = input_edge.name_id; - output_edge.data.access_restricted = input_edge.access_restricted; - output_edge.data.travel_mode = input_edge.travel_mode; - output_edge.data.startpoint = input_edge.startpoint; - } - ); - - tbb::parallel_sort(edges_list.begin(), edges_list.end()); - - auto graph = std::make_shared( - static_cast(number_of_nodes), edges_list); - - return graph; -} - -#endif // NODE_BASED_GRAPH_HPP diff --git a/data_structures/node_id.hpp b/data_structures/node_id.hpp deleted file mode 100644 index 0671a4d9d..000000000 --- a/data_structures/node_id.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef NODE_ID_HPP -#define NODE_ID_HPP - -#include "../typedefs.h" - -struct Cmp -{ - using value_type = OSMNodeID; - bool operator()(const value_type left, const value_type right) const { return left < right; } - value_type max_value() { return MAX_OSM_NODEID; } - value_type min_value() { return MIN_OSM_NODEID; } -}; - -#endif // NODE_ID_HPP diff --git a/data_structures/original_edge_data.hpp b/data_structures/original_edge_data.hpp deleted file mode 100644 index cbbc1b276..000000000 --- a/data_structures/original_edge_data.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef ORIGINAL_EDGE_DATA_HPP -#define ORIGINAL_EDGE_DATA_HPP - -#include "travel_mode.hpp" -#include "turn_instructions.hpp" -#include "../typedefs.h" - -#include - -struct OriginalEdgeData -{ - explicit OriginalEdgeData(NodeID via_node, - unsigned name_id, - TurnInstruction turn_instruction, - bool compressed_geometry, - TravelMode travel_mode) - : via_node(via_node), name_id(name_id), turn_instruction(turn_instruction), - compressed_geometry(compressed_geometry), travel_mode(travel_mode) - { - } - - OriginalEdgeData() - : via_node(std::numeric_limits::max()), - name_id(std::numeric_limits::max()), turn_instruction(TurnInstruction::NoTurn), - compressed_geometry(false), travel_mode(TRAVEL_MODE_INACCESSIBLE) - { - } - - NodeID via_node; - unsigned name_id; - TurnInstruction turn_instruction; - bool compressed_geometry; - TravelMode travel_mode; -}; - -#endif // ORIGINAL_EDGE_DATA_HPP diff --git a/data_structures/phantom_node.cpp b/data_structures/phantom_node.cpp deleted file mode 100644 index 95c5bbb4a..000000000 --- a/data_structures/phantom_node.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "phantom_node.hpp" - -#include "../typedefs.h" -#include "travel_mode.hpp" - -#include - -#include - -PhantomNode::PhantomNode(NodeID forward_node_id, - NodeID reverse_node_id, - unsigned name_id, - int forward_weight, - int reverse_weight, - int forward_offset, - int reverse_offset, - unsigned packed_geometry_id, - bool is_tiny_component, - unsigned component_id, - FixedPointCoordinate &location, - unsigned short fwd_segment_position, - TravelMode forward_travel_mode, - TravelMode backward_travel_mode) - : forward_node_id(forward_node_id), reverse_node_id(reverse_node_id), name_id(name_id), - forward_weight(forward_weight), reverse_weight(reverse_weight), - forward_offset(forward_offset), reverse_offset(reverse_offset), - packed_geometry_id(packed_geometry_id), component{component_id, is_tiny_component}, location(location), - fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode), - backward_travel_mode(backward_travel_mode) -{ -} - -PhantomNode::PhantomNode() - : forward_node_id(SPECIAL_NODEID), reverse_node_id(SPECIAL_NODEID), - name_id(std::numeric_limits::max()), forward_weight(INVALID_EDGE_WEIGHT), - reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0), - packed_geometry_id(SPECIAL_EDGEID), component{INVALID_COMPONENTID, false}, - fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), - backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) -{ -} - -int PhantomNode::GetForwardWeightPlusOffset() const -{ - if (SPECIAL_NODEID == forward_node_id) - { - return 0; - } - return forward_offset + forward_weight; -} - -int PhantomNode::GetReverseWeightPlusOffset() const -{ - if (SPECIAL_NODEID == reverse_node_id) - { - return 0; - } - return reverse_offset + reverse_weight; -} - -bool PhantomNode::is_bidirected() const -{ - return (forward_node_id != SPECIAL_NODEID) && (reverse_node_id != SPECIAL_NODEID); -} - -bool PhantomNode::is_compressed() const { return (forward_offset != 0) || (reverse_offset != 0); } - -bool PhantomNode::is_valid(const unsigned number_of_nodes) const -{ - return location.is_valid() && - ((forward_node_id < number_of_nodes) || (reverse_node_id < number_of_nodes)) && - ((forward_weight != INVALID_EDGE_WEIGHT) || (reverse_weight != INVALID_EDGE_WEIGHT)) && - (component.id != INVALID_COMPONENTID) && (name_id != INVALID_NAMEID); -} - -bool PhantomNode::is_valid() const { return location.is_valid() && (name_id != INVALID_NAMEID); } - -bool PhantomNode::operator==(const PhantomNode &other) const { return location == other.location; } diff --git a/data_structures/phantom_node.hpp b/data_structures/phantom_node.hpp deleted file mode 100644 index b12c4feb6..000000000 --- a/data_structures/phantom_node.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef PHANTOM_NODES_H -#define PHANTOM_NODES_H - -#include "travel_mode.hpp" -#include "../typedefs.h" - -#include - -#include -#include -#include - -struct PhantomNode -{ - PhantomNode(NodeID forward_node_id, - NodeID reverse_node_id, - unsigned name_id, - int forward_weight, - int reverse_weight, - int forward_offset, - int reverse_offset, - unsigned packed_geometry_id, - bool is_tiny_component, - unsigned component_id, - FixedPointCoordinate &location, - unsigned short fwd_segment_position, - TravelMode forward_travel_mode, - TravelMode backward_travel_mode); - - PhantomNode(); - - template PhantomNode(const OtherT &other, const FixedPointCoordinate &foot_point) - { - forward_node_id = other.forward_edge_based_node_id; - reverse_node_id = other.reverse_edge_based_node_id; - name_id = other.name_id; - - forward_weight = other.forward_weight; - reverse_weight = other.reverse_weight; - - forward_offset = other.forward_offset; - reverse_offset = other.reverse_offset; - - packed_geometry_id = other.packed_geometry_id; - - component.id = other.component.id; - component.is_tiny = other.component.is_tiny; - - location = foot_point; - fwd_segment_position = other.fwd_segment_position; - - forward_travel_mode = other.forward_travel_mode; - backward_travel_mode = other.backward_travel_mode; - } - - NodeID forward_node_id; - NodeID reverse_node_id; - unsigned name_id; - int forward_weight; - int reverse_weight; - int forward_offset; - int reverse_offset; - unsigned packed_geometry_id; - struct ComponentType { - uint32_t id : 31; - bool is_tiny : 1; - } component; -// bit-fields are broken on Windows -#ifndef _MSC_VER - static_assert(sizeof(ComponentType) == 4, "ComponentType needs to 4 bytes big"); -#endif - FixedPointCoordinate location; - unsigned short fwd_segment_position; - // note 4 bits would suffice for each, - // but the saved byte would be padding anyway - TravelMode forward_travel_mode; - TravelMode backward_travel_mode; - - int GetForwardWeightPlusOffset() const; - - int GetReverseWeightPlusOffset() const; - - bool is_bidirected() const; - - bool is_compressed() const; - - bool is_valid(const unsigned numberOfNodes) const; - - bool is_valid() const; - - bool operator==(const PhantomNode &other) const; -}; - -#ifndef _MSC_VER -static_assert(sizeof(PhantomNode) == 48, "PhantomNode has more padding then expected"); -#endif - -using PhantomNodePair = std::pair; - -struct PhantomNodeWithDistance -{ - PhantomNode phantom_node; - double distance; -}; - -struct PhantomNodes -{ - PhantomNode source_phantom; - PhantomNode target_phantom; -}; - -inline std::ostream &operator<<(std::ostream &out, const PhantomNodes &pn) -{ - out << "source_coord: " << pn.source_phantom.location << "\n"; - out << "target_coord: " << pn.target_phantom.location << std::endl; - return out; -} - -inline std::ostream &operator<<(std::ostream &out, const PhantomNode &pn) -{ - out << "node1: " << pn.forward_node_id << ", " - << "node2: " << pn.reverse_node_id << ", " - << "name: " << pn.name_id << ", " - << "fwd-w: " << pn.forward_weight << ", " - << "rev-w: " << pn.reverse_weight << ", " - << "fwd-o: " << pn.forward_offset << ", " - << "rev-o: " << pn.reverse_offset << ", " - << "geom: " << pn.packed_geometry_id << ", " - << "comp: " << pn.component.is_tiny << " / " << pn.component.id << ", " - << "pos: " << pn.fwd_segment_position << ", " - << "loc: " << pn.location; - return out; -} - -#endif // PHANTOM_NODES_H diff --git a/data_structures/query_node.hpp b/data_structures/query_node.hpp deleted file mode 100644 index 0f32a536f..000000000 --- a/data_structures/query_node.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef QUERY_NODE_HPP -#define QUERY_NODE_HPP - -#include "../typedefs.h" - -#include - -#include - -#include - -struct QueryNode -{ - using key_type = OSMNodeID; // type of NodeID - using value_type = int; // type of lat,lons - - explicit QueryNode(int lat, int lon, OSMNodeID node_id) : lat(lat), lon(lon), node_id(node_id) {} - QueryNode() - : lat(std::numeric_limits::max()), lon(std::numeric_limits::max()), - node_id(SPECIAL_OSM_NODEID) - { - } - - int lat; - int lon; - OSMNodeID node_id; - - static QueryNode min_value() - { - return QueryNode(static_cast(-90 * COORDINATE_PRECISION), - static_cast(-180 * COORDINATE_PRECISION), - MIN_OSM_NODEID); - } - - static QueryNode max_value() - { - return QueryNode(static_cast(90 * COORDINATE_PRECISION), - static_cast(180 * COORDINATE_PRECISION), - MAX_OSM_NODEID); - } - - value_type operator[](const std::size_t n) const - { - switch (n) - { - case 1: - return lat; - case 0: - return lon; - default: - break; - } - BOOST_ASSERT_MSG(false, "should not happen"); - return std::numeric_limits::lowest(); - } -}; - -#endif // QUERY_NODE_HPP diff --git a/data_structures/rectangle.hpp b/data_structures/rectangle.hpp deleted file mode 100644 index 7f6704a9b..000000000 --- a/data_structures/rectangle.hpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef RECTANGLE_HPP -#define RECTANGLE_HPP - -#include "../algorithms/coordinate_calculation.hpp" - -#include - -#include - -#include -#include -#include - -// TODO: Make template type, add tests -struct RectangleInt2D -{ - RectangleInt2D() - : min_lon(std::numeric_limits::max()), - max_lon(std::numeric_limits::min()), - min_lat(std::numeric_limits::max()), max_lat(std::numeric_limits::min()) - { - } - - int32_t min_lon, max_lon; - int32_t min_lat, max_lat; - - void MergeBoundingBoxes(const RectangleInt2D &other) - { - min_lon = std::min(min_lon, other.min_lon); - max_lon = std::max(max_lon, other.max_lon); - min_lat = std::min(min_lat, other.min_lat); - max_lat = std::max(max_lat, other.max_lat); - BOOST_ASSERT(min_lat != std::numeric_limits::min()); - BOOST_ASSERT(min_lon != std::numeric_limits::min()); - BOOST_ASSERT(max_lat != std::numeric_limits::min()); - BOOST_ASSERT(max_lon != std::numeric_limits::min()); - } - - FixedPointCoordinate Centroid() const - { - FixedPointCoordinate centroid; - // The coordinates of the midpoints are given by: - // x = (x1 + x2) /2 and y = (y1 + y2) /2. - centroid.lon = (min_lon + max_lon) / 2; - centroid.lat = (min_lat + max_lat) / 2; - return centroid; - } - - bool Intersects(const RectangleInt2D &other) const - { - FixedPointCoordinate upper_left(other.max_lat, other.min_lon); - FixedPointCoordinate upper_right(other.max_lat, other.max_lon); - FixedPointCoordinate lower_right(other.min_lat, other.max_lon); - FixedPointCoordinate lower_left(other.min_lat, other.min_lon); - - return (Contains(upper_left) || Contains(upper_right) || Contains(lower_right) || - Contains(lower_left)); - } - - float GetMinDist(const FixedPointCoordinate &location) const - { - const bool is_contained = Contains(location); - if (is_contained) - { - return 0.0f; - } - - enum Direction - { - INVALID = 0, - NORTH = 1, - SOUTH = 2, - EAST = 4, - NORTH_EAST = 5, - SOUTH_EAST = 6, - WEST = 8, - NORTH_WEST = 9, - SOUTH_WEST = 10 - }; - - Direction d = INVALID; - if (location.lat > max_lat) - d = (Direction)(d | NORTH); - else if (location.lat < min_lat) - d = (Direction)(d | SOUTH); - if (location.lon > max_lon) - d = (Direction)(d | EAST); - else if (location.lon < min_lon) - d = (Direction)(d | WEST); - - BOOST_ASSERT(d != INVALID); - - float min_dist = std::numeric_limits::max(); - switch (d) - { - case NORTH: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(max_lat, location.lon)); - break; - case SOUTH: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(min_lat, location.lon)); - break; - case WEST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(location.lat, min_lon)); - break; - case EAST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(location.lat, max_lon)); - break; - case NORTH_EAST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(max_lat, max_lon)); - break; - case NORTH_WEST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(max_lat, min_lon)); - break; - case SOUTH_EAST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(min_lat, max_lon)); - break; - case SOUTH_WEST: - min_dist = coordinate_calculation::great_circle_distance( - location, FixedPointCoordinate(min_lat, min_lon)); - break; - default: - break; - } - - BOOST_ASSERT(min_dist < std::numeric_limits::max()); - - return min_dist; - } - - float GetMinMaxDist(const FixedPointCoordinate &location) const - { - float min_max_dist = std::numeric_limits::max(); - // Get minmax distance to each of the four sides - const FixedPointCoordinate upper_left(max_lat, min_lon); - const FixedPointCoordinate upper_right(max_lat, max_lon); - const FixedPointCoordinate lower_right(min_lat, max_lon); - const FixedPointCoordinate lower_left(min_lat, min_lon); - - min_max_dist = std::min( - min_max_dist, - std::max(coordinate_calculation::great_circle_distance(location, upper_left), - coordinate_calculation::great_circle_distance(location, upper_right))); - - min_max_dist = std::min( - min_max_dist, - std::max(coordinate_calculation::great_circle_distance(location, upper_right), - coordinate_calculation::great_circle_distance(location, lower_right))); - - min_max_dist = - std::min(min_max_dist, - std::max(coordinate_calculation::great_circle_distance(location, lower_right), - coordinate_calculation::great_circle_distance(location, lower_left))); - - min_max_dist = - std::min(min_max_dist, - std::max(coordinate_calculation::great_circle_distance(location, lower_left), - coordinate_calculation::great_circle_distance(location, upper_left))); - return min_max_dist; - } - - bool Contains(const FixedPointCoordinate &location) const - { - const bool lats_contained = (location.lat >= min_lat) && (location.lat <= max_lat); - const bool lons_contained = (location.lon >= min_lon) && (location.lon <= max_lon); - return lats_contained && lons_contained; - } -}; - -#endif diff --git a/data_structures/route_parameters.cpp b/data_structures/route_parameters.cpp deleted file mode 100644 index 61789bbb4..000000000 --- a/data_structures/route_parameters.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include - -#include - -#include "../algorithms/polyline_compressor.hpp" - -RouteParameters::RouteParameters() - : zoom_level(18), print_instructions(false), alternate_route(true), geometry(true), - compression(true), deprecatedAPI(false), uturn_default(false), classify(false), - matching_beta(5), gps_precision(5), check_sum(-1), num_results(1) -{ -} - -void RouteParameters::setZoomLevel(const short level) -{ - if (18 >= level && 0 <= level) - { - zoom_level = level; - } -} - -void RouteParameters::setNumberOfResults(const short number) -{ - if (number > 0 && number <= 100) - { - num_results = number; - } -} - -void RouteParameters::setAlternateRouteFlag(const bool flag) { alternate_route = flag; } - -void RouteParameters::setUTurn(const bool flag) -{ - // the API grammar should make sure this never happens - BOOST_ASSERT(!uturns.empty()); - uturns.back() = flag; -} - -void RouteParameters::setAllUTurns(const bool flag) -{ - // if the flag flips the default, then we erase everything. - if (flag) - { - uturn_default = flag; - uturns.clear(); - uturns.resize(coordinates.size(), uturn_default); - } -} - -void RouteParameters::setDeprecatedAPIFlag(const std::string &) { deprecatedAPI = true; } - -void RouteParameters::setChecksum(const unsigned sum) { check_sum = sum; } - -void RouteParameters::setInstructionFlag(const bool flag) { print_instructions = flag; } - -void RouteParameters::setService(const std::string &service_string) { service = service_string; } - -void RouteParameters::setClassify(const bool flag) { classify = flag; } - -void RouteParameters::setMatchingBeta(const double beta) { matching_beta = beta; } - -void RouteParameters::setGPSPrecision(const double precision) { gps_precision = precision; } - -void RouteParameters::setOutputFormat(const std::string &format) { output_format = format; } - -void RouteParameters::setJSONpParameter(const std::string ¶meter) -{ - jsonp_parameter = parameter; -} - -void RouteParameters::addHint(const std::string &hint) -{ - hints.resize(coordinates.size()); - if (!hints.empty()) - { - hints.back() = hint; - } -} - -void RouteParameters::addTimestamp(const unsigned timestamp) -{ - timestamps.resize(coordinates.size()); - if (!timestamps.empty()) - { - timestamps.back() = timestamp; - } -} - -void RouteParameters::addBearing( - const boost::fusion::vector> &received_bearing, - boost::spirit::qi::unused_type /* unused */, bool& pass) -{ - pass = false; - const int bearing = boost::fusion::at_c<0>(received_bearing); - const boost::optional range = boost::fusion::at_c<1>(received_bearing); - if (bearing < 0 || bearing > 359) return; - if (range && (*range < 0 || *range > 180)) return; - bearings.emplace_back(std::make_pair(bearing,range)); - pass = true; -} - -void RouteParameters::setLanguage(const std::string &language_string) -{ - language = language_string; -} - -void RouteParameters::setGeometryFlag(const bool flag) { geometry = flag; } - -void RouteParameters::setCompressionFlag(const bool flag) { compression = flag; } - -void RouteParameters::addCoordinate( - const boost::fusion::vector &received_coordinates) -{ - coordinates.emplace_back( - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<0>(received_coordinates)), - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); - is_source.push_back(true); - is_destination.push_back(true); - uturns.push_back(uturn_default); -} - -void RouteParameters::addDestination( - const boost::fusion::vector &received_coordinates) -{ - coordinates.emplace_back( - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<0>(received_coordinates)), - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); - is_source.push_back(false); - is_destination.push_back(true); - uturns.push_back(uturn_default); -} - -void RouteParameters::addSource( - const boost::fusion::vector &received_coordinates) -{ - coordinates.emplace_back( - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<0>(received_coordinates)), - static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); - is_source.push_back(true); - is_destination.push_back(false); - uturns.push_back(uturn_default); -} - -void RouteParameters::getCoordinatesFromGeometry(const std::string &geometry_string) -{ - PolylineCompressor pc; - coordinates = pc.decode_string(geometry_string); -} - diff --git a/data_structures/search_engine.hpp b/data_structures/search_engine.hpp deleted file mode 100644 index 5af734e77..000000000 --- a/data_structures/search_engine.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SEARCH_ENGINE_HPP -#define SEARCH_ENGINE_HPP - -#include "search_engine_data.hpp" -#include "../routing_algorithms/alternative_path.hpp" -#include "../routing_algorithms/many_to_many.hpp" -#include "../routing_algorithms/map_matching.hpp" -#include "../routing_algorithms/shortest_path.hpp" -#include "../routing_algorithms/direct_shortest_path.hpp" - -#include - -template class SearchEngine -{ - private: - DataFacadeT *facade; - SearchEngineData engine_working_data; - - public: - ShortestPathRouting shortest_path; - DirectShortestPathRouting direct_shortest_path; - AlternativeRouting alternative_path; - ManyToManyRouting distance_table; - MapMatching map_matching; - - explicit SearchEngine(DataFacadeT *facade) - : facade(facade), - shortest_path(facade, engine_working_data), - direct_shortest_path(facade, engine_working_data), - alternative_path(facade, engine_working_data), - distance_table(facade, engine_working_data), - map_matching(facade, engine_working_data) - { - static_assert(!std::is_pointer::value, "don't instantiate with ptr type"); - static_assert(std::is_object::value, - "don't instantiate with void, function, or reference"); - } - - ~SearchEngine() {} -}; - -#endif // SEARCH_ENGINE_HPP diff --git a/data_structures/search_engine_data.cpp b/data_structures/search_engine_data.cpp deleted file mode 100644 index 3282a0ccb..000000000 --- a/data_structures/search_engine_data.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "search_engine_data.hpp" - -#include "binary_heap.hpp" - -void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes) -{ - if (forward_heap_1.get()) - { - forward_heap_1->Clear(); - } - else - { - forward_heap_1.reset(new QueryHeap(number_of_nodes)); - } - - if (reverse_heap_1.get()) - { - reverse_heap_1->Clear(); - } - else - { - reverse_heap_1.reset(new QueryHeap(number_of_nodes)); - } -} - -void SearchEngineData::InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes) -{ - if (forward_heap_2.get()) - { - forward_heap_2->Clear(); - } - else - { - forward_heap_2.reset(new QueryHeap(number_of_nodes)); - } - - if (reverse_heap_2.get()) - { - reverse_heap_2->Clear(); - } - else - { - reverse_heap_2.reset(new QueryHeap(number_of_nodes)); - } -} - -void SearchEngineData::InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes) -{ - if (forward_heap_3.get()) - { - forward_heap_3->Clear(); - } - else - { - forward_heap_3.reset(new QueryHeap(number_of_nodes)); - } - - if (reverse_heap_3.get()) - { - reverse_heap_3->Clear(); - } - else - { - reverse_heap_3.reset(new QueryHeap(number_of_nodes)); - } -} diff --git a/data_structures/search_engine_data.hpp b/data_structures/search_engine_data.hpp deleted file mode 100644 index 8c1c1619e..000000000 --- a/data_structures/search_engine_data.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SEARCH_ENGINE_DATA_HPP -#define SEARCH_ENGINE_DATA_HPP - -#include - -#include "../typedefs.h" -#include "binary_heap.hpp" - -struct HeapData -{ - NodeID parent; - /* explicit */ HeapData(NodeID p) : parent(p) {} -}; - -struct SearchEngineData -{ - using QueryHeap = BinaryHeap>; - using SearchEngineHeapPtr = boost::thread_specific_ptr; - - static SearchEngineHeapPtr forward_heap_1; - static SearchEngineHeapPtr reverse_heap_1; - static SearchEngineHeapPtr forward_heap_2; - static SearchEngineHeapPtr reverse_heap_2; - static SearchEngineHeapPtr forward_heap_3; - static SearchEngineHeapPtr reverse_heap_3; - - void InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes); - - void InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes); - - void InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes); -}; - -#endif // SEARCH_ENGINE_DATA_HPP diff --git a/data_structures/segment_information.hpp b/data_structures/segment_information.hpp deleted file mode 100644 index d9cdc8160..000000000 --- a/data_structures/segment_information.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SEGMENT_INFORMATION_HPP -#define SEGMENT_INFORMATION_HPP - -#include "turn_instructions.hpp" - -#include "../data_structures/travel_mode.hpp" -#include "../typedefs.h" - -#include -#include - -// Struct fits everything in one cache line -struct SegmentInformation -{ - FixedPointCoordinate location; - NodeID name_id; - EdgeWeight duration; - float length; - short pre_turn_bearing; // more than enough [0..3600] fits into 12 bits - short post_turn_bearing; - TurnInstruction turn_instruction; - TravelMode travel_mode; - bool necessary; - bool is_via_location; - - explicit SegmentInformation(FixedPointCoordinate location, - const NodeID name_id, - const EdgeWeight duration, - const float length, - const TurnInstruction turn_instruction, - const bool necessary, - const bool is_via_location, - const TravelMode travel_mode) - : location(std::move(location)), name_id(name_id), duration(duration), length(length), - pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction), travel_mode(travel_mode), - necessary(necessary), is_via_location(is_via_location) - { - } - - explicit SegmentInformation(FixedPointCoordinate location, - const NodeID name_id, - const EdgeWeight duration, - const float length, - const TurnInstruction turn_instruction, - const TravelMode travel_mode) - : location(std::move(location)), name_id(name_id), duration(duration), length(length), - pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction), travel_mode(travel_mode), - necessary(turn_instruction != TurnInstruction::NoTurn), is_via_location(false) - { - } -}; - -#endif /* SEGMENT_INFORMATION_HPP */ diff --git a/data_structures/static_kdtree.hpp b/data_structures/static_kdtree.hpp deleted file mode 100644 index 1e65dc844..000000000 --- a/data_structures/static_kdtree.hpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -// KD Tree coded by Christian Vetter, Monav Project - -#ifndef STATICKDTREE_HPP -#define STATICKDTREE_HPP - -#include -#include -#include -#include -#include - -namespace KDTree -{ - -#define KDTREE_BASESIZE (8) - -template class BoundingBox -{ - public: - BoundingBox() - { - for (unsigned dim = 0; dim < k; ++dim) - { - min[dim] = std::numeric_limits::min(); - max[dim] = std::numeric_limits::max(); - } - } - - T min[k]; - T max[k]; -}; - -struct NoData -{ -}; - -template class EuclidianMetric -{ - public: - double operator()(const T left[k], const T right[k]) - { - double result = 0; - for (unsigned i = 0; i < k; ++i) - { - double temp = (double)left[i] - (double)right[i]; - result += temp * temp; - } - return result; - } - - double operator()(const BoundingBox &box, const T point[k]) - { - T nearest[k]; - for (unsigned dim = 0; dim < k; ++dim) - { - if (point[dim] < box.min[dim]) - nearest[dim] = box.min[dim]; - else if (point[dim] > box.max[dim]) - nearest[dim] = box.max[dim]; - else - nearest[dim] = point[dim]; - } - return operator()(point, nearest); - } -}; - -template > -class StaticKDTree -{ - public: - struct InputPoint - { - T coordinates[k]; - Data data; - bool operator==(const InputPoint &right) - { - for (int i = 0; i < k; i++) - { - if (coordinates[i] != right.coordinates[i]) - return false; - } - return true; - } - }; - - explicit StaticKDTree(std::vector *points) - { - BOOST_ASSERT(k > 0); - BOOST_ASSERT(points->size() > 0); - size = points->size(); - kdtree = new InputPoint[size]; - for (Iterator i = 0; i != size; ++i) - { - kdtree[i] = points->at(i); - for (unsigned dim = 0; dim < k; ++dim) - { - if (kdtree[i].coordinates[dim] < boundingBox.min[dim]) - boundingBox.min[dim] = kdtree[i].coordinates[dim]; - if (kdtree[i].coordinates[dim] > boundingBox.max[dim]) - boundingBox.max[dim] = kdtree[i].coordinates[dim]; - } - } - std::stack s; - s.push(Tree(0, size, 0)); - while (!s.empty()) - { - Tree tree = s.top(); - s.pop(); - - if (tree.right - tree.left < KDTREE_BASESIZE) - continue; - - Iterator middle = tree.left + (tree.right - tree.left) / 2; - std::nth_element(kdtree + tree.left, kdtree + middle, kdtree + tree.right, - Less(tree.dimension)); - s.push(Tree(tree.left, middle, (tree.dimension + 1) % k)); - s.push(Tree(middle + 1, tree.right, (tree.dimension + 1) % k)); - } - } - - ~StaticKDTree() { delete[] kdtree; } - - bool NearestNeighbor(InputPoint *result, const InputPoint &point) - { - Metric distance; - bool found = false; - double nearestDistance = std::numeric_limits::max(); - std::stack s; - s.push(NNTree(0, size, 0, boundingBox)); - while (!s.empty()) - { - NNTree tree = s.top(); - s.pop(); - - if (distance(tree.box, point.coordinates) >= nearestDistance) - continue; - - if (tree.right - tree.left < KDTREE_BASESIZE) - { - for (unsigned i = tree.left; i < tree.right; i++) - { - double newDistance = distance(kdtree[i].coordinates, point.coordinates); - if (newDistance < nearestDistance) - { - nearestDistance = newDistance; - *result = kdtree[i]; - found = true; - } - } - continue; - } - - Iterator middle = tree.left + (tree.right - tree.left) / 2; - - double newDistance = distance(kdtree[middle].coordinates, point.coordinates); - if (newDistance < nearestDistance) - { - nearestDistance = newDistance; - *result = kdtree[middle]; - found = true; - } - - Less comperator(tree.dimension); - if (!comperator(point, kdtree[middle])) - { - NNTree first(middle + 1, tree.right, (tree.dimension + 1) % k, tree.box); - NNTree second(tree.left, middle, (tree.dimension + 1) % k, tree.box); - first.box.min[tree.dimension] = kdtree[middle].coordinates[tree.dimension]; - second.box.max[tree.dimension] = kdtree[middle].coordinates[tree.dimension]; - s.push(second); - s.push(first); - } - else - { - NNTree first(middle + 1, tree.right, (tree.dimension + 1) % k, tree.box); - NNTree second(tree.left, middle, (tree.dimension + 1) % k, tree.box); - first.box.min[tree.dimension] = kdtree[middle].coordinates[tree.dimension]; - second.box.max[tree.dimension] = kdtree[middle].coordinates[tree.dimension]; - s.push(first); - s.push(second); - } - } - return found; - } - - private: - using Iterator = unsigned; - struct Tree - { - Iterator left; - Iterator right; - unsigned dimension; - Tree() {} - Tree(Iterator l, Iterator r, unsigned d) : left(l), right(r), dimension(d) {} - }; - struct NNTree - { - Iterator left; - Iterator right; - unsigned dimension; - BoundingBox box; - NNTree() {} - NNTree(Iterator l, Iterator r, unsigned d, const BoundingBox &b) - : left(l), right(r), dimension(d), box(b) - { - } - }; - class Less - { - public: - explicit Less(unsigned d) - { - dimension = d; - BOOST_ASSERT(dimension < k); - } - - bool operator()(const InputPoint &left, const InputPoint &right) - { - BOOST_ASSERT(dimension < k); - return left.coordinates[dimension] < right.coordinates[dimension]; - } - - private: - unsigned dimension; - }; - - BoundingBox boundingBox; - InputPoint *kdtree; - Iterator size; -}; -} - -#endif // STATICKDTREE_HPP diff --git a/data_structures/turn_instructions.hpp b/data_structures/turn_instructions.hpp deleted file mode 100644 index 1ca065f68..000000000 --- a/data_structures/turn_instructions.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef TURN_INSTRUCTIONS_HPP -#define TURN_INSTRUCTIONS_HPP - -enum class TurnInstruction : unsigned char -{ - NoTurn = 0, - GoStraight, - TurnSlightRight, - TurnRight, - TurnSharpRight, - UTurn, - TurnSharpLeft, - TurnLeft, - TurnSlightLeft, - ReachViaLocation, - HeadOn, - EnterRoundAbout, - LeaveRoundAbout, - StayOnRoundAbout, - StartAtEndOfStreet, - ReachedYourDestination, - EnterAgainstAllowedDirection, - LeaveAgainstAllowedDirection, - InverseAccessRestrictionFlag = 127, - AccessRestrictionFlag = 128, - AccessRestrictionPenalty = 129 -}; - -struct TurnInstructionsClass -{ - TurnInstructionsClass() = delete; - TurnInstructionsClass(const TurnInstructionsClass &) = delete; - - static inline TurnInstruction GetTurnDirectionOfInstruction(const double angle) - { - if (angle >= 23 && angle < 67) - { - return TurnInstruction::TurnSharpRight; - } - if (angle >= 67 && angle < 113) - { - return TurnInstruction::TurnRight; - } - if (angle >= 113 && angle < 158) - { - return TurnInstruction::TurnSlightRight; - } - if (angle >= 158 && angle < 202) - { - return TurnInstruction::GoStraight; - } - if (angle >= 202 && angle < 248) - { - return TurnInstruction::TurnSlightLeft; - } - if (angle >= 248 && angle < 292) - { - return TurnInstruction::TurnLeft; - } - if (angle >= 292 && angle < 336) - { - return TurnInstruction::TurnSharpLeft; - } - return TurnInstruction::UTurn; - } - - static inline bool TurnIsNecessary(const TurnInstruction turn_instruction) - { - if (TurnInstruction::NoTurn == turn_instruction || - TurnInstruction::StayOnRoundAbout == turn_instruction) - { - return false; - } - return true; - } -}; - -#endif /* TURN_INSTRUCTIONS_HPP */ diff --git a/data_structures/upper_bound.hpp b/data_structures/upper_bound.hpp deleted file mode 100644 index 80695f2c5..000000000 --- a/data_structures/upper_bound.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef LOWER_BOUND_HPP -#define LOWER_BOUND_HPP - -#include -#include -#include -#include - -// max pq holds k elements -// insert if key is smaller than max -// if size > k then remove element -// get() always yields a bound to the k smallest element in the stream - -template class upper_bound -{ - private: - using parameter_type = - typename std::conditional::value, key_type, key_type &>::type; - - public: - upper_bound() = delete; - upper_bound(std::size_t size) : size(size) {} - - key_type get() const - { - if (queue.size() < size) - { - return std::numeric_limits::max(); - } - return queue.top(); - } - - void insert(const parameter_type key) - { - if (key < get()) - { - queue.emplace(key); - while (queue.size() > size) - { - queue.pop(); - } - } - } - - private: - std::priority_queue, std::less> queue; - const std::size_t size; -}; - -#endif // LOWER_BOUND_HPP diff --git a/data_structures/xor_fast_hash.hpp b/data_structures/xor_fast_hash.hpp deleted file mode 100644 index 3af5ab1db..000000000 --- a/data_structures/xor_fast_hash.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef XOR_FAST_HASH_HPP -#define XOR_FAST_HASH_HPP - -#include -#include - -/* - This is an implementation of Tabulation hashing, which has suprising properties like - universality. - The space requirement is 2*2^16 = 256 kb of memory, which fits into L2 cache. - Evaluation boils down to 10 or less assembly instruction on any recent X86 CPU: - - 1: movq table2(%rip), %rdx - 2: movl %edi, %eax - 3: movzwl %di, %edi - 4: shrl $16, %eax - 5: movzwl %ax, %eax - 6: movzbl (%rdx,%rax), %eax - 7: movq table1(%rip), %rdx - 8: xorb (%rdx,%rdi), %al - 9: movzbl %al, %eax - 10: ret - -*/ -class XORFastHash -{ // 65k entries - std::vector table1; - std::vector table2; - - public: - XORFastHash() - { - table1.resize(2 << 16); - table2.resize(2 << 16); - for (unsigned i = 0; i < (2 << 16); ++i) - { - table1[i] = static_cast(i); - table2[i] = static_cast(i); - } - std::random_shuffle(table1.begin(), table1.end()); - std::random_shuffle(table2.begin(), table2.end()); - } - - inline unsigned short operator()(const unsigned originalValue) const - { - unsigned short lsb = ((originalValue)&0xffff); - unsigned short msb = (((originalValue) >> 16) & 0xffff); - return table1[lsb] ^ table2[msb]; - } -}; - -class XORMiniHash -{ // 256 entries - std::vector table1; - std::vector table2; - std::vector table3; - std::vector table4; - - public: - XORMiniHash() - { - table1.resize(1 << 8); - table2.resize(1 << 8); - table3.resize(1 << 8); - table4.resize(1 << 8); - for (unsigned i = 0; i < (1 << 8); ++i) - { - table1[i] = static_cast(i); - table2[i] = static_cast(i); - table3[i] = static_cast(i); - table4[i] = static_cast(i); - } - std::random_shuffle(table1.begin(), table1.end()); - std::random_shuffle(table2.begin(), table2.end()); - std::random_shuffle(table3.begin(), table3.end()); - std::random_shuffle(table4.begin(), table4.end()); - } - unsigned char operator()(const unsigned originalValue) const - { - unsigned char byte1 = ((originalValue)&0xff); - unsigned char byte2 = ((originalValue >> 8) & 0xff); - unsigned char byte3 = ((originalValue >> 16) & 0xff); - unsigned char byte4 = ((originalValue >> 24) & 0xff); - return table1[byte1] ^ table2[byte2] ^ table3[byte3] ^ table4[byte4]; - } -}; - -#endif // XOR_FAST_HASH_HPP diff --git a/data_structures/xor_fast_hash_storage.hpp b/data_structures/xor_fast_hash_storage.hpp deleted file mode 100644 index ff65717a3..000000000 --- a/data_structures/xor_fast_hash_storage.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef XOR_FAST_HASH_STORAGE_HPP -#define XOR_FAST_HASH_STORAGE_HPP - -#include "xor_fast_hash.hpp" - -#include -#include - -template class XORFastHashStorage -{ - public: - struct HashCell - { - unsigned time; - NodeID id; - Key key; - HashCell() - : time(std::numeric_limits::max()), id(std::numeric_limits::max()), - key(std::numeric_limits::max()) - { - } - - HashCell(const HashCell &other) : time(other.key), id(other.id), key(other.time) {} - - operator Key() const { return key; } - - void operator=(const Key key_to_insert) { key = key_to_insert; } - }; - - XORFastHashStorage() = delete; - - explicit XORFastHashStorage(size_t) : positions(2 << 16), current_timestamp(0) {} - - HashCell &operator[](const NodeID node) - { - unsigned short position = fast_hasher(node); - while ((positions[position].time == current_timestamp) && (positions[position].id != node)) - { - ++position %= (2 << 16); - } - - positions[position].time = current_timestamp; - positions[position].id = node; - return positions[position]; - } - - // peek into table, get key for node, think of it as a read-only operator[] - Key peek_index(const NodeID node) const - { - unsigned short position = fast_hasher(node); - while ((positions[position].time == current_timestamp) && (positions[position].id != node)) - { - ++position %= (2 << 16); - } - return positions[position].key; - } - - void Clear() - { - ++current_timestamp; - if (std::numeric_limits::max() == current_timestamp) - { - positions.clear(); - positions.resize(2 << 16); - } - } - - private: - std::vector positions; - XORFastHash fast_hasher; - unsigned current_timestamp; -}; - -#endif // XOR_FAST_HASH_STORAGE_HPP diff --git a/descriptors/description_factory.cpp b/descriptors/description_factory.cpp deleted file mode 100644 index 5086e0d17..000000000 --- a/descriptors/description_factory.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "description_factory.hpp" - -#include "../algorithms/polyline_formatter.hpp" -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/internal_route_result.hpp" -#include "../data_structures/turn_instructions.hpp" -#include "../util/container.hpp" -#include "../util/integer_range.hpp" -#include "../typedefs.h" - -DescriptionFactory::DescriptionFactory() : entire_length(0) { via_indices.push_back(0); } - -std::vector const &DescriptionFactory::GetViaIndices() const { return via_indices; } - -void DescriptionFactory::SetStartSegment(const PhantomNode &source, const bool traversed_in_reverse) -{ - start_phantom = source; - const EdgeWeight segment_duration = - (traversed_in_reverse ? source.reverse_weight : source.forward_weight); - const TravelMode travel_mode = - (traversed_in_reverse ? source.backward_travel_mode : source.forward_travel_mode); - AppendSegment(source.location, PathData(0, source.name_id, TurnInstruction::HeadOn, - segment_duration, travel_mode)); - BOOST_ASSERT(path_description.back().duration == segment_duration); -} - -void DescriptionFactory::SetEndSegment(const PhantomNode &target, - const bool traversed_in_reverse, - const bool is_via_location) -{ - target_phantom = target; - const EdgeWeight segment_duration = - (traversed_in_reverse ? target.reverse_weight : target.forward_weight); - const TravelMode travel_mode = - (traversed_in_reverse ? target.backward_travel_mode : target.forward_travel_mode); - path_description.emplace_back(target.location, target.name_id, segment_duration, 0.f, - is_via_location ? TurnInstruction::ReachViaLocation - : TurnInstruction::NoTurn, - true, true, travel_mode); - BOOST_ASSERT(path_description.back().duration == segment_duration); -} - -void DescriptionFactory::AppendSegment(const FixedPointCoordinate &coordinate, - const PathData &path_point) -{ - // if the start location is on top of a node, the first movement might be zero-length, - // in which case we dont' add a new description, but instead update the existing one - if ((1 == path_description.size()) && (path_description.front().location == coordinate)) - { - if (path_point.segment_duration > 0) - { - path_description.front().name_id = path_point.name_id; - path_description.front().travel_mode = path_point.travel_mode; - } - return; - } - - // make sure mode changes are announced, even when there otherwise is no turn - const TurnInstruction turn = [&]() -> TurnInstruction - { - if (TurnInstruction::NoTurn == path_point.turn_instruction && - path_description.front().travel_mode != path_point.travel_mode && - path_point.segment_duration > 0) - { - return TurnInstruction::GoStraight; - } - return path_point.turn_instruction; - }(); - - path_description.emplace_back(coordinate, path_point.name_id, path_point.segment_duration, 0.f, - turn, path_point.travel_mode); -} - -osrm::json::Value DescriptionFactory::AppendGeometryString(const bool return_encoded) -{ - if (return_encoded) - { - return PolylineFormatter().printEncodedString(path_description); - } - return PolylineFormatter().printUnencodedString(path_description); -} - -void DescriptionFactory::BuildRouteSummary(const double distance, const unsigned time) -{ - summary.source_name_id = start_phantom.name_id; - summary.target_name_id = target_phantom.name_id; - summary.BuildDurationAndLengthStrings(distance, time); -} - -void DescriptionFactory::Run(const unsigned zoom_level) -{ - if (path_description.empty()) - { - return; - } - - /** starts at index 1 */ - path_description[0].length = 0.f; - for (const auto i : osrm::irange(1, path_description.size())) - { - // move down names by one, q&d hack - path_description[i - 1].name_id = path_description[i].name_id; - path_description[i].length = coordinate_calculation::great_circle_distance( - path_description[i - 1].location, path_description[i].location); - } - - /*Simplify turn instructions - Input : - 10. Turn left on B 36 for 20 km - 11. Continue on B 35; B 36 for 2 km - 12. Continue on B 36 for 13 km - - becomes: - 10. Turn left on B 36 for 35 km - */ - // TODO: rework to check only end and start of string. - // stl string is way to expensive - - // unsigned lastTurn = 0; - // for(unsigned i = 1; i < path_description.size(); ++i) { - // string1 = sEngine.GetEscapedNameForNameID(path_description[i].name_id); - // if(TurnInstruction::GoStraight == path_description[i].turn_instruction) { - // if(std::string::npos != string0.find(string1+";") - // || std::string::npos != string0.find(";"+string1) - // || std::string::npos != string0.find(string1+" ;") - // || std::string::npos != string0.find("; "+string1) - // ){ - // SimpleLogger().Write() << "->next correct: " << string0 << " contains " << - // string1; - // for(; lastTurn != i; ++lastTurn) - // path_description[lastTurn].name_id = path_description[i].name_id; - // path_description[i].turn_instruction = TurnInstruction::NoTurn; - // } else if(std::string::npos != string1.find(string0+";") - // || std::string::npos != string1.find(";"+string0) - // || std::string::npos != string1.find(string0+" ;") - // || std::string::npos != string1.find("; "+string0) - // ){ - // SimpleLogger().Write() << "->prev correct: " << string1 << " contains " << - // string0; - // path_description[i].name_id = path_description[i-1].name_id; - // path_description[i].turn_instruction = TurnInstruction::NoTurn; - // } - // } - // if (TurnInstruction::NoTurn != path_description[i].turn_instruction) { - // lastTurn = i; - // } - // string0 = string1; - // } - // - - float segment_length = 0.; - EdgeWeight segment_duration = 0; - std::size_t segment_start_index = 0; - - for (const auto i : osrm::irange(1, path_description.size())) - { - entire_length += path_description[i].length; - segment_length += path_description[i].length; - segment_duration += path_description[i].duration; - path_description[segment_start_index].length = segment_length; - path_description[segment_start_index].duration = segment_duration; - - if (TurnInstruction::NoTurn != path_description[i].turn_instruction) - { - BOOST_ASSERT(path_description[i].necessary); - segment_length = 0; - segment_duration = 0; - segment_start_index = i; - } - } - - // Post-processing to remove empty or nearly empty path segments - if (path_description.size() > 2 && - std::numeric_limits::epsilon() > path_description.back().length && - !(path_description.end() - 2)->is_via_location) - { - path_description.pop_back(); - path_description.back().necessary = true; - path_description.back().turn_instruction = TurnInstruction::NoTurn; - target_phantom.name_id = (path_description.end() - 2)->name_id; - } - - if (path_description.size() > 2 && - std::numeric_limits::epsilon() > path_description.front().length && - !(path_description.begin() + 1)->is_via_location) - { - path_description.erase(path_description.begin()); - path_description.front().turn_instruction = TurnInstruction::HeadOn; - path_description.front().necessary = true; - start_phantom.name_id = path_description.front().name_id; - } - - // Generalize poly line - polyline_generalizer.Run(path_description.begin(), path_description.end(), zoom_level); - - // fix what needs to be fixed else - unsigned necessary_segments = 0; // a running index that counts the necessary pieces - osrm::for_each_pair( - path_description, [&](SegmentInformation &first, const SegmentInformation &second) - { - if (!first.necessary) - { - return; - } - - if (first.is_via_location) - { // mark the end of a leg (of several segments) - via_indices.push_back(necessary_segments); - } - - const double post_turn_bearing = coordinate_calculation::bearing(first.location, second.location); - const double pre_turn_bearing = coordinate_calculation::bearing(second.location, first.location); - first.post_turn_bearing = static_cast(post_turn_bearing * 10); - first.pre_turn_bearing = static_cast(pre_turn_bearing * 10); - - ++necessary_segments; - }); - - via_indices.push_back(necessary_segments); - BOOST_ASSERT(via_indices.size() >= 2); - return; -} diff --git a/descriptors/description_factory.hpp b/descriptors/description_factory.hpp deleted file mode 100644 index 985f9c1c5..000000000 --- a/descriptors/description_factory.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef DESCRIPTION_FACTORY_HPP -#define DESCRIPTION_FACTORY_HPP - -#include "../algorithms/douglas_peucker.hpp" -#include "../data_structures/phantom_node.hpp" -#include "../data_structures/segment_information.hpp" -#include "../data_structures/turn_instructions.hpp" - -#include - -#include -#include - -#include - -#include -#include - -struct PathData; -/* This class is fed with all way segments in consecutive order - * and produces the description plus the encoded polyline */ - -class DescriptionFactory -{ - DouglasPeucker polyline_generalizer; - PhantomNode start_phantom, target_phantom; - - double DegreeToRadian(const double degree) const; - double RadianToDegree(const double degree) const; - - std::vector via_indices; - - double entire_length; - - public: - struct RouteSummary - { - unsigned distance; - EdgeWeight duration; - unsigned source_name_id; - unsigned target_name_id; - RouteSummary() : distance(0), duration(0), source_name_id(0), target_name_id(0) {} - - void BuildDurationAndLengthStrings(const double raw_distance, const unsigned raw_duration) - { - // compute distance/duration for route summary - distance = static_cast(std::round(raw_distance)); - duration = static_cast(std::round(raw_duration / 10.)); - } - } summary; - - // I know, declaring this public is considered bad. I'm lazy - std::vector path_description; - DescriptionFactory(); - void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data); - void BuildRouteSummary(const double distance, const unsigned time); - void SetStartSegment(const PhantomNode &start_phantom, const bool traversed_in_reverse); - void SetEndSegment(const PhantomNode &start_phantom, - const bool traversed_in_reverse, - const bool is_via_location = false); - osrm::json::Value AppendGeometryString(const bool return_encoded); - std::vector const &GetViaIndices() const; - - double get_entire_length() const { return entire_length; } - - void Run(const unsigned zoom_level); -}; - -#endif /* DESCRIPTION_FACTORY_HPP */ diff --git a/descriptors/descriptor_base.hpp b/descriptors/descriptor_base.hpp deleted file mode 100644 index e85ea902f..000000000 --- a/descriptors/descriptor_base.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef DESCRIPTOR_BASE_HPP -#define DESCRIPTOR_BASE_HPP - -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/internal_route_result.hpp" -#include "../data_structures/phantom_node.hpp" -#include "../typedefs.h" - -#include - -#include - -#include -#include -#include - -struct DescriptorTable : public std::unordered_map -{ - unsigned get_id(const std::string &key) - { - auto iter = find(key); - if (iter != end()) - { - return iter->second; - } - return 0; - } -}; - -struct DescriptorConfig -{ - DescriptorConfig() : instructions(true), geometry(true), encode_geometry(true), zoom_level(18) - { - } - - template - DescriptorConfig(const OtherT &other) - : instructions(other.print_instructions), geometry(other.geometry), - encode_geometry(other.compression), zoom_level(other.zoom_level) - { - BOOST_ASSERT(zoom_level >= 0); - } - - bool instructions; - bool geometry; - bool encode_geometry; - short zoom_level; -}; - -template class BaseDescriptor -{ - public: - BaseDescriptor() {} - // Maybe someone can explain the pure virtual destructor thing to me (dennis) - virtual ~BaseDescriptor() {} - virtual void Run(const InternalRouteResult &raw_route, osrm::json::Object &json_result) = 0; - virtual void SetConfig(const DescriptorConfig &c) = 0; -}; - -#endif // DESCRIPTOR_BASE_HPP diff --git a/descriptors/gpx_descriptor.hpp b/descriptors/gpx_descriptor.hpp deleted file mode 100644 index fb4c5b9de..000000000 --- a/descriptors/gpx_descriptor.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef GPX_DESCRIPTOR_HPP -#define GPX_DESCRIPTOR_HPP - -#include "descriptor_base.hpp" -#include "../util/xml_renderer.hpp" - -#include - -#include - -template class GPXDescriptor final : public BaseDescriptor -{ - private: - DescriptorConfig config; - DataFacadeT *facade; - - void AddRoutePoint(const FixedPointCoordinate &coordinate, osrm::json::Array &json_route) - { - osrm::json::Object json_lat; - osrm::json::Object json_lon; - osrm::json::Array json_row; - - std::string tmp; - - coordinate_calculation::lat_or_lon_to_string(coordinate.lat, tmp); - json_lat.values["_lat"] = tmp; - - coordinate_calculation::lat_or_lon_to_string(coordinate.lon, tmp); - json_lon.values["_lon"] = tmp; - - json_row.values.push_back(json_lat); - json_row.values.push_back(json_lon); - osrm::json::Object entry; - entry.values["rtept"] = json_row; - json_route.values.push_back(entry); - } - - public: - explicit GPXDescriptor(DataFacadeT *facade) : facade(facade) {} - - virtual void SetConfig(const DescriptorConfig &c) final { config = c; } - - virtual void Run(const InternalRouteResult &raw_route, osrm::json::Object &json_result) final - { - osrm::json::Array json_route; - if (raw_route.shortest_path_length != INVALID_EDGE_WEIGHT) - { - AddRoutePoint(raw_route.segment_end_coordinates.front().source_phantom.location, - json_route); - - for (const std::vector &path_data_vector : raw_route.unpacked_path_segments) - { - for (const PathData &path_data : path_data_vector) - { - const FixedPointCoordinate current_coordinate = - facade->GetCoordinateOfNode(path_data.node); - AddRoutePoint(current_coordinate, json_route); - } - } - AddRoutePoint(raw_route.segment_end_coordinates.back().target_phantom.location, - json_route); - } - // osrm::json::gpx_render(reply.content, json_route); - json_result.values["route"] = json_route; - } -}; -#endif // GPX_DESCRIPTOR_HPP diff --git a/descriptors/json_descriptor.hpp b/descriptors/json_descriptor.hpp deleted file mode 100644 index e476dd827..000000000 --- a/descriptors/json_descriptor.hpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef JSON_DESCRIPTOR_HPP -#define JSON_DESCRIPTOR_HPP - -#include "descriptor_base.hpp" -#include "description_factory.hpp" -#include "../algorithms/object_encoder.hpp" -#include "../algorithms/route_name_extraction.hpp" -#include "../data_structures/segment_information.hpp" -#include "../data_structures/turn_instructions.hpp" -#include "../util/bearing.hpp" -#include "../util/cast.hpp" -#include "../util/integer_range.hpp" -#include "../util/json_renderer.hpp" -#include "../util/simple_logger.hpp" -#include "../util/string_util.hpp" -#include "../util/timing_util.hpp" - -#include - -#include -#include -#include - -template class JSONDescriptor final : public BaseDescriptor -{ - private: - DataFacadeT *facade; - DescriptorConfig config; - DescriptionFactory description_factory, alternate_description_factory; - FixedPointCoordinate current; - - public: - struct Segment - { - Segment() : name_id(INVALID_NAMEID), length(-1), position(0) {} - Segment(unsigned n, int l, unsigned p) : name_id(n), length(l), position(p) {} - unsigned name_id; - int length; - unsigned position; - }; - private: - std::vector shortest_path_segments, alternative_path_segments; - ExtractRouteNames GenerateRouteNames; - - public: - explicit JSONDescriptor(DataFacadeT *facade) : facade(facade) - { - } - - virtual void SetConfig(const DescriptorConfig &c) override final { config = c; } - - unsigned DescribeLeg(const std::vector &route_leg, - const PhantomNodes &leg_phantoms, - const bool target_traversed_in_reverse, - const bool is_via_leg) - { - unsigned added_element_count = 0; - // Get all the coordinates for the computed route - FixedPointCoordinate current_coordinate; - for (const PathData &path_data : route_leg) - { - current_coordinate = facade->GetCoordinateOfNode(path_data.node); - description_factory.AppendSegment(current_coordinate, path_data); - ++added_element_count; - } - description_factory.SetEndSegment(leg_phantoms.target_phantom, target_traversed_in_reverse, - is_via_leg); - ++added_element_count; - BOOST_ASSERT((route_leg.size() + 1) == added_element_count); - return added_element_count; - } - - virtual void Run(const InternalRouteResult &raw_route, - osrm::json::Object &json_result) override final - { - if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) - { - // We do not need to do much, if there is no route ;-) - return; - } - - // check if first segment is non-zero - BOOST_ASSERT(raw_route.unpacked_path_segments.size() == - raw_route.segment_end_coordinates.size()); - - description_factory.SetStartSegment( - raw_route.segment_end_coordinates.front().source_phantom, - raw_route.source_traversed_in_reverse.front()); - - // for each unpacked segment add the leg to the description - for (const auto i : osrm::irange(0, raw_route.unpacked_path_segments.size())) - { -#ifndef NDEBUG - const int added_segments = -#endif - DescribeLeg(raw_route.unpacked_path_segments[i], - raw_route.segment_end_coordinates[i], - raw_route.target_traversed_in_reverse[i], raw_route.is_via_leg(i)); - BOOST_ASSERT(0 < added_segments); - } - description_factory.Run(config.zoom_level); - - if (config.geometry) - { - osrm::json::Value route_geometry = - description_factory.AppendGeometryString(config.encode_geometry); - json_result.values["route_geometry"] = route_geometry; - } - if (config.instructions) - { - osrm::json::Array json_route_instructions = BuildTextualDescription(description_factory, shortest_path_segments); - json_result.values["route_instructions"] = json_route_instructions; - } - description_factory.BuildRouteSummary(description_factory.get_entire_length(), - raw_route.shortest_path_length); - osrm::json::Object json_route_summary; - json_route_summary.values["total_distance"] = description_factory.summary.distance; - json_route_summary.values["total_time"] = description_factory.summary.duration; - json_route_summary.values["start_point"] = - facade->get_name_for_id(description_factory.summary.source_name_id); - json_route_summary.values["end_point"] = - facade->get_name_for_id(description_factory.summary.target_name_id); - json_result.values["route_summary"] = json_route_summary; - - BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); - - osrm::json::Array json_via_points_array; - osrm::json::Array json_first_coordinate; - json_first_coordinate.values.push_back( - raw_route.segment_end_coordinates.front().source_phantom.location.lat / - COORDINATE_PRECISION); - json_first_coordinate.values.push_back( - raw_route.segment_end_coordinates.front().source_phantom.location.lon / - COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_first_coordinate); - for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) - { - std::string tmp; - osrm::json::Array json_coordinate; - json_coordinate.values.push_back(nodes.target_phantom.location.lat / - COORDINATE_PRECISION); - json_coordinate.values.push_back(nodes.target_phantom.location.lon / - COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_coordinate); - } - json_result.values["via_points"] = json_via_points_array; - - osrm::json::Array json_via_indices_array; - - std::vector const &shortest_leg_end_indices = description_factory.GetViaIndices(); - json_via_indices_array.values.insert(json_via_indices_array.values.end(), - shortest_leg_end_indices.begin(), - shortest_leg_end_indices.end()); - json_result.values["via_indices"] = json_via_indices_array; - - // only one alternative route is computed at this time, so this is hardcoded - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - json_result.values["found_alternative"] = osrm::json::True(); - BOOST_ASSERT(!raw_route.alt_source_traversed_in_reverse.empty()); - alternate_description_factory.SetStartSegment( - raw_route.segment_end_coordinates.front().source_phantom, - raw_route.alt_source_traversed_in_reverse.front()); - // Get all the coordinates for the computed route - for (const PathData &path_data : raw_route.unpacked_alternative) - { - current = facade->GetCoordinateOfNode(path_data.node); - alternate_description_factory.AppendSegment(current, path_data); - } - alternate_description_factory.SetEndSegment( - raw_route.segment_end_coordinates.back().target_phantom, - raw_route.alt_source_traversed_in_reverse.back()); - alternate_description_factory.Run(config.zoom_level); - - if (config.geometry) - { - osrm::json::Value alternate_geometry_string = - alternate_description_factory.AppendGeometryString(config.encode_geometry); - osrm::json::Array json_alternate_geometries_array; - json_alternate_geometries_array.values.push_back(alternate_geometry_string); - json_result.values["alternative_geometries"] = json_alternate_geometries_array; - } - // Generate instructions for each alternative (simulated here) - osrm::json::Array json_alt_instructions; - osrm::json::Array json_current_alt_instructions; - if (config.instructions) - { - json_current_alt_instructions = BuildTextualDescription(alternate_description_factory, alternative_path_segments); - json_alt_instructions.values.push_back(json_current_alt_instructions); - json_result.values["alternative_instructions"] = json_alt_instructions; - } - alternate_description_factory.BuildRouteSummary( - alternate_description_factory.get_entire_length(), - raw_route.alternative_path_length); - - osrm::json::Object json_alternate_route_summary; - osrm::json::Array json_alternate_route_summary_array; - json_alternate_route_summary.values["total_distance"] = - alternate_description_factory.summary.distance; - json_alternate_route_summary.values["total_time"] = - alternate_description_factory.summary.duration; - json_alternate_route_summary.values["start_point"] = - facade->get_name_for_id(alternate_description_factory.summary.source_name_id); - json_alternate_route_summary.values["end_point"] = - facade->get_name_for_id(alternate_description_factory.summary.target_name_id); - json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); - json_result.values["alternative_summaries"] = json_alternate_route_summary_array; - - std::vector const &alternate_leg_end_indices = - alternate_description_factory.GetViaIndices(); - osrm::json::Array json_altenative_indices_array; - json_altenative_indices_array.values.insert(json_altenative_indices_array.values.end(), - alternate_leg_end_indices.begin(), - alternate_leg_end_indices.end()); - json_result.values["alternative_indices"] = json_altenative_indices_array; - } - else - { - json_result.values["found_alternative"] = osrm::json::False(); - } - - // Get Names for both routes - RouteNames route_names = - GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade); - osrm::json::Array json_route_names; - json_route_names.values.push_back(route_names.shortest_path_name_1); - json_route_names.values.push_back(route_names.shortest_path_name_2); - json_result.values["route_name"] = json_route_names; - - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - osrm::json::Array json_alternate_names_array; - osrm::json::Array json_alternate_names; - json_alternate_names.values.push_back(route_names.alternative_path_name_1); - json_alternate_names.values.push_back(route_names.alternative_path_name_2); - json_alternate_names_array.values.push_back(json_alternate_names); - json_result.values["alternative_names"] = json_alternate_names_array; - } - - json_result.values["hint_data"] = BuildHintData(raw_route); - } - - inline osrm::json::Object BuildHintData(const InternalRouteResult& raw_route) const - { - osrm::json::Object json_hint_object; - json_hint_object.values["checksum"] = facade->GetCheckSum(); - osrm::json::Array json_location_hint_array; - std::string hint; - for (const auto i : osrm::irange(0, raw_route.segment_end_coordinates.size())) - { - ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates[i].source_phantom, - hint); - json_location_hint_array.values.push_back(hint); - } - ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates.back().target_phantom, - hint); - json_location_hint_array.values.push_back(hint); - json_hint_object.values["locations"] = json_location_hint_array; - - return json_hint_object; - } - - inline osrm::json::Array BuildTextualDescription(const DescriptionFactory &description_factory, - std::vector &route_segments_list) const - { - osrm::json::Array json_instruction_array; - // Segment information has following format: - //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] - unsigned necessary_segments_running_index = 0; - - struct RoundAbout - { - RoundAbout() : start_index(std::numeric_limits::max()), name_id(INVALID_NAMEID), leave_at_exit(std::numeric_limits::max()) {} - int start_index; - unsigned name_id; - int leave_at_exit; - } round_about; - - round_about.leave_at_exit = 0; - round_about.name_id = 0; - std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; - TravelMode last_travel_mode = TRAVEL_MODE_DEFAULT; - - // Fetch data from Factory and generate a string from it. - for (const SegmentInformation &segment : description_factory.path_description) - { - osrm::json::Array json_instruction_row; - TurnInstruction current_instruction = segment.turn_instruction; - if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) - { - if (TurnInstruction::EnterRoundAbout == current_instruction) - { - round_about.name_id = segment.name_id; - round_about.start_index = necessary_segments_running_index; - } - else - { - std::string current_turn_instruction; - if (TurnInstruction::LeaveRoundAbout == current_instruction) - { - temp_instruction = std::to_string( - cast::enum_to_underlying(TurnInstruction::EnterRoundAbout)); - current_turn_instruction += temp_instruction; - current_turn_instruction += "-"; - temp_instruction = std::to_string(round_about.leave_at_exit + 1); - current_turn_instruction += temp_instruction; - round_about.leave_at_exit = 0; - } - else - { - temp_instruction = - std::to_string(cast::enum_to_underlying(current_instruction)); - current_turn_instruction += temp_instruction; - } - json_instruction_row.values.push_back(current_turn_instruction); - - json_instruction_row.values.push_back(facade->get_name_for_id(segment.name_id)); - json_instruction_row.values.push_back(std::round(segment.length)); - json_instruction_row.values.push_back(necessary_segments_running_index); - json_instruction_row.values.push_back(std::round(segment.duration / 10.)); - json_instruction_row.values.push_back( - std::to_string(static_cast(segment.length)) + "m"); - - // post turn bearing - const double post_turn_bearing_value = (segment.post_turn_bearing / 10.); - json_instruction_row.values.push_back(bearing::get(post_turn_bearing_value)); - json_instruction_row.values.push_back( - static_cast(round(post_turn_bearing_value))); - - json_instruction_row.values.push_back(segment.travel_mode); - last_travel_mode = segment.travel_mode; - - // pre turn bearing - const double pre_turn_bearing_value = (segment.pre_turn_bearing / 10.); - json_instruction_row.values.push_back(bearing::get(pre_turn_bearing_value)); - json_instruction_row.values.push_back( - static_cast(round(pre_turn_bearing_value))); - - json_instruction_array.values.push_back(json_instruction_row); - - route_segments_list.emplace_back( - segment.name_id, static_cast(segment.length), - static_cast(route_segments_list.size())); - } - } - else if (TurnInstruction::StayOnRoundAbout == current_instruction) - { - ++round_about.leave_at_exit; - } - if (segment.necessary) - { - ++necessary_segments_running_index; - } - } - - osrm::json::Array json_last_instruction_row; - temp_instruction = - std::to_string(cast::enum_to_underlying(TurnInstruction::ReachedYourDestination)); - json_last_instruction_row.values.push_back(temp_instruction); - json_last_instruction_row.values.push_back(""); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back("0m"); - json_last_instruction_row.values.push_back(bearing::get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_last_instruction_row.values.push_back(last_travel_mode); - json_last_instruction_row.values.push_back(bearing::get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_instruction_array.values.push_back(json_last_instruction_row); - - return json_instruction_array; - } -}; - -#endif /* JSON_DESCRIPTOR_H_ */ diff --git a/docker/Dockerfile b/docker/Dockerfile index 4535949a1..aa37682f5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -12,8 +12,6 @@ RUN pip install awscli # luabind RUN curl https://gist.githubusercontent.com/DennisOSRM/f2eb7b948e6fe1ae319e/raw/install-luabind.sh | sudo bash -# osmosis -RUN curl -s https://gist.githubusercontent.com/DennisOSRM/803a64a9178ec375069f/raw/ | sudo bash RUN useradd -ms /bin/bash mapbox USER mapbox diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 000000000..86a8a7f10 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.8.8) + +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR AND NOT MSVC_IDE) + message(FATAL_ERROR "In-source builds are not allowed. +Please create a directory and run cmake from there, passing the path to this source directory as the last argument. +This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. Please delete them.") +endif() + +project(osrm-example C CXX) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ") + +set(bitness 32) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(bitness 64) + message(STATUS "Building on a 64 bit system") +else() + message(WARNING "Building on a 32 bit system is unsupported") +endif() + +if(WIN32 AND MSVC_VERSION LESS 1800) + message(FATAL_ERROR "Building with Microsoft compiler needs Visual Studio 2013 or later (Express version works too)") +endif() + +add_executable(osrm-example example.cpp) + +find_package(LibOSRM REQUIRED) +find_package(Boost 1.49.0 COMPONENTS filesystem system thread REQUIRED) + +target_link_libraries(osrm-example ${LibOSRM_LIBRARIES} ${Boost_LIBRARIES}) +include_directories(SYSTEM ${LibOSRM_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + diff --git a/example/cmake/FindLibOSRM.cmake b/example/cmake/FindLibOSRM.cmake new file mode 100644 index 000000000..8475c742f --- /dev/null +++ b/example/cmake/FindLibOSRM.cmake @@ -0,0 +1,65 @@ +# - Try to find LibOSRM +# Once done this will define +# LibOSRM_FOUND - System has LibOSRM +# LibOSRM_INCLUDE_DIRS - The LibOSRM include directories +# LibOSRM_LIBRARIES - The libraries needed to use LibOSRM +# LibOSRM_DEFINITIONS - Compiler switches required for using LibOSRM + +find_package(PkgConfig) +pkg_check_modules(PC_LibOSRM QUIET libosrm) +set(LibOSRM_DEFINITIONS ${PC_LibOSRM_CFLAGS_OTHER}) + +find_path(LibOSRM_INCLUDE_DIR osrm/osrm.hpp + PATH_SUFFIXES osrm include/osrm include + HINTS ${PC_LibOSRM_INCLUDEDIR} ${PC_LibOSRM_INCLUDE_DIRS} + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /opt/local + /opt) + +set(LibOSRM_INCLUDE_DIRS ${LibOSRM_INCLUDE_DIR} ${LibOSRM_INCLUDE_DIR}/osrm) + +find_library(TEST_LibOSRM_STATIC_LIBRARY Names osrm.lib libosrm.a + PATH_SUFFIXES osrm lib/osrm lib + HINTS ${PC_LibOSRM_LIBDIR} ${PC_LibOSRM_LIBRARY_DIRS} + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /opt/local + /opt) +find_library(TEST_LibOSRM_DYNAMIC_LIBRARY Names osrm.dynlib libosrm.so + PATH_SUFFIXES osrm lib/osrm lib + HINTS ${PC_LibOSRM_LIBDIR} ${PC_LibOSRM_LIBRARY_DIRS} + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /opt/local + /opt) + +if (NOT ("${TEST_LibOSRM_STATIC_LIBRARY}" STREQUAL "TEST_LibOSRM_STATIC_LIBRARY-NOTFOUND")) + if ("${PC_LibOSRM_STATIC_LIBRARIES}" STREQUAL "") + set(LibOSRM_STATIC_LIBRARIES ${TEST_LibOSRM_STATIC_LIBRARY}) + else() + set(LibOSRM_STATIC_LIBRARIES ${PC_LibOSRM_STATIC_LIBRARIES}) + endif() + set(LibOSRM_LIBRARIES ${LibOSRM_STATIC_LIBRARIES}) +endif() + +if (NOT ("${TEST_LibOSRM_DYNAMIC_LIBRARY}" STREQUAL "TEST_LibOSRM_DYNAMIC_LIBRARY-NOTFOUND")) + if ("${PC_LibOSRM_LIBRARIES}" STREQUAL "") + set(LibOSRM_DYNAMIC_LIBRARIES ${TEST_LibOSRM_DYNAMIC_LIBRARY}) + else() + set(LibOSRM_DYNAMIC_LIBRARIES ${PC_LibOSRM_LIBRARIES}) + endif() + set(LibOSRM_LIBRARIES ${LibOSRM_DYNAMIC_LIBRARIES}) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LIBOSRM_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(LibOSRM DEFAULT_MSG + LibOSRM_LIBRARIES LibOSRM_INCLUDE_DIR) diff --git a/example/cmake/FindTBB.cmake b/example/cmake/FindTBB.cmake new file mode 100644 index 000000000..f9e3e0f57 --- /dev/null +++ b/example/cmake/FindTBB.cmake @@ -0,0 +1,283 @@ +# Locate Intel Threading Building Blocks include paths and libraries +# FindTBB.cmake can be found at https://code.google.com/p/findtbb/ +# Written by Hannes Hofmann +# Improvements by Gino van den Bergen , +# Florian Uhlig , +# Jiri Marsik + +# The MIT License +# +# Copyright (c) 2011 Hannes Hofmann +# +# 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. + +# GvdB: This module uses the environment variable TBB_ARCH_PLATFORM which defines architecture and compiler. +# e.g. "ia32/vc8" or "em64t/cc4.1.0_libc2.4_kernel2.6.16.21" +# TBB_ARCH_PLATFORM is set by the build script tbbvars[.bat|.sh|.csh], which can be found +# in the TBB installation directory (TBB_INSTALL_DIR). +# +# GvdB: Mac OS X distribution places libraries directly in lib directory. +# +# For backwards compatibility, you may explicitely set the CMake variables TBB_ARCHITECTURE and TBB_COMPILER. +# TBB_ARCHITECTURE [ ia32 | em64t | itanium ] +# which architecture to use +# TBB_COMPILER e.g. vc9 or cc3.2.3_libc2.3.2_kernel2.4.21 or cc4.0.1_os10.4.9 +# which compiler to use (detected automatically on Windows) + +# This module respects +# TBB_INSTALL_DIR or $ENV{TBB21_INSTALL_DIR} or $ENV{TBB_INSTALL_DIR} + +# This module defines +# TBB_INCLUDE_DIRS, where to find task_scheduler_init.h, etc. +# TBB_LIBRARY_DIRS, where to find libtbb, libtbbmalloc +# TBB_DEBUG_LIBRARY_DIRS, where to find libtbb_debug, libtbbmalloc_debug +# TBB_INSTALL_DIR, the base TBB install directory +# TBB_LIBRARIES, the libraries to link against to use TBB. +# TBB_DEBUG_LIBRARIES, the libraries to link against to use TBB with debug symbols. +# TBB_FOUND, If false, don't try to use TBB. +# TBB_INTERFACE_VERSION, as defined in tbb/tbb_stddef.h + + +if (WIN32) + # has em64t/vc8 em64t/vc9 + # has ia32/vc7.1 ia32/vc8 ia32/vc9 + set(_TBB_DEFAULT_INSTALL_DIR "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB") + set(_TBB_LIB_NAME "tbb") + set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc") + set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug") + set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug") + if (MSVC71) + set (_TBB_COMPILER "vc7.1") + endif(MSVC71) + if (MSVC80) + set(_TBB_COMPILER "vc8") + endif(MSVC80) + if (MSVC90) + set(_TBB_COMPILER "vc9") + endif(MSVC90) + if(MSVC10) + set(_TBB_COMPILER "vc10") + endif(MSVC10) + # Todo: add other Windows compilers such as ICL. + set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE}) +endif (WIN32) + +if (UNIX) + if (APPLE) + # MAC + set(_TBB_DEFAULT_INSTALL_DIR "/Library/Frameworks/Intel_TBB.framework/Versions") + # libs: libtbb.dylib, libtbbmalloc.dylib, *_debug + set(_TBB_LIB_NAME "tbb") + set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc") + set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug") + set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug") + # default flavor on apple: ia32/cc4.0.1_os10.4.9 + # Jiri: There is no reason to presume there is only one flavor and + # that user's setting of variables should be ignored. + if(NOT TBB_COMPILER) + set(_TBB_COMPILER "cc4.0.1_os10.4.9") + elseif (NOT TBB_COMPILER) + set(_TBB_COMPILER ${TBB_COMPILER}) + endif(NOT TBB_COMPILER) + if(NOT TBB_ARCHITECTURE) + set(_TBB_ARCHITECTURE "ia32") + elseif(NOT TBB_ARCHITECTURE) + set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE}) + endif(NOT TBB_ARCHITECTURE) + else (APPLE) + # LINUX + set(_TBB_DEFAULT_INSTALL_DIR "/opt/intel/tbb" "/usr/local/include" "/usr/include") + set(_TBB_LIB_NAME "tbb") + set(_TBB_LIB_MALLOC_NAME "${_TBB_LIB_NAME}malloc") + set(_TBB_LIB_DEBUG_NAME "${_TBB_LIB_NAME}_debug") + set(_TBB_LIB_MALLOC_DEBUG_NAME "${_TBB_LIB_MALLOC_NAME}_debug") + # has em64t/cc3.2.3_libc2.3.2_kernel2.4.21 em64t/cc3.3.3_libc2.3.3_kernel2.6.5 em64t/cc3.4.3_libc2.3.4_kernel2.6.9 em64t/cc4.1.0_libc2.4_kernel2.6.16.21 + # has ia32/* + # has itanium/* + set(_TBB_COMPILER ${TBB_COMPILER}) + set(_TBB_ARCHITECTURE ${TBB_ARCHITECTURE}) + endif (APPLE) +endif (UNIX) + +if (CMAKE_SYSTEM MATCHES "SunOS.*") +# SUN +# not yet supported +# has em64t/cc3.4.3_kernel5.10 +# has ia32/* +endif (CMAKE_SYSTEM MATCHES "SunOS.*") + + +#-- Clear the public variables +set (TBB_FOUND "NO") + + +#-- Find TBB install dir and set ${_TBB_INSTALL_DIR} and cached ${TBB_INSTALL_DIR} +# first: use CMake variable TBB_INSTALL_DIR +if (TBB_INSTALL_DIR) + set (_TBB_INSTALL_DIR ${TBB_INSTALL_DIR}) +endif (TBB_INSTALL_DIR) +# second: use environment variable +if (NOT _TBB_INSTALL_DIR) + if (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "") + set (_TBB_INSTALL_DIR $ENV{TBB_INSTALL_DIR}) + endif (NOT "$ENV{TBB_INSTALL_DIR}" STREQUAL "") + # Intel recommends setting TBB21_INSTALL_DIR + if (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "") + set (_TBB_INSTALL_DIR $ENV{TBB21_INSTALL_DIR}) + endif (NOT "$ENV{TBB21_INSTALL_DIR}" STREQUAL "") + if (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "") + set (_TBB_INSTALL_DIR $ENV{TBB22_INSTALL_DIR}) + endif (NOT "$ENV{TBB22_INSTALL_DIR}" STREQUAL "") + if (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "") + set (_TBB_INSTALL_DIR $ENV{TBB30_INSTALL_DIR}) + endif (NOT "$ENV{TBB30_INSTALL_DIR}" STREQUAL "") +endif (NOT _TBB_INSTALL_DIR) +# third: try to find path automatically +if (NOT _TBB_INSTALL_DIR) + if (_TBB_DEFAULT_INSTALL_DIR) + set (_TBB_INSTALL_DIR ${_TBB_DEFAULT_INSTALL_DIR}) + endif (_TBB_DEFAULT_INSTALL_DIR) +endif (NOT _TBB_INSTALL_DIR) +# sanity check +if (NOT _TBB_INSTALL_DIR) + message ("ERROR: Unable to find Intel TBB install directory. ${_TBB_INSTALL_DIR}") +else (NOT _TBB_INSTALL_DIR) +# finally: set the cached CMake variable TBB_INSTALL_DIR +if (NOT TBB_INSTALL_DIR) + set (TBB_INSTALL_DIR ${_TBB_INSTALL_DIR} CACHE PATH "Intel TBB install directory") + mark_as_advanced(TBB_INSTALL_DIR) +endif (NOT TBB_INSTALL_DIR) + + +#-- A macro to rewrite the paths of the library. This is necessary, because +# find_library() always found the em64t/vc9 version of the TBB libs +macro(TBB_CORRECT_LIB_DIR var_name) +# if (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t") + string(REPLACE em64t "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}}) +# endif (NOT "${_TBB_ARCHITECTURE}" STREQUAL "em64t") + string(REPLACE ia32 "${_TBB_ARCHITECTURE}" ${var_name} ${${var_name}}) + string(REPLACE vc7.1 "${_TBB_COMPILER}" ${var_name} ${${var_name}}) + string(REPLACE vc8 "${_TBB_COMPILER}" ${var_name} ${${var_name}}) + string(REPLACE vc9 "${_TBB_COMPILER}" ${var_name} ${${var_name}}) + string(REPLACE vc10 "${_TBB_COMPILER}" ${var_name} ${${var_name}}) +endmacro(TBB_CORRECT_LIB_DIR var_content) + + +#-- Look for include directory and set ${TBB_INCLUDE_DIR} +set (TBB_INC_SEARCH_DIR ${_TBB_INSTALL_DIR}/include) +# Jiri: tbbvars now sets the CPATH environment variable to the directory +# containing the headers. +find_path(TBB_INCLUDE_DIR + tbb/task_scheduler_init.h + PATHS ${TBB_INC_SEARCH_DIR} ENV CPATH +) +mark_as_advanced(TBB_INCLUDE_DIR) + + +#-- Look for libraries +# GvdB: $ENV{TBB_ARCH_PLATFORM} is set by the build script tbbvars[.bat|.sh|.csh] +if (NOT $ENV{TBB_ARCH_PLATFORM} STREQUAL "") + set (_TBB_LIBRARY_DIR + ${_TBB_INSTALL_DIR}/lib/$ENV{TBB_ARCH_PLATFORM} + ${_TBB_INSTALL_DIR}/$ENV{TBB_ARCH_PLATFORM}/lib + ) +endif (NOT $ENV{TBB_ARCH_PLATFORM} STREQUAL "") +# Jiri: This block isn't mutually exclusive with the previous one +# (hence no else), instead I test if the user really specified +# the variables in question. +if ((NOT ${TBB_ARCHITECTURE} STREQUAL "") AND (NOT ${TBB_COMPILER} STREQUAL "")) + # HH: deprecated + message(STATUS "[Warning] FindTBB.cmake: The use of TBB_ARCHITECTURE and TBB_COMPILER is deprecated and may not be supported in future versions. Please set \$ENV{TBB_ARCH_PLATFORM} (using tbbvars.[bat|csh|sh]).") + # Jiri: It doesn't hurt to look in more places, so I store the hints from + # ENV{TBB_ARCH_PLATFORM} and the TBB_ARCHITECTURE and TBB_COMPILER + # variables and search them both. + set (_TBB_LIBRARY_DIR "${_TBB_INSTALL_DIR}/${_TBB_ARCHITECTURE}/${_TBB_COMPILER}/lib" ${_TBB_LIBRARY_DIR}) +endif ((NOT ${TBB_ARCHITECTURE} STREQUAL "") AND (NOT ${TBB_COMPILER} STREQUAL "")) + +# GvdB: Mac OS X distribution places libraries directly in lib directory. +list(APPEND _TBB_LIBRARY_DIR ${_TBB_INSTALL_DIR}/lib) + +# Jiri: No reason not to check the default paths. From recent versions, +# tbbvars has started exporting the LIBRARY_PATH and LD_LIBRARY_PATH +# variables, which now point to the directories of the lib files. +# It all makes more sense to use the ${_TBB_LIBRARY_DIR} as a HINTS +# argument instead of the implicit PATHS as it isn't hard-coded +# but computed by system introspection. Searching the LIBRARY_PATH +# and LD_LIBRARY_PATH environment variables is now even more important +# that tbbvars doesn't export TBB_ARCH_PLATFORM and it facilitates +# the use of TBB built from sources. +find_library(TBB_LIBRARY ${_TBB_LIB_NAME} HINTS ${_TBB_LIBRARY_DIR} + PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) +find_library(TBB_MALLOC_LIBRARY ${_TBB_LIB_MALLOC_NAME} HINTS ${_TBB_LIBRARY_DIR} + PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) + +#Extract path from TBB_LIBRARY name +get_filename_component(TBB_LIBRARY_DIR ${TBB_LIBRARY} PATH) + +#TBB_CORRECT_LIB_DIR(TBB_LIBRARY) +#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY) +mark_as_advanced(TBB_LIBRARY TBB_MALLOC_LIBRARY) + +#-- Look for debug libraries +# Jiri: Changed the same way as for the release libraries. +find_library(TBB_LIBRARY_DEBUG ${_TBB_LIB_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR} + PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) +find_library(TBB_MALLOC_LIBRARY_DEBUG ${_TBB_LIB_MALLOC_DEBUG_NAME} HINTS ${_TBB_LIBRARY_DIR} + PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) + +# Jiri: Self-built TBB stores the debug libraries in a separate directory. +# Extract path from TBB_LIBRARY_DEBUG name +get_filename_component(TBB_LIBRARY_DEBUG_DIR ${TBB_LIBRARY_DEBUG} PATH) + +#TBB_CORRECT_LIB_DIR(TBB_LIBRARY_DEBUG) +#TBB_CORRECT_LIB_DIR(TBB_MALLOC_LIBRARY_DEBUG) +mark_as_advanced(TBB_LIBRARY_DEBUG TBB_MALLOC_LIBRARY_DEBUG) + + +if (TBB_INCLUDE_DIR) + if (TBB_LIBRARY) + set (TBB_FOUND "YES") + set (TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY} ${TBB_LIBRARIES}) + set (TBB_DEBUG_LIBRARIES ${TBB_LIBRARY_DEBUG} ${TBB_MALLOC_LIBRARY_DEBUG} ${TBB_DEBUG_LIBRARIES}) + set (TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR} CACHE PATH "TBB include directory" FORCE) + set (TBB_LIBRARY_DIRS ${TBB_LIBRARY_DIR} CACHE PATH "TBB library directory" FORCE) + # Jiri: Self-built TBB stores the debug libraries in a separate directory. + set (TBB_DEBUG_LIBRARY_DIRS ${TBB_LIBRARY_DEBUG_DIR} CACHE PATH "TBB debug library directory" FORCE) + mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARY_DIRS TBB_DEBUG_LIBRARY_DIRS TBB_LIBRARIES TBB_DEBUG_LIBRARIES) + message(STATUS "Found Intel TBB") + endif (TBB_LIBRARY) +endif (TBB_INCLUDE_DIR) + +if (NOT TBB_FOUND) + message("ERROR: Intel TBB NOT found!") + message(STATUS "Looked for Threading Building Blocks in ${_TBB_INSTALL_DIR}") + # do only throw fatal, if this pkg is REQUIRED + if (TBB_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find TBB library.") + endif (TBB_FIND_REQUIRED) +endif (NOT TBB_FOUND) + +endif (NOT _TBB_INSTALL_DIR) + +if (TBB_FOUND) + set(TBB_INTERFACE_VERSION 0) + FILE(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _TBB_VERSION_CONTENTS) + STRING(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${_TBB_VERSION_CONTENTS}") + set(TBB_INTERFACE_VERSION "${TBB_INTERFACE_VERSION}") +endif (TBB_FOUND) diff --git a/example/example.cpp b/example/example.cpp new file mode 100644 index 000000000..fe1de360a --- /dev/null +++ b/example/example.cpp @@ -0,0 +1,85 @@ +#include "osrm/route_parameters.hpp" +#include "osrm/table_parameters.hpp" +#include "osrm/nearest_parameters.hpp" +#include "osrm/trip_parameters.hpp" +#include "osrm/match_parameters.hpp" + +#include "osrm/coordinate.hpp" +#include "osrm/engine_config.hpp" +#include "osrm/json_container.hpp" + +#include "osrm/status.hpp" +#include "osrm/osrm.hpp" + +#include +#include +#include +#include + +#include + +int main(int argc, const char *argv[]) try +{ + if (argc < 2) + { + std::cerr << "Usage: " << argv[0] << " data.osrm\n"; + return EXIT_FAILURE; + } + + using namespace osrm; + + // Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore + EngineConfig config; + config.storage_config = {argv[1]}; + config.use_shared_memory = false; + + // Routing machine with several services (such as Route, Table, Nearest, Trip, Match) + OSRM osrm{config}; + + // The following shows how to use the Route service; configure this service + RouteParameters params; + + // Route in monaco + params.coordinates.push_back({util::FloatLongitude(7.419758), util::FloatLatitude(43.731142)}); + params.coordinates.push_back({util::FloatLongitude(7.419505), util::FloatLatitude(43.736825)}); + + // Response is in JSON format + json::Object result; + + // Execute routing request, this does the heavy lifting + const auto status = osrm.Route(params, result); + + if (status == Status::Ok) + { + auto &routes = result.values["routes"].get(); + + // Let's just use the first route + auto &route = routes.values.at(0).get(); + const auto distance = route.values["distance"].get().value; + const auto duration = route.values["duration"].get().value; + + // Warn users if extract does not contain the default Berlin coordinates from above + if (distance == 0 or duration == 0) + { + std::cout << "Note: distance or duration is zero. "; + std::cout << "You are probably doing a query outside of the OSM extract.\n\n"; + } + + std::cout << "Distance: " << distance << " meter\n"; + std::cout << "Duration: " << duration << " seconds\n"; + } + else if (status == Status::Error) + { + const auto code = result.values["code"].get().value; + const auto message = result.values["message"].get().value; + + std::cout << "Code: " << code << "\n"; + std::cout << "Message: " << code << "\n"; + return EXIT_FAILURE; + } +} +catch (const std::exception &e) +{ + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; +} diff --git a/extract.cpp b/extract.cpp deleted file mode 100644 index dabc19a5c..000000000 --- a/extract.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "extractor/extractor.hpp" -#include "extractor/extractor_options.hpp" -#include "util/simple_logger.hpp" - -#include - -#include -#include -#include - -int main(int argc, char *argv[]) try -{ - LogPolicy::GetInstance().Unmute(); - ExtractorConfig extractor_config; - - const return_code result = ExtractorOptions::ParseArguments(argc, argv, extractor_config); - - if (return_code::fail == result) - { - return EXIT_FAILURE; - } - - if (return_code::exit == result) - { - return EXIT_SUCCESS; - } - - ExtractorOptions::GenerateOutputFilesNames(extractor_config); - - if (1 > extractor_config.requested_num_threads) - { - SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; - return EXIT_FAILURE; - } - - if (!boost::filesystem::is_regular_file(extractor_config.input_path)) - { - SimpleLogger().Write(logWARNING) << "Input file " << extractor_config.input_path.string() - << " not found!"; - return EXIT_FAILURE; - } - - if (!boost::filesystem::is_regular_file(extractor_config.profile_path)) - { - SimpleLogger().Write(logWARNING) << "Profile " << extractor_config.profile_path.string() - << " not found!"; - return EXIT_FAILURE; - } - return extractor(extractor_config).run(); -} -catch (const std::bad_alloc &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - SimpleLogger().Write(logWARNING) - << "Please provide more memory or consider using a larger swapfile"; - return EXIT_FAILURE; -} -catch (const std::exception &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - return EXIT_FAILURE; -} diff --git a/extractor/edge_based_graph_factory.cpp b/extractor/edge_based_graph_factory.cpp deleted file mode 100644 index 1ddeb6a08..000000000 --- a/extractor/edge_based_graph_factory.cpp +++ /dev/null @@ -1,702 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "edge_based_graph_factory.hpp" -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/percent.hpp" -#include "../util/compute_angle.hpp" -#include "../util/integer_range.hpp" -#include "../util/lua_util.hpp" -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" -#include "../util/osrm_exception.hpp" - -#include "../util/debug_geometry.hpp" - -#include - -#include -#include -#include - -EdgeBasedGraphFactory::EdgeBasedGraphFactory( - std::shared_ptr node_based_graph, - const CompressedEdgeContainer &compressed_edge_container, - const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, - std::shared_ptr restriction_map, - const std::vector &node_info_list, - SpeedProfileProperties speed_profile) - : m_max_edge_id(0), m_node_info_list(node_info_list), m_node_based_graph(std::move(node_based_graph)), - m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes), - m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container), - speed_profile(std::move(speed_profile)) -{ -} - -void EdgeBasedGraphFactory::GetEdgeBasedEdges(DeallocatingVector &output_edge_list) -{ - BOOST_ASSERT_MSG(0 == output_edge_list.size(), "Vector is not empty"); - using std::swap; // Koenig swap - swap(m_edge_based_edge_list, output_edge_list); -} - -void EdgeBasedGraphFactory::GetEdgeBasedNodes(std::vector &nodes) -{ -#ifndef NDEBUG - for (const EdgeBasedNode &node : m_edge_based_node_list) - { - BOOST_ASSERT(m_node_info_list.at(node.u).lat != INT_MAX); - BOOST_ASSERT(m_node_info_list.at(node.u).lon != INT_MAX); - BOOST_ASSERT(m_node_info_list.at(node.v).lon != INT_MAX); - BOOST_ASSERT(m_node_info_list.at(node.v).lat != INT_MAX); - } -#endif - using std::swap; // Koenig swap - swap(nodes, m_edge_based_node_list); -} - -void EdgeBasedGraphFactory::GetStartPointMarkers(std::vector &node_is_startpoint) -{ - using std::swap; // Koenig swap - swap(m_edge_based_node_is_startpoint, node_is_startpoint); -} - -unsigned EdgeBasedGraphFactory::GetHighestEdgeID() -{ - return m_max_edge_id; -} - -void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, - const NodeID node_v) -{ - // merge edges together into one EdgeBasedNode - BOOST_ASSERT(node_u != SPECIAL_NODEID); - BOOST_ASSERT(node_v != SPECIAL_NODEID); - - // find forward edge id and - const EdgeID edge_id_1 = m_node_based_graph->FindEdge(node_u, node_v); - BOOST_ASSERT(edge_id_1 != SPECIAL_EDGEID); - - const EdgeData &forward_data = m_node_based_graph->GetEdgeData(edge_id_1); - - // find reverse edge id and - const EdgeID edge_id_2 = m_node_based_graph->FindEdge(node_v, node_u); - BOOST_ASSERT(edge_id_2 != SPECIAL_EDGEID); - - const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(edge_id_2); - - if (forward_data.edge_id == SPECIAL_NODEID && - reverse_data.edge_id == SPECIAL_NODEID) - { - return; - } - - BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) == - m_compressed_edge_container.HasEntryForID(edge_id_2)); - if (m_compressed_edge_container.HasEntryForID(edge_id_1)) - { - BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_2)); - - // reconstruct geometry and put in each individual edge with its offset - const auto& forward_geometry = m_compressed_edge_container.GetBucketReference(edge_id_1); - const auto& reverse_geometry = m_compressed_edge_container.GetBucketReference(edge_id_2); - BOOST_ASSERT(forward_geometry.size() == reverse_geometry.size()); - BOOST_ASSERT(0 != forward_geometry.size()); - const unsigned geometry_size = static_cast(forward_geometry.size()); - BOOST_ASSERT(geometry_size > 1); - - // reconstruct bidirectional edge with individual weights and put each into the NN index - - std::vector forward_dist_prefix_sum(forward_geometry.size(), 0); - std::vector reverse_dist_prefix_sum(reverse_geometry.size(), 0); - - // quick'n'dirty prefix sum as std::partial_sum needs addtional casts - // TODO: move to lambda function with C++11 - int temp_sum = 0; - - for (const auto i : osrm::irange(0u, geometry_size)) - { - forward_dist_prefix_sum[i] = temp_sum; - temp_sum += forward_geometry[i].second; - - BOOST_ASSERT(forward_data.distance >= temp_sum); - } - - temp_sum = 0; - for (const auto i : osrm::irange(0u, geometry_size)) - { - temp_sum += reverse_geometry[reverse_geometry.size() - 1 - i].second; - reverse_dist_prefix_sum[i] = reverse_data.distance - temp_sum; - // BOOST_ASSERT(reverse_data.distance >= temp_sum); - } - - NodeID current_edge_source_coordinate_id = node_u; - - // traverse arrays from start and end respectively - for (const auto i : osrm::irange(0u, geometry_size)) - { - BOOST_ASSERT(current_edge_source_coordinate_id == - reverse_geometry[geometry_size - 1 - i].first); - const NodeID current_edge_target_coordinate_id = forward_geometry[i].first; - BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id); - - // build edges - m_edge_based_node_list.emplace_back( - forward_data.edge_id, reverse_data.edge_id, - current_edge_source_coordinate_id, current_edge_target_coordinate_id, - forward_data.name_id, forward_geometry[i].second, - reverse_geometry[geometry_size - 1 - i].second, forward_dist_prefix_sum[i], - reverse_dist_prefix_sum[i], m_compressed_edge_container.GetPositionForID(edge_id_1), - false, INVALID_COMPONENTID, i, forward_data.travel_mode, reverse_data.travel_mode); - m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || reverse_data.startpoint); - current_edge_source_coordinate_id = current_edge_target_coordinate_id; - - BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed()); - - BOOST_ASSERT(node_u != m_edge_based_node_list.back().u || - node_v != m_edge_based_node_list.back().v); - - BOOST_ASSERT(node_u != m_edge_based_node_list.back().v || - node_v != m_edge_based_node_list.back().u); - } - - BOOST_ASSERT(current_edge_source_coordinate_id == node_v); - BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed()); - } - else - { - BOOST_ASSERT(!m_compressed_edge_container.HasEntryForID(edge_id_2)); - - if (forward_data.edge_id != SPECIAL_NODEID) - { - BOOST_ASSERT(!forward_data.reversed); - } - else - { - BOOST_ASSERT(forward_data.reversed); - } - - if (reverse_data.edge_id != SPECIAL_NODEID) - { - BOOST_ASSERT(!reverse_data.reversed); - } - else - { - BOOST_ASSERT(reverse_data.reversed); - } - - BOOST_ASSERT(forward_data.edge_id != SPECIAL_NODEID || - reverse_data.edge_id != SPECIAL_NODEID); - - m_edge_based_node_list.emplace_back( - forward_data.edge_id, reverse_data.edge_id, node_u, node_v, - forward_data.name_id, forward_data.distance, reverse_data.distance, 0, 0, SPECIAL_EDGEID, - false, INVALID_COMPONENTID, 0, forward_data.travel_mode, reverse_data.travel_mode); - m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || reverse_data.startpoint); - BOOST_ASSERT(!m_edge_based_node_list.back().IsCompressed()); - } -} - -void EdgeBasedGraphFactory::FlushVectorToStream( - std::ofstream &edge_data_file, std::vector &original_edge_data_vector) const -{ - if (original_edge_data_vector.empty()) - { - return; - } - edge_data_file.write((char *)&(original_edge_data_vector[0]), - original_edge_data_vector.size() * sizeof(OriginalEdgeData)); - original_edge_data_vector.clear(); -} - -#ifdef DEBUG_GEOMETRY -void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, - const bool generate_edge_lookup, - const std::string &debug_turns_path) -#else -void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, - const bool generate_edge_lookup) -#endif -{ - TIMER_START(renumber); - m_max_edge_id = RenumberEdges() - 1; - TIMER_STOP(renumber); - - TIMER_START(generate_nodes); - GenerateEdgeExpandedNodes(); - TIMER_STOP(generate_nodes); - - TIMER_START(generate_edges); -#ifdef DEBUG_GEOMETRY - GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state, - edge_segment_lookup_filename,edge_penalty_filename, - generate_edge_lookup, debug_turns_path); -#else - GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state, - edge_segment_lookup_filename,edge_penalty_filename, - generate_edge_lookup); -#endif - - TIMER_STOP(generate_edges); - - SimpleLogger().Write() << "Timing statistics for edge-expanded graph:"; - SimpleLogger().Write() << "Renumbering edges: " << TIMER_SEC(renumber) << "s"; - SimpleLogger().Write() << "Generating nodes: " << TIMER_SEC(generate_nodes) << "s"; - SimpleLogger().Write() << "Generating edges: " << TIMER_SEC(generate_edges) << "s"; -} - - -/// Renumbers all _forward_ edges and sets the edge_id. -/// A specific numbering is not important. Any unique ID will do. -/// Returns the number of edge based nodes. -unsigned EdgeBasedGraphFactory::RenumberEdges() -{ - // renumber edge based node of outgoing edges - unsigned numbered_edges_count = 0; - for (const auto current_node : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes())) - { - for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node)) - { - EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge); - - // only number incoming edges - if (edge_data.reversed) - { - continue; - } - - BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges()); - edge_data.edge_id = numbered_edges_count; - ++numbered_edges_count; - - BOOST_ASSERT(SPECIAL_NODEID != edge_data.edge_id); - } - } - - return numbered_edges_count; -} - -/// Creates the nodes in the edge expanded graph from edges in the node-based graph. -void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() -{ - Percent progress(m_node_based_graph->GetNumberOfNodes()); - - // loop over all edges and generate new set of nodes - for (const auto node_u : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes())) - { - BOOST_ASSERT(node_u != SPECIAL_NODEID); - BOOST_ASSERT(node_u < m_node_based_graph->GetNumberOfNodes()); - progress.printStatus(node_u); - for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u)) - { - const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1); - BOOST_ASSERT(e1 != SPECIAL_EDGEID); - const NodeID node_v = m_node_based_graph->GetTarget(e1); - - BOOST_ASSERT(SPECIAL_NODEID != node_v); - // pick only every other edge, since we have every edge as an outgoing - // and incoming egde - if (node_u > node_v) - { - continue; - } - - BOOST_ASSERT(node_u < node_v); - - // if we found a non-forward edge reverse and try again - if (edge_data.edge_id == SPECIAL_NODEID) - { - InsertEdgeBasedNode(node_v, node_u); - } - else - { - InsertEdgeBasedNode(node_u, node_v); - } - } - } - - BOOST_ASSERT(m_edge_based_node_list.size() == m_edge_based_node_is_startpoint.size()); - - SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() - << " nodes in edge-expanded graph"; -} - -/// Actually it also generates OriginalEdgeData and serializes them... -#ifdef DEBUG_GEOMETRY -void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( - const std::string &original_edge_data_filename, lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, - const bool generate_edge_lookup, - const std::string &debug_turns_path) -#else -void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( - const std::string &original_edge_data_filename, lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, - const bool generate_edge_lookup) -#endif -{ - SimpleLogger().Write() << "generating edge-expanded edges"; - - unsigned node_based_edge_counter = 0; - unsigned original_edges_counter = 0; - - std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary); - std::ofstream edge_segment_file; - std::ofstream edge_penalty_file; - - if (generate_edge_lookup) - { - edge_segment_file.open(edge_segment_lookup_filename.c_str(), std::ios::binary); - edge_penalty_file.open(edge_fixed_penalties_filename.c_str(), std::ios::binary); - } - - // writes a dummy value that is updated later - edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned)); - - std::vector original_edge_data_vector; - original_edge_data_vector.reserve(1024 * 1024); - - // Loop over all turns and generate new set of edges. - // Three nested loop look super-linear, but we are dealing with a (kind of) - // linear number of turns only. - unsigned restricted_turns_counter = 0; - unsigned skipped_uturns_counter = 0; - unsigned skipped_barrier_turns_counter = 0; - unsigned compressed = 0; - - Percent progress(m_node_based_graph->GetNumberOfNodes()); - -#ifdef DEBUG_GEOMETRY - DEBUG_TURNS_START(debug_turns_path); -#endif - - for (const auto node_u : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes())) - { - //progress.printStatus(node_u); - for (const EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u)) - { - if (m_node_based_graph->GetEdgeData(e1).reversed) - { - continue; - } - - ++node_based_edge_counter; - const NodeID node_v = m_node_based_graph->GetTarget(e1); - const NodeID only_restriction_to_node = - m_restriction_map->CheckForEmanatingIsOnlyTurn(node_u, node_v); - const bool is_barrier_node = m_barrier_nodes.find(node_v) != m_barrier_nodes.end(); - - for (const EdgeID e2 : m_node_based_graph->GetAdjacentEdgeRange(node_v)) - { - if (m_node_based_graph->GetEdgeData(e2).reversed) - { - continue; - } - const NodeID node_w = m_node_based_graph->GetTarget(e2); - - if ((only_restriction_to_node != SPECIAL_NODEID) && - (node_w != only_restriction_to_node)) - { - // We are at an only_-restriction but not at the right turn. - ++restricted_turns_counter; - continue; - } - - if (is_barrier_node) - { - if (node_u != node_w) - { - ++skipped_barrier_turns_counter; - continue; - } - } - else - { - if (node_u == node_w && m_node_based_graph->GetOutDegree(node_v) > 1) - { - auto number_of_emmiting_bidirectional_edges = 0; - for (auto edge : m_node_based_graph->GetAdjacentEdgeRange(node_v)) - { - auto target = m_node_based_graph->GetTarget(edge); - auto reverse_edge = m_node_based_graph->FindEdge(target, node_v); - if (!m_node_based_graph->GetEdgeData(reverse_edge).reversed) - { - ++number_of_emmiting_bidirectional_edges; - } - } - if (number_of_emmiting_bidirectional_edges > 1) - { - ++skipped_uturns_counter; - continue; - } - } - } - - // only add an edge if turn is not a U-turn except when it is - // at the end of a dead-end street - if (m_restriction_map->CheckIfTurnIsRestricted(node_u, node_v, node_w) && - (only_restriction_to_node == SPECIAL_NODEID) && - (node_w != only_restriction_to_node)) - { - // We are at an only_-restriction but not at the right turn. - ++restricted_turns_counter; - continue; - } - - // only add an edge if turn is not prohibited - const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(e1); - const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(e2); - - BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id); - BOOST_ASSERT(!edge_data1.reversed); - BOOST_ASSERT(!edge_data2.reversed); - - // the following is the core of the loop. - unsigned distance = edge_data1.distance; - if (m_traffic_lights.find(node_v) != m_traffic_lights.end()) - { - distance += speed_profile.traffic_signal_penalty; - - DEBUG_SIGNAL(node_v, m_node_info_list, speed_profile.traffic_signal_penalty); - } - - // unpack last node of first segment if packed - const auto first_coordinate = - m_node_info_list[(m_compressed_edge_container.HasEntryForID(e1) - ? m_compressed_edge_container.GetLastEdgeSourceID(e1) - : node_u)]; - - // unpack first node of second segment if packed - const auto third_coordinate = - m_node_info_list[(m_compressed_edge_container.HasEntryForID(e2) - ? m_compressed_edge_container.GetFirstEdgeTargetID(e2) - : node_w)]; - - const double turn_angle = ComputeAngle::OfThreeFixedPointCoordinates( - first_coordinate, m_node_info_list[node_v], third_coordinate); - - const int turn_penalty = GetTurnPenalty(turn_angle, lua_state); - TurnInstruction turn_instruction = AnalyzeTurn(node_u, node_v, node_w, turn_angle); - if (turn_instruction == TurnInstruction::UTurn) - { - distance += speed_profile.u_turn_penalty; - - DEBUG_UTURN(node_v, m_node_info_list, speed_profile.u_turn_penalty); - } - - DEBUG_TURN(node_v, m_node_info_list, first_coordinate, turn_angle, turn_penalty); - - distance += turn_penalty; - - const bool edge_is_compressed = m_compressed_edge_container.HasEntryForID(e1); - - if (edge_is_compressed) - { - ++compressed; - } - - original_edge_data_vector.emplace_back( - (edge_is_compressed ? m_compressed_edge_container.GetPositionForID(e1) : node_v), - edge_data1.name_id, turn_instruction, edge_is_compressed, - edge_data2.travel_mode); - - ++original_edges_counter; - - if (original_edge_data_vector.size() > 1024 * 1024 * 10) - { - FlushVectorToStream(edge_data_file, original_edge_data_vector); - } - - BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); - BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); - - - // NOTE: potential overflow here if we hit 2^32 routable edges - BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits::max()); - m_edge_based_edge_list.emplace_back(edge_data1.edge_id, edge_data2.edge_id, - m_edge_based_edge_list.size(), distance, true, false); - - - // Here is where we write out the mapping between the edge-expanded edges, and - // the node-based edges that are originally used to calculate the `distance` - // for the edge-expanded edges. About 40 lines back, there is: - // - // unsigned distance = edge_data1.distance; - // - // This tells us that the weight for an edge-expanded-edge is based on the weight - // of the *source* node-based edge. Therefore, we will look up the individual - // segments of the source node-based edge, and write out a mapping between - // those and the edge-based-edge ID. - // External programs can then use this mapping to quickly perform - // updates to the edge-expanded-edge based directly on its ID. - if (generate_edge_lookup) - { - unsigned fixed_penalty = distance - edge_data1.distance; - edge_penalty_file.write(reinterpret_cast(&fixed_penalty), sizeof(fixed_penalty)); - if (edge_is_compressed) - { - const auto node_based_edges = m_compressed_edge_container.GetBucketReference(e1); - NodeID previous = node_u; - - const unsigned node_count = node_based_edges.size()+1; - edge_segment_file.write(reinterpret_cast(&node_count), sizeof(node_count)); - const QueryNode &first_node = m_node_info_list[previous]; - edge_segment_file.write(reinterpret_cast(&first_node.node_id), sizeof(first_node.node_id)); - - for (auto target_node : node_based_edges) - { - const QueryNode &from = m_node_info_list[previous]; - const QueryNode &to = m_node_info_list[target_node.first]; - const double segment_length = coordinate_calculation::great_circle_distance(from.lat, from.lon, to.lat, to.lon); - - edge_segment_file.write(reinterpret_cast(&to.node_id), sizeof(to.node_id)); - edge_segment_file.write(reinterpret_cast(&segment_length), sizeof(segment_length)); - edge_segment_file.write(reinterpret_cast(&target_node.second), sizeof(target_node.second)); - previous = target_node.first; - } - } - else - { - static const unsigned node_count = 2; - const QueryNode from = m_node_info_list[node_u]; - const QueryNode to = m_node_info_list[node_v]; - const double segment_length = coordinate_calculation::great_circle_distance(from.lat, from.lon, to.lat, to.lon); - edge_segment_file.write(reinterpret_cast(&node_count), sizeof(node_count)); - edge_segment_file.write(reinterpret_cast(&from.node_id), sizeof(from.node_id)); - edge_segment_file.write(reinterpret_cast(&to.node_id), sizeof(to.node_id)); - edge_segment_file.write(reinterpret_cast(&segment_length), sizeof(segment_length)); - edge_segment_file.write(reinterpret_cast(&edge_data1.distance), sizeof(edge_data1.distance)); - } - } - } - } - } - - DEBUG_TURNS_STOP(); - - FlushVectorToStream(edge_data_file, original_edge_data_vector); - - edge_data_file.seekp(std::ios::beg); - edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned)); - edge_data_file.close(); - - SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() << " edge based nodes"; - SimpleLogger().Write() << "Node-based graph contains " << node_based_edge_counter << " edges"; - SimpleLogger().Write() << "Edge-expanded graph ..."; - SimpleLogger().Write() << " contains " << m_edge_based_edge_list.size() << " edges"; - SimpleLogger().Write() << " skips " << restricted_turns_counter << " turns, " - "defined by " - << m_restriction_map->size() << " restrictions"; - SimpleLogger().Write() << " skips " << skipped_uturns_counter << " U turns"; - SimpleLogger().Write() << " skips " << skipped_barrier_turns_counter << " turns over barriers"; -} - -int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const -{ - - if (speed_profile.has_turn_penalty_function) - { - try - { - // call lua profile to compute turn penalty - double penalty = luabind::call_function(lua_state, "turn_function", 180. - angle); - return static_cast(penalty); - } - catch (const luabind::error &er) - { - SimpleLogger().Write(logWARNING) << er.what(); - } - } - return 0; -} - -TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u, - const NodeID node_v, - const NodeID node_w, - const double angle) const -{ - if (node_u == node_w) - { - return TurnInstruction::UTurn; - } - - const EdgeID edge1 = m_node_based_graph->FindEdge(node_u, node_v); - const EdgeID edge2 = m_node_based_graph->FindEdge(node_v, node_w); - - const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1); - const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2); - - // roundabouts need to be handled explicitely - if (data1.roundabout && data2.roundabout) - { - // Is a turn possible? If yes, we stay on the roundabout! - if (1 == m_node_based_graph->GetDirectedOutDegree(node_v)) - { - // No turn possible. - return TurnInstruction::NoTurn; - } - return TurnInstruction::StayOnRoundAbout; - } - // Does turn start or end on roundabout? - if (data1.roundabout || data2.roundabout) - { - // We are entering the roundabout - if ((!data1.roundabout) && data2.roundabout) - { - return TurnInstruction::EnterRoundAbout; - } - // We are leaving the roundabout - if (data1.roundabout && (!data2.roundabout)) - { - return TurnInstruction::LeaveRoundAbout; - } - } - - // If street names stay the same and if we are certain that it is not a - // a segment of a roundabout, we skip it. - if (data1.name_id == data2.name_id && data1.travel_mode == data2.travel_mode) - { - // TODO: Here we should also do a small graph exploration to check for - // more complex situations - if (0 != data1.name_id || m_node_based_graph->GetOutDegree(node_v) <= 2) - { - return TurnInstruction::NoTurn; - } - } - - return TurnInstructionsClass::GetTurnDirectionOfInstruction(angle); -} - diff --git a/extractor/edge_based_graph_factory.hpp b/extractor/edge_based_graph_factory.hpp deleted file mode 100644 index 997c7ea8e..000000000 --- a/extractor/edge_based_graph_factory.hpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -// This class constructs the edge-expanded routing graph - -#ifndef EDGE_BASED_GRAPH_FACTORY_HPP_ -#define EDGE_BASED_GRAPH_FACTORY_HPP_ - -#include "speed_profile.hpp" -#include "../typedefs.h" -#include "../data_structures/compressed_edge_container.hpp" -#include "../data_structures/deallocating_vector.hpp" -#include "../data_structures/edge_based_node.hpp" -#include "../data_structures/original_edge_data.hpp" -#include "../data_structures/query_node.hpp" -#include "../data_structures/turn_instructions.hpp" -#include "../data_structures/node_based_graph.hpp" -#include "../data_structures/restriction_map.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -struct lua_State; - -class EdgeBasedGraphFactory -{ - public: - EdgeBasedGraphFactory() = delete; - EdgeBasedGraphFactory(const EdgeBasedGraphFactory &) = delete; - - explicit EdgeBasedGraphFactory(std::shared_ptr node_based_graph, - const CompressedEdgeContainer &compressed_edge_container, - const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, - std::shared_ptr restriction_map, - const std::vector &node_info_list, - SpeedProfileProperties speed_profile); - -#ifdef DEBUG_GEOMETRY - void Run(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, - const bool generate_edge_lookup, - const std::string &debug_turns_path); -#else - void Run(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_penalty_filename, - const bool generate_edge_lookup); -#endif - - void GetEdgeBasedEdges(DeallocatingVector &edges); - - void GetEdgeBasedNodes(std::vector &nodes); - void GetStartPointMarkers(std::vector &node_is_startpoint); - - unsigned GetHighestEdgeID(); - - TurnInstruction AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w, const double angle) const; - - int GetTurnPenalty(double angle, lua_State *lua_state) const; - - private: - using EdgeData = NodeBasedDynamicGraph::EdgeData; - - //! maps index from m_edge_based_node_list to ture/false if the node is an entry point to the graph - std::vector m_edge_based_node_is_startpoint; - //! list of edge based nodes (compressed segments) - std::vector m_edge_based_node_list; - DeallocatingVector m_edge_based_edge_list; - unsigned m_max_edge_id; - - const std::vector& m_node_info_list; - std::shared_ptr m_node_based_graph; - std::shared_ptr m_restriction_map; - - const std::unordered_set& m_barrier_nodes; - const std::unordered_set& m_traffic_lights; - const CompressedEdgeContainer& m_compressed_edge_container; - - SpeedProfileProperties speed_profile; - - void CompressGeometry(); - unsigned RenumberEdges(); - void GenerateEdgeExpandedNodes(); -#ifdef DEBUG_GEOMETRY - void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, - const bool generate_edge_lookup, - const std::string &debug_turns_path); -#else - void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename, - lua_State *lua_state, - const std::string &edge_segment_lookup_filename, - const std::string &edge_fixed_penalties_filename, - const bool generate_edge_lookup); -#endif - - void InsertEdgeBasedNode(const NodeID u, const NodeID v); - - void FlushVectorToStream(std::ofstream &edge_data_file, - std::vector &original_edge_data_vector) const; - -}; - -#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */ diff --git a/extractor/extraction_node.hpp b/extractor/extraction_node.hpp deleted file mode 100644 index e821d6ffa..000000000 --- a/extractor/extraction_node.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef EXTRACTION_NODE_HPP -#define EXTRACTION_NODE_HPP - -struct ExtractionNode -{ - ExtractionNode() : traffic_lights(false), barrier(false) {} - void clear() { traffic_lights = barrier = false; } - bool traffic_lights; - bool barrier; -}; -#endif // EXTRACTION_NODE_HPP diff --git a/extractor/extraction_way.hpp b/extractor/extraction_way.hpp deleted file mode 100644 index 094c10a54..000000000 --- a/extractor/extraction_way.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef EXTRACTION_WAY_HPP -#define EXTRACTION_WAY_HPP - -#include "../data_structures/travel_mode.hpp" -#include "../typedefs.h" - -#include -#include - -/** - * This struct is the direct result of the call to ```way_function``` - * in the lua based profile. - * - * It is split into multiple edge segments in the ExtractorCallback. - */ -struct ExtractionWay -{ - ExtractionWay() { clear(); } - - void clear() - { - forward_speed = -1; - backward_speed = -1; - duration = -1; - roundabout = false; - is_startpoint = true; - is_access_restricted = false; - name.clear(); - forward_travel_mode = TRAVEL_MODE_DEFAULT; - backward_travel_mode = TRAVEL_MODE_DEFAULT; - } - - enum Directions - { - notSure = 0, - oneway, - bidirectional, - opposite - }; - - // These accessor methods exists to support the depreciated "way.direction" access - // in LUA. Since the direction attribute was removed from ExtractionWay, the - // accessors translate to/from the mode attributes. - void set_direction(const Directions m) - { - if (Directions::oneway == m) - { - forward_travel_mode = TRAVEL_MODE_DEFAULT; - backward_travel_mode = TRAVEL_MODE_INACCESSIBLE; - } - else if (Directions::opposite == m) - { - forward_travel_mode = TRAVEL_MODE_INACCESSIBLE; - backward_travel_mode = TRAVEL_MODE_DEFAULT; - } - else if (Directions::bidirectional == m) - { - forward_travel_mode = TRAVEL_MODE_DEFAULT; - backward_travel_mode = TRAVEL_MODE_DEFAULT; - } - } - - Directions get_direction() const - { - if (TRAVEL_MODE_INACCESSIBLE != forward_travel_mode && - TRAVEL_MODE_INACCESSIBLE != backward_travel_mode) - { - return Directions::bidirectional; - } - else if (TRAVEL_MODE_INACCESSIBLE != forward_travel_mode) - { - return Directions::oneway; - } - else if (TRAVEL_MODE_INACCESSIBLE != backward_travel_mode) - { - return Directions::opposite; - } - else - { - return Directions::notSure; - } - } - - // These accessors exists because it's not possible to take the address of a bitfield, - // and LUA therefore cannot read/write the mode attributes directly. - void set_forward_mode(const TravelMode m) { forward_travel_mode = m; } - TravelMode get_forward_mode() const { return forward_travel_mode; } - void set_backward_mode(const TravelMode m) { backward_travel_mode = m; } - TravelMode get_backward_mode() const { return backward_travel_mode; } - - double forward_speed; - double backward_speed; - double duration; - std::string name; - bool roundabout; - bool is_access_restricted; - bool is_startpoint; - TravelMode forward_travel_mode : 4; - TravelMode backward_travel_mode : 4; -}; - -#endif // EXTRACTION_WAY_HPP diff --git a/extractor/extractor_options.cpp b/extractor/extractor_options.cpp deleted file mode 100644 index b607dca95..000000000 --- a/extractor/extractor_options.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "extractor_options.hpp" - -#include "util/version.hpp" -#include "../util/ini_file.hpp" -#include "../util/simple_logger.hpp" - -#include -#include - -#include - -return_code -ExtractorOptions::ParseArguments(int argc, char *argv[], ExtractorConfig &extractor_config) -{ - // declare a group of options that will be allowed only on command line - boost::program_options::options_description generic_options("Options"); - generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")( - /* - * TODO: re-enable this - "restrictions,r", - boost::program_options::value(&extractor_config.restrictions_path), - "Restrictions file in .osrm.restrictions format")( - */ - "config,c", boost::program_options::value( - &extractor_config.config_file_path)->default_value("extractor.ini"), - "Path to a configuration file."); - - // declare a group of options that will be allowed both on command line and in config file - boost::program_options::options_description config_options("Configuration"); - config_options.add_options()("profile,p", - boost::program_options::value( - &extractor_config.profile_path)->default_value("profile.lua"), - "Path to LUA routing profile")( - "threads,t", - boost::program_options::value(&extractor_config.requested_num_threads) - ->default_value(tbb::task_scheduler_init::default_num_threads()), - "Number of threads to use")( - "generate-edge-lookup",boost::program_options::value( - &extractor_config.generate_edge_lookup)->implicit_value(true)->default_value(false), - "Generate a lookup table for internal edge-expanded-edge IDs to OSM node pairs")( - "small-component-size", - boost::program_options::value(&extractor_config.small_component_size) - ->default_value(1000), - "Number of nodes required before a strongly-connected-componennt is considered big (affects nearest neighbor snapping)"); - -#ifdef DEBUG_GEOMETRY - config_options.add_options()("debug-turns", - boost::program_options::value(&extractor_config.debug_turns_path), - "Write out GeoJSON with turn penalty data"); -#endif // DEBUG_GEOMETRY - - // hidden options, will be allowed both on command line and in config file, but will not be - // shown to the user - boost::program_options::options_description hidden_options("Hidden options"); - hidden_options.add_options()("input,i", boost::program_options::value( - &extractor_config.input_path), - "Input file in .osm, .osm.bz2 or .osm.pbf format"); - - - // positional option - boost::program_options::positional_options_description positional_options; - positional_options.add("input", 1); - - // combine above options for parsing - boost::program_options::options_description cmdline_options; - cmdline_options.add(generic_options).add(config_options).add(hidden_options); - - boost::program_options::options_description config_file_options; - config_file_options.add(config_options).add(hidden_options); - - boost::program_options::options_description visible_options( - boost::filesystem::basename(argv[0]) + " [options]"); - visible_options.add(generic_options).add(config_options); - - // parse command line options - try - { - boost::program_options::variables_map option_variables; - boost::program_options::store(boost::program_options::command_line_parser(argc, argv) - .options(cmdline_options) - .positional(positional_options) - .run(), - option_variables); - if (option_variables.count("version")) - { - SimpleLogger().Write() << OSRM_VERSION; - return return_code::exit; - } - - if (option_variables.count("help")) - { - SimpleLogger().Write() << visible_options; - return return_code::exit; - } - - boost::program_options::notify(option_variables); - - // parse config file - if (boost::filesystem::is_regular_file(extractor_config.config_file_path)) - { - SimpleLogger().Write() - << "Reading options from: " << extractor_config.config_file_path.string(); - std::string ini_file_contents = - read_file_lower_content(extractor_config.config_file_path); - std::stringstream config_stream(ini_file_contents); - boost::program_options::store(parse_config_file(config_stream, config_file_options), - option_variables); - boost::program_options::notify(option_variables); - } - - if (!option_variables.count("input")) - { - SimpleLogger().Write() << visible_options; - return return_code::exit; - } - } - catch (std::exception &e) - { - SimpleLogger().Write(logWARNING) << e.what(); - return return_code::fail; - } - - return return_code::ok; -} - -void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_config) -{ - boost::filesystem::path &input_path = extractor_config.input_path; - extractor_config.output_file_name = input_path.string(); - extractor_config.restriction_file_name = input_path.string(); - extractor_config.names_file_name = input_path.string(); - extractor_config.timestamp_file_name = input_path.string(); - extractor_config.geometry_output_path = input_path.string(); - extractor_config.edge_output_path = input_path.string(); - extractor_config.edge_graph_output_path = input_path.string(); - extractor_config.node_output_path = input_path.string(); - extractor_config.rtree_nodes_output_path = input_path.string(); - extractor_config.rtree_leafs_output_path = input_path.string(); - extractor_config.edge_segment_lookup_path = input_path.string(); - extractor_config.edge_penalty_path = input_path.string(); - std::string::size_type pos = extractor_config.output_file_name.find(".osm.bz2"); - if (pos == std::string::npos) - { - pos = extractor_config.output_file_name.find(".osm.pbf"); - if (pos == std::string::npos) - { - pos = extractor_config.output_file_name.find(".osm.xml"); - } - } - if (pos == std::string::npos) - { - pos = extractor_config.output_file_name.find(".pbf"); - } - if (pos == std::string::npos) - { - pos = extractor_config.output_file_name.find(".osm"); - if (pos == std::string::npos) - { - extractor_config.output_file_name.append(".osrm"); - extractor_config.restriction_file_name.append(".osrm.restrictions"); - extractor_config.names_file_name.append(".osrm.names"); - extractor_config.timestamp_file_name.append(".osrm.timestamp"); - extractor_config.geometry_output_path.append(".osrm.geometry"); - extractor_config.node_output_path.append(".osrm.nodes"); - extractor_config.edge_output_path.append(".osrm.edges"); - extractor_config.edge_graph_output_path.append(".osrm.ebg"); - extractor_config.rtree_nodes_output_path.append(".osrm.ramIndex"); - extractor_config.rtree_leafs_output_path.append(".osrm.fileIndex"); - extractor_config.edge_segment_lookup_path.append(".osrm.edge_segment_lookup"); - extractor_config.edge_penalty_path.append(".osrm.edge_penalties"); - } - else - { - extractor_config.output_file_name.replace(pos, 5, ".osrm"); - extractor_config.restriction_file_name.replace(pos, 5, ".osrm.restrictions"); - extractor_config.names_file_name.replace(pos, 5, ".osrm.names"); - extractor_config.timestamp_file_name.replace(pos, 5, ".osrm.timestamp"); - extractor_config.geometry_output_path.replace(pos, 5, ".osrm.geometry"); - extractor_config.node_output_path.replace(pos, 5, ".osrm.nodes"); - extractor_config.edge_output_path.replace(pos, 5, ".osrm.edges"); - extractor_config.edge_graph_output_path.replace(pos, 5, ".osrm.ebg"); - extractor_config.rtree_nodes_output_path.replace(pos, 5, ".osrm.ramIndex"); - extractor_config.rtree_leafs_output_path.replace(pos, 5, ".osrm.fileIndex"); - extractor_config.edge_segment_lookup_path.replace(pos,5, ".osrm.edge_segment_lookup"); - extractor_config.edge_penalty_path.replace(pos,5, ".osrm.edge_penalties"); - } - } - else - { - extractor_config.output_file_name.replace(pos, 8, ".osrm"); - extractor_config.restriction_file_name.replace(pos, 8, ".osrm.restrictions"); - extractor_config.names_file_name.replace(pos, 8, ".osrm.names"); - extractor_config.timestamp_file_name.replace(pos, 8, ".osrm.timestamp"); - extractor_config.geometry_output_path.replace(pos, 8, ".osrm.geometry"); - extractor_config.node_output_path.replace(pos, 8, ".osrm.nodes"); - extractor_config.edge_output_path.replace(pos, 8, ".osrm.edges"); - extractor_config.edge_graph_output_path.replace(pos, 8, ".osrm.ebg"); - extractor_config.rtree_nodes_output_path.replace(pos, 8, ".osrm.ramIndex"); - extractor_config.rtree_leafs_output_path.replace(pos, 8, ".osrm.fileIndex"); - extractor_config.edge_segment_lookup_path.replace(pos,8, ".osrm.edge_segment_lookup"); - extractor_config.edge_penalty_path.replace(pos,8, ".osrm.edge_penalties"); - } -} diff --git a/extractor/first_and_last_segment_of_way.hpp b/extractor/first_and_last_segment_of_way.hpp deleted file mode 100644 index ead8b4c5c..000000000 --- a/extractor/first_and_last_segment_of_way.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef FIRST_AND_LAST_SEGMENT_OF_WAY_HPP -#define FIRST_AND_LAST_SEGMENT_OF_WAY_HPP - -#include "../data_structures/external_memory_node.hpp" -#include "../typedefs.h" - -#include -#include - -struct FirstAndLastSegmentOfWay -{ - OSMWayID way_id; - OSMNodeID first_segment_source_id; - OSMNodeID first_segment_target_id; - OSMNodeID last_segment_source_id; - OSMNodeID last_segment_target_id; - - FirstAndLastSegmentOfWay() - : way_id(SPECIAL_OSM_WAYID), - first_segment_source_id(SPECIAL_OSM_NODEID), - first_segment_target_id(SPECIAL_OSM_NODEID), - last_segment_source_id(SPECIAL_OSM_NODEID), - last_segment_target_id(SPECIAL_OSM_NODEID) - { - } - - FirstAndLastSegmentOfWay(OSMWayID w, OSMNodeID fs, OSMNodeID ft, OSMNodeID ls, OSMNodeID lt) - : way_id(w), first_segment_source_id(fs), first_segment_target_id(ft), - last_segment_source_id(ls), last_segment_target_id(lt) - { - } - - static FirstAndLastSegmentOfWay min_value() - { - return {MIN_OSM_WAYID, - MIN_OSM_NODEID, - MIN_OSM_NODEID, - MIN_OSM_NODEID, - MIN_OSM_NODEID}; - } - static FirstAndLastSegmentOfWay max_value() - { - return {MAX_OSM_WAYID, - MAX_OSM_NODEID, - MAX_OSM_NODEID, - MAX_OSM_NODEID, - MAX_OSM_NODEID}; - } -}; - -struct FirstAndLastSegmentOfWayStxxlCompare -{ - using value_type = FirstAndLastSegmentOfWay; - bool operator()(const FirstAndLastSegmentOfWay &a, const FirstAndLastSegmentOfWay &b) const - { - return a.way_id < b.way_id; - } - value_type max_value() { return FirstAndLastSegmentOfWay::max_value(); } - value_type min_value() { return FirstAndLastSegmentOfWay::min_value(); } -}; - -#endif /* FIRST_AND_LAST_SEGMENT_OF_WAY_HPP */ diff --git a/extractor/restriction_parser.hpp b/extractor/restriction_parser.hpp deleted file mode 100644 index 11f94ee21..000000000 --- a/extractor/restriction_parser.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef RESTRICTION_PARSER_HPP -#define RESTRICTION_PARSER_HPP - -#include "../data_structures/restriction.hpp" - -#include - -#include -#include - -struct lua_State; -namespace osmium -{ -class Relation; -} - -/** - * Parses the relations that represents turn restrictions. - * - * Currently only restrictions where the via objects is a node are supported. - * from via to - * ------->(x)--------> - * - * While this class does not directly invoke any lua code _per relation_ it does - * load configuration values from the profile, that are saved in variables. - * Namely ```use_turn_restrictions``` and ```get_exceptions```. - * - * The restriction is represented by the osm id of the from way, the osm id of the - * to way and the osm id of the via node. This representation must be post-processed - * in the extractor to work with the edge-based data-model of OSRM: - * Since the from and to way share the via-node a turn will have the following form: - * ...----(a)-----(via)------(b)----... - * So it can be represented by the tripe (a, via, b). - */ -class RestrictionParser -{ - public: - RestrictionParser(lua_State *lua_state); - boost::optional TryParse(const osmium::Relation &relation) const; - - private: - void ReadUseRestrictionsSetting(lua_State *lua_state); - void ReadRestrictionExceptions(lua_State *lua_state); - bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; - - std::vector restriction_exceptions; - bool use_turn_restrictions; -}; - -#endif /* RESTRICTION_PARSER_HPP */ diff --git a/extractor/scripting_environment.cpp b/extractor/scripting_environment.cpp deleted file mode 100644 index 7ccc08ab5..000000000 --- a/extractor/scripting_environment.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "scripting_environment.hpp" - -#include "extraction_helper_functions.hpp" -#include "extraction_node.hpp" -#include "extraction_way.hpp" -#include "internal_extractor_edge.hpp" -#include "../data_structures/external_memory_node.hpp" -#include "../data_structures/raster_source.hpp" -#include "../util/lua_util.hpp" -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" -#include "../typedefs.h" - -#include -#include - -#include - -#include -namespace -{ -// wrapper method as luabind doesn't automatically overload funcs w/ default parameters -template -auto get_value_by_key(T const &object, const char *key) -> decltype(object.get_value_by_key(key)) -{ - return object.get_value_by_key(key, ""); -} - -int lua_error_callback(lua_State *L) // This is so I can use my own function as an -// exception handler, pcall_log() -{ - std::string error_msg = lua_tostring(L, -1); - std::ostringstream error_stream; - error_stream << error_msg; - throw osrm::exception("ERROR occured in profile script:\n" + error_stream.str()); -} -} - -ScriptingEnvironment::ScriptingEnvironment(const std::string &file_name) : file_name(file_name) -{ - SimpleLogger().Write() << "Using script " << file_name; -} - -void ScriptingEnvironment::init_lua_state(lua_State *lua_state) -{ - typedef double (osmium::Location::*location_member_ptr_type)() const; - - luabind::open(lua_state); - // open utility libraries string library; - luaL_openlibs(lua_state); - - luaAddScriptFolderToLoadPath(lua_state, file_name.c_str()); - - // Add our function to the state's global scope - luabind::module(lua_state)[ - luabind::def("print", LUA_print), - luabind::def("durationIsValid", durationIsValid), - luabind::def("parseDuration", parseDuration), - luabind::class_("sources") - .def(luabind::constructor<>()) - .def("load", &SourceContainer::loadRasterSource) - .def("query", &SourceContainer::getRasterDataFromSource) - .def("interpolate", &SourceContainer::getRasterInterpolateFromSource), - luabind::class_("constants") - .enum_("enums")[luabind::value("precision", COORDINATE_PRECISION)], - - luabind::class_>("vector") - .def("Add", static_cast::*)(const std::string &)>( - &std::vector::push_back)), - - luabind::class_("Location") - .def("lat", &osmium::Location::lat) - .def("lon", &osmium::Location::lon), - - luabind::class_("Node") - // .def("tags", &osmium::Node::tags) - .def("location", &osmium::Node::location) - .def("get_value_by_key", &osmium::Node::get_value_by_key) - .def("get_value_by_key", &get_value_by_key) - .def("id", &osmium::Node::id), - - luabind::class_("ResultNode") - .def_readwrite("traffic_lights", &ExtractionNode::traffic_lights) - .def_readwrite("barrier", &ExtractionNode::barrier), - - luabind::class_("ResultWay") - // .def(luabind::constructor<>()) - .def_readwrite("forward_speed", &ExtractionWay::forward_speed) - .def_readwrite("backward_speed", &ExtractionWay::backward_speed) - .def_readwrite("name", &ExtractionWay::name) - .def_readwrite("roundabout", &ExtractionWay::roundabout) - .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted) - .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint) - .def_readwrite("duration", &ExtractionWay::duration) - .property("forward_mode", &ExtractionWay::get_forward_mode, - &ExtractionWay::set_forward_mode) - .property("backward_mode", &ExtractionWay::get_backward_mode, - &ExtractionWay::set_backward_mode) - .enum_("constants")[ - luabind::value("notSure", 0), - luabind::value("oneway", 1), - luabind::value("bidirectional", 2), - luabind::value("opposite", 3) - ], - luabind::class_("Way") - .def("get_value_by_key", &osmium::Way::get_value_by_key) - .def("get_value_by_key", &get_value_by_key) - .def("id", &osmium::Way::id), - luabind::class_("EdgeSource") - .property("source_coordinate", &InternalExtractorEdge::source_coordinate) - .property("weight_data", &InternalExtractorEdge::weight_data), - luabind::class_("WeightData") - .def_readwrite("speed", &InternalExtractorEdge::WeightData::speed), - luabind::class_("EdgeTarget") - .property("lat", &ExternalMemoryNode::lat) - .property("lon", &ExternalMemoryNode::lon), - luabind::class_("Coordinate") - .property("lat", &FixedPointCoordinate::lat) - .property("lon", &FixedPointCoordinate::lon), - luabind::class_("RasterDatum") - .property("datum", &RasterDatum::datum) - .def("invalid_data", &RasterDatum::get_invalid) - ]; - - if (0 != luaL_dofile(lua_state, file_name.c_str())) - { - luabind::object error_msg(luabind::from_stack(lua_state, -1)); - std::ostringstream error_stream; - error_stream << error_msg; - throw osrm::exception("ERROR occured in profile script:\n" + error_stream.str()); - } -} - -lua_State *ScriptingEnvironment::get_lua_state() -{ - std::lock_guard lock(init_mutex); - bool initialized = false; - auto &ref = script_contexts.local(initialized); - if (!initialized) - { - std::shared_ptr state(luaL_newstate(), lua_close); - ref = state; - init_lua_state(ref.get()); - } - luabind::set_pcall_callback(&lua_error_callback); - - return ref.get(); -} diff --git a/extractor/scripting_environment.hpp b/extractor/scripting_environment.hpp deleted file mode 100644 index 8722aee8f..000000000 --- a/extractor/scripting_environment.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SCRIPTING_ENVIRONMENT_HPP -#define SCRIPTING_ENVIRONMENT_HPP - -#include -#include -#include -#include - -struct lua_State; - -/** - * Creates a lua context and binds osmium way, node and relation objects and - * ExtractionWay and ExtractionNode to lua objects. - * - * Each thread has its own lua state which is implemented with thread specific - * storage from TBB. - */ -class ScriptingEnvironment -{ - public: - ScriptingEnvironment() = delete; - explicit ScriptingEnvironment(const std::string &file_name); - - lua_State *get_lua_state(); - - private: - void init_lua_state(lua_State *lua_state); - std::mutex init_mutex; - std::string file_name; - tbb::enumerable_thread_specific> script_contexts; -}; - -#endif /* SCRIPTING_ENVIRONMENT_HPP */ diff --git a/extractor/speed_profile.hpp b/extractor/speed_profile.hpp deleted file mode 100644 index 534ccce34..000000000 --- a/extractor/speed_profile.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef SPEED_PROFILE_PROPERTIES_HPP -#define SPEED_PROFILE_PROPERTIES_HPP - -struct SpeedProfileProperties -{ - SpeedProfileProperties() - : traffic_signal_penalty(0), u_turn_penalty(0), has_turn_penalty_function(false) - { - } - - int traffic_signal_penalty; - int u_turn_penalty; - bool has_turn_penalty_function; -}; - -#endif diff --git a/features/bicycle/access.feature b/features/bicycle/access.feature index 3a2fffbc0..67c81803a 100644 --- a/features/bicycle/access.feature +++ b/features/bicycle/access.feature @@ -5,7 +5,7 @@ Feature: Bike - Access tags on ways Background: Given the profile "bicycle" - Scenario: Bike - Access tag hierachy on ways + Scenario: Bike - Access tag hierarchy on ways Then routability should be | highway | access | vehicle | bicycle | bothw | | | | | | x | @@ -121,6 +121,7 @@ Feature: Bike - Access tags on ways | private | | | | | agricultural | | | | | forestry | | | | + | delivery | | | | | | yes | | x | | | permissive | | x | | | designated | | x | @@ -129,6 +130,7 @@ Feature: Bike - Access tags on ways | | private | | | | | agricultural | | | | | forestry | | | + | | delivery | | | | | | yes | x | | | | permissive | x | | | | designated | x | @@ -137,6 +139,7 @@ Feature: Bike - Access tags on ways | | | private | | | | | agricultural | | | | | forestry | | + | | | delivery | | Scenario: Bike - Access tags on both node and way Then routability should be diff --git a/features/bicycle/access_node.feature b/features/bicycle/access_node.feature index 05e94feae..3c1a75a5f 100644 --- a/features/bicycle/access_node.feature +++ b/features/bicycle/access_node.feature @@ -5,7 +5,7 @@ Feature: Bike - Access tags on nodes Background: Given the profile "bicycle" - Scenario: Bike - Access tag hierachy on nodes + Scenario: Bike - Access tag hierarchy on nodes Then routability should be | node/access | node/vehicle | node/bicycle | node/highway | bothw | | | | | | x | @@ -47,6 +47,7 @@ Feature: Bike - Access tags on nodes | private | | | | | agricultural | | | | | forestry | | | | + | delivery | | | | | | yes | | x | | | permissive | | x | | | designated | | x | @@ -55,6 +56,7 @@ Feature: Bike - Access tags on nodes | | private | | | | | agricultural | | | | | forestry | | | + | | delivery | | | | | | yes | x | | | | permissive | x | | | | designated | x | @@ -63,3 +65,4 @@ Feature: Bike - Access tags on nodes | | | private | | | | | agricultural | | | | | forestry | | + | | | delivery | | diff --git a/features/bicycle/area.feature b/features/bicycle/area.feature index 062e86f15..626b383df 100644 --- a/features/bicycle/area.feature +++ b/features/bicycle/area.feature @@ -4,7 +4,7 @@ Feature: Bike - Squares and other areas Background: Given the profile "bicycle" - @square + @square @mokob @2154 Scenario: Bike - Route along edge of a squares Given the node map | x | | @@ -17,15 +17,15 @@ Feature: Bike - Squares and other areas | abcda | yes | residential | When I route I should get - | from | to | route | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | @building Scenario: Bike - Don't route on buildings @@ -41,16 +41,16 @@ Feature: Bike - Squares and other areas When I route I should get | from | to | route | - | a | b | xa | - | a | d | xa | - | b | c | xa | - | c | b | xa | - | c | d | xa | - | d | c | xa | - | d | a | xa | - | a | d | xa | + | a | b | xa,xa | + | a | d | xa,xa | + | b | c | xa,xa | + | c | b | xa,xa | + | c | d | xa,xa | + | d | c | xa,xa | + | d | a | xa,xa | + | a | d | xa,xa | - @parking + @parking @mokob @2154 Scenario: Bike - parking areas Given the node map | e | | | f | @@ -65,19 +65,20 @@ Feature: Bike - Squares and other areas | abcda | (nil) | parking | When I route I should get - | from | to | route | - | x | y | xa,abcda,by | - | y | x | by,abcda,xa | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | x | y | xa,abcda,by,by | + | y | x | by,abcda,xa,xa | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | - @train @platform + + @train @platform @mokob @2154 Scenario: Bike - railway platforms Given the node map | x | a | b | y | @@ -90,14 +91,14 @@ Feature: Bike - Squares and other areas | abcda | (nil) | platform | When I route I should get - | from | to | route | - | x | y | xa,abcda,by | - | y | x | by,abcda,xa | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | x | y | xa,abcda,by,by | + | y | x | by,abcda,xa,xa | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | diff --git a/features/bicycle/bridge.feature b/features/bicycle/bridge.feature index 8c26ee99d..9e3873f9d 100644 --- a/features/bicycle/bridge.feature +++ b/features/bicycle/bridge.feature @@ -1,10 +1,10 @@ @routing @bicycle @bridge -Feature: Bicycle - Handle movable bridge +Feature: Bicycle - Handle cycling Background: Given the profile "bicycle" - Scenario: Car - Use a ferry route + Scenario: Bicycle - Use a ferry route Given the node map | a | b | c | | | | | | d | | | @@ -17,17 +17,17 @@ Feature: Bicycle - Handle movable bridge | efg | primary | | | When I route I should get - | from | to | route | modes | - | a | g | abc,cde,efg | 1,5,1 | - | b | f | abc,cde,efg | 1,5,1 | - | e | c | cde | 5 | - | e | b | cde,abc | 5,1 | - | e | a | cde,abc | 5,1 | - | c | e | cde | 5 | - | c | f | cde,efg | 5,1 | - | c | g | cde,efg | 5,1 | + | from | to | route | modes | + | a | g | abc,cde,efg,efg | cycling,cycling,cycling,cycling | + | b | f | abc,cde,efg,efg | cycling,cycling,cycling,cycling | + | e | c | cde,cde | cycling,cycling | + | e | b | cde,abc,abc | cycling,cycling,cycling | + | e | a | cde,abc,abc | cycling,cycling,cycling | + | c | e | cde,cde | cycling,cycling | + | c | f | cde,efg,efg | cycling,cycling,cycling | + | c | g | cde,efg,efg | cycling,cycling,cycling | - Scenario: Car - Properly handle durations + Scenario: Bicycle - Properly handle durations Given the node map | a | b | c | | | | | | d | | | @@ -40,8 +40,8 @@ Feature: Bicycle - Handle movable bridge | efg | primary | | | When I route I should get - | from | to | route | modes | speed | - | a | g | abc,cde,efg | 1,5,1 | 5 km/h | - | b | f | abc,cde,efg | 1,5,1 | 4 km/h | - | c | e | cde | 5 | 2 km/h | - | e | c | cde | 5 | 2 km/h | + | from | to | route | modes | speed | + | a | g | abc,cde,efg,efg | cycling,cycling,cycling,cycling | 5 km/h | + | b | f | abc,cde,efg,efg | cycling,cycling,cycling,cycling | 4 km/h | + | c | e | cde,cde | cycling,cycling | 2 km/h | + | e | c | cde,cde | cycling,cycling | 2 km/h | diff --git a/features/bicycle/destination.feature b/features/bicycle/destination.feature index 9c7119888..c3a0173cd 100644 --- a/features/bicycle/destination.feature +++ b/features/bicycle/destination.feature @@ -19,15 +19,15 @@ Feature: Bike - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | b | ab | - | a | c | ab,bcd | - | a | d | ab,bcd | - | a | e | axye | - | e | d | de | - | e | c | de,bcd | - | e | b | de,bcd | - | e | a | axye | + | from | to | route | + | a | b | ab,ab | + | a | c | ab,bcd,bcd | + | a | d | ab,bcd,bcd | + | a | e | axye,axye | + | e | d | de,de | + | e | c | de,bcd,bcd | + | e | b | de,bcd,bcd | + | e | a | axye,axye | Scenario: Bike - Destination only street Given the node map @@ -45,15 +45,15 @@ Feature: Bike - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | b | ab | - | a | c | ab,bc | - | a | d | ab,bc,cd | - | a | e | axye | - | e | d | de | - | e | c | de,dc | - | e | b | de,dc,bc | - | e | a | axye | + | from | to | route | + | a | b | ab,ab | + | a | c | ab,bc,bc | + | a | d | ab,bc,cd,cd | + | a | e | axye,axye | + | e | d | de,de | + | e | c | de,cd,cd | + | e | b | de,cd,bc,bc | + | e | a | axye,axye | Scenario: Bike - Routing inside a destination only area Given the node map @@ -70,8 +70,8 @@ Feature: Bike - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | e | ab,bc,cd,de | - | e | a | de,cd,bc,ab | - | b | d | bc,cd | - | d | b | cd,bc | + | from | to | route | + | a | e | ab,bc,cd,de,de | + | e | a | de,cd,bc,ab,ab | + | b | d | bc,cd,cd | + | d | b | cd,bc,bc | diff --git a/features/bicycle/ferry.feature b/features/bicycle/ferry.feature index 85c925f8b..aaec0ef9c 100644 --- a/features/bicycle/ferry.feature +++ b/features/bicycle/ferry.feature @@ -17,15 +17,15 @@ Feature: Bike - Handle ferry routes | efg | primary | | | When I route I should get - | from | to | route | - | a | g | abc,cde,efg | - | b | f | abc,cde,efg | - | e | c | cde | - | e | b | cde,abc | - | e | a | cde,abc | - | c | e | cde | - | c | f | cde,efg | - | c | g | cde,efg | + | from | to | route | + | a | g | abc,cde,efg,efg | + | b | f | abc,cde,efg,efg | + | e | c | cde,cde | + | e | b | cde,abc,abc | + | e | a | cde,abc,abc | + | c | e | cde,cde | + | c | f | cde,efg,efg | + | c | g | cde,efg,efg | Scenario: Bike - Ferry duration, single node Given the node map @@ -59,5 +59,5 @@ Feature: Bike - Handle ferry routes When I route I should get | from | to | route | time | - | a | d | abcd | 3600s +-10 | - | d | a | abcd | 3600s +-10 | + | a | d | abcd,abcd | 3600s +-10 | + | d | a | abcd,abcd | 3600s +-10 | diff --git a/features/bicycle/maxspeed.feature b/features/bicycle/maxspeed.feature index ba5bfba4b..ad2c2bf2d 100644 --- a/features/bicycle/maxspeed.feature +++ b/features/bicycle/maxspeed.feature @@ -48,9 +48,9 @@ Feature: Bike - Max speed restrictions | bc | residential | 80 | When I route I should get - | from | to | route | speed | - | a | b | ab | 15 km/h | - | b | c | bc | 15 km/h | + | from | to | route | speed | + | a | b | ab,ab | 15 km/h | + | b | c | bc,bc | 15 km/h | Scenario: Bike - Forward/backward maxspeed Given the shortcuts diff --git a/features/bicycle/mode.feature b/features/bicycle/mode.feature index 2bfa4de5b..852402a72 100644 --- a/features/bicycle/mode.feature +++ b/features/bicycle/mode.feature @@ -1,15 +1,9 @@ @routing @bicycle @mode Feature: Bike - Mode flag -# bicycle modes: -# 1 bike -# 2 pushing -# 3 ferry -# 4 train - Background: Given the profile "bicycle" - + Scenario: Bike - Mode when using a ferry Given the node map | a | b | | @@ -22,13 +16,13 @@ Feature: Bike - Mode flag | cd | primary | | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,3,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,3,1 | - | c | a | bc,ab | head,left,destination | 3,1 | - | d | b | cd,bc | head,right,destination | 1,3 | - | a | c | ab,bc | head,right,destination | 1,3 | - | b | d | bc,cd | head,left,destination | 3,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,ferry,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,ferry,cycling,cycling | + | c | a | bc,ab,ab | ferry,cycling,cycling | + | d | b | cd,bc,bc | cycling,ferry,ferry | + | a | c | ab,bc,bc | cycling,ferry,ferry | + | b | d | bc,cd,cd | ferry,cycling,cycling | Scenario: Bike - Mode when using a train Given the node map @@ -42,13 +36,13 @@ Feature: Bike - Mode flag | cd | primary | | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,4,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,4,1 | - | c | a | bc,ab | head,left,destination | 4,1 | - | d | b | cd,bc | head,right,destination | 1,4 | - | a | c | ab,bc | head,right,destination | 1,4 | - | b | d | bc,cd | head,left,destination | 4,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,train,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,train,cycling,cycling | + | c | a | bc,ab,ab | train,cycling,cycling | + | d | b | cd,bc,bc | cycling,train,train | + | a | c | ab,bc,bc | cycling,train,train | + | b | d | bc,cd,cd | train,cycling,cycling | Scenario: Bike - Mode when pushing bike against oneways Given the node map @@ -62,13 +56,13 @@ Feature: Bike - Mode flag | cd | primary | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,1,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,1 | - | b | d | bc,cd | head,left,destination | 1,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,cycling,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | + | a | c | ab,bc,bc | cycling,cycling,cycling | + | b | d | bc,cd,cd | cycling,cycling,cycling | Scenario: Bike - Mode when pushing on pedestrain streets Given the node map @@ -82,13 +76,13 @@ Feature: Bike - Mode flag | cd | primary | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,2,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,2 | - | b | d | bc,cd | head,left,destination | 2,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | + | a | c | ab,bc,bc | cycling,pushing bike,pushing bike | + | b | d | bc,cd,cd | pushing bike,cycling,cycling | Scenario: Bike - Mode when pushing on pedestrain areas Given the node map @@ -102,13 +96,13 @@ Feature: Bike - Mode flag | df | primary | | When I route I should get - | from | to | route | modes | - | a | f | ab,bcd,df | 1,2,1 | - | f | a | df,bcd,ab | 1,2,1 | - | d | a | bcd,ab | 2,1 | - | f | b | df,bcd | 1,2 | - | a | d | ab,bcd | 1,2 | - | b | f | bcd,df | 2,1 | + | from | to | route | modes | + | a | f | ab,bcd,df,df | cycling,pushing bike,cycling,cycling | + | f | a | df,bcd,ab,ab | cycling,pushing bike,cycling,cycling | + | d | a | bcd,ab,ab | pushing bike,cycling,cycling | + | f | b | df,bcd,bcd | cycling,pushing bike,pushing bike | + | a | d | ab,bcd,bcd | cycling,pushing bike,pushing bike | + | b | f | bcd,df,df | pushing bike,cycling,cycling | Scenario: Bike - Mode when pushing on steps Given the node map @@ -122,13 +116,13 @@ Feature: Bike - Mode flag | cd | primary | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,2,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,2 | - | b | d | bc,cd | head,left,destination | 2,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | + | a | c | ab,bc,bc | cycling,pushing bike,pushing bike | + | b | d | bc,cd,cd | pushing bike,cycling,cycling | Scenario: Bike - Mode when bicycle=dismount Given the node map @@ -142,13 +136,13 @@ Feature: Bike - Mode flag | cd | primary | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,2,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,2 | - | b | d | bc,cd | head,left,destination | 2,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | + | a | c | ab,bc,bc | cycling,pushing bike,pushing bike | + | b | d | bc,cd,cd | pushing bike,cycling,cycling | Scenario: Bicycle - Modes when starting on forward oneway Given the node map @@ -159,9 +153,9 @@ Feature: Bike - Mode flag | ab | yes | When I route I should get - | from | to | route | modes | - | a | b | ab | 1 | - | b | a | ab | 2 | + | from | to | route | modes | + | a | b | ab,ab | cycling,cycling | + | b | a | ab,ab | pushing bike,pushing bike | Scenario: Bicycle - Modes when starting on reverse oneway Given the node map @@ -172,6 +166,6 @@ Feature: Bike - Mode flag | ab | -1 | When I route I should get - | from | to | route | modes | - | a | b | ab | 2 | - | b | a | ab | 1 | + | from | to | route | modes | + | a | b | ab,ab | pushing bike,pushing bike | + | b | a | ab,ab | cycling,cycling | diff --git a/features/bicycle/names.feature b/features/bicycle/names.feature index b0e2ec048..f67f6b1e1 100644 --- a/features/bicycle/names.feature +++ b/features/bicycle/names.feature @@ -15,8 +15,8 @@ Feature: Bike - Street names in instructions | bc | Your Way | When I route I should get - | from | to | route | - | a | c | My Way,Your Way | + | from | to | route | + | a | c | My Way,Your Way,Your Way | @unnamed Scenario: Bike - Use way type to describe unnamed ways @@ -29,5 +29,5 @@ Feature: Bike - Street names in instructions | bcd | track | | When I route I should get - | from | to | route | - | a | d | {highway:cycleway},{highway:track} | + | from | to | route | + | a | d | {highway:cycleway},{highway:track},{highway:track} | diff --git a/features/bicycle/oneway.feature b/features/bicycle/oneway.feature index 4dd9e1fa7..0cb774c55 100644 --- a/features/bicycle/oneway.feature +++ b/features/bicycle/oneway.feature @@ -29,9 +29,9 @@ Feature: Bike - Oneway streets | da | | no | When I route I should get - | from | to | route | - | a | b | ab | - | b | a | bc,cd,da | + | from | to | route | + | a | b | ab,ab | + | b | a | bc,cd,da,da | Scenario: Bike - Handle various oneway tag values Then routability should be @@ -124,5 +124,5 @@ Feature: Bike - Oneway streets When I route I should get - | from | to | route | - | a | c | ab,bc | + | from | to | route | + | a | c | ab,bc,bc | diff --git a/features/bicycle/pushing.feature b/features/bicycle/pushing.feature index 5741fb1c0..d67282168 100644 --- a/features/bicycle/pushing.feature +++ b/features/bicycle/pushing.feature @@ -32,7 +32,7 @@ Feature: Bike - Accessability of different way types | primary | -1 | foot | bike | | pedestrian | -1 | foot | foot | - @square + @square Scenario: Bike - Push bikes on pedestrian areas Given the node map | x | | @@ -46,14 +46,14 @@ Feature: Bike - Accessability of different way types When I route I should get | from | to | route | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | Scenario: Bike - Pushing bikes on ways with foot=yes Then routability should be @@ -62,8 +62,7 @@ Feature: Bike - Accessability of different way types | motorway | yes | foot | | | runway | | | | | runway | yes | foot | foot | - - @todo + Scenario: Bike - Pushing bikes on ways with foot=yes in one direction Then routability should be | highway | foot:forward | foot:backward | forw | backw | @@ -72,20 +71,20 @@ Feature: Bike - Accessability of different way types | motorway | | yes | | foot | @construction - Scenario: Bike - Don't allow routing on ways still under construction + Scenario: Bike - Don't allow routing on ways still under construction Then routability should be | highway | foot | bicycle | bothw | | primary | | | x | | construction | | | | | construction | yes | | | | construction | | yes | | - + @roundabout Scenario: Bike - Don't push bikes against oneway flow on roundabouts Then routability should be | junction | forw | backw | | roundabout | x | | - + Scenario: Bike - Instructions when pushing bike on oneways Given the node map | a | b | | @@ -98,13 +97,12 @@ Feature: Bike - Accessability of different way types | cd | primary | | When I route I should get - | from | to | route | turns | - | a | d | ab,bc,cd | head,right,left,destination | - | d | a | cd,bc,ab | head,right,left,destination | - | c | a | bc,ab | head,left,destination | - | d | b | cd,bc | head,right,destination | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,cycling,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | - @todo Scenario: Bike - Instructions when pushing bike on footway/pedestrian, etc. Given the node map | a | b | | @@ -117,8 +115,8 @@ Feature: Bike - Accessability of different way types | cd | primary | When I route I should get - | from | to | route | turns | - | a | d | ab,bc,cd | head,right,left,destination | - | d | a | cd,bc,ab | head,right,left,destination | - | c | a | bc,ab | head,left,destination | - | d | b | cd,bc | head,right,destination | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | cycling,pushing bike,cycling,cycling | + | d | a | cd,bc,ab,ab | cycling,pushing bike,cycling,cycling | + | c | a | bc,ab,ab | pushing bike,cycling,cycling | + | d | b | cd,bc,bc | cycling,pushing bike,pushing bike | diff --git a/features/bicycle/ref.feature b/features/bicycle/ref.feature index 3afde7738..195089e0e 100644 --- a/features/bicycle/ref.feature +++ b/features/bicycle/ref.feature @@ -13,8 +13,8 @@ Feature: Bike - Way ref | ab | Utopia Drive | E7 | When I route I should get - | from | to | route | - | a | b | Utopia Drive / E7 | + | from | to | route | + | a | b | Utopia Drive / E7,Utopia Drive / E7 | Scenario: Bike - Way with only ref Given the node map @@ -26,7 +26,7 @@ Feature: Bike - Way ref When I route I should get | from | to | route | - | a | b | E7 | + | a | b | E7,E7 | Scenario: Bike - Way with only name Given the node map @@ -37,5 +37,5 @@ Feature: Bike - Way ref | ab | Utopia Drive | When I route I should get - | from | to | route | - | a | b | Utopia Drive | + | from | to | route | + | a | b | Utopia Drive,Utopia Drive | diff --git a/features/bicycle/restrictions.feature b/features/bicycle/restrictions.feature index 6218d812b..1f604714c 100644 --- a/features/bicycle/restrictions.feature +++ b/features/bicycle/restrictions.feature @@ -25,10 +25,10 @@ Feature: Bike - Turn restrictions | restriction | sj | wj | j | no_left_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Bike - No right turn @@ -49,10 +49,10 @@ Feature: Bike - Turn restrictions | restriction | sj | ej | j | no_right_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Bike - No u-turn @@ -73,10 +73,10 @@ Feature: Bike - Turn restrictions | restriction | sj | wj | j | no_u_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Bike - Handle any no_* relation @@ -97,10 +97,10 @@ Feature: Bike - Turn restrictions | restriction | sj | wj | j | no_weird_zigzags | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Bike - Only left turn @@ -121,10 +121,10 @@ Feature: Bike - Turn restrictions | restriction | sj | wj | j | only_left_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Bike - Only right turn @@ -145,10 +145,10 @@ Feature: Bike - Turn restrictions | restriction | sj | ej | j | only_right_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Bike - Only straight on @@ -169,10 +169,10 @@ Feature: Bike - Turn restrictions | restriction | sj | nj | j | only_straight_on | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Bike - Handle any only_* restriction @@ -193,10 +193,10 @@ Feature: Bike - Turn restrictions | restriction | sj | nj | j | only_weird_zigzags | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @except Scenario: Bike - Except tag and on no_ restrictions @@ -222,11 +222,11 @@ Feature: Bike - Turn restrictions | restriction | sj | dj | j | no_right_turn | bicycle | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | sj,bj | - | s | c | sj,cj | - | s | d | sj,dj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | sj,bj,bj | + | s | c | sj,cj,cj | + | s | d | sj,dj,dj | @except Scenario: Bike - Except tag and on only_ restrictions @@ -246,9 +246,9 @@ Feature: Bike - Turn restrictions | restriction | sj | aj | j | only_straight_on | bicycle | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | sj,bj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | sj,bj,bj | @except Scenario: Bike - Multiple except tag values @@ -280,10 +280,10 @@ Feature: Bike - Turn restrictions | restriction | sj | jf | j | no_straight_on | bicycle, bus | When I route I should get - | from | to | route | - | s | a | sj,ja | - | s | b | sj,jb | - | s | c | sj,jc | - | s | d | sj,jd | - | s | e | sj,je | - | s | f | sj,jf | + | from | to | route | + | s | a | sj,ja,ja | + | s | b | sj,jb,jb | + | s | c | sj,jc,jc | + | s | d | sj,jd,jd | + | s | e | sj,je,je | + | s | f | sj,jf,jf | diff --git a/features/bicycle/roundabout.feature b/features/bicycle/roundabout.feature deleted file mode 100644 index 6123280ed..000000000 --- a/features/bicycle/roundabout.feature +++ /dev/null @@ -1,30 +0,0 @@ -@routing @bicycle @roundabout @instruction -Feature: Roundabout Instructions - - Background: - Given the profile "bicycle" - - Scenario: Bicycle - Roundabout instructions - Given the node map - | | | v | | | - | | | d | | | - | s | a | | c | u | - | | | b | | | - | | | t | | | - - And the ways - | nodes | junction | - | sa | | - | tb | | - | uc | | - | vd | | - | abcda | roundabout | - - When I route I should get - | from | to | route | turns | - | s | t | sa,tb | head,enter_roundabout-1,destination | - | s | u | sa,uc | head,enter_roundabout-2,destination | - | s | v | sa,vd | head,enter_roundabout-3,destination | - | u | v | uc,vd | head,enter_roundabout-1,destination | - | u | s | uc,sa | head,enter_roundabout-2,destination | - | u | t | uc,tb | head,enter_roundabout-3,destination | diff --git a/features/bicycle/stop_area.feature b/features/bicycle/stop_area.feature index 5f6c3bc26..9d02c1308 100644 --- a/features/bicycle/stop_area.feature +++ b/features/bicycle/stop_area.feature @@ -31,7 +31,7 @@ Feature: Bike - Stop areas for public transport | public_transport | stop_area | c | st | When I route I should get - | from | to | route | - | a | d | abcd | - | s | t | st | - | s | d | /st,.+,abcd/ | + | from | to | route | + | a | d | abcd,abcd | + | s | t | st,st | + | s | d | /st,.+,abcd/,abcd/ | diff --git a/features/bicycle/turn_penalty.feature b/features/bicycle/turn_penalty.feature index d8c46b88e..4d66cc34f 100644 --- a/features/bicycle/turn_penalty.feature +++ b/features/bicycle/turn_penalty.feature @@ -23,11 +23,11 @@ Feature: Turn Penalties | jg | When I route I should get - | from | to | route | time | distance | - | s | a | sj,ja | 39s +-1 | 242m +-1 | - | s | b | sj,jb | 30s +-1 | 200m +-1 | - | s | c | sj,jc | 29s +-1 | 242m +-1 | - | s | d | sj,jd | 20s +-1 | 200m +-1 | - | s | e | sj,je | 29s +-1 | 242m +-1 | - | s | f | sj,jf | 30s +-1 | 200m +-1 | - | s | g | sj,jg | 39s +-1 | 242m +-1 | \ No newline at end of file + | from | to | route | time | distance | + | s | a | sj,ja,ja | 39s +-1 | 242m +-1 | + | s | b | sj,jb,jb | 30s +-1 | 200m +-1 | + | s | c | sj,jc,jc | 29s +-1 | 242m +-1 | + | s | d | sj,jd,jd | 20s +-1 | 200m +-1 | + | s | e | sj,je,je | 29s +-1 | 242m +-1 | + | s | f | sj,jf,jf | 30s +-1 | 200m +-1 | + | s | g | sj,jg,jg | 39s +-1 | 242m +-1 | diff --git a/features/car/access.feature b/features/car/access.feature index 8f05ccbca..93691d4d0 100644 --- a/features/car/access.feature +++ b/features/car/access.feature @@ -5,7 +5,7 @@ Feature: Car - Restricted access Background: Given the profile "car" - Scenario: Car - Access tag hierachy on ways + Scenario: Car - Access tag hierarchy on ways Then routability should be | access | vehicle | motor_vehicle | motorcar | bothw | | | | | | x | @@ -30,7 +30,7 @@ Feature: Car - Restricted access | | | no | yes | x | | | | yes | no | | - Scenario: Car - Access tag hierachy on nodes + Scenario: Car - Access tag hierarchy on nodes Then routability should be | node/access | node/vehicle | node/motor_vehicle | node/motorcar | bothw | | | | | | x | @@ -94,6 +94,7 @@ Feature: Car - Restricted access | agricultural | | | forestry | | | psv | | + | delivery | | | some_tag | x | @@ -108,6 +109,7 @@ Feature: Car - Restricted access | agricultural | | | forestry | | | psv | | + | delivery | | | some_tag | x | Scenario: Car - Access tags on both node and way diff --git a/features/car/advisory.feature b/features/car/advisory.feature index db5e66d4a..b84ff0555 100644 --- a/features/car/advisory.feature +++ b/features/car/advisory.feature @@ -17,8 +17,8 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | a | b | ab | 47 km/h +- 1 | - | b | c | bc | 47 km/h +- 1 | + | a | b | ab,ab | 47 km/h +- 1 | + | b | c | bc,bc | 47 km/h +- 1 | Scenario: Car - Advisory speed overwrites forward maxspeed Given the node map @@ -31,9 +31,10 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | a | b | ab | 47 km/h +- 1 | - | b | c | bc | 47 km/h +- 1 | + | a | b | ab,ab | 47 km/h +- 1 | + | b | c | bc,bc | 47 km/h +- 1 | + @mokob @2162 Scenario: Car - Advisory speed overwrites backwards maxspeed Given the node map | a | b | c | @@ -45,9 +46,26 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | b | a | ab | 47 km/h +- 1 | - | c | b | bc | 47 km/h +- 1 | + | b | a | ab,ab | 47 km/h +- 1 | + | c | b | bc,bc | 47 km/h +- 1 | + @mokob @2162 @deleteme + Scenario: Car - Advisory speed overwrites backwards maxspeed + Given the node map + | a | b | c | d | + + And the ways + | nodes | highway | maxspeed:backward | maxspeed:advisory:backward | + | ab | residential | | 45 | + | bc | residential | 90 | 45 | + | cd | residential | | 45 | + + When I route I should get + | from | to | route | speed | + | c | b | bc,bc | 47 km/h +- 1 | + | d | c | cd,cd | 47 km/h +- 1 | + + @mokob @2162 Scenario: Car - Directional advisory speeds play nice with eachother Given the node map | a | b | c | @@ -59,9 +77,9 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | a | b | ab | 47 km/h +- 1 | - | b | a | ab | 59 km/h +- 1 | - | b | c | bc | 59 km/h +- 1 | - | c | b | bc | 47 km/h +- 1 | + | a | b | ab,ab | 47 km/h +- 1 | + | b | a | ab,ab | 59 km/h +- 1 | + | b | c | bc,bc | 59 km/h +- 1 | + | c | b | bc,bc | 47 km/h +- 1 | diff --git a/features/car/bridge.feature b/features/car/bridge.feature index d3e470e41..c1cb50636 100644 --- a/features/car/bridge.feature +++ b/features/car/bridge.feature @@ -1,5 +1,5 @@ @routing @car @bridge -Feature: Car - Handle movable bridge +Feature: Car - Handle driving Background: Given the profile "car" @@ -17,15 +17,15 @@ Feature: Car - Handle movable bridge | efg | primary | | | When I route I should get - | from | to | route | modes | - | a | g | abc,cde,efg | 1,3,1 | - | b | f | abc,cde,efg | 1,3,1 | - | e | c | cde | 3 | - | e | b | cde,abc | 3,1 | - | e | a | cde,abc | 3,1 | - | c | e | cde | 3 | - | c | f | cde,efg | 3,1 | - | c | g | cde,efg | 3,1 | + | from | to | route | modes | + | a | g | abc,cde,efg,efg | driving,driving,driving,driving | + | b | f | abc,cde,efg,efg | driving,driving,driving,driving | + | e | c | cde,cde | driving,driving | + | e | b | cde,abc,abc | driving,driving,driving | + | e | a | cde,abc,abc | driving,driving,driving | + | c | e | cde,cde | driving,driving | + | c | f | cde,efg,efg | driving,driving,driving | + | c | g | cde,efg,efg | driving,driving,driving | Scenario: Car - Properly handle durations Given the node map @@ -40,8 +40,8 @@ Feature: Car - Handle movable bridge | efg | primary | | | When I route I should get - | from | to | route | modes | speed | - | a | g | abc,cde,efg | 1,3,1 | 7 km/h | - | b | f | abc,cde,efg | 1,3,1 | 5 km/h | - | c | e | cde | 3 | 2 km/h | - | e | c | cde | 3 | 2 km/h | + | from | to | route | modes | speed | + | a | g | abc,cde,efg,efg | driving,driving,driving,driving | 7 km/h | + | b | f | abc,cde,efg,efg | driving,driving,driving,driving | 5 km/h | + | c | e | cde,cde | driving,driving | 2 km/h | + | e | c | cde,cde | driving,driving | 2 km/h | diff --git a/features/car/destination.feature b/features/car/destination.feature index 506aa2101..3bf07331f 100644 --- a/features/car/destination.feature +++ b/features/car/destination.feature @@ -19,15 +19,15 @@ Feature: Car - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | b | ab | - | a | c | ab,bcd | - | a | d | ab,bcd | - | a | e | axye | - | e | d | de | - | e | c | de,bcd | - | e | b | de,bcd | - | e | a | axye | + | from | to | route | + | a | b | ab,ab | + | a | c | ab,bcd,bcd | + | a | d | ab,bcd,bcd | + | a | e | axye,axye | + | e | d | de,de | + | e | c | de,bcd,bcd | + | e | b | de,bcd,bcd | + | e | a | axye,axye | Scenario: Car - Destination only street Given the node map @@ -45,15 +45,15 @@ Feature: Car - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | b | ab | - | a | c | ab,bc | - | a | d | ab,bc,cd | - | a | e | axye | - | e | d | de | - | e | c | de,dc | - | e | b | de,dc,bc | - | e | a | axye | + | from | to | route | + | a | b | ab,ab | + | a | c | ab,bc,bc | + | a | d | ab,bc,cd,cd | + | a | e | axye,axye | + | e | d | de,de | + | e | c | de,cd,cd | + | e | b | de,cd,bc,bc | + | e | a | axye,axye | Scenario: Car - Routing inside a destination only area Given the node map @@ -70,8 +70,8 @@ Feature: Car - Destination only, no passing through | axye | | When I route I should get - | from | to | route | - | a | e | ab,bc,cd,de | - | e | a | de,cd,bc,ab | - | b | d | bc,cd | - | d | b | cd,bc | + | from | to | route | + | a | e | ab,bc,cd,de,de | + | e | a | de,cd,bc,ab,ab | + | b | d | bc,cd,cd | + | d | b | cd,bc,bc | diff --git a/features/car/ferry.feature b/features/car/ferry.feature index 37f520ed9..c6adc9fa1 100644 --- a/features/car/ferry.feature +++ b/features/car/ferry.feature @@ -17,15 +17,15 @@ Feature: Car - Handle ferry routes | efg | primary | | | When I route I should get - | from | to | route | modes | - | a | g | abc,cde,efg | 1,2,1 | - | b | f | abc,cde,efg | 1,2,1 | - | e | c | cde | 2 | - | e | b | cde,abc | 2,1 | - | e | a | cde,abc | 2,1 | - | c | e | cde | 2 | - | c | f | cde,efg | 2,1 | - | c | g | cde,efg | 2,1 | + | from | to | route | modes | + | a | g | abc,cde,efg,efg | driving,ferry,driving,driving | + | b | f | abc,cde,efg,efg | driving,ferry,driving,driving | + | e | c | cde,cde | ferry,ferry | + | e | b | cde,abc,abc | ferry,driving,driving | + | e | a | cde,abc,abc | ferry,driving,driving | + | c | e | cde,cde | ferry,ferry | + | c | f | cde,efg,efg | ferry,driving,driving | + | c | g | cde,efg,efg | ferry,driving,driving | Scenario: Car - Properly handle simple durations Given the node map @@ -40,11 +40,11 @@ Feature: Car - Handle ferry routes | efg | primary | | | When I route I should get - | from | to | route | modes | speed | - | a | g | abc,cde,efg | 1,2,1 | 25 km/h | - | b | f | abc,cde,efg | 1,2,1 | 20 km/h | - | c | e | cde | 2 | 12 km/h | - | e | c | cde | 2 | 12 km/h | + | from | to | route | modes | speed | + | a | g | abc,cde,efg,efg | driving,ferry,driving,driving | 25 km/h | + | b | f | abc,cde,efg,efg | driving,ferry,driving,driving | 20 km/h | + | c | e | cde,cde | ferry,ferry | 12 km/h | + | e | c | cde,cde | ferry,ferry | 12 km/h | Scenario: Car - Properly handle ISO 8601 durations Given the node map @@ -59,8 +59,8 @@ Feature: Car - Handle ferry routes | efg | primary | | | When I route I should get - | from | to | route | modes | speed | - | a | g | abc,cde,efg | 1,2,1 | 25 km/h | - | b | f | abc,cde,efg | 1,2,1 | 20 km/h | - | c | e | cde | 2 | 12 km/h | - | e | c | cde | 2 | 12 km/h | + | from | to | route | modes | speed | + | a | g | abc,cde,efg,efg | driving,ferry,driving,driving | 25 km/h | + | b | f | abc,cde,efg,efg | driving,ferry,driving,driving | 20 km/h | + | c | e | cde,cde | ferry,ferry | 12 km/h | + | e | c | cde,cde | ferry,ferry | 12 km/h | diff --git a/features/car/link.feature b/features/car/link.feature index f4628e4ce..26f32d1ea 100644 --- a/features/car/link.feature +++ b/features/car/link.feature @@ -24,9 +24,9 @@ Feature: Car - Speed on links | dy | unclassified | When I route I should get - | from | to | route | - | x | y | xa,ae,ef,fd,dy | - | b | c | bc | + | from | to | route | + | x | y | xa,ae,ef,fd,dy,dy | + | b | c | bc,bc | Scenario: Car - Use trunk_link when reasonable Given the node map @@ -44,9 +44,9 @@ Feature: Car - Speed on links | fd | trunk | | dy | unclassified | When I route I should get - | from | to | route | - | x | y | xa,ae,ef,fd,dy | - | b | c | bc | + | from | to | route | + | x | y | xa,ae,ef,fd,dy,dy | + | b | c | bc,bc | Scenario: Car - Use primary_link when reasonable Given the node map @@ -64,9 +64,9 @@ Feature: Car - Speed on links | fd | primary | | dy | unclassified | When I route I should get - | from | to | route | - | x | y | xa,ae,ef,fd,dy | - | b | c | bc | + | from | to | route | + | x | y | xa,ae,ef,fd,dy,dy | + | b | c | bc,bc | Scenario: Car - Use secondary_link when reasonable Given the node map @@ -85,9 +85,9 @@ Feature: Car - Speed on links | dy | unclassified | When I route I should get - | from | to | route | - | x | y | xa,ae,ef,fd,dy | - | b | c | bc | + | from | to | route | + | x | y | xa,ae,ef,fd,dy,dy | + | b | c | bc,bc | Scenario: Car - Use tertiary_link when reasonable Given the node map @@ -106,6 +106,6 @@ Feature: Car - Speed on links | dy | unclassified | When I route I should get - | from | to | route | - | x | y | xa,ae,ef,fd,dy | - | b | c | bc | + | from | to | route | + | x | y | xa,ae,ef,fd,dy,dy | + | b | c | bc,bc | diff --git a/features/car/maxspeed.feature b/features/car/maxspeed.feature index e2d26e0e0..3b94ac763 100644 --- a/features/car/maxspeed.feature +++ b/features/car/maxspeed.feature @@ -21,12 +21,12 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | a | b | ab | 78 km/h | - | b | c | bc | 59 km/h +- 1 | - | c | d | cd | 51 km/h | - | d | e | de | 75 km/h | - | e | f | ef | 90 km/h | - | f | g | fg | 106 km/h | + | a | b | ab,ab | 79 km/h | + | b | c | bc,bc | 59 km/h +- 1 | + | c | d | cd,cd | 51 km/h | + | d | e | de,de | 75 km/h | + | e | f | ef,ef | 91 km/h | + | f | g | fg,fg | 107 km/h | Scenario: Car - Do not ignore maxspeed when higher than way speed Given the node map @@ -40,9 +40,9 @@ OSRM will use 4/5 of the projected free-flow speed. When I route I should get | from | to | route | speed | - | a | b | ab | 31 km/h | - | b | c | bc | 83 km/h +- 1 | - | c | d | cd | 51 km/h | + | a | b | ab,ab | 31 km/h | + | b | c | bc,bc | 83 km/h +- 1 | + | c | d | cd,cd | 51 km/h | Scenario: Car - Forward/backward maxspeed Given a grid size of 100 meters diff --git a/features/car/mode.feature b/features/car/mode.feature index ff156a0b2..e0feff11a 100644 --- a/features/car/mode.feature +++ b/features/car/mode.feature @@ -15,13 +15,13 @@ Feature: Car - Mode flag | cd | primary | | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,2,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,2 | - | b | d | bc,cd | head,left,destination | 2,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | driving,ferry,driving,driving | + | d | a | cd,bc,ab,ab | driving,ferry,driving,driving | + | c | a | bc,ab,ab | ferry,driving,driving | + | d | b | cd,bc,bc | driving,ferry,ferry | + | a | c | ab,bc,bc | driving,ferry,ferry | + | b | d | bc,cd,cd | ferry,driving,driving | Scenario: Car - Snapping when using a ferry Given the node map @@ -34,7 +34,7 @@ Feature: Car - Mode flag | ef | primary | | | When I route I should get - | from | to | route | turns | modes | time | - | c | d | bcde | head,destination | 2 | 600s | + | from | to | route | modes | time | + | c | d | bcde,bcde | ferry,ferry | 600s | diff --git a/features/car/names.feature b/features/car/names.feature index b6a95ae7f..7a4725340 100644 --- a/features/car/names.feature +++ b/features/car/names.feature @@ -15,8 +15,8 @@ Feature: Car - Street names in instructions | bc | Your Way | A1 | When I route I should get - | from | to | route | - | a | c | My Way,Your Way (A1) | + | from | to | route | + | a | c | My Way,Your Way (A1),Your Way (A1) | @todo Scenario: Car - Use way type to describe unnamed ways @@ -29,5 +29,5 @@ Feature: Car - Street names in instructions | bcd | residential | | When I route I should get - | from | to | route | - | a | c | tertiary,residential | + | from | to | route | + | a | c | tertiary,residential,residential | diff --git a/features/car/oneway.feature b/features/car/oneway.feature index b47c3c6fd..b1b08bfb2 100644 --- a/features/car/oneway.feature +++ b/features/car/oneway.feature @@ -44,9 +44,9 @@ Feature: Car - Oneway streets | da | | When I route I should get - | from | to | route | - | a | b | ab | - | b | a | bc,cd,da | + | from | to | route | + | a | b | ab,ab | + | b | a | bc,cd,da,da | Scenario: Car - Cars should not be affected by bicycle tags Then routability should be @@ -75,5 +75,5 @@ Feature: Car - Oneway streets When I route I should get - | from | to | route | - | a | c | ab,bc | + | from | to | route | + | a | c | ab,bc,bc | diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index 89fffff75..d311fb0a9 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -25,10 +25,10 @@ Feature: Car - Turn restrictions | restriction | sj | wj | j | no_left_turn | When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Car - No straight on @@ -55,8 +55,8 @@ Feature: Car - Turn restrictions | restriction | bj | jd | j | no_straight_on | When I route I should get - | from | to | route | - | a | e | av,vw,wx,xy,yz,ze | + | from | to | route | + | a | e | av,vw,wx,xy,yz,ze,ze | @no_turning Scenario: Car - No right turn @@ -77,10 +77,10 @@ Feature: Car - Turn restrictions | restriction | sj | ej | j | no_right_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | | @no_turning Scenario: Car - No u-turn @@ -101,10 +101,10 @@ Feature: Car - Turn restrictions | restriction | sj | wj | j | no_u_turn | When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Car - Handle any no_* relation @@ -125,10 +125,10 @@ Feature: Car - Turn restrictions | restriction | sj | wj | j | no_weird_zigzags | When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Car - Only left turn @@ -149,10 +149,10 @@ Feature: Car - Turn restrictions | restriction | sj | wj | j | only_left_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | | - | s | e | | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | | + | s | e | | @only_turning Scenario: Car - Only right turn @@ -173,10 +173,10 @@ Feature: Car - Turn restrictions | restriction | sj | ej | j | only_right_turn | When I route I should get - | from | to | route | - | s | w | | - | s | n | | - | s | e | sj,ej | + | from | to | route | + | s | w | | + | s | n | | + | s | e | sj,ej,ej | @only_turning Scenario: Car - Only straight on @@ -197,10 +197,10 @@ Feature: Car - Turn restrictions | restriction | sj | nj | j | only_straight_on | When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | | + | from | to | route | + | s | w | | + | s | n | sj,nj,nj | + | s | e | | @no_turning Scenario: Car - Handle any only_* restriction @@ -221,10 +221,10 @@ Feature: Car - Turn restrictions | restriction | sj | nj | j | only_weird_zigzags | When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | | + | from | to | route | + | s | w | | + | s | n | sj,nj,nj | + | s | e | | @specific Scenario: Car - :hgv-qualified on a standard turn restriction @@ -245,10 +245,10 @@ Feature: Car - Turn restrictions | restriction | sj | nj | j | no_straight_on | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @specific Scenario: Car - :motorcar-qualified on a standard turn restriction @@ -269,10 +269,10 @@ Feature: Car - Turn restrictions | restriction | sj | nj | j | no_straight_on | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | | + | s | e | sj,ej,ej | @except Scenario: Car - Except tag and on no_ restrictions @@ -298,11 +298,11 @@ Feature: Car - Turn restrictions | restriction | sj | dj | j | no_right_turn | motorcar | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | | - | s | c | | - | s | d | sj,dj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | | + | s | c | | + | s | d | sj,dj,dj | @except Scenario: Car - Except tag and on only_ restrictions @@ -322,9 +322,9 @@ Feature: Car - Turn restrictions | restriction | sj | aj | j | only_straight_on | motorcar | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | sj,bj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | sj,bj,bj | @except Scenario: Car - Several only_ restrictions at the same segment @@ -356,10 +356,10 @@ Feature: Car - Turn restrictions | restriction | da | ae | a | only_right_turn | When I route I should get - | from | to | route | - | e | f | ae,xa,bx,fb | - | c | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb | - | d | f | da,ae,ge,hg,hg,ge,ae,xa,bx,fb | + | from | to | route | + | e | f | ae,xa,bx,fb,fb | + | c | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb | + | d | f | da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb | @except Scenario: Car - two only_ restrictions share same to-way @@ -391,9 +391,9 @@ Feature: Car - Turn restrictions | restriction | by | xy | y | only_straight_on | When I route I should get - | from | to | route | - | a | b | ax,xy,yb | - | b | a | yb,xy,ax | + | from | to | route | + | a | b | ax,xy,yb,yb | + | b | a | yb,xy,ax,ax | @except Scenario: Car - two only_ restrictions share same from-way @@ -425,7 +425,7 @@ Feature: Car - Turn restrictions | restriction | xy | yb | y | only_straight_on | When I route I should get - | from | to | route | - | a | b | ax,xy,yb | - | b | a | yb,xy,ax | + | from | to | route | + | a | b | ax,xy,yb,yb | + | b | a | yb,xy,ax,ax | diff --git a/features/car/roundabout.feature b/features/car/roundabout.feature deleted file mode 100644 index 2965479b9..000000000 --- a/features/car/roundabout.feature +++ /dev/null @@ -1,30 +0,0 @@ -@routing @car @roundabout @instruction -Feature: Roundabout Instructions - - Background: - Given the profile "car" - - Scenario: Car - Roundabout instructions - Given the node map - | | | v | | | - | | | d | | | - | s | a | | c | u | - | | | b | | | - | | | t | | | - - And the ways - | nodes | junction | - | sa | | - | tb | | - | uc | | - | vd | | - | abcda | roundabout | - - When I route I should get - | from | to | route | turns | - | s | t | sa,tb | head,enter_roundabout-1,destination | - | s | u | sa,uc | head,enter_roundabout-2,destination | - | s | v | sa,vd | head,enter_roundabout-3,destination | - | u | v | uc,vd | head,enter_roundabout-1,destination | - | u | s | uc,sa | head,enter_roundabout-2,destination | - | u | t | uc,tb | head,enter_roundabout-3,destination | diff --git a/features/car/shuttle_train.feature b/features/car/shuttle_train.feature index 97f32e565..f6e8b84a6 100644 --- a/features/car/shuttle_train.feature +++ b/features/car/shuttle_train.feature @@ -19,13 +19,13 @@ Feature: Car - Handle ferryshuttle train routes | gh | primary | | no | When I route I should get - | from | to | route | - | a | f | abc,cde,ef | - | b | f | abc,cde,ef | - | e | c | cde | - | e | b | cde,abc | - | e | a | cde,abc | - | c | e | cde | - | c | f | cde,ef | - | f | g | | - | g | h | gh | + | from | to | route | + | a | f | abc,cde,ef,ef | + | b | f | abc,cde,ef,ef | + | e | c | cde,cde | + | e | b | cde,abc,abc | + | e | a | cde,abc,abc | + | c | e | cde,cde | + | c | f | cde,ef,ef | + | f | g | | + | g | h | gh,gh | diff --git a/features/car/traffic.feature b/features/car/traffic.feature new file mode 100644 index 000000000..6d0c62d6b --- /dev/null +++ b/features/car/traffic.feature @@ -0,0 +1,47 @@ +@routing @speed @traffic +Feature: Traffic - speeds + + Background: Use specific speeds + Given the node locations + | node | lat | lon | + | a | 0.1 | 0.1 | + | b | .05 | 0.1 | + | c | 0.0 | 0.1 | + | d | .05 | .03 | + | e | .05 | .066 | + | f | .075 | .066 | + | g | .075 | 0.1 | + And the ways + | nodes | highway | + | ab | primary | + | ad | primary | + | bc | primary | + | dc | primary | + | de | primary | + | eb | primary | + | df | primary | + | fb | primary | + And the speed file + """ + 1,2,27 + 2,1,27 + 2,3,27 + 3,2,27 + 1,4,27 + 4,1,27 + """ + + Scenario: Weighting not based on raster sources + Given the profile "testbot" + Given the extract extra arguments "--generate-edge-lookup" + Given the contract extra arguments "--segment-speed-file speeds.csv" + And I route I should get + | from | to | route | speed | + | a | b | ab,ab | 27 km/h | + | a | c | ab,bc,bc | 27 km/h | + | b | c | bc,bc | 27 km/h | + | a | d | ad,ad | 27 km/h | + | d | c | dc,dc | 36 km/h | + | g | b | ab,ab | 27 km/h | + | a | g | ab,ab | 27 km/h | + diff --git a/features/foot/access.feature b/features/foot/access.feature index 353076c40..4642a3b56 100644 --- a/features/foot/access.feature +++ b/features/foot/access.feature @@ -5,7 +5,7 @@ Feature: Foot - Access tags on ways Background: Given the profile "foot" - Scenario: Foot - Access tag hierachy on ways + Scenario: Foot - Access tag hierarchy on ways Then routability should be | highway | access | foot | bothw | | footway | | | x | @@ -52,6 +52,7 @@ Feature: Foot - Access tags on ways | private | | | | agricultural | | | | forestry | | | + | delivery | | | | | yes | x | | | permissive | x | | | designated | x | @@ -60,6 +61,7 @@ Feature: Foot - Access tags on ways | | private | | | | agricultural | | | | forestry | | + | | delivery | | Scenario: Foot - Access tags on both node and way Then routability should be diff --git a/features/foot/access_node.feature b/features/foot/access_node.feature index 8519d0564..8dc0ebec1 100644 --- a/features/foot/access_node.feature +++ b/features/foot/access_node.feature @@ -5,7 +5,7 @@ Feature: Foot - Access tags on nodes Background: Given the profile "foot" - Scenario: Foot - Access tag hierachy on nodes + Scenario: Foot - Access tag hierarchy on nodes Then routability should be | node/access | node/foot | bothw | | | | x | @@ -40,6 +40,7 @@ Feature: Foot - Access tags on nodes | private | | | | agricultural | | | | forestry | | | + | delivery | | | | no | yes | x | | no | permissive | x | | no | designated | x | @@ -48,3 +49,4 @@ Feature: Foot - Access tags on nodes | yes | private | | | yes | agricultural | | | yes | forestry | | + | yes | delivery | | diff --git a/features/foot/area.feature b/features/foot/area.feature index 6edd585d6..8cfc0a839 100644 --- a/features/foot/area.feature +++ b/features/foot/area.feature @@ -17,15 +17,15 @@ Feature: Foot - Squares and other areas | abcda | yes | residential | When I route I should get - | from | to | route | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | @building Scenario: Foot - Don't route on buildings @@ -41,14 +41,14 @@ Feature: Foot - Squares and other areas When I route I should get | from | to | route | - | a | b | xa | - | a | d | xa | - | b | c | xa | - | c | b | xa | - | c | d | xa | - | d | c | xa | - | d | a | xa | - | a | d | xa | + | a | b | xa,xa | + | a | d | xa,xa | + | b | c | xa,xa | + | c | b | xa,xa | + | c | d | xa,xa | + | d | c | xa,xa | + | d | a | xa,xa | + | a | d | xa,xa | @parking Scenario: Foot - parking areas @@ -65,17 +65,17 @@ Feature: Foot - Squares and other areas | abcda | (nil) | parking | When I route I should get - | from | to | route | - | x | y | xa,abcda,by | - | y | x | by,abcda,xa | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | x | y | xa,abcda,by,by | + | y | x | by,abcda,xa,xa | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | @train @platform Scenario: Foot - railway platforms @@ -90,14 +90,14 @@ Feature: Foot - Squares and other areas | abcda | (nil) | platform | When I route I should get - | from | to | route | - | x | y | xa,abcda,by | - | y | x | by,abcda,xa | - | a | b | abcda | - | a | d | abcda | - | b | c | abcda | - | c | b | abcda | - | c | d | abcda | - | d | c | abcda | - | d | a | abcda | - | a | d | abcda | + | from | to | route | + | x | y | xa,abcda,by,by | + | y | x | by,abcda,xa,xa | + | a | b | abcda,abcda | + | a | d | abcda,abcda | + | b | c | abcda,abcda | + | c | b | abcda,abcda | + | c | d | abcda,abcda | + | d | c | abcda,abcda | + | d | a | abcda,abcda | + | a | d | abcda,abcda | diff --git a/features/foot/ferry.feature b/features/foot/ferry.feature index 866644d26..9ad1c9d74 100644 --- a/features/foot/ferry.feature +++ b/features/foot/ferry.feature @@ -17,15 +17,15 @@ Feature: Foot - Handle ferry routes | efg | primary | | | When I route I should get - | from | to | route | modes | - | a | g | abc,cde,efg | 1,2,1 | - | b | f | abc,cde,efg | 1,2,1 | - | e | c | cde | 2 | - | e | b | cde,abc | 2,1 | - | e | a | cde,abc | 2,1 | - | c | e | cde | 2 | - | c | f | cde,efg | 2,1 | - | c | g | cde,efg | 2,1 | + | from | to | route | modes | + | a | g | abc,cde,efg,efg | walking,ferry,walking,walking | + | b | f | abc,cde,efg,efg | walking,ferry,walking,walking | + | e | c | cde,cde | ferry,ferry | + | e | b | cde,abc,abc | ferry,walking,walking | + | e | a | cde,abc,abc | ferry,walking,walking | + | c | e | cde,cde | ferry,ferry | + | c | f | cde,efg,efg | ferry,walking,walking | + | c | g | cde,efg,efg | ferry,walking,walking | Scenario: Foot - Ferry duration, single node Given the node map @@ -58,6 +58,6 @@ Feature: Foot - Handle ferry routes | abcd | | ferry | yes | 1:00 | When I route I should get - | from | to | route | time | - | a | d | abcd | 3600s +-10 | - | d | a | abcd | 3600s +-10 | + | from | to | route | time | + | a | d | abcd,abcd | 3600s +-10 | + | d | a | abcd,abcd | 3600s +-10 | diff --git a/features/foot/names.feature b/features/foot/names.feature index d5fdcc7e4..a765de682 100644 --- a/features/foot/names.feature +++ b/features/foot/names.feature @@ -15,8 +15,8 @@ Feature: Foot - Street names in instructions | bc | Your Way | When I route I should get - | from | to | route | - | a | c | My Way,Your Way | + | from | to | route | + | a | c | My Way,Your Way,Your Way | @unnamed Scenario: Foot - Use way type to describe unnamed ways @@ -29,5 +29,5 @@ Feature: Foot - Street names in instructions | bcd | track | | When I route I should get - | from | to | route | - | a | d | {highway:footway},{highway:track} | + | from | to | route | + | a | d | {highway:footway},{highway:track},{highway:track} | diff --git a/features/foot/ref.feature b/features/foot/ref.feature index bc0c77e14..8fed58692 100644 --- a/features/foot/ref.feature +++ b/features/foot/ref.feature @@ -13,8 +13,8 @@ Feature: Foot - Way ref | ab | Utopia Drive | E7 | When I route I should get - | from | to | route | - | a | b | Utopia Drive / E7 | + | from | to | route | + | a | b | Utopia Drive / E7,Utopia Drive / E7 | Scenario: Foot - Way with only ref Given the node map @@ -26,7 +26,7 @@ Feature: Foot - Way ref When I route I should get | from | to | route | - | a | b | E7 | + | a | b | E7,E7 | Scenario: Foot - Way with only name Given the node map @@ -37,5 +37,5 @@ Feature: Foot - Way ref | ab | Utopia Drive | When I route I should get - | from | to | route | - | a | b | Utopia Drive | + | from | to | route | + | a | b | Utopia Drive,Utopia Drive | diff --git a/features/foot/restrictions.feature b/features/foot/restrictions.feature index 8d6a0297f..ae66658a6 100644 --- a/features/foot/restrictions.feature +++ b/features/foot/restrictions.feature @@ -24,10 +24,10 @@ Feature: Foot - Turn restrictions | restriction | sj | wj | j | no_left_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Foot - No right turn @@ -48,10 +48,10 @@ Feature: Foot - Turn restrictions | restriction | sj | ej | j | no_right_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Foot - No u-turn @@ -72,10 +72,10 @@ Feature: Foot - Turn restrictions | restriction | sj | wj | j | no_u_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Foot - Handle any no_* relation @@ -96,10 +96,10 @@ Feature: Foot - Turn restrictions | restriction | sj | wj | j | no_weird_zigzags | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Foot - Only left turn @@ -120,10 +120,10 @@ Feature: Foot - Turn restrictions | restriction | sj | wj | j | only_left_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Foot - Only right turn @@ -144,10 +144,10 @@ Feature: Foot - Turn restrictions | restriction | sj | ej | j | only_right_turn | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @only_turning Scenario: Foot - Only straight on @@ -168,10 +168,10 @@ Feature: Foot - Turn restrictions | restriction | sj | nj | j | only_straight_on | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @no_turning Scenario: Foot - Handle any only_* restriction @@ -192,10 +192,10 @@ Feature: Foot - Turn restrictions | restriction | sj | nj | j | only_weird_zigzags | When I route I should get - | from | to | route | - | s | w | sj,wj | - | s | n | sj,nj | - | s | e | sj,ej | + | from | to | route | + | s | w | sj,wj,wj | + | s | n | sj,nj,nj | + | s | e | sj,ej,ej | @except Scenario: Foot - Except tag and on no_ restrictions @@ -221,11 +221,11 @@ Feature: Foot - Turn restrictions | restriction | sj | dj | j | no_right_turn | foot | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | sj,bj | - | s | c | sj,cj | - | s | d | sj,dj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | sj,bj,bj | + | s | c | sj,cj,cj | + | s | d | sj,dj,dj | @except Scenario: Foot - Except tag and on only_ restrictions @@ -245,9 +245,9 @@ Feature: Foot - Turn restrictions | restriction | sj | aj | j | only_straight_on | foot | When I route I should get - | from | to | route | - | s | a | sj,aj | - | s | b | sj,bj | + | from | to | route | + | s | a | sj,aj,aj | + | s | b | sj,bj,bj | @except Scenario: Foot - Multiple except tag values @@ -279,10 +279,10 @@ Feature: Foot - Turn restrictions | restriction | sj | jf | j | no_straight_on | foot, bus | When I route I should get - | from | to | route | - | s | a | sj,ja | - | s | b | sj,jb | - | s | c | sj,jc | - | s | d | sj,jd | - | s | e | sj,je | - | s | f | sj,jf | + | from | to | route | + | s | a | sj,ja,ja | + | s | b | sj,jb,jb | + | s | c | sj,jc,jc | + | s | d | sj,jd,jd | + | s | e | sj,je,je | + | s | f | sj,jf,jf | diff --git a/features/foot/roundabout.feature b/features/foot/roundabout.feature index 5aa9860fb..181f24028 100644 --- a/features/foot/roundabout.feature +++ b/features/foot/roundabout.feature @@ -3,12 +3,12 @@ Feature: Roundabout Instructions Background: Given the profile "foot" - + @todo Scenario: Foot - Roundabout instructions # You can walk in both directions on a roundabout, bu the normal roundabout instructions don't # make sense when you're going the opposite way around the roundabout. - + Given the node map | | | v | | | | | | d | | | @@ -25,10 +25,10 @@ Feature: Roundabout Instructions | abcda | roundabout | When I route I should get - | from | to | route | turns | - | s | t | sa,tb | head,enter_roundabout-1,destination | - | s | u | sa,uc | head,enter_roundabout-2,destination | - | s | v | sa,vd | head,enter_roundabout-3,destination | - | u | v | uc,vd | head,enter_roundabout-1,destination | - | u | s | uc,sa | head,enter_roundabout-2,destination | - | u | t | uc,tb | head,enter_roundabout-3,destination | + | from | to | route | turns | + | s | t | sa,tb | depart,roundabout-exit-1,arrive | + | s | u | sa,uc | depart,roundabout-exit-2,arrive | + | s | v | sa,vd | depart,roundabout-exit-3,arrive | + | u | v | uc,vd | depart,roundabout-exit-1,arrive | + | u | s | uc,sa | depart,roundabout-exit-2,arrive | + | u | t | uc,tb | depart,roundabout-exit-3,arrive | diff --git a/features/guidance/continue.feature b/features/guidance/continue.feature new file mode 100644 index 000000000..b3abe4843 --- /dev/null +++ b/features/guidance/continue.feature @@ -0,0 +1,90 @@ +@routing @guidance +Feature: Continue Instructions + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Road turning left + Given the node map + | | | c | | + | a | | b | d | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,continue left,arrive | + | a,d | abc,bd,bd | depart,new name straight,arrive | + + Scenario: Road turning right + Given the node map + | a | | b | d | + | | | c | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,continue right,arrive | + | a,d | abc,bd,bd | depart,new name straight,arrive | + + Scenario: Road turning slight left + Given the node map + | | | | | c | + | | | | | | + | a | | b | | | + | | | | d | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,continue left,arrive | + | a,d | abc,bd,bd | depart,turn right,arrive | + + Scenario: Road turning slight right + Given the node map + | | | | d | | + | a | | b | | | + | | | | | | + | | | | | c | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,continue right,arrive | + | a,d | abc,bd,bd | depart,turn left,arrive | + + Scenario: Road Loop + Given the node map + | | | f | | e | + | | | | | | + | a | | b | g | | + | | | | | | + | | | c | | d | + + And the ways + | nodes | highway | + | abcdefb | primary | + | bg | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abcdefb,abcdefb,abcdefb | depart,continue right,arrive | + | a,f | abcdefb,abcdefb,abcdefb | depart,continue left,arrive | + | a,d | abcdefb,abcdefb,abcdefb | depart,continue right,arrive | + | a,e | abcdefb,abcdefb,abcdefb | depart,continue left,arrive | diff --git a/features/guidance/end-of-road.feature b/features/guidance/end-of-road.feature new file mode 100644 index 000000000..323f5df35 --- /dev/null +++ b/features/guidance/end-of-road.feature @@ -0,0 +1,123 @@ +@routing @guidance +Feature: End Of Road Instructions + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: End of Road with through street + Given the node map + | | | c | + | a | | b | + | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cbd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cbd,cbd | depart,end of road left,arrive | + | a,d | ab,cbd,cbd | depart,end of road right,arrive | + + Scenario: End of Road with three streets + Given the node map + | | | c | + | a | | b | + | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,end of road left,arrive | + | a,d | ab,bd,bd | depart,end of road right,arrive | + + Scenario: End of Road with three streets, slightly angled + Given the node map + | a | | | | | c | + | | | | | | b | + | | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,end of road left,arrive | + | a,d | ab,bd,bd | depart,end of road right,arrive | + + Scenario: End of Road with three streets, slightly angled + Given the node map + | | | | | | c | + | | | | | | b | + | a | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,end of road left,arrive | + | a,d | ab,bd,bd | depart,end of road right,arrive | + + Scenario: End of Road with through street, slightly angled + Given the node map + | a | | | | | c | + | | | | | | b | + | | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cbd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cbd,cbd | depart,end of road left,arrive | + | a,d | ab,cbd,cbd | depart,end of road right,arrive | + + Scenario: End of Road with through street, slightly angled + Given the node map + | | | | | | c | + | | | | | | b | + | a | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | cbd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cbd,cbd | depart,end of road left,arrive | + | a,d | ab,cbd,cbd | depart,end of road right,arrive | + + Scenario: End of Road with two ramps - prefer ramp over end of road + Given the node map + | | | c | + | a | | b | + | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | bc | motorway_link | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,ramp left,arrive | + | a,d | ab,bd,bd | depart,ramp right,arrive | + diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature new file mode 100644 index 000000000..916799e74 --- /dev/null +++ b/features/guidance/fork.feature @@ -0,0 +1,284 @@ +@routing @guidance +Feature: Fork Instructions + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Fork Same Road Class + Given the node map + | | | | | c | + | a | | b | | | + | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Do not fork on link type + Given the node map + | | | | | c | + | a | | b | | | + | | | | | d | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary_link | + + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | a,d | abc,bd,bd | depart,turn slight right,arrive | + + Scenario: Fork in presence of other roads + Given the node map + | | | | | c | + | a | | b | | | + | | e | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + | eb | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Fork Turning Slight Left + Given the node map + | | | | | | c | + | | | | | | | + | a | | b | | | | + | | | | | d | | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Fork Turning Slight Right + Given the node map + | | | | | c | | + | a | | b | | | | + | | | | | | | + | | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Do not fork on service + Given the node map + | | | | | c | + | a | | b | | | + | | | | | d | + + And the ways + | nodes | highway | + | abc | residential | + | bd | service | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | a,d | abc,bd,bd | depart,turn slight right,arrive | + + Scenario: Fork Both Turning Slight Right + Given the node map + | a | | b | | | | + | | | | | | c | + | | | | | | d | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Fork Both Turning Slight Left + Given the node map + | | | | | | c | + | | | | | | d | + | a | | b | | | | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork slight right,arrive | + + Scenario: Fork Both Turning Slight Right - Unnamed + Given the node map + | a | | b | | | | + | | | | | | c | + | | | | | | d | + + And the ways + | nodes | highway | name | + | ab | primary | | + | bc | primary | | + | bd | primary | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,, | depart,fork slight left,arrive | + | a,d | ,, | depart,fork slight right,arrive | + + Scenario: Fork Both Turning Slight Left - Unnamed + Given the node map + | | | | | | c | + | | | | | | d | + | a | | b | | | | + + And the ways + | nodes | highway | name | + | ab | primary | | + | bc | primary | | + | bd | primary | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,, | depart,fork slight left,arrive | + | a,d | ,, | depart,fork slight right,arrive | + + Scenario: Fork Both Turning Very Slightly Right - Unnamed + Given the node map + | a | | b | | | | | | | | | | | | | | | | + | | | | | | | | | | | | c | | | | | | | + | | | | | | | | | | | | | | | | | | d | + + And the ways + | nodes | highway | name | + | ab | primary | | + | bc | primary | | + | bd | primary | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,, | depart,fork slight left,arrive | + | a,d | ,, | depart,fork slight right,arrive | + + Scenario: Fork Both Turning Very Slightly Right - Unnamed Ramps + Given the node map + | a | | b | | | | | | | | | | | | | | | | + | | | | | | | | | | | | c | | | | | | | + | | | | | | | | | | | | | | | | | | d | + + And the ways + | nodes | highway | name | + | ab | motorway_link | | + | bc | motorway_link | | + | bd | motorway_link | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,, | depart,fork slight left,arrive | + | a,d | ,, | depart,fork slight right,arrive | + + Scenario: Non-Fork on complex intersection - left + Given the node map + | | | | | c | + | a | | b | | | + | | e | | | d | + + And the ways + | nodes | highway | + | abc | secondary | + | bd | tertiary | + | eb | tertiary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | a,d | abc,bd,bd | depart,turn slight right,arrive | + + Scenario: Non-Fork on complex intersection - right + Given the node map + | | e | | | c | + | a | | b | | | + | | | | | d | + + And the ways + | nodes | highway | + | abd | secondary | + | bc | tertiary | + | eb | tertiary | + + When I route I should get + | waypoints | route | turns | + | a,c | abd,bc,bc | depart,turn slight left,arrive | + | a,d | abd,abd | depart,arrive | + + @pr2275 @bug + Scenario: Tripple fork + Given the node map + | | | | | | | | | c | + | a | | b | | d | | | | | + | | | | | | | | | e | + + And the ways + | nodes | highway | + | ab | secondary | + | bc | secondary | + | bd | secondary | + | be | secondary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,fork slight left,arrive | + | a,d | ab,bd,bd | depart,fork straight,arrive | + | a,e | ab,be,be | depart,fork slight right,arrive | + + Scenario: Tripple fork -- middle obvious + Given the node map + | | | | | c | + | a | | b | | d | + | | | | | e | + + And the ways + | nodes | highway | + | abd | secondary | + | bc | secondary | + | be | secondary | + + When I route I should get + | waypoints | route | turns | + | a,c | abd,bc,bc | depart,turn slight left,arrive | + | a,d | abd,abd | depart,arrive | + | a,e | abd,be,be | depart,turn slight right,arrive | diff --git a/features/guidance/merge.feature b/features/guidance/merge.feature new file mode 100644 index 000000000..0736e8f7c --- /dev/null +++ b/features/guidance/merge.feature @@ -0,0 +1,52 @@ +@routing @guidance +Feature: Merging + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Merge on Four Way Intersection + Given the node map + | d | | | + | a | b | c | + | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | db | primary | + | eb | primary | + + When I route I should get + | waypoints | route | turns | + | d,c | db,abc,abc | depart,merge slight right,arrive | + | e,c | eb,abc,abc | depart,merge slight left,arrive | + + Scenario: Merge on Three Way Intersection Right + Given the node map + | d | | | + | a | b | c | + + And the ways + | nodes | highway | + | abc | primary | + | db | primary | + + When I route I should get + | waypoints | route | turns | + | d,c | db,abc,abc | depart,merge slight right,arrive | + + Scenario: Merge on Three Way Intersection Right + Given the node map + | a | b | c | + | d | | | + + And the ways + | nodes | highway | + | abc | primary | + | db | primary | + + When I route I should get + | waypoints | route | turns | + | d,c | db,abc,abc | depart,merge slight left,arrive | + diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature new file mode 100644 index 000000000..5e9b3128c --- /dev/null +++ b/features/guidance/motorway.feature @@ -0,0 +1,218 @@ +@routing @guidance +Feature: Motorway Guidance + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Ramp Exit Right + Given the node map + | a | b | c | d | e | + | | | | f | g | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,bfg,bfg | depart,ramp slight right,arrive | + + Scenario: Ramp Exit Right Curved Right + Given the node map + | a | b | c | | | + | | | f | d | | + | | | | g | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,bfg,bfg | depart,ramp right,arrive | + + Scenario: Ramp Exit Right Curved Left + Given the node map + | | | | | e | + | | | | d | g | + | a | b | c | f | | + + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,cfg,cfg | depart,ramp slight right,arrive | + + + Scenario: Ramp Exit Left + Given the node map + | | | | f | g | + | a | b | c | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,bfg,bfg | depart,ramp slight left,arrive | + + Scenario: Ramp Exit Left Curved Left + Given the node map + | | | | g | e | + | | | f | d | | + | a | b | c | | | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,bfg,bfg | depart,ramp left,arrive | + + Scenario: Ramp Exit Left Curved Right + Given the node map + | a | b | c | f | | + | | | | d | g | + | | | | | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | a,g | abcde,cfg,cfg | depart,ramp slight left,arrive | + + Scenario: On Ramp Right + Given the node map + | a | b | c | d | e | + | f | g | | | | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | f,e | fgd,abcde,abcde | depart,merge slight left,arrive | + + Scenario: On Ramp Left + Given the node map + | f | g | | | | + | a | b | c | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | f,e | fgd,abcde,abcde | depart,merge slight right,arrive | + + Scenario: Highway Fork + Given the node map + | | | | | d | e | + | a | b | c | | | | + | | | | | f | g | + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde,abcde | depart,fork slight left,arrive | + | a,g | abcde,cfg,cfg | depart,fork slight right,arrive | + + Scenario: Fork After Ramp + Given the node map + | | | | | d | e | + | a | b | c | | | | + | | | | | f | g | + + And the ways + | nodes | highway | + | abc | motorway_link | + | cde | motorway | + | cfg | motorway | + + When I route I should get + | waypoints | route | turns | + | a,e | abc,cde,cde | depart,fork slight left,arrive | + | a,g | abc,cfg,cfg | depart,fork slight right,arrive | + + Scenario: On And Off Ramp Right + Given the node map + | a | b | | c | | d | e | + | f | g | | | | h | i | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgc | motorway_link | + | chi | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | f,e | fgc,abcde,abcde | depart,merge slight left,arrive | + | a,i | abcde,chi,chi | depart,ramp slight right,arrive | + | f,i | fgc,chi,chi | depart,ramp right,arrive | + + Scenario: On And Off Ramp Left + Given the node map + | f | g | | | | h | i | + | a | b | | c | | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgc | motorway_link | + | chi | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + | f,e | fgc,abcde,abcde | depart,merge slight right,arrive | + | a,i | abcde,chi,chi | depart,ramp slight left,arrive | + | f,i | fgc,chi,chi | depart,ramp left,arrive | + + Scenario: Merging Motorways + Given the node map + | e | | | + | a | b | c | + | d | | | + + And the ways + | nodes | highway | + | abc | motorway | + | db | motorway | + | eb | motorway | + + When I route I should get + | waypoints | route | turns | + | d,c | db,abc,abc | depart,merge slight left,arrive | + | e,c | eb,abc,abc | depart,merge slight right,arrive | diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature new file mode 100644 index 000000000..1fdf1ce42 --- /dev/null +++ b/features/guidance/new-name.feature @@ -0,0 +1,135 @@ +@routing @guidance +Feature: New-Name Instructions + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Undisturbed name Change + Given the node map + | a | | b | | c | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name straight,arrive | + + + Scenario: Undisturbed Name Change with unannounced Turn Right + Given the node map + | a | | b | | | + | | | | | c | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name slight right,arrive | + + Scenario: Undisturbed Name Change with unannounced Turn Left + Given the node map + | | | | | c | + | a | | b | | | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name slight left,arrive | + + Scenario: Disturbed Name Change with Turn + Given the node map + | a | | b | | | + | | d | | | c | + + And the ways + | nodes | + | ab | + | bc | + | db | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name slight right,arrive | + + Scenario: Undisturbed Name Change with announced Turn Left + Given the node map + | | | c | + | a | | b | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name left,arrive | + + Scenario: Undisturbed Name Change with announced Turn Sharp Left + Given the node map + | c | | | + | a | | b | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name sharp left,arrive | + + Scenario: Undisturbed Name Change with announced Turn Right + Given the node map + | a | | b | + | | | c | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name right,arrive | + + Scenario: Undisturbed Name Change with announced Turn Sharp Right + Given the node map + | a | | b | + | c | | | + + And the ways + | nodes | + | ab | + | bc | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name sharp right,arrive | + + + Scenario: Disturbed Name Change with minor road class + Given the node map + | a | | b | | d | + | | | | | c | + + And the ways + | nodes | highway | + | ab | residential | + | bc | residential | + | bd | service | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,new name slight right,arrive | diff --git a/features/guidance/ramp.feature b/features/guidance/ramp.feature new file mode 100644 index 000000000..3c3ce3959 --- /dev/null +++ b/features/guidance/ramp.feature @@ -0,0 +1,229 @@ +@routing @guidance +Feature: Ramp Guidance + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Ramp On Through Street Right + Given the node map + | a | b | c | + | | d | | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp right,arrive | + + Scenario: Ramp On Through Street Left + Given the node map + | | d | | + | a | b | c | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp left,arrive | + + Scenario: Ramp On Through Street Left and Right + Given the node map + | | e | | + | a | b | c | + | | d | | + + And the ways + | nodes | highway | + | be | motorway_link | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp right,arrive | + | a,e | abc,be,be | depart,ramp left,arrive | + + Scenario: Ramp On Three Way Intersection Right + Given the node map + | a | b | c | + | | d | | + + And the ways + | nodes | highway | + | ab | tertiary | + | bc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,bd,bd | depart,ramp right,arrive | + + Scenario: Ramp On Three Way Intersection Right + Given the node map + | | | c | + | a | b | | + | | d | | + + And the ways + | nodes | highway | + | ab | tertiary | + | bc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,bd,bd | depart,ramp right,arrive | + + Scenario: Ramp Off Though Street + Given the node map + | | | c | + | a | b | | + | | d | | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp right,arrive | + | a,c | abc,abc | depart,arrive | + + Scenario: Straight Ramp Off Turning Though Street + Given the node map + | | | c | + | a | b | d | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp straight,arrive | + | a,c | abc,abc,abc | depart,continue left,arrive | + + Scenario: Fork Ramp Off Turning Though Street + Given the node map + | | | c | + | a | b | | + | | | d | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp right,arrive | + | a,c | abc,abc,abc | depart,continue left,arrive | + + Scenario: Fork Ramp + Given the node map + | | | c | + | a | b | | + | | | d | + + And the ways + | nodes | highway | + | ab | tertiary | + | bc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,bd,bd | depart,ramp right,arrive | + | a,c | ab,bc,bc | depart,turn left,arrive | + + Scenario: Fork Slight Ramp + Given the node map + | | | | c | + | a | b | | | + | | | | d | + + And the ways + | nodes | highway | + | ab | tertiary | + | bc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,bd,bd | depart,ramp slight right,arrive | + | a,c | ab,bc,bc | depart,turn slight left,arrive | + + Scenario: Fork Slight Ramp on Through Street + Given the node map + | | | | c | + | a | b | | | + | | | | d | + + And the ways + | nodes | highway | + | abc | tertiary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp slight right,arrive | + | a,c | abc,abc,abc | depart,continue slight left,arrive | + + Scenario: Fork Slight Ramp on Obvious Through Street + Given the node map + | | | | c | + | a | b | | | + | | | | d | + + And the ways + | nodes | highway | + | abc | primary | + | bd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,ramp slight right,arrive | + | a,c | abc,abc | depart,arrive | + + Scenario: Two Ramps Joining into common Motorway + Given the node map + | a | | | | + | | | c | d | + | b | | | | + + And the ways + | nodes | highway | + | ac | motorway_link | + | bc | motorway_link | + | cd | motorway | + + When I route I should get + | waypoints | route | turns | + | a,d | ac,cd,cd | depart,new name slight left,arrive | + | b,d | bc,cd,cd | depart,new name slight right,arrive | + + Scenario: Two Ramps Joining into common Motorway Unnamed + Given the node map + | a | | | | + | | | c | d | + | b | | | | + + And the ways + | nodes | highway | name | + | ac | motorway_link | | + | bc | motorway_link | | + | cd | motorway | | + + When I route I should get + | waypoints | route | turns | + | a,d | , | depart,arrive | + | b,d | , | depart,arrive | diff --git a/features/guidance/rotary-bike.feature b/features/guidance/rotary-bike.feature new file mode 100644 index 000000000..512b81580 --- /dev/null +++ b/features/guidance/rotary-bike.feature @@ -0,0 +1,167 @@ +@routing @guidance +Feature: Rotary + + Background: + Given the profile "bicycle" + Given a grid size of 30 meters + + Scenario: Enter and Exit + Given the node map + | | | a | | | + | | | b | | | + | h | g | | c | d | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bgecb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,cd,cd | depart,bgecb-exit-3,arrive | + | a,f | ab,ef,ef | depart,bgecb-exit-2,arrive | + | a,h | ab,gh,gh | depart,bgecb-exit-1,arrive | + | d,f | cd,ef,ef | depart,bgecb-exit-3,arrive | + | d,h | cd,gh,gh | depart,bgecb-exit-2,arrive | + | d,a | cd,ab,ab | depart,bgecb-exit-1,arrive | + | f,h | ef,gh,gh | depart,bgecb-exit-3,arrive | + | f,a | ef,ab,ab | depart,bgecb-exit-2,arrive | + | f,d | ef,cd,cd | depart,bgecb-exit-1,arrive | + | h,a | gh,ab,ab | depart,bgecb-exit-3,arrive | + | h,d | gh,cd,cd | depart,bgecb-exit-2,arrive | + | h,f | gh,ef,ef | depart,bgecb-exit-1,arrive | + + Scenario: Only Enter + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | a,e | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | a,g | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,e | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,g | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,b | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,g | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,b | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,c | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,b | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,c | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,e | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + + Scenario: Only Exit + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive | + | b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive | + | b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive | + | c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive | + | c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive | + | c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive | + | e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive | + | e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive | + | e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive | + | g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive | + | g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive | + | g,f | bcegb,ef,ef | depart,bcegb-exit-3,arrive | + #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits + + Scenario: Drive Around + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,c | bcegb,bcegb | depart,arrive | + | b,e | bcegb,bcegb | depart,arrive | + | b,g | bcegb,bcegb | depart,arrive | + | c,e | bcegb,bcegb | depart,arrive | + | c,g | bcegb,bcegb | depart,arrive | + | c,b | bcegb,bcegb | depart,arrive | + | e,g | bcegb,bcegb | depart,arrive | + | e,b | bcegb,bcegb | depart,arrive | + | e,c | bcegb,bcegb | depart,arrive | + | g,b | bcegb,bcegb | depart,arrive | + | g,c | bcegb,bcegb | depart,arrive | + | g,e | bcegb,bcegb | depart,arrive | + + #needs to be adjusted when name-discovery works for entrys + Scenario: Mixed Entry and Exit + Given the node map + | | c | | a | | + | j | | b | | f | + | | k | | e | | + | l | | h | | d | + | | g | | i | | + + And the ways + | nodes | junction | oneway | + | abc | | yes | + | def | | yes | + | ghi | | yes | + | jkl | | yes | + | bkheb | roundabout | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,rotary-exit-1,arrive | + | a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive | + | a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive | + | a,f | abc,def,def | depart,bkheb-exit-4,arrive | + | d,f | def,def,def | depart,rotary-exit-1,arrive | + | d,c | def,abc,abc | depart,bkheb-exit-2,arrive | + | d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive | + | d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive | + | g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive | + | g,f | ghi,def,def | depart,bkheb-exit-2,arrive | + | g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive | + | g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive | + | j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive | + | j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive | + | j,f | jkl,def,def | depart,bkheb-exit-3,arrive | + | j,c | jkl,abc,abc | depart,bkheb-exit-4,arrive | diff --git a/features/guidance/rotary.feature b/features/guidance/rotary.feature new file mode 100644 index 000000000..e1b14d3a3 --- /dev/null +++ b/features/guidance/rotary.feature @@ -0,0 +1,262 @@ +@routing @guidance +Feature: Rotary + + Background: + Given the profile "car" + Given a grid size of 30 meters + + Scenario: Enter and Exit + Given the node map + | | | a | | | + | | | b | | | + | h | g | | c | d | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bgecb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,cd,cd | depart,bgecb-exit-3,arrive | + | a,f | ab,ef,ef | depart,bgecb-exit-2,arrive | + | a,h | ab,gh,gh | depart,bgecb-exit-1,arrive | + | d,f | cd,ef,ef | depart,bgecb-exit-3,arrive | + | d,h | cd,gh,gh | depart,bgecb-exit-2,arrive | + | d,a | cd,ab,ab | depart,bgecb-exit-1,arrive | + | f,h | ef,gh,gh | depart,bgecb-exit-3,arrive | + | f,a | ef,ab,ab | depart,bgecb-exit-2,arrive | + | f,d | ef,cd,cd | depart,bgecb-exit-1,arrive | + | h,a | gh,ab,ab | depart,bgecb-exit-3,arrive | + | h,d | gh,cd,cd | depart,bgecb-exit-2,arrive | + | h,f | gh,ef,ef | depart,bgecb-exit-1,arrive | + + Scenario: Only Enter + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | a,e | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | a,g | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,e | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,g | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | d,b | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,g | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,b | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | f,c | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,b | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,c | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + | h,e | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive | + + Scenario: Only Exit + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive | + | b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive | + | b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive | + | c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive | + | c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive | + | c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive | + | e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive | + | e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive | + | e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive | + | g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive | + | g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive | + | g,f | bcegb,ef,ef | depart,bcegb-exit-3,arrive | + #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits + + Scenario: Drive Around + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,c | bcegb,bcegb | depart,arrive | + | b,e | bcegb,bcegb | depart,arrive | + | b,g | bcegb,bcegb | depart,arrive | + | c,e | bcegb,bcegb | depart,arrive | + | c,g | bcegb,bcegb | depart,arrive | + | c,b | bcegb,bcegb | depart,arrive | + | e,g | bcegb,bcegb | depart,arrive | + | e,b | bcegb,bcegb | depart,arrive | + | e,c | bcegb,bcegb | depart,arrive | + | g,b | bcegb,bcegb | depart,arrive | + | g,c | bcegb,bcegb | depart,arrive | + | g,e | bcegb,bcegb | depart,arrive | + + #needs to be adjusted when name-discovery works for entrys + Scenario: Mixed Entry and Exit + Given the node map + | | c | | a | | + | j | | b | | f | + | | k | | e | | + | l | | h | | d | + | | g | | i | | + + And the ways + | nodes | junction | oneway | + | abc | | yes | + | def | | yes | + | ghi | | yes | + | jkl | | yes | + | bkheb | roundabout | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,rotary-exit-1,arrive | + | a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive | + | a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive | + | a,f | abc,def,def | depart,bkheb-exit-4,arrive | + | d,f | def,def,def | depart,rotary-exit-1,arrive | + | d,c | def,abc,abc | depart,bkheb-exit-2,arrive | + | d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive | + | d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive | + | g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive | + | g,f | ghi,def,def | depart,bkheb-exit-2,arrive | + | g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive | + | g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive | + | j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive | + | j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive | + | j,f | jkl,def,def | depart,bkheb-exit-3,arrive | + | j,c | jkl,abc,abc | depart,bkheb-exit-4,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | b | | | + | c | d | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,bcdb-exit-1,arrive | + | a,f | ab,df,df | depart,bcdb-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | d | | | + | b | c | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,bcdb-exit-1,arrive | + | a,f | ab,df,df | depart,bcdb-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | c | | | + | d | b | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,bcdb-exit-1,arrive | + | a,f | ab,df,df | depart,bcdb-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | f | | | + | d | c | e | + | | b | | + | | a | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,bcdb-exit-1,arrive | + | a,f | ab,df,df | depart,bcdb-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | f | | | + | d | c | e | + | b | | | + | a | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,bcdb-exit-1,arrive | + | a,f | ab,df,df | depart,bcdb-exit-2,arrive | diff --git a/features/guidance/roundabout-bike.feature b/features/guidance/roundabout-bike.feature new file mode 100644 index 000000000..3652c1520 --- /dev/null +++ b/features/guidance/roundabout-bike.feature @@ -0,0 +1,166 @@ +@routing @guidance +Feature: Basic Roundabout + + Background: + Given the profile "bicycle" + Given a grid size of 10 meters + + Scenario: Enter and Exit + Given the node map + | | | a | | | + | | | b | | | + | h | g | | c | d | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bgecb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,cd,cd | depart,roundabout-exit-3,arrive | + | a,f | ab,ef,ef | depart,roundabout-exit-2,arrive | + | a,h | ab,gh,gh | depart,roundabout-exit-1,arrive | + | d,f | cd,ef,ef | depart,roundabout-exit-3,arrive | + | d,h | cd,gh,gh | depart,roundabout-exit-2,arrive | + | d,a | cd,ab,ab | depart,roundabout-exit-1,arrive | + | f,h | ef,gh,gh | depart,roundabout-exit-3,arrive | + | f,a | ef,ab,ab | depart,roundabout-exit-2,arrive | + | f,d | ef,cd,cd | depart,roundabout-exit-1,arrive | + | h,a | gh,ab,ab | depart,roundabout-exit-3,arrive | + | h,d | gh,cd,cd | depart,roundabout-exit-2,arrive | + | h,f | gh,ef,ef | depart,roundabout-exit-1,arrive | + + Scenario: Only Enter + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | a,e | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | a,g | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,e | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,g | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,b | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,g | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,b | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,c | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,b | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + + Scenario: Only Exit + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive | + | b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive | + | b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive | + | c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive | + | c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive | + | c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive | + | e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive | + | e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive | + | e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive | + | g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive | + | g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive | + | g,f | bcegb,ef,ef | depart,roundabout-exit-3,arrive | + #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits + + Scenario: Drive Around + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,c | bcegb,bcegb | depart,arrive | + | b,e | bcegb,bcegb | depart,arrive | + | b,g | bcegb,bcegb | depart,arrive | + | c,e | bcegb,bcegb | depart,arrive | + | c,g | bcegb,bcegb | depart,arrive | + | c,b | bcegb,bcegb | depart,arrive | + | e,g | bcegb,bcegb | depart,arrive | + | e,b | bcegb,bcegb | depart,arrive | + | e,c | bcegb,bcegb | depart,arrive | + | g,b | bcegb,bcegb | depart,arrive | + | g,c | bcegb,bcegb | depart,arrive | + | g,e | bcegb,bcegb | depart,arrive | + + Scenario: Mixed Entry and Exit + Given the node map + | | c | | a | | + | j | | b | | f | + | | k | | e | | + | l | | h | | d | + | | g | | i | | + + And the ways + | nodes | junction | oneway | + | abc | | yes | + | def | | yes | + | ghi | | yes | + | jkl | | yes | + | bkheb | roundabout | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,roundabout-exit-1,arrive | + | a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive | + | a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive | + | a,f | abc,def,def | depart,roundabout-exit-4,arrive | + | d,f | def,def,def | depart,roundabout-exit-1,arrive | + | d,c | def,abc,abc | depart,roundabout-exit-2,arrive | + | d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive | + | d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive | + | g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive | + | g,f | ghi,def,def | depart,roundabout-exit-2,arrive | + | g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive | + | g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive | + | j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive | + | j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive | + | j,f | jkl,def,def | depart,roundabout-exit-3,arrive | + | j,c | jkl,abc,abc | depart,roundabout-exit-4,arrive | diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature new file mode 100644 index 000000000..d03737c9c --- /dev/null +++ b/features/guidance/roundabout.feature @@ -0,0 +1,261 @@ +@routing @guidance +Feature: Basic Roundabout + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Enter and Exit + Given the node map + | | | a | | | + | | | b | | | + | h | g | | c | d | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bgecb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,cd,cd | depart,roundabout-exit-3,arrive | + | a,f | ab,ef,ef | depart,roundabout-exit-2,arrive | + | a,h | ab,gh,gh | depart,roundabout-exit-1,arrive | + | d,f | cd,ef,ef | depart,roundabout-exit-3,arrive | + | d,h | cd,gh,gh | depart,roundabout-exit-2,arrive | + | d,a | cd,ab,ab | depart,roundabout-exit-1,arrive | + | f,h | ef,gh,gh | depart,roundabout-exit-3,arrive | + | f,a | ef,ab,ab | depart,roundabout-exit-2,arrive | + | f,d | ef,cd,cd | depart,roundabout-exit-1,arrive | + | h,a | gh,ab,ab | depart,roundabout-exit-3,arrive | + | h,d | gh,cd,cd | depart,roundabout-exit-2,arrive | + | h,f | gh,ef,ef | depart,roundabout-exit-1,arrive | + + Scenario: Only Enter + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | a,e | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | a,g | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,e | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,g | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | d,b | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,g | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,b | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | f,c | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,b | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + | h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + + Scenario: Only Exit + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive | + | b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive | + | b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive | + | c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive | + | c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive | + | c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive | + | e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive | + | e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive | + | e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive | + | g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive | + | g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive | + | g,f | bcegb,ef,ef | depart,roundabout-exit-3,arrive | + #phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits + + Scenario: Drive Around + Given the node map + | | | a | | | + | | | b | | | + | d | c | | g | h | + | | | e | | | + | | | f | | | + + And the ways + | nodes | junction | + | ab | | + | cd | | + | ef | | + | gh | | + | bcegb | roundabout | + + When I route I should get + | waypoints | route | turns | + | b,c | bcegb,bcegb | depart,arrive | + | b,e | bcegb,bcegb | depart,arrive | + | b,g | bcegb,bcegb | depart,arrive | + | c,e | bcegb,bcegb | depart,arrive | + | c,g | bcegb,bcegb | depart,arrive | + | c,b | bcegb,bcegb | depart,arrive | + | e,g | bcegb,bcegb | depart,arrive | + | e,b | bcegb,bcegb | depart,arrive | + | e,c | bcegb,bcegb | depart,arrive | + | g,b | bcegb,bcegb | depart,arrive | + | g,c | bcegb,bcegb | depart,arrive | + | g,e | bcegb,bcegb | depart,arrive | + + Scenario: Mixed Entry and Exit + Given the node map + | | c | | a | | + | j | | b | | f | + | | k | | e | | + | l | | h | | d | + | | g | | i | | + + And the ways + | nodes | junction | oneway | + | abc | | yes | + | def | | yes | + | ghi | | yes | + | jkl | | yes | + | bkheb | roundabout | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart,roundabout-exit-1,arrive | + | a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive | + | a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive | + | a,f | abc,def,def | depart,roundabout-exit-4,arrive | + | d,f | def,def,def | depart,roundabout-exit-1,arrive | + | d,c | def,abc,abc | depart,roundabout-exit-2,arrive | + | d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive | + | d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive | + | g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive | + | g,f | ghi,def,def | depart,roundabout-exit-2,arrive | + | g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive | + | g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive | + | j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive | + | j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive | + | j,f | jkl,def,def | depart,roundabout-exit-3,arrive | + | j,c | jkl,abc,abc | depart,roundabout-exit-4,arrive | + + Scenario: Collinear in X + Given the node map + | a | b | c | d | f | + | | | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | + | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + + Scenario: Collinear in Y + Given the node map + | a | | + | b | | + | c | e | + | d | | + | f | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | + | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | b | | | + | c | d | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | + | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | d | | | + | b | c | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | + | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + + Scenario: Collinear in X,Y + Given the node map + | a | | | + | c | | | + | d | b | f | + | e | | | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | + | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + diff --git a/features/guidance/suppressed.feature b/features/guidance/suppressed.feature new file mode 100644 index 000000000..89b58655c --- /dev/null +++ b/features/guidance/suppressed.feature @@ -0,0 +1,66 @@ +@routing @guidance +Feature: Suppressed Turns + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Do not announce passing a exit ramp + Given the node map + | a | b | c | d | e | + | | | | f | g | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde,abcde | depart,arrive | + + Scenario: Do not announce reference changes + Given the node map + | a | b | c | d | e | f | + + And the ways + | nodes | highway | name | ref | + | ab | motorway | highway | A1 | + | bc | motorway | highway | A1,A2 | + | cd | motorway | highway | A2 | + | de | motorway | highway | | + | ef | motorway | highway | A1 | + + When I route I should get + | waypoints | route | turns | + | a,f | highway (A1),highway (A1) | depart,arrive | + + + Scenario: Don't Announce Turn on following major road class -- service + Given the node map + | a | b | d | + | | | c | + + And the ways + | nodes | highway | + | abc | primary | + | bd | service | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + + Scenario: Don't Announce Turn on following major road class -- residential + Given the node map + | a | b | d | + | | | c | + + And the ways + | nodes | highway | + | abc | primary | + | bd | residential | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | a,d | abc,bd,bd | depart,turn straight,arrive | diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature new file mode 100644 index 000000000..20f4226f2 --- /dev/null +++ b/features/guidance/turn.feature @@ -0,0 +1,813 @@ +@routing @guidance +Feature: Simple Turns + + Background: + Given the profile "car" + Given a grid size of 10 meters + + Scenario: Four Way Intersection + Given the node map + | | c | | + | a | b | e | + | | d | | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | db | primary | + | eb | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,turn left,arrive | + | a,e | ab,eb,eb | depart,new name straight,arrive | + | a,d | ab,db,db | depart,turn right,arrive | + | c,a | cb,ab,ab | depart,turn right,arrive | + | c,d | cb,db,db | depart,new name straight,arrive | + | c,e | cb,eb,eb | depart,turn left,arrive | + | d,a | db,ab,ab | depart,turn left,arrive | + | d,c | db,cb,cb | depart,new name straight,arrive | + | d,e | db,eb,eb | depart,turn right,arrive | + | e,a | eb,ab,ab | depart,new name straight,arrive | + | e,c | eb,cb,cb | depart,turn right,arrive | + | e,d | eb,db,db | depart,turn left,arrive | + + Scenario: Rotated Four Way Intersection + Given the node map + | a | | c | + | | b | | + | d | | e | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | db | primary | + | eb | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,turn left,arrive | + | a,e | ab,eb,eb | depart,new name straight,arrive | + | a,d | ab,db,db | depart,turn right,arrive | + | c,a | cb,ab,ab | depart,turn right,arrive | + | c,d | cb,db,db | depart,new name straight,arrive | + | c,e | cb,eb,eb | depart,turn left,arrive | + | d,a | db,ab,ab | depart,turn left,arrive | + | d,c | db,cb,cb | depart,new name straight,arrive | + | d,e | db,eb,eb | depart,turn right,arrive | + | e,a | eb,ab,ab | depart,new name straight,arrive | + | e,c | eb,cb,cb | depart,turn right,arrive | + | e,d | eb,db,db | depart,turn left,arrive | + + + Scenario: Four Way Intersection Through Street + Given the node map + | | c | | + | a | b | e | + | | d | | + + And the ways + | nodes | highway | + | abe | primary | + | cb | primary | + | db | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abe,cb,cb | depart,turn left,arrive | + | a,e | abe,abe | depart,arrive | + | a,d | abe,db,db | depart,turn right,arrive | + | c,a | cb,abe,abe | depart,turn right,arrive | + | c,d | cb,db,db | depart,new name straight,arrive | + | c,e | cb,abe,abe | depart,turn left,arrive | + | d,a | db,abe,abe | depart,turn left,arrive | + | d,c | db,cb,cb | depart,new name straight,arrive | + | d,e | db,abe,abe | depart,turn right,arrive | + | e,a | abe,abe | depart,arrive | + | e,c | abe,cb,cb | depart,turn right,arrive | + | e,d | abe,db,db | depart,turn left,arrive | + + Scenario: Four Way Intersection Double Through Street + Given the node map + | | c | | + | a | b | e | + | | d | | + + And the ways + | nodes | highway | + | abe | primary | + | cbd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abe,cbd,cbd | depart,turn left,arrive | + | a,e | abe,abe | depart,arrive | + | a,d | abe,cbd,cbd | depart,turn right,arrive | + | c,a | cbd,abe,abe | depart,turn right,arrive | + | c,d | cbd,cbd | depart,arrive | + | c,e | cbd,abe,abe | depart,turn left,arrive | + | d,a | cbd,abe,abe | depart,turn left,arrive | + | d,c | cbd,cbd | depart,arrive | + | d,e | cbd,abe,abe | depart,turn right,arrive | + | e,a | abe,abe | depart,arrive | + | e,c | abe,cbd,cbd | depart,turn right,arrive | + | e,d | abe,cbd,cbd | depart,turn left,arrive | + + Scenario: Three Way Intersection + Given the node map + | | c | | + | a | b | d | + + And the ways + | nodes | highway | + | ab | primary | + | cb | primary | + | db | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,cb,cb | depart,turn left,arrive | + | a,d | ab,db,db | depart,new name straight,arrive | + | d,c | db,cb,cb | depart,turn right,arrive | + | d,a | db,ab,ab | depart,new name straight,arrive | + + Scenario: Three Way Intersection on Through Street + Given the node map + | | d | | + | a | b | c | + + And the ways + | nodes | highway | + | abc | primary | + | db | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | a,d | abc,db,db | depart,turn left,arrive | + | c,a | abc,abc | depart,arrive | + | c,d | abc,db,db | depart,turn right,arrive | + + Scenario: High Degree Intersection + Given the node map + | i | | b | | c | + | | | | | | + | | | | | | + | h | | a | | d | + | | | | | | + | | | | | | + | g | | f | | e | + + And the ways + | nodes | highway | + | ab | primary | + | ac | primary | + | ad | primary | + | ae | primary | + | af | primary | + | ag | primary | + | ah | primary | + | ai | primary | + + When I route I should get + | waypoints | route | turns | + | b,c | ab,ac,ac | depart,turn sharp left,arrive | + | b,d | ab,ad,ad | depart,turn left,arrive | + | b,e | ab,ae,ae | depart,turn slight left,arrive | + | b,f | ab,af,af | depart,new name straight,arrive | + | b,g | ab,ag,ag | depart,turn slight right,arrive | + | b,h | ab,ah,ah | depart,turn right,arrive | + | b,i | ab,ai,ai | depart,turn sharp right,arrive | + + Scenario: Disturbed High Degree Intersection + Given the node map + | | | b | | | + | i | | | | c | + | | | | | | + | h | | a | | d | + | | | | | | + | g | | | | e | + | | | f | | | + + And the ways + | nodes | highway | + | ab | primary | + | ac | primary | + | ad | primary | + | ae | primary | + | af | primary | + | ag | primary | + | ah | primary | + | ai | primary | + + When I route I should get + | waypoints | route | turns | + | b,c | ab,ac,ac | depart,turn sharp left,arrive | + | b,d | ab,ad,ad | depart,turn left,arrive | + | b,e | ab,ae,ae | depart,turn slight left,arrive | + | b,f | ab,af,af | depart,new name straight,arrive | + | b,g | ab,ag,ag | depart,turn slight right,arrive | + | b,h | ab,ah,ah | depart,turn right,arrive | + | b,i | ab,ai,ai | depart,turn sharp right,arrive | + + Scenario: Turn instructions at high latitude + Given the node locations + | node | lat | lon | + | a | 55.68740 | 12.52430 | + | b | 55.68745 | 12.52409 | + | c | 55.68711 | 12.52383 | + | d | 55.68745 | 12.52450 | + | e | 55.68755 | 12.52450 | + | x | -55.68740 | 12.52430 | + | y | -55.68745 | 12.52409 | + | z | -55.68711 | 12.52383 | + | v | -55.68745 | 12.52450 | + | w | -55.68755 | 12.52450 | + + And the ways + | nodes | + | ab | + | bc | + | bd | + | be | + | xy | + | yz | + | vy | + | wy | + + When I route I should get + | from | to | route | turns | + | a | c | ab,bc,bc | depart,turn left,arrive | + | c | a | bc,ab,ab | depart,turn right,arrive | + | x | z | xy,yz,yz | depart,turn right,arrive | + | z | x | yz,xy,xy | depart,turn left,arrive | + + Scenario: Four Way Intersection Double Through Street Segregated + Given the node map + | | b | | c | | + | i | | | | d | + | | | a | | | + | h | | | | e | + | | g | | f | | + + And the ways + | nodes | highway | oneway | name | + | ha | primary | yes | first | + | ai | primary | yes | first | + | ae | primary | yes | first | + | da | primary | yes | first | + | ba | primary | yes | second | + | ac | primary | yes | second | + | fa | primary | yes | second | + | ag | primary | yes | second | + + When I route I should get + | waypoints | route | turns | + | f,e | second,first,first | depart,turn right,arrive | + | f,c | second,second | depart,arrive | + | f,i | second,first,first | depart,turn left,arrive | + | f,g | second,second,second | depart,continue uturn,arrive | + | d,c | first,second,second | depart,turn right,arrive | + | d,i | first,first | depart,arrive | + | d,g | first,second,second | depart,turn left,arrive | + | d,e | first,first,first | depart,continue uturn,arrive | + | b,i | second,first,first | depart,turn right,arrive | + | b,g | second,second | depart,arrive | + | b,e | second,first,first | depart,turn left,arrive | + | b,c | second,second,second | depart,continue uturn,arrive | + | h,g | first,second,second | depart,turn right,arrive | + | h,e | first,first | depart,arrive | + | h,c | first,second,second | depart,turn left,arrive | + | h,i | first,first,first | depart,continue uturn,arrive | + + Scenario: Three Way Similar Sharp Turns + Given the node map + | a | | | | b | + | c | | | | | + | | d | | | | + + And the ways + | nodes | highway | + | ab | primary | + | bc | primary | + | bd | primary | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,turn sharp right,arrive | + | a,d | ab,bd,bd | depart,turn sharp right,arrive | + | d,c | bd,bc,bc | depart,turn sharp left,arrive | + | d,a | bd,ab,ab | depart,turn sharp left,arrive | + + Scenario: Left Turn Assignment (1) + Given the node map + | | | | | d | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn slight left,arrive | + + Scenario: Left Turn Assignment (2) + Given the node map + | | | | | d | + | | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn left,arrive | + + Scenario: Left Turn Assignment (3) + Given the node map + | | | | d | | + | | | | | | + | | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn left,arrive | + + Scenario: Left Turn Assignment (4) + Given the node map + | | | d | | | + | | | | | | + | | | | | | + | | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn left,arrive | + + Scenario: Left Turn Assignment (5) + Given the node map + | | d | | | | + | | | | | | + | | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn left,arrive | + + @bug @pr2275 + Scenario: Left Turn Assignment (6) + Given the node map + | d | | | | | + | | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp left,arrive | + + Scenario: Left Turn Assignment (7) + Given the node map + | d | | | | | + | a | | b | | c | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp left,arrive | + + Scenario: Right Turn Assignment (1) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | d | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn slight right,arrive | + + Scenario: Right Turn Assignment (2) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | | + | | | | | d | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + + Scenario: Right Turn Assignment (3) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | | + | | | | | | + | | | | d | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + + Scenario: Right Turn Assignment (4) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | | + | | | | | | + | | | | | | + | | | d | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + + Scenario: Right Turn Assignment (5) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | | + | | | | | | + | | d | | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + + @bug @pr2275 + Scenario: Right Turn Assignment (6) + Given the node map + | | | e | | | + | a | | b | | c | + | | | | | | + | d | | | | | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp right,arrive | + + Scenario: Right Turn Assignment (7) + Given the node map + | | | e | | | + | a | | b | | c | + | d | | | | | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp right,arrive | + + Scenario: Right Turn Assignment Two Turns + Given the node map + | | | f | | | + | a | | b | | c | + | | | | | | + | d | e | | | | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp right,arrive | + | a,e | abc,be,be | depart,turn right,arrive | + + Scenario: Right Turn Assignment Two Turns (2) + Given the node map + | | | f | c | | + | a | | b | | | + | | | | | e | + | | | | d | | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + | a,e | abc,be,be | depart,turn slight right,arrive | + + Scenario: Right Turn Assignment Two Turns (3) + Given the node map + | | | f | | | + | a | | b | | c | + | | | | | e | + | | | | d | | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + | a,e | abc,be,be | depart,turn slight right,arrive | + + Scenario: Right Turn Assignment Two Turns (4) + Given the node map + | | | f | | | + | a | | b | | c | + | | | | | | + | | | d | | e | + + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn right,arrive | + | a,e | abc,be,be | depart,turn slight right,arrive | + + Scenario: Right Turn Assignment Three Turns + Given the node map + | | | g | | | + | a | | b | | c | + | | d | | f | | + | | | e | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + | bg | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp right,arrive | + | a,e | abc,be,be | depart,turn right,arrive | + | a,f | abc,bf,bf | depart,turn slight right,arrive | + + Scenario: Slight Turn involving Oneways + Given the node map + | | | a | | | + | | | | | | + | | | b | | e | + | d | | | | | + | | | c | | | + + And the ways + | nodes | highway | oneway | + | abc | primary | yes | + | dbe | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | d,e | dbe,dbe | depart,arrive | + | e,d | dbe,dbe | depart,arrive | + + @bug @pr2275 + Scenario: Slight Turn involving Oneways + Given the node map + | | | | a | | + | | | | | | + | | | | | | + | | | b | | e | + | d | | | | | + | | | c | | | + + And the ways + | nodes | highway | oneway | + | abc | primary | yes | + | dbe | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | d,e | dbe,dbe | depart,arrive | + | e,d | dbe,dbe | depart,arrive | + + + Scenario: Slight Turn involving Oneways - Name Change + Given the node map + | | | a | | | + | | | | | | + | | | b | | e | + | d | | | | | + | | | c | | | + + And the ways + | nodes | highway | oneway | + | abc | primary | yes | + | db | primary | no | + | be | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc | depart,arrive | + | d,e | db,be,be | depart,new name slight right,arrive | + | e,d | be,db,db | depart,new name slight left,arrive | + + Scenario: Right Turn Assignment Three Conflicting Turns with invalid - 1 + Given the node map + | | | g | | | + | a | | b | | c | + | | | | | | + | | d | e | f | | + + And the ways + | nodes | highway | oneway | + | abc | primary | no | + | db | primary | yes | + | eb | primary | no | + | fb | primary | no | + | bg | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,e | abc,eb,eb | depart,turn right,arrive | + | a,f | abc,fb,fb | depart,turn slight right,arrive | + + @bug @pr2275 + Scenario: Right Turn Assignment Three Conflicting Turns with invalid - 2 + Given the node map + | | | g | | | + | a | | b | | c | + | | | | | | + | | d | e | f | | + + And the ways + | nodes | highway | oneway | + | abc | primary | no | + | db | primary | no | + | eb | primary | yes | + | fb | primary | no | + | bg | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,db,db | depart,turn sharp right,arrive | + | a,f | abc,fb,fb | depart,turn right,arrive | + + Scenario: Right Turn Assignment Three Conflicting Turns with invalid - 3 + Given the node map + | | | g | | | + | a | | b | | c | + | | | | | | + | | d | e | f | | + + And the ways + | nodes | highway | oneway | + | abc | primary | no | + | db | primary | no | + | be | primary | no | + | fb | primary | yes | + | bg | primary | no | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,db,db | depart,turn sharp right,arrive | + | a,e | abc,be,be | depart,turn right,arrive | + + Scenario: Conflicting Turns with well distinguished turn + Given the node map + | a | | | b | | | c | + | | | | | | | | + | f | | | | | | d | + | | | | | | | e | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn slight right,arrive | + | a,e | abc,be,be | depart,turn right,arrive | + | a,f | abc,bf,bf | depart,turn sharp right,arrive | + + @bug @pr2275 + Scenario: Conflicting Turns with well distinguished turn (back) + Given the node map + | a | | | b | | | c | + | | | | | | | | + | d | | | | | | f | + | | e | | | | | | + + And the ways + | nodes | highway | + | abc | primary | + | bd | primary | + | be | primary | + | bf | primary | + + When I route I should get + | waypoints | route | turns | + | a,d | abc,bd,bd | depart,turn sharp right,arrive | + | a,e | abc,be,be | depart,turn right,arrive | + | a,f | abc,bf,bf | depart,turn slight right,arrive | + diff --git a/features/options/prepare/files.feature b/features/options/contract/files.feature similarity index 52% rename from features/options/prepare/files.feature rename to features/options/contract/files.feature index 6e82a6b1b..5e50e14f2 100644 --- a/features/options/prepare/files.feature +++ b/features/options/contract/files.feature @@ -1,5 +1,5 @@ @prepare @options @files -Feature: osrm-prepare command line options: files +Feature: osrm-contract command line options: files # expansions: # {extracted_base} => path to current extracted input file # {profile} => path to current profile script @@ -13,18 +13,13 @@ Feature: osrm-prepare command line options: files | ab | And the data has been extracted - Scenario: osrm-prepare - Passing base file - When I run "osrm-prepare {extracted_base}.osrm --profile {profile}" + Scenario: osrm-contract - Passing base file + When I run "osrm-contract {extracted_base}.osrm" Then stderr should be empty And it should exit with code 0 - Scenario: osrm-prepare - Order of options should not matter - When I run "osrm-prepare --profile {profile} {extracted_base}.osrm" - Then stderr should be empty - And it should exit with code 0 - - Scenario: osrm-prepare - Missing input file - When I run "osrm-prepare over-the-rainbow.osrm --profile {profile}" + Scenario: osrm-contract - Missing input file + When I run "osrm-contract over-the-rainbow.osrm" And stderr should contain "over-the-rainbow.osrm" And stderr should contain "not found" And it should exit with code 1 diff --git a/features/options/prepare/help.feature b/features/options/contract/help.feature similarity index 58% rename from features/options/prepare/help.feature rename to features/options/contract/help.feature index 34ec3d5f3..411bc55da 100644 --- a/features/options/prepare/help.feature +++ b/features/options/contract/help.feature @@ -1,56 +1,44 @@ @prepare @options @help -Feature: osrm-prepare command line options: help +Feature: osrm-contract command line options: help - Background: - Given the profile "testbot" - - Scenario: osrm-prepare - Help should be shown when no options are passed - When I run "osrm-prepare" + Scenario: osrm-contract - Help should be shown when no options are passed + When I run "osrm-contract" Then stderr should be empty - And stdout should contain "osrm-prepare [options]:" + And stdout should contain "osrm-contract [options]:" And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" - And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And stdout should contain 21 lines And it should exit with code 1 - Scenario: osrm-prepare - Help, short - When I run "osrm-prepare -h" + Scenario: osrm-contract - Help, short + When I run "osrm-contract -h" Then stderr should be empty - And stdout should contain "osrm-prepare [options]:" + And stdout should contain "osrm-contract [options]:" And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" - And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And stdout should contain 21 lines And it should exit with code 0 - Scenario: osrm-prepare - Help, long - When I run "osrm-prepare --help" + Scenario: osrm-contract - Help, long + When I run "osrm-contract --help" Then stderr should be empty - And stdout should contain "osrm-prepare [options]:" + And stdout should contain "osrm-contract [options]:" And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" - And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And stdout should contain 21 lines And it should exit with code 0 diff --git a/features/options/prepare/invalid.feature b/features/options/contract/invalid.feature similarity index 60% rename from features/options/prepare/invalid.feature rename to features/options/contract/invalid.feature index 7450390a8..38ee3ace9 100644 --- a/features/options/prepare/invalid.feature +++ b/features/options/contract/invalid.feature @@ -1,11 +1,11 @@ @prepare @options @invalid -Feature: osrm-prepare command line options: invalid options +Feature: osrm-contract command line options: invalid options Background: Given the profile "testbot" - Scenario: osrm-prepare - Non-existing option - When I run "osrm-prepare --fly-me-to-the-moon" + Scenario: osrm-contract - Non-existing option + When I run "osrm-contract --fly-me-to-the-moon" Then stdout should be empty And stderr should contain "option" And stderr should contain "fly-me-to-the-moon" diff --git a/features/options/prepare/version.feature b/features/options/contract/version.feature similarity index 74% rename from features/options/prepare/version.feature rename to features/options/contract/version.feature index 7a821c626..be99bbed1 100644 --- a/features/options/prepare/version.feature +++ b/features/options/contract/version.feature @@ -1,5 +1,5 @@ @prepare @options @version -Feature: osrm-prepare command line options: version +Feature: osrm-contract command line options: version # the regex will match these two formats: # v0.3.7.0 # this is the normal format when you build from a git clone # -128-NOTFOUND # if you build from a shallow clone (used on Travis) @@ -7,15 +7,15 @@ Feature: osrm-prepare command line options: version Background: Given the profile "testbot" - Scenario: osrm-prepare - Version, short - When I run "osrm-prepare --v" + Scenario: osrm-contract - Version, short + When I run "osrm-contract --v" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ And it should exit with code 0 - Scenario: osrm-prepare - Version, long - When I run "osrm-prepare --version" + Scenario: osrm-contract - Version, long + When I run "osrm-contract --version" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ diff --git a/features/options/extract/help.feature b/features/options/extract/help.feature index 722c4dc98..cdf1eb9a3 100644 --- a/features/options/extract/help.feature +++ b/features/options/extract/help.feature @@ -11,13 +11,11 @@ Feature: osrm-extract command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And stdout should contain 20 lines And it should exit with code 0 Scenario: osrm-extract - Help, short @@ -27,13 +25,11 @@ Feature: osrm-extract command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And stdout should contain 20 lines And it should exit with code 0 Scenario: osrm-extract - Help, long @@ -43,11 +39,9 @@ Feature: osrm-extract command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "Configuration:" And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And stdout should contain 20 lines And it should exit with code 0 diff --git a/features/options/routed/files.feature b/features/options/routed/files.feature index 15ce679eb..59ce7c213 100644 --- a/features/options/routed/files.feature +++ b/features/options/routed/files.feature @@ -4,8 +4,8 @@ Feature: osrm-routed command line options: files # For testing program options, the --trial option is used, which causes osrm-routed to quit # immediately after initialization. This makes testing easier and faster. # -# The {prepared_base} part of the options to osrm-routed will be expanded to the actual base path of -# the prepared input file. +# The {contracted_base} part of the options to osrm-routed will be expanded to the actual base path of +# the contracted input file. # TODO # Since we're not using osmr-datastore for all testing, osrm-routed is kept running. @@ -19,14 +19,14 @@ Feature: osrm-routed command line options: files And the ways | nodes | | ab | - And the data has been prepared + And the data has been contracted Scenario: osrm-routed - Passing base file - When I run "osrm-routed {prepared_base}.osrm --trial" + When I run "osrm-routed {contracted_base}.osrm --trial" Then stdout should contain /^\[info\] starting up engines/ And stdout should contain /\d{1,2}\.\d{1,2}\.\d{1,2}/ And stdout should contain /compiled at/ And stdout should contain /^\[info\] loaded plugin: viaroute/ And stdout should contain /^\[info\] trial run/ And stdout should contain /^\[info\] shutdown completed/ - And it should exit with code 0 \ No newline at end of file + And it should exit with code 0 diff --git a/features/options/routed/help.feature b/features/options/routed/help.feature index 34c6ee2a8..8f64bd967 100644 --- a/features/options/routed/help.feature +++ b/features/options/routed/help.feature @@ -11,16 +11,8 @@ Feature: osrm-routed command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "--trial" And stdout should contain "Configuration:" - And stdout should contain "--hsgrdata arg" - And stdout should contain "--nodesdata arg" - And stdout should contain "--edgesdata arg" - And stdout should contain "--ramindex arg" - And stdout should contain "--fileindex arg" - And stdout should contain "--namesdata arg" - And stdout should contain "--timestamp arg" And stdout should contain "--ip" And stdout should contain "--port" And stdout should contain "--threads" @@ -29,7 +21,6 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And stdout should contain 30 lines And it should exit with code 0 Scenario: osrm-routed - Help, short @@ -39,16 +30,8 @@ Feature: osrm-routed command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "--trial" And stdout should contain "Configuration:" - And stdout should contain "--hsgrdata arg" - And stdout should contain "--nodesdata arg" - And stdout should contain "--edgesdata arg" - And stdout should contain "--ramindex arg" - And stdout should contain "--fileindex arg" - And stdout should contain "--namesdata arg" - And stdout should contain "--timestamp arg" And stdout should contain "--ip" And stdout should contain "--port" And stdout should contain "--threads" @@ -57,7 +40,6 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And stdout should contain 30 lines And it should exit with code 0 Scenario: osrm-routed - Help, long @@ -67,16 +49,8 @@ Feature: osrm-routed command line options: help And stdout should contain "Options:" And stdout should contain "--version" And stdout should contain "--help" - And stdout should contain "--config" And stdout should contain "--trial" And stdout should contain "Configuration:" - And stdout should contain "--hsgrdata arg" - And stdout should contain "--nodesdata arg" - And stdout should contain "--edgesdata arg" - And stdout should contain "--ramindex arg" - And stdout should contain "--fileindex arg" - And stdout should contain "--namesdata arg" - And stdout should contain "--timestamp arg" And stdout should contain "--ip" And stdout should contain "--port" And stdout should contain "--threads" @@ -85,5 +59,4 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-table-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And stdout should contain 30 lines And it should exit with code 0 diff --git a/features/options/routed/invalid.feature b/features/options/routed/invalid.feature index a6d62d7e0..f3d90eafb 100644 --- a/features/options/routed/invalid.feature +++ b/features/options/routed/invalid.feature @@ -13,7 +13,6 @@ Feature: osrm-routed command line options: invalid options Scenario: osrm-routed - Missing file When I run "osrm-routed over-the-rainbow.osrm" - Then stdout should contain "over-the-rainbow.osrm" - And stderr should contain "exception" + Then stderr should contain "over-the-rainbow.osrm" And stderr should contain "not found" And it should exit with code 1 diff --git a/features/raster/weights.feature b/features/raster/weights.feature index 1d521d076..ae782a722 100644 --- a/features/raster/weights.feature +++ b/features/raster/weights.feature @@ -28,51 +28,52 @@ Feature: Raster - weights 0 0 0 250 0 0 0 0 """ + And the data has been saved to disk Scenario: Weighting not based on raster sources Given the profile "testbot" When I run "osrm-extract {osm_base}.osm -p {profile}" - And I run "osrm-prepare {osm_base}.osm" + And I run "osrm-contract {osm_base}.osm" And I route I should get - | from | to | route | speed | - | a | b | ab | 36 km/h | - | a | c | ab,bc | 36 km/h | - | b | c | bc | 36 km/h | - | a | d | ad | 36 km/h | - | d | c | dc | 36 km/h | + | from | to | route | speed | + | a | b | ab,ab | 36 km/h | + | a | c | ab,bc,bc | 36 km/h | + | b | c | bc,bc | 36 km/h | + | a | d | ad,ad | 36 km/h | + | d | c | dc,dc | 36 km/h | Scenario: Weighting based on raster sources Given the profile "rasterbot" When I run "osrm-extract {osm_base}.osm -p {profile}" Then stdout should contain "evaluating segment" - And I run "osrm-prepare {osm_base}.osm" + And I run "osrm-contract {osm_base}.osm" And I route I should get - | from | to | route | speed | - | a | b | ab | 8 km/h | - | a | c | ad,dc | 15 km/h | - | b | c | bc | 8 km/h | - | a | d | ad | 15 km/h | - | d | c | dc | 15 km/h | - | d | e | de | 10 km/h | - | e | b | eb | 10 km/h | - | d | f | df | 15 km/h | - | f | b | fb | 7 km/h | - | d | b | de,eb | 10 km/h | + | from | to | route | speed | + | a | b | ab,ab | 8 km/h | + | a | c | ad,dc,dc | 15 km/h | + | b | c | bc,bc | 8 km/h | + | a | d | ad,ad | 15 km/h | + | d | c | dc,dc | 15 km/h | + | d | e | de,de | 10 km/h | + | e | b | eb,eb | 10 km/h | + | d | f | df,df | 15 km/h | + | f | b | fb,fb | 7 km/h | + | d | b | de,eb,eb | 10 km/h | Scenario: Weighting based on raster sources - Given the profile "rasterbot-interp" + Given the profile "rasterbotinterp" When I run "osrm-extract {osm_base}.osm -p {profile}" Then stdout should contain "evaluating segment" - And I run "osrm-prepare {osm_base}.osm" + And I run "osrm-contract {osm_base}.osm" And I route I should get - | from | to | route | speed | - | a | b | ab | 8 km/h | - | a | c | ad,dc | 15 km/h | - | b | c | bc | 8 km/h | - | a | d | ad | 15 km/h | - | d | c | dc | 15 km/h | - | d | e | de | 10 km/h | - | e | b | eb | 10 km/h | - | d | f | df | 15 km/h | - | f | b | fb | 7 km/h | - | d | b | de,eb | 10 km/h | + | from | to | route | speed | + | a | b | ab,ab | 8 km/h | + | a | c | ad,dc,dc | 15 km/h | + | b | c | bc,bc | 8 km/h | + | a | d | ad,ad | 15 km/h | + | d | c | dc,dc | 15 km/h | + | d | e | de,de | 10 km/h | + | e | b | eb,eb | 10 km/h | + | d | f | df,df | 15 km/h | + | f | b | fb,fb | 7 km/h | + | d | b | de,eb,eb | 10 km/h | diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js new file mode 100644 index 000000000..db0071cb6 --- /dev/null +++ b/features/step_definitions/data.js @@ -0,0 +1,273 @@ +var util = require('util'); +var path = require('path'); +var fs = require('fs'); +var d3 = require('d3-queue'); +var OSM = require('../support/build_osm'); + +module.exports = function () { + this.Given(/^the profile "([^"]*)"$/, (profile, callback) => { + this.setProfile(profile, callback); + }); + + this.Given(/^the extract extra arguments "(.*?)"$/, (args, callback) => { + this.setExtractArgs(args); + callback(); + }); + + this.Given(/^the contract extra arguments "(.*?)"$/, (args, callback) => { + this.setContractArgs(args); + callback(); + }); + + this.Given(/^a grid size of (\d+) meters$/, (meters, callback) => { + this.setGridSize(meters); + callback(); + }); + + this.Given(/^the origin ([-+]?[0-9]*\.?[0-9]+),([-+]?[0-9]*\.?[0-9]+)$/, (lat, lon, callback) => { + this.setOrigin([parseFloat(lon), parseFloat(lat)]); + callback(); + }); + + this.Given(/^the shortcuts$/, (table, callback) => { + var q = d3.queue(); + + var addShortcut = (row, cb) => { + this.shortcutsHash[row.key] = row.value; + cb(); + }; + + table.hashes().forEach((row) => { + q.defer(addShortcut, row); + }); + + q.awaitAll(callback); + }); + + this.Given(/^the node map$/, (table, callback) => { + var q = d3.queue(); + + var addNode = (name, ri, ci, cb) => { + if (name) { + if (name.length !== 1) throw new Error(util.format('*** node invalid name %s, must be single characters', name)); + if (!name.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name %s, must me alphanumeric', name)); + + var lonLat; + if (name.match(/[a-z]/)) { + if (this.nameNodeHash[name]) throw new Error(util.format('*** duplicate node %s', name)); + lonLat = this.tableCoordToLonLat(ci, ri); + this.addOSMNode(name, lonLat[0], lonLat[1], null); + } else { + if (this.locationHash[name]) throw new Error(util.format('*** duplicate node %s'), name); + lonLat = this.tableCoordToLonLat(ci, ri); + this.addLocation(name, lonLat[0], lonLat[1], null); + } + + cb(); + } + else cb(); + }; + + table.raw().forEach((row, ri) => { + row.forEach((name, ci) => { + q.defer(addNode, name, ri, ci); + }); + }); + + q.awaitAll(callback); + }); + + this.Given(/^the node locations$/, (table, callback) => { + var q = d3.queue(); + + var addNodeLocations = (row, cb) => { + var name = row.node; + if (this.findNodeByName(name)) throw new Error(util.format('*** duplicate node %s'), name); + + if (name.match(/[a-z]/)) { + var id = row.id && parseInt(row.id); + this.addOSMNode(name, row.lon, row.lat, id); + } else { + this.addLocation(name, row.lon, row.lat); + } + + cb(); + }; + + table.hashes().forEach((row) => q.defer(addNodeLocations, row)); + + q.awaitAll(callback); + }); + + this.Given(/^the nodes$/, (table, callback) => { + var q = d3.queue(); + + var addNode = (row, cb) => { + var name = row.node, + node = this.findNodeByName(name); + delete row.node; + if (!node) throw new Error(util.format('*** unknown node %s'), name); + for (var key in row) { + node.addTag(key, row[key]); + } + cb(); + }; + + table.hashes().forEach((row) => q.defer(addNode, row)); + + q.awaitAll(callback); + }); + + this.Given(/^the ways$/, (table, callback) => { + if (this.osm_str) throw new Error('*** Map data already defined - did you pass an input file in this scenario?'); + + var q = d3.queue(); + + var addWay = (row, cb) => { + var way = new OSM.Way(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); + + var nodes = row.nodes; + if (this.nameWayHash.nodes) throw new Error(util.format('*** duplicate way %s', nodes)); + + for (var i=0; i q.defer(addWay, row)); + + q.awaitAll(callback); + }); + + this.Given(/^the relations$/, (table, callback) => { + if (this.osm_str) throw new Error('*** Map data already defined - did you pass an input file in this scenario?'); + + var q = d3.queue(); + + var addRelation = (row, cb) => { + var relation = new OSM.Relation(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); + + for (var key in row) { + var isNode = key.match(/^node:(.*)/), + isWay = key.match(/^way:(.*)/), + isColonSeparated = key.match(/^(.*):(.*)/); + if (isNode) { + row[key].split(',').map(function(v) { return v.trim(); }).forEach((nodeName) => { + if (nodeName.length !== 1) throw new Error(util.format('*** invalid relation node member "%s"'), nodeName); + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown relation node member "%s"'), nodeName); + relation.addMember('node', node.id, isNode[1]); + }); + } else if (isWay) { + row[key].split(',').map(function(v) { return v.trim(); }).forEach((wayName) => { + var way = this.findWayByName(wayName); + if (!way) throw new Error(util.format('*** unknown relation way member "%s"'), wayName); + relation.addMember('way', way.id, isWay[1]); + }); + } else if (isColonSeparated && isColonSeparated[1] !== 'restriction') { + throw new Error(util.format('*** unknown relation member type "%s:%s", must be either "node" or "way"'), isColonSeparated[1], isColonSeparated[2]); + } else { + relation.addTag(key, row[key]); + } + } + relation.uid = this.OSM_UID; + + this.OSMDB.addRelation(relation); + + cb(); + }; + + table.hashes().forEach((row) => q.defer(addRelation, row)); + + q.awaitAll(callback); + }); + + this.Given(/^the input file ([^"]*)$/, (file, callback) => { + if (path.extname(file) !== '.osm') throw new Error('*** Input file must be in .osm format'); + fs.readFile(file, 'utf8', (err, data) => { + if (!err) this.osm_str = data.toString(); + callback(err); + }); + }); + + this.Given(/^the raster source$/, (data, callback) => { + fs.writeFile(path.resolve(this.TEST_FOLDER, 'rastersource.asc'), data, callback); + }); + + this.Given(/^the speed file$/, (data, callback) => { + fs.writeFile(path.resolve(this.TEST_FOLDER, 'speeds.csv'), data, callback); + }); + + this.Given(/^the data has been saved to disk$/, (callback) => { + try { + this.reprocess(callback); + } catch(e) { + this.processError = e; + callback(e); + } + }); + + this.Given(/^the data has been extracted$/, (callback) => { + this.writeAndExtract((err) => { + if (err) this.processError = err; + callback(); + }); + }); + + this.Given(/^the data has been contracted$/, (callback) => { + this.reprocess((err) => { + if (err) this.processError = err; + callback(); + }); + }); + + this.Given(/^osrm\-routed is stopped$/, (callback) => { + this.OSRMLoader.shutdown((err) => { + if (err) this.processError = err; + callback(); + }); + }); + + this.Given(/^data is loaded directly/, () => { + this.loadMethod = 'directly'; + }); + + this.Given(/^data is loaded with datastore$/, () => { + this.loadMethod = 'datastore'; + }); + + this.Given(/^the HTTP method "([^"]*)"$/, (method, callback) => { + this.httpMethod = method; + callback(); + }); +}; diff --git a/features/step_definitions/data.rb b/features/step_definitions/data.rb deleted file mode 100644 index 3e4ccc91c..000000000 --- a/features/step_definitions/data.rb +++ /dev/null @@ -1,192 +0,0 @@ -Given /^the profile "([^"]*)"$/ do |profile| - set_profile profile -end - -Given(/^the import format "(.*?)"$/) do |format| - set_input_format format -end - -Given /^the extract extra arguments "(.*?)"$/ do |args| - set_extract_args args -end - -Given /^a grid size of (\d+) meters$/ do |meters| - set_grid_size meters -end - -Given /^the origin ([-+]?[0-9]*\.?[0-9]+),([-+]?[0-9]*\.?[0-9]+)$/ do |lat,lon| - set_origin [lon.to_f,lat.to_f] -end - -Given /^the shortcuts$/ do |table| - table.hashes.each do |row| - shortcuts_hash[ row['key'] ] = row['value'] - end -end - -Given /^the node map$/ do |table| - table.raw.each_with_index do |row,ri| - row.each_with_index do |name,ci| - unless name.empty? - raise "*** node invalid name '#{name}', must be single characters" unless name.size == 1 - raise "*** invalid node name '#{name}', must me alphanumeric" unless name.match /[a-z0-9]/ - if name.match /[a-z]/ - raise "*** duplicate node '#{name}'" if name_node_hash[name] - add_osm_node name, *table_coord_to_lonlat(ci,ri), nil - else - raise "*** duplicate node '#{name}'" if location_hash[name] - add_location name, *table_coord_to_lonlat(ci,ri) - end - end - end - end -end - -Given /^the node locations$/ do |table| - table.hashes.each do |row| - name = row['node'] - raise "*** duplicate node '#{name}'" if find_node_by_name name - if name.match /[a-z]/ - id = row['id'] - id = id.to_i if id - add_osm_node name, row['lon'].to_f, row['lat'].to_f, id - else - add_location name, row['lon'].to_f, row['lat'].to_f - end - end -end - -Given /^the nodes$/ do |table| - table.hashes.each do |row| - name = row.delete 'node' - node = find_node_by_name(name) - raise "*** unknown node '#{c}'" unless node - node << row - end -end - -Given /^the ways$/ do |table| - raise "*** Map data already defined - did you pass an input file in this scenaria?" if @osm_str - table.hashes.each do |row| - way = OSM::Way.new make_osm_id, OSM_USER, OSM_TIMESTAMP - way.uid = OSM_UID - - nodes = row.delete 'nodes' - raise "*** duplicate way '#{nodes}'" if name_way_hash[nodes] - nodes.each_char do |c| - raise "*** ways can only use names a-z, '#{name}'" unless c.match /[a-z]/ - node = find_node_by_name(c) - raise "*** unknown node '#{c}'" unless node - way << node - end - - defaults = { 'highway' => 'primary' } - tags = defaults.merge(row) - - if row['highway'] == '(nil)' - tags.delete 'highway' - end - - if row['name'] == nil - tags['name'] = nodes - elsif (row['name'] == '""') || (row['name'] == "''") - tags['name'] = '' - elsif row['name'] == '' || row['name'] == '(nil)' - tags.delete 'name' - else - tags['name'] = row['name'] - end - - way << tags - osm_db << way - name_way_hash[nodes] = way - end -end - -Given /^the relations$/ do |table| - raise "*** Map data already defined - did you pass an input file in this scenaria?" if @osm_str - table.hashes.each do |row| - relation = OSM::Relation.new make_osm_id, OSM_USER, OSM_TIMESTAMP - row.each_pair do |key,value| - if key =~ /^node:(.*)/ - value.split(',').map { |v| v.strip }.each do |node_name| - raise "***invalid relation node member '#{node_name}', must be single character" unless node_name.size == 1 - node = find_node_by_name(node_name) - raise "*** unknown relation node member '#{node_name}'" unless node - relation << OSM::Member.new( 'node', node.id, $1 ) - end - elsif key =~ /^way:(.*)/ - value.split(',').map { |v| v.strip }.each do |way_name| - way = find_way_by_name(way_name) - raise "*** unknown relation way member '#{way_name}'" unless way - relation << OSM::Member.new( 'way', way.id, $1 ) - end - elsif key =~ /^(.*):(.*)/ && "#{$1}" != 'restriction' - raise "*** unknown relation member type '#{$1}:#{$2}', must be either 'node' or 'way'" - else - relation << { key => value } - end - end - relation.uid = OSM_UID - osm_db << relation - end -end - -Given /^the defaults$/ do -end - -Given /^the input file ([^"]*)$/ do |file| - raise "*** Input file must in .osm format" unless File.extname(file)=='.osm' - @osm_str = File.read file -end - -Given /^the raster source$/ do |data| - Dir.chdir TEST_FOLDER do - File.open("rastersource.asc", "w") {|f| f.write(data)} - end -end - -Given /^the data has been saved to disk$/ do - begin - write_input_data - rescue OSRMError => e - @process_error = e - end -end - -Given /^the data has been extracted$/ do - begin - write_input_data - extract_data unless extracted? - rescue OSRMError => e - @process_error = e - end -end - -Given /^the data has been prepared$/ do - begin - reprocess - rescue OSRMError => e - @process_error = e - end -end - -Given /^osrm\-routed is stopped$/ do - begin - OSRMLoader.shutdown - rescue OSRMError => e - @process_error = e - end -end - -Given /^data is loaded directly/ do - @load_method = 'directly' -end - -Given /^data is loaded with datastore$/ do - @load_method = 'datastore' -end - -Given /^the HTTP method "([^"]*)"$/ do |method| - @http_method = method -end diff --git a/features/step_definitions/distance_matrix.js b/features/step_definitions/distance_matrix.js new file mode 100644 index 000000000..735847d56 --- /dev/null +++ b/features/step_definitions/distance_matrix.js @@ -0,0 +1,81 @@ +var util = require('util'); + +module.exports = function () { + this.When(/^I request a travel time matrix I should get$/, (table, callback) => { + var NO_ROUTE = 2147483647; // MAX_INT + + var tableRows = table.raw(); + + if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty'); + + var waypoints = [], + columnHeaders = tableRows[0].slice(1), + rowHeaders = tableRows.map((h) => h[0]).slice(1), + symmetric = columnHeaders.every((ele, i) => ele === rowHeaders[i]); + + if (symmetric) { + columnHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"'), nodeName); + waypoints.push({ coord: node, type: 'loc' }); + }); + } else { + columnHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"'), nodeName); + waypoints.push({ coord: node, type: 'dst' }); + }); + rowHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"'), nodeName); + waypoints.push({ coord: node, type: 'src' }); + }); + } + + var actual = []; + actual.push(table.headers); + + this.reprocessAndLoadData(() => { + // compute matrix + var params = this.queryParams; + + this.requestTable(waypoints, params, (err, response) => { + if (err) return callback(err); + if (!response.body.length) return callback(new Error('Invalid response body')); + + var json = JSON.parse(response.body); + + var result = json['durations'].map(row => { + var hashes = {}; + row.forEach((v, i) => { hashes[tableRows[0][i+1]] = isNaN(parseInt(v)) ? '' : v; }); + return hashes; + }); + + var testRow = (row, ri, cb) => { + var ok = true; + + for (var k in result[ri]) { + if (this.FuzzyMatch.match(result[ri][k], row[k])) { + result[ri][k] = row[k]; + } else if (row[k] === '' && result[ri][k] === NO_ROUTE) { + result[ri][k] = ''; + } else { + result[ri][k] = result[ri][k].toString(); + ok = false; + } + } + + if (!ok) { + var failed = { attempt: 'distance_matrix', query: this.query, response: response }; + this.logFail(row, result[ri], [failed]); + } + + result[ri][''] = row['']; + cb(null, result[ri]); + }; + + this.processRowsAndDiff(table, testRow, callback); + }); + }); + }); +}; diff --git a/features/step_definitions/distance_matrix.rb b/features/step_definitions/distance_matrix.rb deleted file mode 100644 index 2143d375d..000000000 --- a/features/step_definitions/distance_matrix.rb +++ /dev/null @@ -1,66 +0,0 @@ -When /^I request a travel time matrix I should get$/ do |table| - no_route = 2147483647 # MAX_INT - - raise "*** Top-left cell of matrix table must be empty" unless table.headers[0]=="" - - waypoints = [] - column_headers = table.headers[1..-1] - row_headers = table.rows.map { |h| h.first } - symmetric = Set.new(column_headers) == Set.new(row_headers) - if symmetric then - column_headers.each do |node_name| - node = find_node_by_name(node_name) - raise "*** unknown node '#{node_name}" unless node - waypoints << {:coord => node, :type => "loc"} - end - else - column_headers.each do |node_name| - node = find_node_by_name(node_name) - raise "*** unknown node '#{node_name}" unless node - waypoints << {:coord => node, :type => "dst"} - end - row_headers.each do |node_name| - node = find_node_by_name(node_name) - raise "*** unknown node '#{node_name}" unless node - waypoints << {:coord => node, :type => "src"} - end - end - - reprocess - actual = [] - actual << table.headers - OSRMLoader.load(self,"#{prepared_file}.osrm") do - - # compute matrix - params = @query_params - response = request_table waypoints, params - if response.body.empty? == false - json_result = JSON.parse response.body - result = json_result["distance_table"] - end - - - # compare actual and expected result, one row at a time - table.rows.each_with_index do |row,ri| - # fuzzy match - ok = true - 0.upto(result[ri].size-1) do |i| - if FuzzyMatch.match result[ri][i], row[i+1] - result[ri][i] = row[i+1] - elsif row[i+1]=="" and result[ri][i]==no_route - result[ri][i] = "" - else - result[ri][i] = result[ri][i].to_s - ok = false - end - end - - # add row header - r = [row[0],result[ri]].flatten - - # store row for comparison - actual << r - end - end - table.diff! actual -end diff --git a/features/step_definitions/hooks.js b/features/step_definitions/hooks.js new file mode 100644 index 000000000..07b47195a --- /dev/null +++ b/features/step_definitions/hooks.js @@ -0,0 +1,30 @@ +var util = require('util'); + +module.exports = function () { + this.Before((scenario, callback) => { + this.scenarioTitle = scenario.getName(); + + this.loadMethod = this.DEFAULT_LOAD_METHOD; + this.queryParams = {}; + var d = new Date(); + this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()); + this.resetData(); + this.hasLoggedPreprocessInfo = false; + this.hasLoggedScenarioInfo = false; + this.setGridSize(this.DEFAULT_GRID_SIZE); + this.setOrigin(this.DEFAULT_ORIGIN); + callback(); + }); + + this.Before('@ignore-platform-windows', () => { + this.skipThisScenario(); + }); + + this.Before('@ignore-platform-unix', () => { + this.skipThisScenario(); + }); + + this.Before('@ignore-platform-mac', () => { + this.skipThisScenario(); + }); +}; diff --git a/features/step_definitions/matching.js b/features/step_definitions/matching.js new file mode 100644 index 000000000..1b770d76e --- /dev/null +++ b/features/step_definitions/matching.js @@ -0,0 +1,174 @@ +var util = require('util'); +var d3 = require('d3-queue'); + +module.exports = function () { + this.When(/^I match I should get$/, (table, callback) => { + var got; + + this.reprocessAndLoadData(() => { + var testRow = (row, ri, cb) => { + var afterRequest = (err, res) => { + if (err) return cb(err); + var json; + + var headers = new Set(table.raw()[0]); + + if (res.body.length) { + json = JSON.parse(res.body); + } + + if (headers.has('status')) { + got.status = json.status.toString(); + } + + if (headers.has('message')) { + got.message = json.status_message; + } + + if (headers.has('#')) { + // comment column + got['#'] = row['#']; + } + + var subMatchings = [], + turns = '', + route = '', + duration = ''; + + if (res.statusCode === 200) { + if (headers.has('matchings')) { + subMatchings = json.matchings.filter(m => !!m).map(sub => sub.matched_points); + } + + if (headers.has('turns')) { + if (json.matchings.length != 1) throw new Error('*** Checking turns only supported for matchings with one subtrace'); + turns = this.turnList(json.matchings[0].instructions); + } + + if (headers.has('route')) { + if (json.matchings.length != 1) throw new Error('*** Checking route only supported for matchings with one subtrace'); + route = this.wayList(json.matchings[0]); + } + + if (headers.has('duration')) { + if (json.matchings.length != 1) throw new Error('*** Checking duration only supported for matchings with one subtrace'); + duration = json.matchings[0].duration; + } + } + + if (headers.has('turns')) { + got.turns = turns; + } + + if (headers.has('route')) { + got.route = route; + } + + if (headers.has('duration')) { + got.duration = duration.toString(); + } + + var ok = true; + var encodedResult = '', + extendedTarget = ''; + + var q = d3.queue(); + + var testSubMatching = (sub, si, scb) => { + if (si >= subMatchings.length) { + ok = false; + q.abort(); + scb(); + } else { + var sq = d3.queue(); + + var testSubNode = (ni, ncb) => { + var node = this.findNodeByName(sub[ni]), + outNode = subMatchings[si][ni]; + + if (this.FuzzyMatch.matchLocation(outNode, node)) { + encodedResult += sub[ni]; + extendedTarget += sub[ni]; + } else { + encodedResult += util.format('? [%s,%s]', outNode[0], outNode[1]); + extendedTarget += util.format('%s [%d,%d]', node.lat, node.lon); + ok = false; + } + ncb(); + }; + + for (var i=0; i { + q.defer(testSubMatching, sub, si); + }); + + q.awaitAll(() => { + if (ok) { + if (headers.has('matchings')) { + got.matchings = row.matchings; + } + + if (headers.has('timestamps')) { + got.timestamps = row.timestamps; + } + } else { + got.matchings = encodedResult; + row.matchings = extendedTarget; + this.logFail(row, got, { matching: { query: this.query, response: res } }); + } + + cb(null, got); + }); + }; + + if (row.request) { + got = {}; + got.request = row.request; + this.requestUrl(row.request, afterRequest); + } else { + var params = this.queryParams; + got = {}; + for (var k in row) { + var match = k.match(/param:(.*)/); + if (match) { + if (row[k] === '(nil)') { + params[match[1]] = null; + } else if (row[k]) { + params[match[1]] = [row[k]]; + } + got[k] = row[k]; + } + } + + var trace = [], + timestamps = []; + + if (row.trace) { + for (var i=0; i !!s).map(t => parseInt(t, 10)); + } + got.trace = row.trace; + this.requestMatching(trace, timestamps, params, afterRequest); + } else { + throw new Error('*** no trace'); + } + } + }; + + this.processRowsAndDiff(table, testRow, callback); + }); + }); +}; diff --git a/features/step_definitions/matching.rb b/features/step_definitions/matching.rb deleted file mode 100644 index c490b4652..000000000 --- a/features/step_definitions/matching.rb +++ /dev/null @@ -1,124 +0,0 @@ -When /^I match I should get$/ do |table| - reprocess - actual = [] - OSRMLoader.load(self,"#{prepared_file}.osrm") do - table.hashes.each_with_index do |row,ri| - if row['request'] - got = {'request' => row['request'] } - response = request_url row['request'] - else - params = @query_params - got = {} - row.each_pair do |k,v| - if k =~ /param:(.*)/ - if v=='(nil)' - params[$1]=nil - elsif v!=nil - params[$1]=[v] - end - got[k]=v - end - end - trace = [] - timestamps = [] - if row['trace'] - row['trace'].each_char do |n| - node = find_node_by_name(n.strip) - raise "*** unknown waypoint node '#{n.strip}" unless node - trace << node - end - if row['timestamps'] - timestamps = row['timestamps'].split(" ").compact.map { |t| t.to_i} - end - got = got.merge({'trace' => row['trace'] }) - response = request_matching trace, timestamps, params - else - raise "*** no trace" - end - end - - if response.body.empty? == false - json = JSON.parse response.body - end - - if table.headers.include? 'status' - got['status'] = json['status'].to_s - end - if table.headers.include? 'message' - got['message'] = json['status_message'] - end - if table.headers.include? '#' # comment column - got['#'] = row['#'] # copy value so it always match - end - - sub_matchings = [] - turns = '' - route = '' - duration = '' - if response.code == "200" - if table.headers.include? 'matchings' - sub_matchings = json['matchings'].compact.map { |sub| sub['matched_points']} - end - if table.headers.include? 'turns' - raise "*** Checking turns only support for matchings with one subtrace" unless json['matchings'].size == 1 - turns = turn_list json['matchings'][0]['instructions'] - end - if table.headers.include? 'route' - raise "*** Checking route only support for matchings with one subtrace" unless json['matchings'].size == 1 - route = way_list json['matchings'][0]['instructions'] - if table.headers.include? 'duration' - raise "*** Checking duration only support for matchings with one subtrace" unless json['matchings'].size == 1 - duration = json['matchings'][0]['route_summary']['total_time'] - end - end - end - - if table.headers.include? 'turns' - got['turns'] = turns - end - if table.headers.include? 'route' - got['route'] = route - end - if table.headers.include? 'duration' - got['duration'] = duration.to_s - end - - ok = true - encoded_result = "" - extended_target = "" - row['matchings'].split(',').each_with_index do |sub, sub_idx| - if sub_idx >= sub_matchings.length - ok = false - break - end - sub.length.times do |node_idx| - node = find_node_by_name(sub[node_idx]) - out_node = sub_matchings[sub_idx][node_idx] - if FuzzyMatch.match_location out_node, node - encoded_result += sub[node_idx] - extended_target += sub[node_idx] - else - encoded_result += "? [#{out_node[0]},#{out_node[1]}]" - extended_target += "#{sub[node_idx]} [#{node.lat},#{node.lon}]" - ok = false - end - end - end - if ok - if table.headers.include? 'matchings' - got['matchings'] = row['matchings'] - end - if table.headers.include? 'timestamps' - got['timestamps'] = row['timestamps'] - end - else - got['matchings'] = encoded_result - row['matchings'] = extended_target - log_fail row,got, { 'matching' => {:query => @query, :response => response} } - end - - actual << got - end - end - table.diff! actual -end diff --git a/features/step_definitions/nearest.js b/features/step_definitions/nearest.js new file mode 100644 index 000000000..300eefed4 --- /dev/null +++ b/features/step_definitions/nearest.js @@ -0,0 +1,53 @@ +var util = require('util'); + +module.exports = function () { + this.When(/^I request nearest I should get$/, (table, callback) => { + this.reprocessAndLoadData(() => { + var testRow = (row, ri, cb) => { + var inNode = this.findNodeByName(row.in); + if (!inNode) throw new Error(util.format('*** unknown in-node "%s"'), row.in); + + var outNode = this.findNodeByName(row.out); + if (!outNode) throw new Error(util.format('*** unknown out-node "%s"'), row.out); + + this.requestNearest(inNode, this.queryParams, (err, response) => { + if (err) return cb(err); + var coord; + + if (response.statusCode === 200 && response.body.length) { + var json = JSON.parse(response.body); + + coord = json.waypoints[0].location; + + var got = { in: row.in, out: row.out }; + + var ok = true; + + Object.keys(row).forEach((key) => { + if (key === 'out') { + if (this.FuzzyMatch.matchLocation(coord, outNode)) { + got[key] = row[key]; + } else { + row[key] = util.format('%s [%d,%d]', row[key], outNode.lat, outNode.lon); + ok = false; + } + } + }); + + if (!ok) { + var failed = { attempt: 'nearest', query: this.query, response: response }; + this.logFail(row, got, [failed]); + } + + cb(null, got); + } + else { + cb(); + } + }); + }; + + this.processRowsAndDiff(table, testRow, callback); + }); + }); +}; diff --git a/features/step_definitions/nearest.rb b/features/step_definitions/nearest.rb deleted file mode 100644 index b0b5f94b8..000000000 --- a/features/step_definitions/nearest.rb +++ /dev/null @@ -1,51 +0,0 @@ -When /^I request nearest I should get$/ do |table| - reprocess - actual = [] - OSRMLoader.load(self,"#{prepared_file}.osrm") do - table.hashes.each_with_index do |row,ri| - in_node = find_node_by_name row['in'] - raise "*** unknown in-node '#{row['in']}" unless in_node - - out_node = find_node_by_name row['out'] - raise "*** unknown out-node '#{row['out']}" unless out_node - - response = request_nearest in_node, @query_params - if response.code == "200" && response.body.empty? == false - json = JSON.parse response.body - if json['status'] == 200 - coord = json['mapped_coordinate'] - end - end - - got = {'in' => row['in'], 'out' => coord } - - ok = true - row.keys.each do |key| - if key=='out' - if FuzzyMatch.match_location coord, out_node - got[key] = row[key] - else - row[key] = "#{row[key]} [#{out_node.lat},#{out_node.lon}]" - ok = false - end - end - end - - unless ok - failed = { :attempt => 'nearest', :query => @query, :response => response } - log_fail row,got,[failed] - end - - actual << got - end - end - table.diff! actual -end - -When /^I request nearest (\d+) times I should get$/ do |n,table| - ok = true - n.to_i.times do - ok = false unless step "I request nearest I should get", table - end - ok -end diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js new file mode 100644 index 000000000..27d6fd3ab --- /dev/null +++ b/features/step_definitions/options.js @@ -0,0 +1,69 @@ +var assert = require('assert'); + +module.exports = function () { + this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.SHUTDOWN_TIMEOUT }, (options, callback) => { + this.runBin('osrm-routed', options, () => { + callback(); + }); + }); + + this.When(/^I run "osrm\-extract\s?(.*?)"$/, (options, callback) => { + this.runBin('osrm-extract', options, () => { + callback(); + }); + }); + + this.When(/^I run "osrm\-contract\s?(.*?)"$/, (options, callback) => { + this.runBin('osrm-contract', options, () => { + callback(); + }); + }); + + this.When(/^I run "osrm\-datastore\s?(.*?)"$/, (options, callback) => { + this.runBin('osrm-datastore', options, () => { + callback(); + }); + }); + + this.Then(/^it should exit with code (\d+)$/, (code) => { + assert.equal(this.exitCode, parseInt(code)); + }); + + this.Then(/^stdout should contain "(.*?)"$/, (str) => { + assert.ok(this.stdout.indexOf(str) > -1); + }); + + this.Then(/^stderr should contain "(.*?)"$/, (str) => { + assert.ok(this.stderr.indexOf(str) > -1); + }); + + this.Then(/^stdout should contain \/(.*)\/$/, (regexStr) => { + var re = new RegExp(regexStr); + assert.ok(this.stdout.match(re)); + }); + + this.Then(/^stderr should contain \/(.*)\/$/, (regexStr) => { + var re = new RegExp(regexStr); + assert.ok(this.stdout.match(re)); + }); + + this.Then(/^stdout should be empty$/, () => { + assert.equal(this.stdout.trim(), ''); + }); + + this.Then(/^stderr should be empty$/, () => { + assert.equal(this.stderr.trim(), ''); + }); + + this.Then(/^stdout should contain (\d+) lines?$/, (lines) => { + assert.equal(this.stdout.split('\n').length - 1, parseInt(lines)); + }); + + this.Given(/^the query options$/, (table, callback) => { + table.raw().forEach(tuple => { + this.queryParams[tuple[0]] = tuple[1]; + }); + + callback(); + }); +}; diff --git a/features/step_definitions/options.rb b/features/step_definitions/options.rb deleted file mode 100644 index 62329d514..000000000 --- a/features/step_definitions/options.rb +++ /dev/null @@ -1,57 +0,0 @@ -When(/^I run "osrm\-routed\s?(.*?)"$/) do |options| - begin - Timeout.timeout(SHUTDOWN_TIMEOUT) { run_bin 'osrm-routed', options } - rescue Timeout::Error - raise "*** osrm-routed didn't quit. Maybe the --trial option wasn't used?" - end -end - -When(/^I run "osrm\-extract\s?(.*?)"$/) do |options| - run_bin 'osrm-extract', options -end - -When(/^I run "osrm\-prepare\s?(.*?)"$/) do |options| - run_bin 'osrm-prepare', options -end - -When(/^I run "osrm\-datastore\s?(.*?)"$/) do |options| - run_bin 'osrm-datastore', options -end - -Then /^it should exit with code (\d+)$/ do |code| - expect(@exit_code).to eq( code.to_i ) -end - -Then /^stdout should contain "(.*?)"$/ do |str| - expect(@stdout).to include(str) -end - -Then /^stderr should contain "(.*?)"$/ do |str| - expect(@stderr).to include(str) -end - -Then(/^stdout should contain \/(.*)\/$/) do |regex_str| - regex = Regexp.new regex_str - expect(@stdout).to match( regex ) -end - -Then(/^stderr should contain \/(.*)\/$/) do |regex_str| - regex = Regexp.new regex_str - expect(@stderr).to match( regex ) -end - -Then /^stdout should be empty$/ do - expect(@stdout).to eq("") -end - -Then /^stderr should be empty$/ do - expect(@stderr).to eq("") -end - -Then /^stdout should contain (\d+) lines?$/ do |lines| - expect(@stdout.lines.count).to eq( lines.to_i ) -end - -Given (/^the query options$/) do |table| - table.rows_hash.each { |k,v| @query_params << [k, v] } -end diff --git a/features/step_definitions/requests.js b/features/step_definitions/requests.js new file mode 100644 index 000000000..99adee63e --- /dev/null +++ b/features/step_definitions/requests.js @@ -0,0 +1,57 @@ +var assert = require('assert'); + +module.exports = function () { + this.When(/^I request \/(.*)$/, (path, callback) => { + this.reprocessAndLoadData(() => { + this.requestPath(path, {}, (err, res, body) => { + this.response = res; + callback(err, res, body); + }); + }); + }); + + this.Then(/^I should get a response/, () => { + this.ShouldGetAResponse(); + }); + + this.Then(/^response should be valid JSON$/, (callback) => { + this.ShouldBeValidJSON(callback); + }); + + this.Then(/^response should be well-formed$/, () => { + this.ShouldBeWellFormed(); + }); + + this.Then(/^status code should be (\d+)$/, (code, callback) => { + try { + this.json = JSON.parse(this.response.body); + } catch(e) { + return callback(e); + } + assert.equal(this.json.status, parseInt(code)); + callback(); + }); + + this.Then(/^status message should be "(.*?)"$/, (message, callback) => { + try { + this.json = JSON.parse(this.response.body); + } catch(e) { + return callback(e); + } + assert(this.json.status_message, message); + callback(); + }); + + this.Then(/^response should be a well-formed route$/, () => { + this.ShouldBeWellFormed(); + assert.equal(this.json.code, 'ok'); + assert.ok(Array.isArray(this.json.routes)); + assert.ok(Array.isArray(this.json.waypoints)); + }); + + this.Then(/^"([^"]*)" should return code (\d+)$/, (binary, code) => { + assert.ok(this.processError instanceof this.OSRMError); + assert.equal(this.processError.process, binary); + assert.equal(parseInt(this.processError.code), parseInt(code)); + }); +}; diff --git a/features/step_definitions/requests.rb b/features/step_definitions/requests.rb deleted file mode 100644 index 1c012d7d7..000000000 --- a/features/step_definitions/requests.rb +++ /dev/null @@ -1,46 +0,0 @@ -When /^I request \/(.*)$/ do |path| - reprocess - OSRMLoader.load(self,"#{prepared_file}.osrm") do - @response = request_path path, [] - end -end - -Then /^I should get a response/ do - expect(@response.code).to eq("200") - expect(@response.body).not_to eq(nil) - expect(@response.body).not_to eq('') -end - -Then /^response should be valid JSON$/ do - @json = JSON.parse @response.body -end - -Then /^response should be well-formed$/ do - expect(@json['status'].class).to eq(Fixnum) -end - -Then /^status code should be (\d+)$/ do |code| - @json = JSON.parse @response.body - expect(@json['status']).to eq(code.to_i) -end - -Then /^status message should be "(.*?)"$/ do |message| - @json = JSON.parse @response.body - expect(@json['status_message']).to eq(message) -end - -Then /^response should be a well-formed route$/ do - step "response should be well-formed" - expect(@json['status_message'].class).to eq(String) - expect(@json['route_summary'].class).to eq(Hash) - expect(@json['route_geometry'].class).to eq(String) - expect(@json['route_instructions'].class).to eq(Array) - expect(@json['via_points'].class).to eq(Array) - expect(@json['via_indices'].class).to eq(Array) -end - -Then /^"([^"]*)" should return code (\d+)$/ do |binary, code| - expect(@process_error.is_a?(OSRMError)).to eq(true) - expect(@process_error.process).to eq(binary) - expect(@process_error.code.to_i).to eq(code.to_i) -end diff --git a/features/step_definitions/routability.js b/features/step_definitions/routability.js new file mode 100644 index 000000000..1ef8643ed --- /dev/null +++ b/features/step_definitions/routability.js @@ -0,0 +1,120 @@ +var util = require('util'); +var d3 = require('d3-queue'); +var classes = require('../support/data_classes'); + +module.exports = function () { + this.Then(/^routability should be$/, (table, callback) => { + this.buildWaysFromTable(table, () => { + var directions = ['forw','backw','bothw'], + headers = new Set(Object.keys(table.hashes()[0])); + + if (!directions.some(k => !!headers.has(k))) { + throw new Error('*** routability table must contain either "forw", "backw" or "bothw" column'); + } + + this.reprocessAndLoadData(() => { + var testRow = (row, i, cb) => { + var outputRow = row; + + testRoutabilityRow(i, (err, result) => { + if (err) return cb(err); + directions.filter(d => headers.has(d)).forEach((direction) => { + var want = this.shortcutsHash[row[direction]] || row[direction]; + + switch (true) { + case '' === want: + case 'x' === want: + outputRow[direction] = result[direction].status ? + result[direction].status.toString() : ''; + break; + case /^\d+s/.test(want): + break; + case /^\d+ km\/h/.test(want): + break; + default: + throw new Error(util.format('*** Unknown expectation format: %s', want)); + } + + if (this.FuzzyMatch.match(outputRow[direction], want)) { + outputRow[direction] = row[direction]; + } + }); + + if (outputRow != row) { + this.logFail(row, outputRow, result); + } + + cb(null, outputRow); + }); + }; + + this.processRowsAndDiff(table, testRow, callback); + }); + }); + }); + + var testRoutabilityRow = (i, cb) => { + var result = {}; + + var testDirection = (dir, callback) => { + var a = new classes.Location(this.origin[0] + (1+this.WAY_SPACING*i) * this.zoom, this.origin[1]), + b = new classes.Location(this.origin[0] + (3+this.WAY_SPACING*i) * this.zoom, this.origin[1]), + r = {}; + + r.which = dir; + + this.requestRoute((dir === 'forw' ? [a, b] : [b, a]), [], this.queryParams, (err, res, body) => { + if (err) return callback(err); + + r.query = this.query; + r.json = JSON.parse(body); + r.status = res.statusCode === 200 ? 'x' : null; + if (r.status) { + r.route = this.wayList(r.json.routes[0]); + + if (r.route.split(',')[0] === util.format('w%d', i)) { + r.time = r.json.routes[0].duration; + r.distance = r.json.routes[0].distance; + r.speed = r.time > 0 ? parseInt(3.6 * r.distance / r.time) : null; + } else { + r.status = null; + } + } + + callback(null, r); + }); + }; + + d3.queue(1) + .defer(testDirection, 'forw') + .defer(testDirection, 'backw') + .awaitAll((err, res) => { + if (err) return cb(err); + // check if forw and backw returned the same values + res.forEach((dirRes) => { + var which = dirRes.which; + delete dirRes.which; + result[which] = dirRes; + }); + + result.bothw = {}; + + var sq = d3.queue(); + + var parseRes = (key, scb) => { + if (result.forw[key] === result.backw[key]) { + result.bothw[key] = result.forw[key]; + } else { + result.bothw[key] = 'diff'; + } + scb(); + }; + + ['status', 'time', 'distance', 'speed'].forEach((key) => { + sq.defer(parseRes, key); + }); + + sq.awaitAll(() => { cb(null, result); }); + }); + }; +}; diff --git a/features/step_definitions/routability.rb b/features/step_definitions/routability.rb deleted file mode 100644 index 6b5134c8c..000000000 --- a/features/step_definitions/routability.rb +++ /dev/null @@ -1,78 +0,0 @@ -def test_routability_row i - result = {} - ['forw','backw'].each do |direction| - a = Location.new @origin[0]+(1+WAY_SPACING*i)*@zoom, @origin[1] - b = Location.new @origin[0]+(3+WAY_SPACING*i)*@zoom, @origin[1] - r = {} - r[:response] = request_route (direction=='forw' ? [a,b] : [b,a]), [], @query_params - r[:query] = @query - r[:json] = JSON.parse(r[:response].body) - - r[:status] = (route_status r[:response]) == 200 ? 'x' : nil - if r[:status] then - r[:route] = way_list r[:json]['route_instructions'] - - if r[:route]=="w#{i}" - r[:time] = r[:json]['route_summary']['total_time'] - r[:distance] = r[:json]['route_summary']['total_distance'] - r[:speed] = r[:time]>0 ? (3.6*r[:distance]/r[:time]).to_i : nil - else - # if we hit the wrong way segment, we assume it's - # because the one we tested was not unroutable - r[:status] = nil - end - end - result[direction] = r - end - - # check if forw and backw returned the same values - result['bothw'] = {} - [:status,:time,:distance,:speed].each do |key| - if result['forw'][key] == result['backw'][key] - result['bothw'][key] = result['forw'][key] - else - result['bothw'][key] = 'diff' - end - end - result -end - -Then /^routability should be$/ do |table| - build_ways_from_table table - reprocess - actual = [] - if table.headers&["forw","backw","bothw"] == [] - raise "*** routability tabel must contain either 'forw', 'backw' or 'bothw' column" - end - OSRMLoader.load(self,"#{prepared_file}.osrm") do - table.hashes.each_with_index do |row,i| - output_row = row.dup - attempts = [] - result = test_routability_row i - directions = ['forw','backw','bothw'] - (directions & table.headers).each do |direction| - want = shortcuts_hash[row[direction]] || row[direction] #expand shortcuts - case want - when '', 'x' - output_row[direction] = result[direction][:status] ? result[direction][:status].to_s : '' - when /^\d+s/ - output_row[direction] = result[direction][:time] ? "#{result[direction][:time]}s" : '' - when /^\d+ km\/h/ - output_row[direction] = result[direction][:speed] ? "#{result[direction][:speed]} km/h" : '' - else - raise "*** Unknown expectation format: #{want}" - end - - if FuzzyMatch.match output_row[direction], want - output_row[direction] = row[direction] - end - end - - if output_row != row - log_fail row,output_row,result - end - actual << output_row - end - end - table.diff! actual -end diff --git a/features/step_definitions/routing.js b/features/step_definitions/routing.js new file mode 100644 index 000000000..d48573e99 --- /dev/null +++ b/features/step_definitions/routing.js @@ -0,0 +1,16 @@ +var d3 = require('d3-queue'); + +module.exports = function () { + this.When(/^I route I should get$/, this.WhenIRouteIShouldGet); + + // This is used to route 100 times; timeout for entire step is therefore set to 100 * STRESS_TIMEOUT + this.When(/^I route (\d+) times I should get$/, { timeout: 30000 }, (n, table, callback) => { + var q = d3.queue(1); + + for (var i=0; i row['request'] } - response = request_url row['request'] - else - default_params = @query_params - user_params = [] - got = {} - row.each_pair do |k,v| - if k =~ /param:(.*)/ - if v=='(nil)' - user_params << [$1, nil] - elsif v!=nil - user_params << [$1, v] - end - got[k]=v - end - end - params = overwrite_params default_params, user_params - waypoints = [] - bearings = [] - if row['bearings'] - got['bearings'] = row['bearings'] - bearings = row['bearings'].split(' ').compact - end - if row['from'] and row['to'] - node = find_node_by_name(row['from']) - raise "*** unknown from-node '#{row['from']}" unless node - waypoints << node - - node = find_node_by_name(row['to']) - raise "*** unknown to-node '#{row['to']}" unless node - waypoints << node - - got = got.merge({'from' => row['from'], 'to' => row['to'] }) - response = request_route waypoints, bearings, params - elsif row['waypoints'] - row['waypoints'].split(',').each do |n| - node = find_node_by_name(n.strip) - raise "*** unknown waypoint node '#{n.strip}" unless node - waypoints << node - end - got = got.merge({'waypoints' => row['waypoints'] }) - response = request_route waypoints, bearings, params - else - raise "*** no waypoints" - end - end - - if response.body.empty? == false - json = JSON.parse response.body - end - - if response.body.empty? == false - if json['status'] == 200 - instructions = way_list json['route_instructions'] - bearings = bearing_list json['route_instructions'] - compasses = compass_list json['route_instructions'] - turns = turn_list json['route_instructions'] - modes = mode_list json['route_instructions'] - times = time_list json['route_instructions'] - distances = distance_list json['route_instructions'] - end - end - - if table.headers.include? 'status' - got['status'] = json['status'].to_s - end - if table.headers.include? 'message' - got['message'] = json['status_message'] - end - if table.headers.include? '#' # comment column - got['#'] = row['#'] # copy value so it always match - end - - if table.headers.include? 'start' - got['start'] = instructions ? json['route_summary']['start_point'] : nil - end - if table.headers.include? 'end' - got['end'] = instructions ? json['route_summary']['end_point'] : nil - end - if table.headers.include? 'geometry' - got['geometry'] = json['route_geometry'] - end - if table.headers.include? 'route' - got['route'] = (instructions || '').strip - if table.headers.include?('alternative') - got['alternative'] = - if json['found_alternative'] - way_list json['alternative_instructions'].first - else - "" - end - end - if table.headers.include?('distance') - if row['distance']!='' - raise "*** Distance must be specied in meters. (ex: 250m)" unless row['distance'] =~ /\d+m/ - end - got['distance'] = instructions ? "#{json['route_summary']['total_distance'].to_s}m" : '' - end - if table.headers.include?('time') - raise "*** Time must be specied in seconds. (ex: 60s)" unless row['time'] =~ /\d+s/ - got['time'] = instructions ? "#{json['route_summary']['total_time'].to_s}s" : '' - end - if table.headers.include?('speed') - if row['speed'] != '' && instructions - raise "*** Speed must be specied in km/h. (ex: 50 km/h)" unless row['speed'] =~ /\d+ km\/h/ - time = json['route_summary']['total_time'] - distance = json['route_summary']['total_distance'] - speed = time>0 ? (3.6*distance/time).round : nil - got['speed'] = "#{speed} km/h" - else - got['speed'] = '' - end - end - if table.headers.include? 'bearing' - got['bearing'] = instructions ? bearings : '' - end - if table.headers.include? 'compass' - got['compass'] = instructions ? compasses : '' - end - if table.headers.include? 'turns' - got['turns'] = instructions ? turns : '' - end - if table.headers.include? 'modes' - got['modes'] = instructions ? modes : '' - end - if table.headers.include? 'times' - got['times'] = instructions ? times : '' - end - if table.headers.include? 'distances' - got['distances'] = instructions ? distances : '' - end - end - - ok = true - row.keys.each do |key| - if FuzzyMatch.match got[key], row[key] - got[key] = row[key] - else - ok = false - end - end - - unless ok - log_fail row,got, { 'route' => {:query => @query, :response => response} } - end - - actual << got - end - end - table.diff! actual -end - -When /^I route (\d+) times I should get$/ do |n,table| - ok = true - n.to_i.times do - ok = false unless step "I route I should get", table - end - ok -end diff --git a/features/step_definitions/timestamp.js b/features/step_definitions/timestamp.js new file mode 100644 index 000000000..55af74b5e --- /dev/null +++ b/features/step_definitions/timestamp.js @@ -0,0 +1,13 @@ +var assert = require('assert'); + +module.exports = function () { + this.Then(/^I should get a valid timestamp/, (callback) => { + this.ShouldGetAResponse(); + this.ShouldBeValidJSON((err) => { + this.ShouldBeWellFormed(); + assert.equal(typeof this.json.timestamp, 'string'); + assert.equal(this.json.timestamp, '2000-01-01T00:00:00Z'); + callback(err); + }); + }); +}; diff --git a/features/step_definitions/timestamp.rb b/features/step_definitions/timestamp.rb deleted file mode 100644 index df456152b..000000000 --- a/features/step_definitions/timestamp.rb +++ /dev/null @@ -1,7 +0,0 @@ -Then /^I should get a valid timestamp/ do - step "I should get a response" - step "response should be valid JSON" - step "response should be well-formed" - expect(@json['timestamp'].class).to eq(String) - expect(@json['timestamp']).to eq("2000-01-01T00:00:00Z") -end diff --git a/features/step_definitions/trip.js b/features/step_definitions/trip.js new file mode 100644 index 000000000..9662ac623 --- /dev/null +++ b/features/step_definitions/trip.js @@ -0,0 +1,142 @@ +var util = require('util'); + +module.exports = function () { + this.When(/^I plan a trip I should get$/, (table, callback) => { + var got; + + this.reprocessAndLoadData(() => { + var testRow = (row, ri, cb) => { + var afterRequest = (err, res) => { + if (err) return cb(err); + var headers = new Set(table.raw()[0]); + + for (var k in row) { + var match = k.match(/param:(.*)/); + if (match) { + if (row[k] === '(nil)') { + params[match[1]] = null; + } else if (row[k]) { + params[match[1]] = [row[k]]; + } + + got[k] = row[k]; + } + } + + var json; + if (res.body.length) { + json = JSON.parse(res.body); + } + + if (headers.has('status')) { + got.status = json.status.toString(); + } + + if (headers.has('message')) { + got.message = json.status_message; + } + + if (headers.has('#')) { + // comment column + got['#'] = row['#']; + } + + var subTrips; + if (res.statusCode === 200) { + if (headers.has('trips')) { + subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => { + var toAdd = []; + if (i === 0) toAdd.push(sl.steps[0].maneuver.location); + toAdd.push(sl.steps[sl.steps.length-1].maneuver.location); + return toAdd; + }))); + } + } + + var ok = true, + encodedResult = '', + extendedTarget = ''; + + row.trips.split(',').forEach((sub, si) => { + if (si >= subTrips.length) { + ok = false; + } else { + ok = false; + // TODO: Check all rotations of the round trip + for (var ni=0; ni { + var node = this.findNodeByName(n); + if (!node) throw new Error(util.format('*** unknown waypoint node "%s"', n.trim())); + waypoints.push(node); + }); + got = { waypoints: row.waypoints }; + this.requestTrip(waypoints, params, afterRequest); + } else { + throw new Error('*** no waypoints'); + } + } + }; + + this.processRowsAndDiff(table, testRow, callback); + }); + }); +}; diff --git a/features/step_definitions/trip.rb b/features/step_definitions/trip.rb deleted file mode 100644 index ecb4c7782..000000000 --- a/features/step_definitions/trip.rb +++ /dev/null @@ -1,121 +0,0 @@ -When /^I plan a trip I should get$/ do |table| - reprocess - actual = [] - OSRMLoader.load(self,"#{prepared_file}.osrm") do - table.hashes.each_with_index do |row,ri| - if row['request'] - got = {'request' => row['request'] } - response = request_url row['request'] - else - params = @query_params - waypoints = [] - if row['from'] and row['to'] - node = find_node_by_name(row['from']) - raise "*** unknown from-node '#{row['from']}" unless node - waypoints << node - - node = find_node_by_name(row['to']) - raise "*** unknown to-node '#{row['to']}" unless node - waypoints << node - - got = {'from' => row['from'], 'to' => row['to'] } - response = request_trip waypoints, params - elsif row['waypoints'] - row['waypoints'].split(',').each do |n| - node = find_node_by_name(n.strip) - raise "*** unknown waypoint node '#{n.strip}" unless node - waypoints << node - end - got = {'waypoints' => row['waypoints'] } - response = request_trip waypoints, params - else - raise "*** no waypoints" - end - end - - row.each_pair do |k,v| - if k =~ /param:(.*)/ - if v=='(nil)' - params[$1]=nil - elsif v!=nil - params[$1]=[v] - end - got[k]=v - end - end - - if response.body.empty? == false - json = JSON.parse response.body - end - - if table.headers.include? 'status' - got['status'] = json['status'].to_s - end - if table.headers.include? 'message' - got['message'] = json['status_message'] - end - if table.headers.include? '#' # comment column - got['#'] = row['#'] # copy value so it always match - end - - if response.code == "200" - if table.headers.include? 'trips' - sub_trips = json['trips'].compact.map { |sub| sub['via_points']} - end - end - - ###################### - ok = true - encoded_result = "" - extended_target = "" - row['trips'].split(',').each_with_index do |sub, sub_idx| - if sub_idx >= sub_trips.length - ok = false - break - end - - ok = false; - #TODO: Check all rotations of the round trip - sub.length.times do |node_idx| - node = find_node_by_name(sub[node_idx]) - out_node = sub_trips[sub_idx][node_idx] - if FuzzyMatch.match_location out_node, node - encoded_result += sub[node_idx] - extended_target += sub[node_idx] - ok = true - else - encoded_result += "? [#{out_node[0]},#{out_node[1]}]" - extended_target += "#{sub[node_idx]} [#{node.lat},#{node.lon}]" - end - end - end - - if ok - got['trips'] = row['trips'] - got['via_points'] = row['via_points'] - else - got['trips'] = encoded_result - row['trips'] = extended_target - log_fail row,got, { 'trip' => {:query => @query, :response => response} } - end - - - ok = true - row.keys.each do |key| - if FuzzyMatch.match got[key], row[key] - got[key] = row[key] - else - ok = false - end - end - - unless ok - log_fail row,got, { 'trip' => {:query => @query, :response => response} } - end - - actual << got - end - end - table.diff! actual -end - diff --git a/features/support/build_osm.js b/features/support/build_osm.js new file mode 100644 index 000000000..7fe6874ae --- /dev/null +++ b/features/support/build_osm.js @@ -0,0 +1,165 @@ +'use strict'; + +var builder = require('xmlbuilder'); + +var ensureDecimal = (i) => { + if (parseInt(i) === i) return i.toFixed(1); + else return i; +}; + +class DB { + constructor () { + this.nodes = new Array(); + this.ways = new Array(); + this.relations = new Array(); + } + + addNode (node) { + this.nodes.push(node); + } + + addWay (way) { + this.ways.push(way); + } + + addRelation (relation) { + this.relations.push(relation); + } + + clear () { + this.nodes = []; + this.ways = []; + this.relations = []; + } + + toXML (callback) { + var xml = builder.create('osm', {'encoding':'UTF-8'}); + xml.att('generator', 'osrm-test') + .att('version', '0.6'); + + this.nodes.forEach((n) => { + var node = xml.ele('node', { + id: n.id, + version: 1, + uid: n.OSM_UID, + user: n.OSM_USER, + timestamp: n.OSM_TIMESTAMP, + lon: ensureDecimal(n.lon), + lat: ensureDecimal(n.lat) + }); + + for (var k in n.tags) { + node.ele('tag') + .att('k', k) + .att('v', n.tags[k]); + } + }); + + this.ways.forEach((w) => { + var way = xml.ele('way', { + id: w.id, + version: 1, + uid: w.OSM_UID, + user: w.OSM_USER, + timestamp: w.OSM_TIMESTAMP + }); + + w.nodes.forEach((k) => { + way.ele('nd') + .att('ref', k.id); + }); + + for (var k in w.tags) { + way.ele('tag') + .att('k', k) + .att('v', w.tags[k]); + } + }); + + this.relations.forEach((r) => { + var relation = xml.ele('relation', { + id: r.id, + user: r.OSM_USER, + timestamp: r.OSM_TIMESTAMP, + uid: r.OSM_UID + }); + + r.members.forEach((m) => { + relation.ele('member', { + type: m.type, + ref: m.id, + role: m.role + }); + }); + + for (var k in r.tags) { + relation.ele('tag') + .att('k', k) + .att('v', r.tags[k]); + } + }); + + callback(xml.end({ pretty: true, indent: ' ' })); + } +} + +class Node { + constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID, lon, lat, tags) { + this.id = id; + this.OSM_USER = OSM_USER; + this.OSM_TIMESTAMP = OSM_TIMESTAMP; + this.OSM_UID = OSM_UID; + this.lon = lon; + this.lat = lat; + this.tags = tags; + } + + addTag (k, v) { + this.tags[k] = v; + } +} + +class Way { + constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID) { + this.id = id; + this.OSM_USER = OSM_USER; + this.OSM_TIMESTAMP = OSM_TIMESTAMP; + this.OSM_UID = OSM_UID; + this.tags = {}; + this.nodes = []; + } + + addNode (node) { + this.nodes.push(node); + } + + setTags (tags) { + this.tags = tags; + } +} + +class Relation { + constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID) { + this.id = id; + this.OSM_USER = OSM_USER; + this.OSM_TIMESTAMP = OSM_TIMESTAMP; + this.OSM_UID = OSM_UID; + this.members = []; + this.tags = {}; + } + + addMember (memberType, id, role) { + this.members.push({type: memberType, id: id, role: role}); + } + + addTag (k, v) { + this.tags[k] = v; + } +} + +module.exports = { + DB: DB, + Node: Node, + Way: Way, + Relation: Relation +}; diff --git a/features/support/config.js b/features/support/config.js new file mode 100644 index 000000000..cae73981c --- /dev/null +++ b/features/support/config.js @@ -0,0 +1,115 @@ +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var d3 = require('d3-queue'); +var OSM = require('./build_osm'); +var classes = require('./data_classes'); + +module.exports = function () { + this.initializeOptions = (callback) => { + this.profile = this.profile || this.DEFAULT_SPEEDPROFILE; + + this.OSMDB = this.OSMDB || new OSM.DB(); + + this.nameNodeHash = this.nameNodeHash || {}; + + this.locationHash = this.locationHash || {}; + + this.nameWayHash = this.nameWayHash || {}; + + this.osmData = new classes.osmData(this); + + this.STRESS_TIMEOUT = 300; + + this.OSRMLoader = this._OSRMLoader(); + + this.PREPROCESS_LOG_FILE = path.resolve(this.TEST_FOLDER, 'preprocessing.log'); + + this.LOG_FILE = path.resolve(this.TEST_FOLDER, 'fail.log'); + + this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT; + + this.DESTINATION_REACHED = 15; // OSRM instruction code + + this.shortcutsHash = this.shortcutsHash || {}; + + var hashLuaLib = (cb) => { + fs.readdir(path.normalize(this.PROFILES_PATH + '/lib/'), (err, files) => { + if (err) cb(err); + var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(this.PROFILES_PATH + '/lib/' + f)); + this.hashOfFiles(luaFiles, hash => { + this.luaLibHash = hash; + cb(); + }); + }); + }; + + var hashProfile = (cb) => { + this.hashProfile((hash) => { + this.profileHash = hash; + cb(); + }); + }; + + var hashExtract = (cb) => { + this.hashOfFiles(util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE), (hash) => { + this.binExtractHash = hash; + cb(); + }); + }; + + var hashContract = (cb) => { + this.hashOfFiles(util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE), (hash) => { + this.binContractHash = hash; + this.fingerprintContract = this.hashString(this.binContractHash); + cb(); + }); + }; + + var hashRouted = (cb) => { + this.hashOfFiles(util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE), (hash) => { + this.binRoutedHash = hash; + this.fingerprintRoute = this.hashString(this.binRoutedHash); + cb(); + }); + }; + + d3.queue() + .defer(hashLuaLib) + .defer(hashProfile) + .defer(hashExtract) + .defer(hashContract) + .defer(hashRouted) + .awaitAll(() => { + this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-')); + this.AfterConfiguration(() => { + callback(); + }); + }); + }; + + this.setProfileBasedHashes = () => { + this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-')); + this.fingerprintContract = this.hashString(this.binContractHash); + }; + + this.setProfile = (profile, cb) => { + var lastProfile = this.profile; + if (profile !== lastProfile) { + this.profile = profile; + this.hashProfile((hash) => { + this.profileHash = hash; + this.setProfileBasedHashes(); + cb(); + }); + } else cb(); + }; + + this.setExtractArgs = (args) => { + this.extractArgs = args; + }; + + this.setContractArgs = (args) => { + this.contractArgs = args; + }; +}; diff --git a/features/support/config.rb b/features/support/config.rb deleted file mode 100644 index 434b4ec9b..000000000 --- a/features/support/config.rb +++ /dev/null @@ -1,16 +0,0 @@ -def profile - @profile ||= reset_profile -end - -def reset_profile - @profile = nil - set_profile DEFAULT_SPEEDPROFILE -end - -def set_profile profile - @profile = profile -end - -def set_extract_args args - @extract_args = args -end diff --git a/features/support/data.js b/features/support/data.js new file mode 100644 index 000000000..29e517093 --- /dev/null +++ b/features/support/data.js @@ -0,0 +1,340 @@ +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var exec = require('child_process').exec; +var d3 = require('d3-queue'); + +var OSM = require('./build_osm'); +var classes = require('./data_classes'); + +module.exports = function () { + this.setGridSize = (meters) => { + // the constant is calculated (with BigDecimal as: 1.0/(DEG_TO_RAD*EARTH_RADIUS_IN_METERS + // see ApproximateDistance() in ExtractorStructs.h + // it's only accurate when measuring along the equator, or going exactly north-south + this.zoom = parseFloat(meters) * 0.8990679362704610899694577444566908445396483347536032203503E-5; + }; + + this.setOrigin = (origin) => { + this.origin = origin; + }; + + this.buildWaysFromTable = (table, callback) => { + // add one unconnected way for each row + var buildRow = (row, ri, cb) => { + // comments ported directly from ruby suite: + // NOTE: currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 edges + // this is related to the fact that a oneway dead-end street doesn't make a lot of sense + + // if we stack ways on different x coordinates, routability tests get messed up, because osrm might pick a neighboring way if the one test can't be used. + // instead we place all lines as a string on the same y coordinate. this prevents using neighboring ways. + + // add some nodes + + var makeFakeNode = (namePrefix, offset) => { + return new OSM.Node(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, + this.OSM_UID, this.origin[0]+(offset + this.WAY_SPACING * ri) * this.zoom, + this.origin[1], {name: util.format('%s%d', namePrefix, ri)}); + }; + + var nodes = ['a','b','c','d','e'].map((l, i) => makeFakeNode(l, i)); + + nodes.forEach(node => { this.OSMDB.addNode(node); }); + + // ...with a way between them + var way = new OSM.Way(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); + + nodes.forEach(node => { way.addNode(node); }); + + // remove tags that describe expected test result, reject empty tags + var tags = {}; + for (var rkey in row) { + if (!rkey.match(/^forw\b/) && + !rkey.match(/^backw\b/) && + !rkey.match(/^bothw\b/) && + row[rkey].length) + tags[rkey] = row[rkey]; + } + + var wayTags = { highway: 'primary' }, + nodeTags = {}; + + for (var key in tags) { + var nodeMatch = key.match(/node\/(.*)/); + if (nodeMatch) { + if (tags[key] === '(nil)') { + delete nodeTags[key]; + } else { + nodeTags[nodeMatch[1]] = tags[key]; + } + } else { + if (tags[key] === '(nil)') { + delete wayTags[key]; + } else { + wayTags[key] = tags[key]; + } + } + } + + wayTags.name = util.format('w%d', ri); + way.setTags(wayTags); + this.OSMDB.addWay(way); + + for (var k in nodeTags) { + nodes[2].addTag(k, nodeTags[k]); + } + cb(); + }; + + var q = d3.queue(); + table.hashes().forEach((row, ri) => { + q.defer(buildRow, row, ri); + }); + + q.awaitAll(callback); + }; + + this.ensureDecimal = (i) => { + if (parseInt(i) === i) return i.toFixed(1); + else return i; + }; + + this.tableCoordToLonLat = (ci, ri) => { + return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(this.ensureDecimal); + }; + + this.addOSMNode = (name, lon, lat, id) => { + id = id || this.makeOSMId(); + var node = new OSM.Node(id, this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID, lon, lat, {name: name}); + this.OSMDB.addNode(node); + this.nameNodeHash[name] = node; + }; + + this.addLocation = (name, lon, lat) => { + this.locationHash[name] = new classes.Location(lon, lat); + }; + + this.findNodeByName = (s) => { + if (s.length !== 1) throw new Error(util.format('*** invalid node name "%s", must be single characters', s)); + if (!s.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name "%s", must be alphanumeric', s)); + + var fromNode; + if (s.match(/[a-z]/)) { + fromNode = this.nameNodeHash[s.toString()]; + } else { + fromNode = this.locationHash[s.toString()]; + } + + return fromNode; + }; + + this.findWayByName = (s) => { + return this.nameWayHash[s.toString()] || this.nameWayHash[s.toString().split('').reverse().join('')]; + }; + + this.resetData = () => { + this.resetOSM(); + }; + + this.makeOSMId = () => { + this.osmID = this.osmID + 1; + return this.osmID; + }; + + this.resetOSM = () => { + this.OSMDB.clear(); + this.osmData.reset(); + this.nameNodeHash = {}; + this.locationHash = {}; + this.nameWayHash = {}; + this.osmID = 0; + }; + + this.writeOSM = (callback) => { + fs.exists(this.DATA_FOLDER, (exists) => { + var mkDirFn = exists ? (cb) => { cb(); } : fs.mkdir.bind(fs.mkdir, this.DATA_FOLDER); + mkDirFn((err) => { + if (err) return callback(err); + var osmPath = path.resolve(this.DATA_FOLDER, util.format('%s.osm', this.osmData.osmFile)); + fs.exists(osmPath, (exists) => { + if (!exists) fs.writeFile(osmPath, this.osmData.str, callback); + else callback(); + }); + }); + }); + }; + + this.isExtracted = (callback) => { + fs.exists(util.format('%s.osrm', this.osmData.extractedFile), (core) => { + if (!core) return callback(false); + fs.exists(util.format('%s.osrm.names', this.osmData.extractedFile), (names) => { + if (!names) return callback(false); + fs.exists(util.format('%s.osrm.restrictions', this.osmData.extractedFile), (restrictions) => { + return callback(restrictions); + }); + }); + }); + }; + + this.isContracted = (callback) => { + fs.exists(util.format('%s.osrm.hsgr', this.osmData.contractedFile), callback); + }; + + this.writeTimestamp = (callback) => { + fs.writeFile(util.format('%s.osrm.timestamp', this.osmData.contractedFile), this.OSM_TIMESTAMP, callback); + }; + + this.writeInputData = (callback) => { + this.writeOSM((err) => { + if (err) return callback(err); + this.writeTimestamp(callback); + }); + }; + + this.extractData = (callback) => { + this.logPreprocessInfo(); + this.log(util.format('== Extracting %s.osm...', this.osmData.osmFile), 'preprocess'); + var cmd = util.format('%s%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1', + this.LOAD_LIBRARIES, this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE); + this.log(cmd); + process.chdir(this.TEST_FOLDER); + exec(cmd, (err) => { + if (err) { + this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); + process.chdir('../'); + return callback(this.ExtractError(err.code, util.format('osrm-extract exited with code %d', err.code))); + } + + var q = d3.queue(); + + var rename = (file, cb) => { + this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.osmFile, file, this.osmData.extractedFile, file), 'preprocess'); + fs.rename([this.osmData.osmFile, file].join('.'), [this.osmData.extractedFile, file].join('.'), (err) => { + if (err) return cb(this.FileError(null, 'failed to rename data file after extracting')); + cb(); + }); + }; + + var renameIfExists = (file, cb) => { + fs.stat([this.osmData.osmFile, file].join('.'), (doesNotExistErr, exists) => { + if (exists) rename(file, cb); + else cb(); + }); + }; + + ['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties'].forEach(file => { + q.defer(rename, file); + }); + + ['osrm.edge_segment_lookup','osrm.edge_penalties'].forEach(file => { + q.defer(renameIfExists, file); + }); + + q.awaitAll((err) => { + this.log('Finished extracting ' + this.osmData.extractedFile, 'preprocess'); + process.chdir('../'); + callback(err); + }); + }); + }; + + this.contractData = (callback) => { + this.logPreprocessInfo(); + this.log(util.format('== Contracting %s.osm...', this.osmData.extractedFile), 'preprocess'); + var cmd = util.format('%s%s/osrm-contract %s %s.osrm >>%s 2>&1', + this.LOAD_LIBRARIES, this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE); + this.log(cmd); + process.chdir(this.TEST_FOLDER); + exec(cmd, (err) => { + if (err) { + this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); + process.chdir('../'); + return callback(this.ContractError(err.code, util.format('osrm-contract exited with code %d', err.code))); + } + + var rename = (file, cb) => { + this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess'); + fs.rename([this.osmData.extractedFile, file].join('.'), [this.osmData.contractedFile, file].join('.'), (err) => { + if (err) return cb(this.FileError(null, 'failed to rename data file after contracting.')); + cb(); + }); + }; + + var copy = (file, cb) => { + this.log(util.format('Copying %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess'); + fs.createReadStream([this.osmData.extractedFile, file].join('.')) + .pipe(fs.createWriteStream([this.osmData.contractedFile, file].join('.')) + .on('finish', cb) + ) + .on('error', () => { + return cb(this.FileError(null, 'failed to copy data after contracting.')); + }); + }; + + var q = d3.queue(); + + ['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level'].forEach((file) => { + q.defer(rename, file); + }); + + ['osrm.names','osrm.restrictions','osrm.properties','osrm'].forEach((file) => { + q.defer(copy, file); + }); + + q.awaitAll((err) => { + this.log('Finished contracting ' + this.osmData.contractedFile, 'preprocess'); + process.chdir('../'); + callback(err); + }); + }); + }; + + var noop = (cb) => cb(); + + this.reprocess = (callback) => { + this.writeAndExtract((e) => { + if (e) return callback(e); + this.isContracted((isContracted) => { + var contractFn = isContracted ? noop : this.contractData; + if (isContracted) this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess'); + contractFn((e) => { + if (e) return callback(e); + this.logPreprocessDone(); + callback(); + }); + }); + }); + }; + + this.writeAndExtract = (callback) => { + this.osmData.populate(() => { + this.writeInputData((e) => { + if (e) return callback(e); + this.isExtracted((isExtracted) => { + var extractFn = isExtracted ? noop : this.extractData; + if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess'); + extractFn((e) => { + callback(e); + }); + }); + }); + }); + }; + + this.reprocessAndLoadData = (callback) => { + this.reprocess(() => { + this.OSRMLoader.load(util.format('%s.osrm', this.osmData.contractedFile), callback); + }); + }; + + this.processRowsAndDiff = (table, fn, callback) => { + var q = d3.queue(1); + + table.hashes().forEach((row, i) => { q.defer(fn, row, i); }); + + q.awaitAll((err, actual) => { + if (err) return callback(err); + this.diffTables(table, actual, {}, callback); + }); + }; +}; diff --git a/features/support/data.rb b/features/support/data.rb deleted file mode 100644 index 62ed7d370..000000000 --- a/features/support/data.rb +++ /dev/null @@ -1,325 +0,0 @@ -require 'OSM/objects' #osmlib gem -require 'OSM/Database' -require 'builder' -require 'fileutils' - -class Location - attr_accessor :lon,:lat - - def initialize lon,lat - @lat = lat - @lon = lon - end -end - - -def set_input_format format - raise '*** Input format must be eiter "osm" or "pbf"' unless ['pbf','osm'].include? format.to_s - @input_format = format.to_s -end - -def input_format - @input_format || DEFAULT_INPUT_FORMAT -end - -def sanitized_scenario_title - @sanitized_scenario_title ||= @scenario_title.to_s.gsub /[^0-9A-Za-z.\-]/, '_' -end - -def set_grid_size meters - #the constant is calculated (with BigDecimal as: 1.0/(DEG_TO_RAD*EARTH_RADIUS_IN_METERS - #see ApproximateDistance() in ExtractorStructs.h - #it's only accurate when measuring along the equator, or going exactly north-south - @zoom = meters.to_f*0.8990679362704610899694577444566908445396483347536032203503E-5 -end - -def set_origin origin - @origin = origin -end - -def build_ways_from_table table - #add one unconnected way for each row - table.hashes.each_with_index do |row,ri| - #NOTE: - #currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 edges - #this is relatated to the fact that a oneway dead-end street doesn't make a lot of sense - - #if we stack ways on different x coordinates, routability tests get messed up, because osrm might pick a neighboring way if the one test can't be used. - #instead we place all lines as a string on the same y coordinate. this prevents using neightboring ways. - - #a few nodes... - node1 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(0+WAY_SPACING*ri)*@zoom, @origin[1] - node2 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(1+WAY_SPACING*ri)*@zoom, @origin[1] - node3 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(2+WAY_SPACING*ri)*@zoom, @origin[1] - node4 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(3+WAY_SPACING*ri)*@zoom, @origin[1] - node5 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(4+WAY_SPACING*ri)*@zoom, @origin[1] - node1.uid = OSM_UID - node2.uid = OSM_UID - node3.uid = OSM_UID - node4.uid = OSM_UID - node5.uid = OSM_UID - node1 << { :name => "a#{ri}" } - node2 << { :name => "b#{ri}" } - node3 << { :name => "c#{ri}" } - node4 << { :name => "d#{ri}" } - node5 << { :name => "e#{ri}" } - - osm_db << node1 - osm_db << node2 - osm_db << node3 - osm_db << node4 - osm_db << node5 - - #...with a way between them - way = OSM::Way.new make_osm_id, OSM_USER, OSM_TIMESTAMP - way.uid = OSM_UID - way << node1 - way << node2 - way << node3 - way << node4 - way << node5 - - tags = row.dup - - # remove tags that describe expected test result - tags.reject! do |k,v| - k =~ /^forw\b/ || - k =~ /^backw\b/ || - k =~ /^bothw\b/ - end - - ##remove empty tags - tags.reject! { |k,v| v=='' } - - # sort tag keys in the form of 'node/....' - way_tags = { 'highway' => 'primary' } - - node_tags = {} - tags.each_pair do |k,v| - if k =~ /node\/(.*)/ - if v=='(nil)' - node_tags.delete k - else - node_tags[$1] = v - end - else - if v=='(nil)' - way_tags.delete k - else - way_tags[k] = v - end - end - end - - way_tags['name'] = "w#{ri}" - way << way_tags - node3 << node_tags - - osm_db << way - end -end - -def table_coord_to_lonlat ci,ri - [@origin[0]+ci*@zoom, @origin[1]-ri*@zoom] -end - -def add_osm_node name,lon,lat,id - id = make_osm_id if id == nil - node = OSM::Node.new id, OSM_USER, OSM_TIMESTAMP, lon, lat - node << { :name => name } - node.uid = OSM_UID - osm_db << node - name_node_hash[name] = node -end - -def add_location name,lon,lat - location_hash[name] = Location.new(lon,lat) -end - -def find_node_by_name s - raise "***invalid node name '#{s}', must be single characters" unless s.size == 1 - raise "*** invalid node name '#{s}', must be alphanumeric" unless s.match /[a-z0-9]/ - if s.match /[a-z]/ - from_node = name_node_hash[ s.to_s ] - else - from_node = location_hash[ s.to_s ] - end -end - -def find_way_by_name s - name_way_hash[s.to_s] || name_way_hash[s.to_s.reverse] -end - -def reset_data - Dir.chdir TEST_FOLDER do - #clear_log - #clear_data_files - end - reset_profile - reset_osm - @fingerprint_osm = nil - @fingerprint_extract = nil - @fingerprint_prepare = nil - @fingerprint_route = nil -end - -def make_osm_id - @osm_id = @osm_id+1 -end - -def reset_osm - osm_db.clear - name_node_hash.clear - location_hash.clear - name_way_hash.clear - @osm_str = nil - @osm_hash = nil - @osm_id = 0 -end - -def clear_data_files - File.delete *Dir.glob("#{DATA_FOLDER}/test.*") -end - -def clear_log - File.delete *Dir.glob("*.log") -end - -def osm_db - @osm_db ||= OSM::Database.new -end - -def name_node_hash - @name_node_hash ||= {} -end - -def location_hash - @location_hash ||= {} -end - -def name_way_hash - @name_way_hash ||= {} -end - -def osm_str - return @osm_str if @osm_str - @osm_str = '' - doc = Builder::XmlMarkup.new :indent => 2, :target => @osm_str - doc.instruct! - osm_db.to_xml doc, OSM_GENERATOR - @osm_str -end - -def osm_file - @osm_file ||= "#{DATA_FOLDER}/#{fingerprint_osm}" -end - -def extracted_file - @extracted_file ||= "#{osm_file}_#{fingerprint_extract}" -end - -def prepared_file - @prepared_file ||= "#{osm_file}_#{fingerprint_extract}_#{fingerprint_prepare}" -end - -def write_osm - Dir.mkdir DATA_FOLDER unless File.exist? DATA_FOLDER - unless File.exist?("#{osm_file}.osm") - File.open( "#{osm_file}.osm", 'w') {|f| f.write(osm_str) } - end -end - -def convert_osm_to_pbf - unless File.exist?("#{osm_file}.osm.pbf") - log_preprocess_info - log "== Converting #{osm_file}.osm to protobuffer format...", :preprocess - unless system "osmosis --read-xml #{osm_file}.osm --write-pbf #{osm_file}.osm.pbf omitmetadata=true >>#{PREPROCESS_LOG_FILE} 2>&1" - raise OsmosisError.new $?, "osmosis exited with code #{$?.exitstatus}" - end - log '', :preprocess - end -end - -def extracted? - Dir.chdir TEST_FOLDER do - File.exist?("#{extracted_file}.osrm") && - File.exist?("#{extracted_file}.osrm.names") && - File.exist?("#{extracted_file}.osrm.restrictions") - end -end - -def prepared? - Dir.chdir TEST_FOLDER do - File.exist?("#{prepared_file}.osrm.hsgr") - end -end - -def write_timestamp - File.open( "#{prepared_file}.osrm.timestamp", 'w') {|f| f.write(OSM_TIMESTAMP) } -end - -def pbf? - input_format=='pbf' -end - -def write_input_data - Dir.chdir TEST_FOLDER do - write_osm - write_timestamp - convert_osm_to_pbf if pbf? - end -end - -def extract_data - Dir.chdir TEST_FOLDER do - log_preprocess_info - log "== Extracting #{osm_file}.osm...", :preprocess - unless system "#{BIN_PATH}/osrm-extract #{osm_file}.osm#{'.pbf' if pbf?} #{@extract_args} --profile #{PROFILES_PATH}/#{@profile}.lua >>#{PREPROCESS_LOG_FILE} 2>&1" - log "*** Exited with code #{$?.exitstatus}.", :preprocess - raise ExtractError.new $?.exitstatus, "osrm-extract exited with code #{$?.exitstatus}." - end - begin - ["osrm","osrm.names","osrm.restrictions","osrm.ebg","osrm.edges","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex"].each do |file| - log "Renaming #{osm_file}.#{file} to #{extracted_file}.#{file}", :preprocess - File.rename "#{osm_file}.#{file}", "#{extracted_file}.#{file}" - end - rescue Exception => e - raise FileError.new nil, "failed to rename data file after extracting." - end - end -end - -def prepare_data - Dir.chdir TEST_FOLDER do - log_preprocess_info - log "== Preparing #{extracted_file}.osm...", :preprocess - unless system "#{BIN_PATH}/osrm-prepare #{extracted_file}.osrm --profile #{PROFILES_PATH}/#{@profile}.lua >>#{PREPROCESS_LOG_FILE} 2>&1" - log "*** Exited with code #{$?.exitstatus}.", :preprocess - raise PrepareError.new $?.exitstatus, "osrm-prepare exited with code #{$?.exitstatus}." - end - begin - ["osrm.hsgr","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex","osrm.core","osrm.edges"].each do |file| - log "Renaming #{extracted_file}.#{file} to #{prepared_file}.#{file}", :preprocess - File.rename "#{extracted_file}.#{file}", "#{prepared_file}.#{file}" - end - rescue Exception => e - raise FileError.new nil, "failed to rename data file after preparing." - end - begin - ["osrm.names","osrm.restrictions","osrm"].each do |file| - log "Copying #{extracted_file}.#{file} to #{prepared_file}.#{file}", :preprocess - FileUtils.cp "#{extracted_file}.#{file}", "#{prepared_file}.#{file}" - end - rescue Exception => e - raise FileError.new nil, "failed to copy data file after preparing." - end - log '', :preprocess - end -end - -def reprocess - write_input_data - extract_data unless extracted? - prepare_data unless prepared? - log_preprocess_done -end diff --git a/features/support/data_classes.js b/features/support/data_classes.js new file mode 100644 index 000000000..8eeaab336 --- /dev/null +++ b/features/support/data_classes.js @@ -0,0 +1,85 @@ +'use strict'; + +var util = require('util'); +var path = require('path'); + +module.exports = { + Location: class { + constructor (lon, lat) { + this.lon = lon; + this.lat = lat; + } + }, + + osmData: class { + constructor (scope) { + this.scope = scope; + this.str = null; + this.hash = null; + this.fingerprintOSM = null; + this.osmFile = null; + this.extractedFile = null; + this.contractedFile = null; + } + + populate (callback) { + this.scope.OSMDB.toXML((str) => { + this.str = str; + + this.hash = this.scope.hashString(str); + this.fingerprintOSM = this.scope.hashString(this.hash); + + this.osmFile = path.resolve(this.scope.DATA_FOLDER, this.fingerprintOSM); + + this.extractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract].join('_')); + this.contractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract, this.scope.fingerprintContract].join('_')); + + callback(); + }); + } + + reset () { + this.str = null; + this.hash = null; + this.fingerprintOSM = null; + this.osmFile = null; + this.extractedFile = null; + this.contractedFile = null; + } + }, + + FuzzyMatch: class { + match (got, want) { + var matchPercent = want.match(/(.*)\s+~(.+)%$/), + matchAbs = want.match(/(.*)\s+\+\-(.+)$/), + matchRe = want.match(/^\/(.*)\/$/); + + if (got === want) { + return true; + } else if (matchPercent) { // percentage range: 100 ~ 5% + var target = parseFloat(matchPercent[1]), + percentage = parseFloat(matchPercent[2]); + if (target === 0) { + return true; + } else { + var ratio = Math.abs(1 - parseFloat(got) / target); + return 100 * ratio < percentage; + } + } else if (matchAbs) { // absolute range: 100 +-5 + var margin = parseFloat(matchAbs[2]), + fromR = parseFloat(matchAbs[1]) - margin, + toR = parseFloat(matchAbs[1]) + margin; + return parseFloat(got) >= fromR && parseFloat(got) <= toR; + } else if (matchRe) { // regex: /a,b,.*/ + return got.match(matchRe[1]); + } else { + return false; + } + } + + matchLocation (got, want) { + return this.match(got[0], util.format('%d ~0.0025%', want.lon)) && + this.match(got[1], util.format('%d ~0.0025%', want.lat)); + } + } +}; diff --git a/features/support/env.js b/features/support/env.js new file mode 100644 index 000000000..5a337bca1 --- /dev/null +++ b/features/support/env.js @@ -0,0 +1,126 @@ +var path = require('path'); +var util = require('util'); +var fs = require('fs'); +var exec = require('child_process').exec; +var d3 = require('d3-queue'); + +module.exports = function () { + this.initializeEnv = (callback) => { + this.DEFAULT_PORT = 5000; + // OSX builds on Travis hit a timeout of ~2000 from time to time + this.DEFAULT_TIMEOUT = 5000; + this.setDefaultTimeout(this.DEFAULT_TIMEOUT); + this.ROOT_FOLDER = process.cwd(); + this.OSM_USER = 'osrm'; + this.OSM_GENERATOR = 'osrm-test'; + this.OSM_UID = 1; + this.TEST_FOLDER = path.resolve(this.ROOT_FOLDER, 'test'); + this.DATA_FOLDER = path.resolve(this.TEST_FOLDER, 'cache'); + this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z'; + this.DEFAULT_SPEEDPROFILE = 'bicycle'; + this.WAY_SPACING = 100; + this.DEFAULT_GRID_SIZE = 100; // meters + this.PROFILES_PATH = path.resolve(this.ROOT_FOLDER, 'profiles'); + this.FIXTURES_PATH = path.resolve(this.ROOT_FOLDER, 'unit_tests/fixtures'); + this.BIN_PATH = path.resolve(this.ROOT_FOLDER, 'build'); + this.DEFAULT_INPUT_FORMAT = 'osm'; + this.DEFAULT_ORIGIN = [1,1]; + this.LAUNCH_TIMEOUT = 1000; + this.SHUTDOWN_TIMEOUT = 10000; + this.DEFAULT_LOAD_METHOD = 'datastore'; + this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log'); + this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log'); + + // OS X shim to ensure shared libraries from custom locations can be loaded + // This is needed in OS X >= 10.11 because DYLD_LIBRARY_PATH is blocked + // https://forums.developer.apple.com/thread/9233 + this.LOAD_LIBRARIES = process.env.OSRM_SHARED_LIBRARY_PATH ? util.format('DYLD_LIBRARY_PATH=%s ', process.env.OSRM_SHARED_LIBRARY_PATH) : ''; + + // TODO make sure this works on win + if (process.platform.match(/indows.*/)) { + this.TERMSIGNAL = 9; + this.EXE = '.exe'; + this.QQ = '"'; + } else { + this.TERMSIGNAL = 'SIGTERM'; + this.EXE = ''; + this.QQ = ''; + } + + // eslint-disable-next-line no-console + console.info(util.format('Node Version', process.version)); + if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests'); + + if (process.env.OSRM_PORT) { + this.OSRM_PORT = parseInt(process.env.OSRM_PORT); + // eslint-disable-next-line no-console + console.info(util.format('Port set to %d', this.OSRM_PORT)); + } else { + this.OSRM_PORT = this.DEFAULT_PORT; + // eslint-disable-next-line no-console + console.info(util.format('Using default port %d', this.OSRM_PORT)); + } + + if (process.env.OSRM_TIMEOUT) { + this.OSRM_TIMEOUT = parseInt(process.env.OSRM_TIMEOUT); + // eslint-disable-next-line no-console + console.info(util.format('Timeout set to %d', this.OSRM_TIMEOUT)); + } else { + this.OSRM_TIMEOUT = this.DEFAULT_TIMEOUT; + // eslint-disable-next-line no-console + console.info(util.format('Using default timeout %d', this.OSRM_TIMEOUT)); + } + + fs.exists(this.TEST_FOLDER, (exists) => { + if (!exists) throw new Error(util.format('*** Test folder %s doesn\'t exist.', this.TEST_FOLDER)); + callback(); + }); + }; + + this.verifyOSRMIsNotRunning = () => { + if (this.OSRMLoader.up()) { + throw new Error('*** osrm-routed is already running.'); + } + }; + + this.verifyExistenceOfBinaries = (callback) => { + var verify = (bin, cb) => { + var binPath = path.resolve(util.format('%s/%s%s', this.BIN_PATH, bin, this.EXE)); + fs.exists(binPath, (exists) => { + if (!exists) throw new Error(util.format('%s is missing. Build failed?', binPath)); + var helpPath = util.format('%s%s --help > /dev/null 2>&1', this.LOAD_LIBRARIES, binPath); + exec(helpPath, (err) => { + if (err) { + this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); + throw new Error(util.format('*** %s exited with code %d', helpPath, err.code)); + } + cb(); + }); + }); + }; + + var q = d3.queue(); + ['osrm-extract', 'osrm-contract', 'osrm-routed'].forEach(bin => { q.defer(verify, bin); }); + q.awaitAll(() => { + callback(); + }); + }; + + this.AfterConfiguration = (callback) => { + this.clearLogFiles(() => { + this.verifyOSRMIsNotRunning(); + this.verifyExistenceOfBinaries(() => { + callback(); + }); + }); + }; + + process.on('exit', () => { + if (this.OSRMLoader.loader) this.OSRMLoader.shutdown(() => {}); + }); + + process.on('SIGINT', () => { + process.exit(2); + // TODO need to handle for windows?? + }); +}; diff --git a/features/support/env.rb b/features/support/env.rb deleted file mode 100644 index cb707f9e1..000000000 --- a/features/support/env.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'rspec/expectations' - - -DEFAULT_PORT = 5000 -DEFAULT_TIMEOUT = 2 -ROOT_FOLDER = Dir.pwd -OSM_USER = 'osrm' -OSM_GENERATOR = 'osrm-test' -OSM_UID = 1 -TEST_FOLDER = File.join ROOT_FOLDER, 'test' -DATA_FOLDER = 'cache' -OSM_TIMESTAMP = '2000-01-01T00:00:00Z' -DEFAULT_SPEEDPROFILE = 'bicycle' -WAY_SPACING = 100 -DEFAULT_GRID_SIZE = 100 #meters -PROFILES_PATH = File.join ROOT_FOLDER, 'profiles' -FIXTURES_PATH = File.join ROOT_FOLDER, 'unit_tests/fixtures' -BIN_PATH = File.join ROOT_FOLDER, 'build' -DEFAULT_INPUT_FORMAT = 'osm' -DEFAULT_ORIGIN = [1,1] -LAUNCH_TIMEOUT = 1 -SHUTDOWN_TIMEOUT = 10 -DEFAULT_LOAD_METHOD = 'datastore' -OSRM_ROUTED_LOG_FILE = 'osrm-routed.log' - -if ENV['OS']=~/Windows.*/ then - TERMSIGNAL=9 -else - TERMSIGNAL='TERM' -end - - -def log_time_and_run cmd - log_time cmd - `#{cmd}` -end - -def log_time cmd - puts "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S:%L')}] #{cmd}" -end - - -puts "Ruby version #{RUBY_VERSION}" -unless RUBY_VERSION.to_f >= 1.9 - raise "*** Please upgrade to Ruby 1.9.x to run the OSRM cucumber tests" -end - -if ENV["OSRM_PORT"] - OSRM_PORT = ENV["OSRM_PORT"].to_i - puts "Port set to #{OSRM_PORT}" -else - OSRM_PORT = DEFAULT_PORT - puts "Using default port #{OSRM_PORT}" -end - -if ENV["OSRM_TIMEOUT"] - OSRM_TIMEOUT = ENV["OSRM_TIMEOUT"].to_i - puts "Timeout set to #{OSRM_TIMEOUT}" -else - OSRM_TIMEOUT = DEFAULT_TIMEOUT - puts "Using default timeout #{OSRM_TIMEOUT}" -end - -unless File.exists? TEST_FOLDER - raise "*** Test folder #{TEST_FOLDER} doesn't exist." -end - -def verify_osrm_is_not_running - if OSRMLoader::OSRMBaseLoader.new.osrm_up? - raise "*** osrm-routed is already running." - end -end - -def verify_existance_of_binaries - ["osrm-extract", "osrm-prepare", "osrm-routed"].each do |bin| - unless File.exists? "#{BIN_PATH}/#{bin}#{EXE}" - raise "*** #{BIN_PATH}/#{bin}#{EXE} is missing. Build failed?" - end - end -end - -if ENV['OS']=~/Windows.*/ then - EXE='.exe' - QQ='"' -else - EXE='' - QQ='' -end - -AfterConfiguration do |config| - clear_log_files - verify_osrm_is_not_running - verify_existance_of_binaries -end - -at_exit do - OSRMLoader::OSRMBaseLoader.new.shutdown -end diff --git a/features/support/exception_classes.js b/features/support/exception_classes.js new file mode 100644 index 000000000..36bdffe8b --- /dev/null +++ b/features/support/exception_classes.js @@ -0,0 +1,132 @@ +'use strict'; + +var util = require('util'); +var path = require('path'); +var fs = require('fs'); +var chalk = require('chalk'); + +var OSRMError = class extends Error { + constructor (process, code, msg, log, lines) { + super(msg); + this.process = process; + this.code = code; + this.msg = msg; + this.lines = lines; + this.log = log; + } + + extract (callback) { + this.logTail(this.log, this.lines, callback); + } + + // toString (callback) { + // this.extract((tail) => { + // callback(util.format('*** %s\nLast %s from %s:\n%s\n', this.msg, this.lines, this.log, tail)); + // }); + // } + + logTail (logPath, n, callback) { + var expanded = path.resolve(this.TEST_FOLDER, logPath); + fs.exists(expanded, (exists) => { + if (exists) { + fs.readFile(expanded, (err, data) => { + var lines = data.toString().trim().split('\n'); + callback(lines + .slice(lines.length - n) + .map(line => util.format(' %s', line)) + .join('\n')); + }); + } else { + callback(util.format('File %s does not exist!', expanded)); + } + }); + } +}; + +var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\'); + +module.exports = { + OSRMError: OSRMError, + + FileError: class extends OSRMError { + constructor (logFile, code, msg) { + super ('fileutil', code, msg, logFile, 5); + } + }, + + LaunchError: class extends OSRMError { + constructor (logFile, launchProcess, code, msg) { + super (launchProcess, code, msg, logFile, 5); + } + }, + + ExtractError: class extends OSRMError { + constructor (logFile, code, msg) { + super('osrm-extract', code, msg, logFile, 3); + } + }, + + ContractError: class extends OSRMError { + constructor (logFile, code, msg) { + super('osrm-contract', code, msg, logFile, 3); + } + }, + + RoutedError: class extends OSRMError { + constructor (logFile, msg) { + super('osrm-routed', null, msg, logFile, 3); + } + }, + + TableDiffError: class extends Error { + constructor (expected, actual) { + super(); + this.headers = expected.raw()[0]; + this.expected = expected.hashes(); + this.actual = actual; + this.diff = []; + this.hasErrors = false; + + var good = 0, bad = 0; + + this.expected.forEach((row, i) => { + var rowError = false; + + for (var j in row) { + if (unescapeStr(row[j]) != actual[i][j]) { + rowError = true; + this.hasErrors = true; + break; + } + } + + if (rowError) { + bad++; + this.diff.push(Object.assign({}, row, {c_status: 'undefined'})); + this.diff.push(Object.assign({}, actual[i], {c_status: 'comment'})); + } else { + good++; + this.diff.push(row); + } + }); + } + + get string () { + if (!this.hasErrors) return null; + + var s = ['Tables were not identical:']; + s.push(this.headers.map(key => ' ' + key).join(' | ')); + this.diff.forEach((row) => { + var rowString = '| '; + this.headers.forEach((header) => { + if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | '); + else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | '); + else rowString += chalk.red('(+) ' + row[header] + ' | '); + }); + s.push(rowString); + }); + + return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534'; + } + } +}; diff --git a/features/support/exceptions.js b/features/support/exceptions.js new file mode 100644 index 000000000..6af1a93b0 --- /dev/null +++ b/features/support/exceptions.js @@ -0,0 +1,15 @@ +var exceptions = require('./exception_classes'); + +module.exports = function () { + this.OSRMError = exceptions.OSRMError, + + this.FileError = (code, msg) => new (exceptions.FileError.bind(exceptions.FileError, this.PREPROCESS_LOG_FILE))(code, msg); + + this.LaunchError = (code, launchProcess, msg) => new (exceptions.LaunchError.bind(exceptions.LaunchError, this.ERROR_LOG_FILE))(code, launchProcess, msg); + + this.ExtractError = (code, msg) => new (exceptions.ExtractError.bind(exceptions.ExtractError, this.PREPROCESS_LOG_FILE))(code, msg); + + this.ContractError = (code, msg) => new (exceptions.ContractError.bind(exceptions.ContractError, this.PREPROCESS_LOG_FILE))(code, msg); + + this.RoutedError = (msg) => new (exceptions.RoutedError.bind(exceptions.RoutedError, this.OSRM_ROUTED_LOG_FILE))(msg); +}; diff --git a/features/support/exceptions.rb b/features/support/exceptions.rb deleted file mode 100644 index 02f59d388..000000000 --- a/features/support/exceptions.rb +++ /dev/null @@ -1,62 +0,0 @@ - -class OSRMError < StandardError - attr_accessor :msg, :code, :process - - def initialize process, code, msg, log, lines - @process = process - @code = code - @msg = msg - @lines = lines - @log = log - @extract = log_tail @log, @lines - end - - def to_s - "*** #{@msg}\nLast #{@lines} lines from #{@log}:\n#{@extract}\n" - end - - private - - def log_tail path, n - Dir.chdir TEST_FOLDER do - expanded = File.expand_path path - if File.exists? expanded - File.open(expanded) do |f| - return f.tail(n).map { |line| " #{line}" }.join "\n" - end - else - return "File '#{expanded} does not exist!" - end - end - end -end - -class FileError < OSRMError - def initialize code, msg - super 'fileutil', code, msg, PREPROCESS_LOG_FILE, 5 - end -end - -class OsmosisError < OSRMError - def initialize code, msg - super 'osmosis', code, msg, PREPROCESS_LOG_FILE, 40 - end -end - -class ExtractError < OSRMError - def initialize code, msg - super 'osrm-extract', code, msg, PREPROCESS_LOG_FILE, 3 - end -end - -class PrepareError < OSRMError - def initialize code, msg - super 'osrm-prepare', code, msg, PREPROCESS_LOG_FILE, 3 - end -end - -class RoutedError < OSRMError - def initialize msg - super 'osrm-routed', nil, msg, OSRM_ROUTED_LOG_FILE, 3 - end -end diff --git a/features/support/file.rb b/features/support/file.rb deleted file mode 100644 index dfaae0a45..000000000 --- a/features/support/file.rb +++ /dev/null @@ -1,34 +0,0 @@ -class File - - # read last n lines of a file (trailing newlines are ignored) - def tail(n) - return [] if size==0 - buffer = 1024 - str = nil - - if size>buffer - chunks = [] - lines = 0 - idx = size - begin - idx -= buffer # rewind - if idx<0 - buffer += idx # adjust last read to avoid negative index - idx = 0 - end - seek(idx) - chunk = read(buffer) - chunk.gsub!(/\n+\Z/,"") if chunks.empty? # strip newlines from end of file (first chunk) - lines += chunk.count("\n") # update total lines found - chunks.unshift chunk # prepend - end while lines<(n) && idx>0 # stop when enough lines found or no more to read - str = chunks.join('') - else - str = read(buffer) - end - - # return last n lines of str - lines = str.split("\n") - lines.size>=n ? lines[-n,n] : lines - end -end \ No newline at end of file diff --git a/features/support/fuzzy.js b/features/support/fuzzy.js new file mode 100644 index 000000000..6eebe0a61 --- /dev/null +++ b/features/support/fuzzy.js @@ -0,0 +1,5 @@ +var classes = require('./data_classes'); + +module.exports = function() { + this.FuzzyMatch = new classes.FuzzyMatch(); +}; diff --git a/features/support/fuzzy.rb b/features/support/fuzzy.rb deleted file mode 100644 index 066d9c5a8..000000000 --- a/features/support/fuzzy.rb +++ /dev/null @@ -1,32 +0,0 @@ -class FuzzyMatch - - def self.match got, want - if got == want - return true - elsif want.match /(.*)\s+~(.+)%$/ #percentage range: 100 ~5% - target = $1.to_f - percentage = $2.to_f - if target==0 - return true - else - ratio = (1-(got.to_f / target)).abs; - return 100*ratio < percentage; - end - elsif want.match /(.*)\s+\+\-(.+)$/ #absolute range: 100 +-5 - margin = $2.to_f - from = $1.to_f-margin - to = $1.to_f+margin - return got.to_f >= from && got.to_f <= to - elsif want =~ /^\/(.*)\/$/ #regex: /a,b,.*/ - return got =~ /#{$1}/ - else - return false - end - end - - def self.match_location got, want - match( got[0], "#{want.lat} ~0.0025%" ) && - match( got[1], "#{want.lon} ~0.0025%" ) - end - -end diff --git a/features/support/hash.js b/features/support/hash.js new file mode 100644 index 000000000..6ab44ad20 --- /dev/null +++ b/features/support/hash.js @@ -0,0 +1,37 @@ +var fs = require('fs'); +var path = require('path'); +var crypto = require('crypto'); +var d3 = require('d3-queue'); + +module.exports = function () { + this.hashOfFiles = (paths, cb) => { + paths = Array.isArray(paths) ? paths : [paths]; + var shasum = crypto.createHash('sha1'); + + var q = d3.queue(1); + + var addFile = (path, cb) => { + fs.readFile(path, (err, data) => { + shasum.update(data); + cb(err); + }); + }; + + paths.forEach(path => { q.defer(addFile, path); }); + + q.awaitAll(err => { + if (err) throw new Error('*** Error reading files:', err); + cb(shasum.digest('hex')); + }); + }; + + this.hashProfile = (cb) => { + this.hashOfFiles(path.resolve(this.PROFILES_PATH, this.profile + '.lua'), cb); + }; + + this.hashString = (str) => { + return crypto.createHash('sha1').update(str).digest('hex'); + }; + + return this; +}; diff --git a/features/support/hash.rb b/features/support/hash.rb deleted file mode 100644 index 6980b6645..000000000 --- a/features/support/hash.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'digest/sha1' - -bin_extract_hash = nil -profile_hashes = nil - -def hash_of_files paths - paths = [paths] unless paths.is_a? Array - hash = Digest::SHA1.new - for path in paths do - open(path,'rb') do |io| - while !io.eof - buf = io.readpartial 1024 - hash.update buf - end - end - end - return hash.hexdigest -end - - -def profile_hash - profile_hashes ||= {} - profile_hashes[@profile] ||= hash_of_files "#{PROFILES_PATH}/#{@profile}.lua" -end - -def osm_hash - @osm_hash ||= Digest::SHA1.hexdigest osm_str -end - -def lua_lib_hash - @lua_lib_hash ||= hash_of_files Dir.glob("../profiles/lib/*.lua") -end - -def bin_extract_hash - @bin_extract_hash ||= hash_of_files "#{BIN_PATH}/osrm-extract#{EXE}" - @bin_extract_hash -end - -def bin_prepare_hash - @bin_prepare_hash ||= hash_of_files "#{BIN_PATH}/osrm-prepare#{EXE}" -end - -def bin_routed_hash - @bin_routed_hash ||= hash_of_files "#{BIN_PATH}/osrm-routed#{EXE}" -end - -# combine state of data, profile and binaries into a hashes that identifies -# the exact test situation at different stages, so we can later skip steps when possible. -def fingerprint_osm - @fingerprint_osm ||= Digest::SHA1.hexdigest "#{osm_hash}" -end - -def fingerprint_extract - @fingerprint_extract ||= Digest::SHA1.hexdigest "#{profile_hash}-#{lua_lib_hash}-#{bin_extract_hash}" -end - -def fingerprint_prepare - @fingerprint_prepare ||= Digest::SHA1.hexdigest "#{bin_prepare_hash}" -end - -def fingerprint_route - @fingerprint_route ||= Digest::SHA1.hexdigest "#{bin_routed_hash}" -end \ No newline at end of file diff --git a/features/support/hooks.js b/features/support/hooks.js new file mode 100644 index 000000000..f8c7a2f65 --- /dev/null +++ b/features/support/hooks.js @@ -0,0 +1,37 @@ +var util = require('util'); + +module.exports = function () { + this.BeforeFeatures((features, callback) => { + this.pid = null; + this.initializeEnv(() => { + this.initializeOptions(callback); + }); + }); + + this.Before((scenario, callback) => { + this.scenarioTitle = scenario.getName(); + + this.loadMethod = this.DEFAULT_LOAD_METHOD; + this.queryParams = {}; + var d = new Date(); + this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()); + this.resetData(); + this.hasLoggedPreprocessInfo = false; + this.hasLoggedScenarioInfo = false; + this.setGridSize(this.DEFAULT_GRID_SIZE); + this.setOrigin(this.DEFAULT_ORIGIN); + callback(); + }); + + this.After((scenario, callback) => { + this.setExtractArgs(''); + this.setContractArgs(''); + if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback); + else callback(); + }); + + this.Around('@stress', (scenario, callback) => { + // TODO implement stress timeout? Around support is being dropped in cucumber-js anyway + callback(); + }); +}; diff --git a/features/support/hooks.rb b/features/support/hooks.rb deleted file mode 100644 index 6af9a8ffa..000000000 --- a/features/support/hooks.rb +++ /dev/null @@ -1,35 +0,0 @@ - -STRESS_TIMEOUT = 300 - - -Before do |scenario| - - # fetch scenario and feature name, so we can use it in log files if needed - case scenario - when Cucumber::RunningTestCase::Scenario - @feature_name = scenario.feature.name - @scenario_title = scenario.name - when Cucumber::RunningTestCase::ExampleRow - @feature_name = scenario.scenario_outline.feature.name - @scenario_title = scenario.scenario_outline.name - end - - @load_method = DEFAULT_LOAD_METHOD - @query_params = [] - @scenario_time = Time.now.strftime("%Y-%m-%dT%H:%m:%SZ") - reset_data - @has_logged_preprocess_info = false - @has_logged_scenario_info = false - set_grid_size DEFAULT_GRID_SIZE - set_origin DEFAULT_ORIGIN - -end - -Around('@stress') do |scenario, block| - Timeout.timeout(STRESS_TIMEOUT) do - block.call - end -end - -After do -end diff --git a/features/support/http.js b/features/support/http.js new file mode 100644 index 000000000..5da59c428 --- /dev/null +++ b/features/support/http.js @@ -0,0 +1,51 @@ +var Timeout = require('node-timeout'); +var request = require('request'); + +module.exports = function () { + this.paramsToString = (params) => { + var paramString = ''; + if (params.coordinates !== undefined) { + // FIXME this disables passing the output if its a default + // Remove after #2173 is fixed. + var outputString = (params.output && params.output !== 'json') ? ('.' + params.output) : ''; + paramString = params.coordinates.join(';') + outputString; + delete params.coordinates; + delete params.output; + } + if (Object.keys(params).length) { + paramString += '?' + Object.keys(params).map(k => k + '=' + params[k]).join('&'); + } + + return paramString; + }; + + this.sendRequest = (baseUri, parameters, callback) => { + var limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } }); + + var runRequest = (cb) => { + var params = this.paramsToString(parameters); + this.query = baseUri + (params.length ? '/' + params : ''); + + request(this.query, (err, res, body) => { + if (err && err.code === 'ECONNREFUSED') { + throw new Error('*** osrm-routed is not running.'); + } else if (err && err.statusCode === 408) { + throw new Error(); + } + + return cb(err, res, body); + }); + }; + + runRequest(limit((err, res, body) => { + if (err) { + if (err.statusCode === 408) + return callback(this.RoutedError('*** osrm-routed did not respond')); + else if (err.code === 'ECONNREFUSED') + return callback(this.RoutedError('*** osrm-routed is not running')); + } + //console.log(body+"\n"); + return callback(err, res, body); + })); + }; +}; diff --git a/features/support/http.rb b/features/support/http.rb deleted file mode 100644 index 0b9fb9af7..000000000 --- a/features/support/http.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'net/http' - -# Converts an array [["param","val1"], ["param","val2"]] into ?param=val1¶m=val2 -def params_to_url params - kv_pairs = params.map { |kv| kv[0].to_s + "=" + kv[1].to_s } - url = kv_pairs.size > 0 ? ("?" + kv_pairs.join("&")) : "" - return url -end - -# Converts an array [["param","val1"], ["param","val2"]] into ["param"=>["val1", "val2"]] -def params_to_map params - result = {} - params.each do |pair| - if not result.has_key? pair[0] - result[pair[0]] = [] - end - result[pair[0]] << [pair[1]] - end -end - -def send_request base_uri, parameters - Timeout.timeout(OSRM_TIMEOUT) do - if @http_method.eql? "POST" - uri = URI.parse base_uri - @query = uri.to_s - response = Net::HTTP.post_form uri, (params_to_map parameters) - else - uri = URI.parse base_uri+(params_to_url parameters) - @query = uri.to_s - response = Net::HTTP.get_response uri - end - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." -end diff --git a/features/support/launch.js b/features/support/launch.js new file mode 100644 index 000000000..ee335e301 --- /dev/null +++ b/features/support/launch.js @@ -0,0 +1,5 @@ +var launchClasses = require('./launch_classes'); + +module.exports = function () { + this._OSRMLoader = () => new (launchClasses._OSRMLoader.bind(launchClasses._OSRMLoader, this))(); +}; diff --git a/features/support/launch.rb b/features/support/launch.rb deleted file mode 100644 index d8d23aeaa..000000000 --- a/features/support/launch.rb +++ /dev/null @@ -1,137 +0,0 @@ -require 'socket' -require 'open3' -require 'json' - -# Only one isntance of osrm-routed is ever launched, to avoid collisions. -# The default is to keep osrm-routed running and load data with datastore. -# however, osrm-routed it shut down and relaunched for each scenario thats -# loads data directly. -class OSRMLoader - - class OSRMBaseLoader - @@pid = nil - - def launch - Timeout.timeout(LAUNCH_TIMEOUT) do - osrm_up - wait_for_connection - end - rescue Timeout::Error - raise RoutedError.new "Launching osrm-routed timed out." - end - - def shutdown - Timeout.timeout(SHUTDOWN_TIMEOUT) do - osrm_down - end - rescue Timeout::Error - kill - raise RoutedError.new "Shutting down osrm-routed timed out." - end - - def osrm_up? - if @@pid - begin - if Process.waitpid(@@pid, Process::WNOHANG) then - false - else - true - end - rescue Errno::ESRCH, Errno::ECHILD - false - end - end - end - - def osrm_down - if @@pid - Process.kill TERMSIGNAL, @@pid - wait_for_shutdown - @@pid = nil - end - end - - def kill - if @@pid - Process.kill 'KILL', @@pid - end - end - - def wait_for_connection - while true - begin - socket = TCPSocket.new('127.0.0.1', OSRM_PORT) - return - rescue Errno::ECONNREFUSED - sleep 0.1 - end - end - end - - def wait_for_shutdown - while osrm_up? - sleep 0.01 - end - end - end - - # looading data directly when lauching osrm-routed: - # under this scheme, osmr-routed is launched and shutdown for each scenario, - # and osrm-datastore is not used - class OSRMDirectLoader < OSRMBaseLoader - def load world, input_file, &block - @world = world - @input_file = input_file - Dir.chdir TEST_FOLDER do - shutdown - launch - yield - shutdown - end - end - - def osrm_up - return if @@pid - @@pid = Process.spawn("#{BIN_PATH}/osrm-routed #{@input_file} --port #{OSRM_PORT}",:out=>OSRM_ROUTED_LOG_FILE, :err=>OSRM_ROUTED_LOG_FILE) - Process.detach(@@pid) # avoid zombie processes - end - - end - - # looading data with osrm-datastore: - # under this scheme, osmr-routed is launched once and kept running for all scenarios, - # and osrm-datastore is used to load data for each scenario - class OSRMDatastoreLoader < OSRMBaseLoader - def load world, input_file, &block - @world = world - @input_file = input_file - Dir.chdir TEST_FOLDER do - load_data - launch unless @@pid - yield - end - end - - def load_data - run_bin "osrm-datastore", @input_file - end - - def osrm_up - return if osrm_up? - @@pid = Process.spawn("#{BIN_PATH}/osrm-routed --shared-memory=1 --port #{OSRM_PORT}",:out=>OSRM_ROUTED_LOG_FILE, :err=>OSRM_ROUTED_LOG_FILE) - Process.detach(@@pid) # avoid zombie processes - end - end - - def self.load world, input_file, &block - method = world.instance_variable_get "@load_method" - if method == 'datastore' - OSRMDatastoreLoader.new.load world, input_file, &block - elsif method == 'directly' - OSRMDirectLoader.new.load world, input_file, &block - else - raise "*** Unknown load method '#{method}'" - end - end - -end diff --git a/features/support/launch_classes.js b/features/support/launch_classes.js new file mode 100644 index 000000000..38f3b313d --- /dev/null +++ b/features/support/launch_classes.js @@ -0,0 +1,163 @@ +'use strict'; + +var fs = require('fs'); +var net = require('net'); +var spawn = require('child_process').spawn; +var util = require('util'); +var Timeout = require('node-timeout'); + +var OSRMBaseLoader = class { + constructor (scope) { + this.scope = scope; + } + + launch (callback) { + var limit = Timeout(this.scope.LAUNCH_TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') }); + + var runLaunch = (cb) => { + this.osrmUp(() => { + this.waitForConnection(cb); + }); + }; + + runLaunch(limit((e) => { if (e) callback(e); else callback(); })); + } + + shutdown (callback) { + var limit = Timeout(this.scope.SHUTDOWN_TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')}); + + var runShutdown = (cb) => { + this.osrmDown(cb); + }; + + runShutdown(limit((e) => { if (e) callback(e); else callback(); })); + } + + osrmIsRunning () { + return !!this.scope.pid && this.child && !this.child.killed; + } + + osrmDown (callback) { + if (this.scope.pid) { + process.kill(this.scope.pid, this.scope.TERMSIGNAL); + this.waitForShutdown(callback); + this.scope.pid = null; + } else callback(true); + } + + waitForConnection (callback) { + net.connect({ + port: this.scope.OSRM_PORT, + host: '127.0.0.1' + }) + .on('connect', () => { + callback(); + }) + .on('error', (e) => { + setTimeout(() => { + callback(e); + }, 100); + }); + } + + waitForShutdown (callback) { + var check = () => { + if (!this.osrmIsRunning()) return callback(); + }; + setTimeout(check, 100); + } +}; + +var OSRMDirectLoader = class extends OSRMBaseLoader { + constructor (scope) { + super(scope); + } + + load (inputFile, callback) { + this.inputFile = inputFile; + this.shutdown(() => { + this.launch(callback); + }); + } + + osrmUp (callback) { + if (this.scope.pid) return callback(); + var writeToLog = (data) => { + fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; }); + }; + + var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)]); + this.scope.pid = child.pid; + child.stdout.on('data', writeToLog); + child.stderr.on('data', writeToLog); + + callback(); + } +}; + +var OSRMDatastoreLoader = class extends OSRMBaseLoader { + constructor (scope) { + super(scope); + } + + load (inputFile, callback) { + this.inputFile = inputFile; + this.loadData((err) => { + if (err) return callback(err); + if (!this.scope.pid) return this.launch(callback); + else callback(); + }); + } + + loadData (callback) { + this.scope.runBin('osrm-datastore', this.inputFile, (err) => { + if (err) return callback(this.scope.LaunchError(this.exitCode, 'datastore', err)); + callback(); + }); + } + + osrmUp (callback) { + if (this.scope.pid) return callback(); + var writeToLog = (data) => { + fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; }); + }; + + var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)]); + this.child = child; + this.scope.pid = child.pid; + child.stdout.on('data', writeToLog); + child.stderr.on('data', writeToLog); + + callback(); + } +}; + +module.exports = { + _OSRMLoader: class { + constructor (scope) { + this.scope = scope; + this.loader = null; + } + + load (inputFile, callback) { + var method = this.scope.loadMethod; + if (method === 'datastore') { + this.loader = new OSRMDatastoreLoader(this.scope); + this.loader.load(inputFile, callback); + } else if (method === 'directly') { + this.loader = new OSRMDirectLoader(this.scope); + this.loader.load(inputFile, callback); + } else { + throw new Error('*** Unknown load method ' + method); + } + } + + shutdown (callback) { + this.loader.shutdown(callback); + } + + up () { + return this.loader ? this.loader.osrmIsRunning() : false; + } + } +}; diff --git a/features/support/log.js b/features/support/log.js new file mode 100644 index 000000000..c428cb9d9 --- /dev/null +++ b/features/support/log.js @@ -0,0 +1,90 @@ +var fs = require('fs'); + +module.exports = function () { + this.clearLogFiles = (callback) => { + // emptying existing files, rather than deleting and writing new ones makes it + // easier to use tail -f from the command line + fs.writeFile(this.OSRM_ROUTED_LOG_FILE, '', err => { + if (err) throw err; + fs.writeFile(this.PREPROCESS_LOG_FILE, '', err => { + if (err) throw err; + fs.writeFile(this.LOG_FILE, '', err => { + if (err) throw err; + callback(); + }); + }); + }); + }; + + var log = this.log = (s, type) => { + s = s || ''; + type = type || null; + var file = type === 'preprocess' ? this.PREPROCESS_LOG_FILE : this.LOG_FILE; + fs.appendFile(file, s + '\n', err => { + if (err) throw err; + }); + }; + + this.logScenarioFailInfo = () => { + if (this.hasLoggedScenarioInfo) return; + + log('========================================='); + log('Failed scenario: ' + this.scenarioTitle); + log('Time: ' + this.scenarioTime); + log('Fingerprint osm stage: ' + this.osmData.fingerprintOSM); + log('Fingerprint extract stage: ' + this.fingerprintExtract); + log('Fingerprint contract stage: ' + this.fingerprintContract); + log('Fingerprint route stage: ' + this.fingerprintRoute); + log('Profile: ' + this.profile); + log(); + log('```xml'); // so output can be posted directly to github comment fields + log(this.osmData.str.trim()); + log('```'); + log(); + log(); + + this.hasLoggedScenarioInfo = true; + }; + + this.logFail = (expected, got, attempts) => { + this.logScenarioFailInfo(); + log('== '); + log('Expected: ' + JSON.stringify(expected)); + log('Got: ' + JSON.stringify(got)); + log(); + ['route','forw','backw'].forEach((direction) => { + if (attempts[direction]) { + log('Direction: ' + direction); + log('Query: ' + attempts[direction].query); + log('Response: ' + attempts[direction].response.body); + log(); + } + }); + }; + + this.logPreprocessInfo = () => { + if (this.hasLoggedPreprocessInfo) return; + log('=========================================', 'preprocess'); + log('Preprocessing data for scenario: ' + this.scenarioTitle, 'preprocess'); + log('Time: ' + this.scenarioTime, 'preprocess'); + log('', 'preprocess'); + log('== OSM data:', 'preprocess'); + log('```xml', 'preprocess'); // so output can be posted directly to github comment fields + log(this.osmData.str, 'preprocess'); + log('```', 'preprocess'); + log('', 'preprocess'); + log('== Profile:', 'preprocess'); + log(this.profile, 'preprocess'); + log('', 'preprocess'); + this.hasLoggedPreprocessInfo = true; + }; + + this.logPreprocess = (str) => { + this.logPreprocessInfo(); + log(str, 'preprocess'); + }; + + this.logPreprocessDone = () => { + log('Done with preprocessing at ' + new Date(), 'preprocess'); + }; +}; diff --git a/features/support/log.rb b/features/support/log.rb deleted file mode 100644 index 8e6f9c146..000000000 --- a/features/support/log.rb +++ /dev/null @@ -1,88 +0,0 @@ -# logging - -PREPROCESS_LOG_FILE = 'preprocessing.log' -LOG_FILE = 'fail.log' - - -def clear_log_files - Dir.chdir TEST_FOLDER do - # emptying existing files, rather than deleting and writing new ones makes it - # easier to use tail -f from the command line - `echo '' > #{OSRM_ROUTED_LOG_FILE}` - `echo '' > #{PREPROCESS_LOG_FILE}` - `echo '' > #{LOG_FILE}` - end -end - -def log s='', type=nil - if type == :preprocess - file = PREPROCESS_LOG_FILE - else - file = LOG_FILE - end - File.open(file, 'a') {|f| f.write("#{s}\n") } -end - - -def log_scenario_fail_info - return if @has_logged_scenario_info - log "=========================================" - log "Failed scenario: #{@scenario_title}" - log "Time: #{@scenario_time}" - log "Fingerprint osm stage: #{@fingerprint_osm}" - log "Fingerprint extract stage: #{@fingerprint_extract}" - log "Fingerprint prepare stage: #{@fingerprint_prepare}" - log "Fingerprint route stage: #{@fingerprint_route}" - log "Profile: #{@profile}" - log - log '```xml' #so output can be posted directly to github comment fields - log osm_str.strip - log '```' - log - log - @has_logged_scenario_info = true -end - -def log_fail expected,got,attempts - return - log_scenario_fail_info - log "== " - log "Expected: #{expected}" - log "Got: #{got}" - log - ['route','forw','backw'].each do |direction| - if attempts[direction] - attempts[direction] - log "Direction: #{direction}" - log "Query: #{attempts[direction][:query]}" - log "Response: #{attempts[direction][:response].body}" - log - end - end -end - - -def log_preprocess_info - return if @has_logged_preprocess_info - log "=========================================", :preprocess - log "Preprocessing data for scenario: #{@scenario_title}", :preprocess - log "Time: #{@scenario_time}", :preprocess - log '', :preprocess - log "== OSM data:", :preprocess - log '```xml', :preprocess #so output can be posted directly to github comment fields - log osm_str, :preprocess - log '```', :preprocess - log '', :preprocess - log "== Profile:", :preprocess - log @profile, :preprocess - log '', :preprocess - @has_logged_preprocess_info = true -end - -def log_preprocess str - log_preprocess_info - log str, :preprocess -end - -def log_preprocess_done -end diff --git a/features/support/osm_parser.rb b/features/support/osm_parser.rb deleted file mode 100644 index 1da7c7318..000000000 --- a/features/support/osm_parser.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'OSM/StreamParser' - -locations = nil - -class OSMTestParserCallbacks < OSM::Callbacks - locations = nil - - def self.locations - if locations - locations - else - #parse the test file, so we can later reference nodes and ways by name in tests - locations = {} - file = 'test/data/test.osm' - callbacks = OSMTestParserCallbacks.new - parser = OSM::StreamParser.new(:filename => file, :callbacks => callbacks) - parser.parse - puts locations - end - end - - def node(node) - locations[node.name] = [node.lat,node.lon] - end -end \ No newline at end of file diff --git a/features/support/osmlib.rb b/features/support/osmlib.rb deleted file mode 100644 index 6b03dfa4a..000000000 --- a/features/support/osmlib.rb +++ /dev/null @@ -1,14 +0,0 @@ -#monkey-patch osmlib to fix a bug - -module OSM - class Way - def to_xml(xml) - xml.way(attributes) do - nodes.each do |node| - xml.nd(:ref => node) - end - tags.to_xml(xml) - end - end - end -end diff --git a/features/support/route.js b/features/support/route.js new file mode 100644 index 000000000..1dbce8cb9 --- /dev/null +++ b/features/support/route.js @@ -0,0 +1,171 @@ +var Timeout = require('node-timeout'); +var request = require('request'); + +module.exports = function () { + this.requestPath = (service, params, callback) => { + var uri; + if (service == 'timestamp') { + uri = [this.HOST, service].join('/'); + } else { + uri = [this.HOST, service, 'v1', this.profile].join('/'); + } + + return this.sendRequest(uri, params, callback); + }; + + this.requestUrl = (path, callback) => { + var uri = this.query = [this.HOST, path].join('/'), + limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } }); + + function runRequest (cb) { + request(uri, cb); + } + + runRequest(limit((err, res, body) => { + if (err) { + if (err.statusCode === 408) return callback(this.RoutedError('*** osrm-routed did not respond')); + else if (err.code === 'ECONNREFUSED') + return callback(this.RoutedError('*** osrm-routed is not running')); + } else + return callback(err, res, body); + })); + }; + + // Overwrites the default values in defaults + // e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]] + this.overwriteParams = (defaults, other) => { + var otherMap = {}; + for (var key in other) otherMap[key] = other[key]; + return Object.assign({}, defaults, otherMap); + }; + + var encodeWaypoints = (waypoints) => { + return waypoints.map(w => [w.lon, w.lat].map(this.ensureDecimal).join(',')); + }; + + this.requestRoute = (waypoints, bearings, userParams, callback) => { + if (bearings.length && bearings.length !== waypoints.length) throw new Error('*** number of bearings does not equal the number of waypoints'); + + var defaults = { + output: 'json', + steps: 'true', + alternatives: 'false' + }, + params = this.overwriteParams(defaults, userParams), + encodedWaypoints = encodeWaypoints(waypoints); + + params.coordinates = encodedWaypoints; + + if (bearings.length) { + params.bearings = bearings.map(b => { + var bs = b.split(','); + if (bs.length === 2) return b; + else return b += ',10'; + }).join(';'); + } + + return this.requestPath('route', params, callback); + }; + + this.requestNearest = (node, userParams, callback) => { + var defaults = { + output: 'json' + }, + params = this.overwriteParams(defaults, userParams); + params.coordinates = [[node.lon, node.lat].join(',')]; + + return this.requestPath('nearest', params, callback); + }; + + this.requestTable = (waypoints, userParams, callback) => { + var defaults = { + output: 'json' + }, + params = this.overwriteParams(defaults, userParams); + + params.coordinates = waypoints.map(w => [w.coord.lon, w.coord.lat].join(',')); + var srcs = waypoints.map((w, i) => [w.type, i]).filter(w => w[0] === 'src').map(w => w[1]), + dsts = waypoints.map((w, i) => [w.type, i]).filter(w => w[0] === 'dst').map(w => w[1]); + if (srcs.length) params.sources = srcs.join(';'); + if (dsts.length) params.destinations = dsts.join(';'); + + return this.requestPath('table', params, callback); + }; + + this.requestTrip = (waypoints, userParams, callback) => { + var defaults = { + output: 'json' + }, + params = this.overwriteParams(defaults, userParams); + + params.coordinates = encodeWaypoints(waypoints); + + return this.requestPath('trip', params, callback); + }; + + this.requestMatching = (waypoints, timestamps, userParams, callback) => { + var defaults = { + output: 'json' + }, + params = this.overwriteParams(defaults, userParams); + + params.coordinates = encodeWaypoints(waypoints); + + if (timestamps.length) { + params.timestamps = timestamps.join(';'); + } + + return this.requestPath('match', params, callback); + }; + + this.extractInstructionList = (instructions, keyFinder, postfix) => { + postfix = postfix || null; + if (instructions) { + return instructions.legs.reduce((m, v) => m.concat(v.steps), []) + .map(keyFinder) + .join(','); + } + }; + + this.wayList = (instructions) => { + return this.extractInstructionList(instructions, s => s.name); + }; + + this.bearingList = (instructions) => { + return this.extractInstructionList(instructions, s => s.maneuver.bearing_after); + }; + + this.turnList = (instructions) => { + return instructions.legs.reduce((m, v) => m.concat(v.steps), []) + .map(v => { + switch (v.maneuver.type) { + case 'depart': + case 'arrive': + return v.maneuver.type; + case 'roundabout': + return 'roundabout-exit-' + v.maneuver.exit; + case 'rotary': + if( 'rotary_name' in v ) + return v.rotary_name + '-exit-' + v.maneuver.exit; + else + return 'rotary-exit-' + v.maneuver.exit; + // FIXME this is a little bit over-simplistic for merge/fork instructions + default: + return v.maneuver.type + ' ' + v.maneuver.modifier; + } + }) + .join(','); + }; + + this.modeList = (instructions) => { + return this.extractInstructionList(instructions, s => s.mode); + }; + + this.timeList = (instructions) => { + return this.extractInstructionList(instructions, s => s.duration + 's'); + }; + + this.distanceList = (instructions) => { + return this.extractInstructionList(instructions, s => s.distance + 'm'); + }; +}; diff --git a/features/support/route.rb b/features/support/route.rb deleted file mode 100644 index 935c5be38..000000000 --- a/features/support/route.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'net/http' - -HOST = "http://127.0.0.1:#{OSRM_PORT}" -DESTINATION_REACHED = 15 #OSRM instruction code - -def request_path service, params - uri = "#{HOST}/" + service - response = send_request uri, params - return response -end - -def request_url path - uri = URI.parse"#{HOST}/#{path}" - @query = uri.to_s - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." -end - -# Overwriters the default values in defaults. -# e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]] -def overwrite_params defaults, other - merged = [] - defaults.each do |k,v| - idx = other.index { |p| p[0] == k } - if idx == nil then - merged << [k, v] - else - merged << [k, other[idx][1]] - end - end - other.each do |k,v| - if merged.index { |pair| pair[0] == k} == nil then - merged << [k, v] - end - end - - return merged -end - -def request_route waypoints, bearings, user_params - raise "*** number of bearings does not equal the number of waypoints" unless bearings.size == 0 || bearings.size == waypoints.size - - defaults = [['output','json'], ['instructions',true], ['alt',false]] - params = overwrite_params defaults, user_params - encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - if bearings.size > 0 - encoded_bearings = bearings.map { |b| ["b", b.to_s]} - parasm = params.concat encoded_waypoint.zip(encoded_bearings).flatten! 1 - else - params = params.concat encoded_waypoint - end - - return request_path "viaroute", params -end - -def request_nearest node, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params << ["loc", "#{node.lat},#{node.lon}"] - - return request_path "nearest", params -end - -def request_table waypoints, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params = params.concat waypoints.map{ |w| [w[:type],"#{w[:coord].lat},#{w[:coord].lon}"] } - - return request_path "table", params -end - -def request_trip waypoints, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params = params.concat waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - - return request_path "trip", params -end - -def request_matching waypoints, timestamps, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - if timestamps.size > 0 - encoded_timestamps = timestamps.map { |t| ["t", t.to_s]} - parasm = params.concat encoded_waypoint.zip(encoded_timestamps).flatten! 1 - else - params = params.concat encoded_waypoint - end - - return request_path "match", params -end - -def got_route? response - if response.code == "200" && !response.body.empty? - json = JSON.parse response.body - if json['status'] == 200 - return way_list( json['route_instructions']).empty? == false - end - end - return false -end - -def route_status response - if response.code == "200" && !response.body.empty? - json = JSON.parse response.body - return json['status'] - else - "HTTP #{response.code}" - end -end - -def extract_instruction_list instructions, index, postfix=nil - if instructions - instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. - map { |r| r[index] }. - map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. - join(',') - end -end - -def way_list instructions - extract_instruction_list instructions, 1 -end - -def compass_list instructions - extract_instruction_list instructions, 6 -end - -def bearing_list instructions - extract_instruction_list instructions, 7 -end - -def turn_list instructions - if instructions - types = { - 0 => :none, - 1 => :straight, - 2 => :slight_right, - 3 => :right, - 4 => :sharp_right, - 5 => :u_turn, - 6 => :sharp_left, - 7 => :left, - 8 => :slight_left, - 9 => :via, - 10 => :head, - 11 => :enter_roundabout, - 12 => :leave_roundabout, - 13 => :stay_roundabout, - 14 => :start_end_of_street, - 15 => :destination, - 16 => :enter_contraflow, - 17 => :leave_contraflow - } - # replace instructions codes with strings - # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" - instructions.map do |r| - r[0].to_s.gsub(/^\d*/) do |match| - types[match.to_i].to_s - end - end.join(',') - end -end - -def mode_list instructions - extract_instruction_list instructions, 8 -end - -def time_list instructions - extract_instruction_list instructions, 4, "s" -end - -def distance_list instructions - extract_instruction_list instructions, 2, "m" -end diff --git a/features/support/run.js b/features/support/run.js new file mode 100644 index 000000000..cede245b6 --- /dev/null +++ b/features/support/run.js @@ -0,0 +1,40 @@ +var fs = require('fs'); +var util = require('util'); +var exec = require('child_process').exec; + +module.exports = function () { + this.runBin = (bin, options, callback) => { + var opts = options.slice(); + + if (opts.match('{osm_base}')) { + if (!this.osmData.osmFile) throw new Error('*** {osm_base} is missing'); + opts = opts.replace('{osm_base}', this.osmData.osmFile); + } + + if (opts.match('{extracted_base}')) { + if (!this.osmData.extractedFile) throw new Error('*** {extracted_base} is missing'); + opts = opts.replace('{extracted_base}', this.osmData.extractedFile); + } + + if (opts.match('{contracted_base}')) { + if (!this.osmData.contractedFile) throw new Error('*** {contracted_base} is missing'); + opts = opts.replace('{contracted_base}', this.osmData.contractedFile); + } + + if (opts.match('{profile}')) { + opts = opts.replace('{profile}', [this.PROFILES_PATH, this.profile + '.lua'].join('/')); + } + + var cmd = util.format('%s%s%s/%s%s%s %s 2>%s', this.QQ, this.LOAD_LIBRARIES, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE); + process.chdir(this.TEST_FOLDER); + exec(cmd, (err, stdout, stderr) => { + this.stdout = stdout.toString(); + fs.readFile(this.ERROR_LOG_FILE, (e, data) => { + this.stderr = data ? data.toString() : ''; + this.exitCode = err && err.code || 0; + process.chdir('../'); + callback(err, stdout, stderr); + }); + }); + }; +}; diff --git a/features/support/run.rb b/features/support/run.rb deleted file mode 100644 index 42dc597a1..000000000 --- a/features/support/run.rb +++ /dev/null @@ -1,28 +0,0 @@ -def run_bin bin, options - Dir.chdir TEST_FOLDER do - opt = options.dup - - if opt.include? '{osm_base}' - raise "*** {osm_base} is missing" unless osm_file - opt.gsub! "{osm_base}", "#{osm_file}" - end - - if opt.include? '{extracted_base}' - raise "*** {extracted_base} is missing" unless extracted_file - opt.gsub! "{extracted_base}", "#{extracted_file}" - end - - if opt.include? '{prepared_base}' - raise "*** {prepared_base} is missing" unless prepared_file - opt.gsub! "{prepared_base}", "#{prepared_file}" - end - if opt.include? '{profile}' - opt.gsub! "{profile}", "#{PROFILES_PATH}/#{@profile}.lua" - end - - cmd = "#{QQ}#{BIN_PATH}/#{bin}#{EXE}#{QQ} #{opt} 2>error.log" - @stdout = `#{cmd}` - @stderr = File.read 'error.log' - @exit_code = $?.exitstatus - end -end \ No newline at end of file diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js new file mode 100644 index 000000000..b0be24b5c --- /dev/null +++ b/features/support/shared_steps.js @@ -0,0 +1,194 @@ +var util = require('util'); +var assert = require('assert'); + +module.exports = function () { + this.ShouldGetAResponse = () => { + assert.equal(this.response.statusCode, 200); + assert.ok(this.response.body); + assert.ok(this.response.body.length); + }; + + this.ShouldBeValidJSON = (callback) => { + try { + this.json = JSON.parse(this.response.body); + callback(); + } catch (e) { + callback(e); + } + }; + + this.ShouldBeWellFormed = () => { + assert.equal(typeof this.json.status, 'number'); + }; + + this.WhenIRouteIShouldGet = (table, callback) => { + this.reprocessAndLoadData(() => { + var headers = new Set(table.raw()[0]); + + var requestRow = (row, ri, cb) => { + var got; + + var afterRequest = (err, res, body) => { + if (err) return cb(err); + if (body && body.length) { + var instructions, bearings, turns, modes, times, distances; + + var json = JSON.parse(body); + + var hasRoute = json.code === 'Ok'; + + if (hasRoute) { + instructions = this.wayList(json.routes[0]); + bearings = this.bearingList(json.routes[0]); + turns = this.turnList(json.routes[0]); + modes = this.modeList(json.routes[0]); + times = this.timeList(json.routes[0]); + distances = this.distanceList(json.routes[0]); + } + + if (headers.has('status')) { + got.status = res.statusCode.toString(); + } + + if (headers.has('message')) { + got.message = json.message || ''; + } + + if (headers.has('#')) { + // comment column + got['#'] = row['#']; + } + + if (headers.has('geometry')) { + got.geometry = json.routes[0].geometry; + } + + if (headers.has('route')) { + got.route = (instructions || '').trim(); + + if (headers.has('alternative')) { + // TODO examine more than first alternative? + got.alternative =''; + if (json.routes && json.routes.length > 1) + got.alternative = this.wayList(json.routes[1]); + } + + var distance = hasRoute && json.routes[0].distance, + time = hasRoute && json.routes[0].duration; + + if (headers.has('distance')) { + if (row.distance.length) { + if (!row.distance.match(/\d+m/)) + throw new Error('*** Distance must be specified in meters. (ex: 250m)'); + got.distance = instructions ? util.format('%dm', distance) : ''; + } else { + got.distance = ''; + } + } + + if (headers.has('time')) { + if (!row.time.match(/\d+s/)) + throw new Error('*** Time must be specied in seconds. (ex: 60s)'); + got.time = instructions ? util.format('%ds', time) : ''; + } + + if (headers.has('speed')) { + if (row.speed !== '' && instructions) { + if (!row.speed.match(/\d+ km\/h/)) + throw new Error('*** Speed must be specied in km/h. (ex: 50 km/h)'); + var speed = time > 0 ? Math.round(3.6*distance/time) : null; + got.speed = util.format('%d km/h', speed); + } else { + got.speed = ''; + } + } + + var putValue = (key, value) => { + if (headers.has(key)) got[key] = instructions ? value : ''; + }; + + putValue('bearing', bearings); + putValue('turns', turns); + putValue('modes', modes); + putValue('times', times); + putValue('distances', distances); + } + + var ok = true; + + for (var key in row) { + if (this.FuzzyMatch.match(got[key], row[key])) { + got[key] = row[key]; + } else { + ok = false; + } + } + + if (!ok) { + this.logFail(row, got, { route: { query: this.query, response: res }}); + } + + cb(null, got); + } else { + cb(new Error('request failed to return valid body')); + } + }; + + if (headers.has('request')) { + got = { request: row.request }; + this.requestUrl(row.request, afterRequest); + } else { + var defaultParams = this.queryParams; + var userParams = []; + got = {}; + for (var k in row) { + var match = k.match(/param:(.*)/); + if (match) { + if (row[k] === '(nil)') { + userParams.push([match[1], null]); + } else if (row[k]) { + userParams.push([match[1], row[k]]); + } + got[k] = row[k]; + } + } + + var params = this.overwriteParams(defaultParams, userParams), + waypoints = [], + bearings = []; + + if (row.bearings) { + got.bearings = row.bearings; + bearings = row.bearings.split(' ').filter(b => !!b); + } + + if (row.from && row.to) { + var fromNode = this.findNodeByName(row.from); + if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"'), row.from); + waypoints.push(fromNode); + + var toNode = this.findNodeByName(row.to); + if (!toNode) throw new Error(util.format('*** unknown to-node "%s"'), row.to); + waypoints.push(toNode); + + got.from = row.from; + got.to = row.to; + this.requestRoute(waypoints, bearings, params, afterRequest); + } else if (row.waypoints) { + row.waypoints.split(',').forEach((n) => { + var node = this.findNodeByName(n.trim()); + if (!node) throw new Error('*** unknown waypoint node "%s"', n.trim()); + waypoints.push(node); + }); + got.waypoints = row.waypoints; + this.requestRoute(waypoints, bearings, params, afterRequest); + } else { + throw new Error('*** no waypoints'); + } + } + }; + + this.processRowsAndDiff(table, requestRow, callback); + }); + }; +}; diff --git a/features/support/shortcuts.rb b/features/support/shortcuts.rb deleted file mode 100644 index 20bc3c0fe..000000000 --- a/features/support/shortcuts.rb +++ /dev/null @@ -1,3 +0,0 @@ -def shortcuts_hash - @shortcuts_hash ||= {} -end diff --git a/features/support/table_patch.js b/features/support/table_patch.js new file mode 100644 index 000000000..16ffebb8c --- /dev/null +++ b/features/support/table_patch.js @@ -0,0 +1,11 @@ +var DifferentError = require('./exception_classes').TableDiffError; + +module.exports = function () { + this.diffTables = (expected, actual, options, callback) => { + // this is a temp workaround while waiting for https://github.com/cucumber/cucumber-js/issues/534 + + var error = new DifferentError(expected, actual); + + return callback(error.string); + }; +}; diff --git a/features/testbot/64bit.feature b/features/testbot/64bit.feature index d2d0c8a7c..83ca884c3 100644 --- a/features/testbot/64bit.feature +++ b/features/testbot/64bit.feature @@ -19,5 +19,5 @@ Feature: Support 64bit node IDs | cdec | When I route I should get - | from | to | route | turns | - | x | y | abc | head,destination | + | from | to | route | turns | + | x | y | abc,abc | depart,arrive | diff --git a/features/testbot/alternative.feature b/features/testbot/alternative.feature index d6ca030c6..4031631a0 100644 --- a/features/testbot/alternative.feature +++ b/features/testbot/alternative.feature @@ -6,7 +6,7 @@ Feature: Alternative route And the node map | | b | c | d | | | - | a | | | | | z | + | a | | k | | | z | | | g | h | i | j | | And the ways @@ -20,19 +20,21 @@ Feature: Alternative route | hi | | ij | | jz | + | ck | + | kh | Scenario: Enabled alternative Given the query options - | alt | true | + | alternatives | true | When I route I should get - | from | to | route | alternative | - | a | z | ab,bc,cd,dz | ag,gh,hi,ij,jz | + | from | to | route | alternative | + | a | z | ab,bc,cd,dz,dz | ag,gh,hi,ij,jz,jz | Scenario: Disabled alternative Given the query options - | alt | false | + | alternatives | false | When I route I should get - | from | to | route | alternative | - | a | z | ab,bc,cd,dz | | + | from | to | route | alternative | + | a | z | ab,bc,cd,dz,dz | | diff --git a/features/testbot/alternative_loop.feature b/features/testbot/alternative_loop.feature new file mode 100644 index 000000000..159a399f8 --- /dev/null +++ b/features/testbot/alternative_loop.feature @@ -0,0 +1,29 @@ +@routing @testbot @alternative +Feature: Alternative route + + Background: + Given the profile "testbot" + + Scenario: Alternative Loop Paths + Given the node map + | a | 2 | 1 | b | + | 7 | | | 4 | + | 8 | | | 3 | + | c | 5 | 6 | d | + + And the ways + | nodes | oneway | + | ab | yes | + | bd | yes | + | dc | yes | + | ca | yes | + + And the query options + | alternatives | true | + + When I route I should get + | from | to | route | alternative | + | 1 | 2 | ab,bd,dc,ca,ab,ab | | + | 3 | 4 | bd,dc,ca,ab,bd,bd | | + | 5 | 6 | dc,ca,ab,bd,dc,dc | | + | 7 | 8 | ca,ab,bd,dc,ca,ca | | diff --git a/features/testbot/bad.feature b/features/testbot/bad.feature index d7e3c95af..18ee5f862 100644 --- a/features/testbot/bad.feature +++ b/features/testbot/bad.feature @@ -11,7 +11,7 @@ Feature: Handle bad data in a graceful manner Given the ways | nodes | - When the data has been prepared + When the data has been contracted Then "osrm-extract" should return code 1 Scenario: Only dead-end oneways @@ -23,8 +23,8 @@ Feature: Handle bad data in a graceful manner | abcde | yes | When I route I should get - | from | to | route | - | b | d | abcde | + | from | to | route | + | b | d | abcde,abcde | @todo Scenario: Start/end point at the same location @@ -78,7 +78,7 @@ Feature: Handle bad data in a graceful manner # | b | c | cd | # | a | d | cd | # | c | d | cd | - | d | e | de | + | d | e | de,de | # | k | l | kl | # | l | m | lm | # | o | l | lm | diff --git a/features/testbot/basic.feature b/features/testbot/basic.feature index 599b0628a..c2f969ddb 100644 --- a/features/testbot/basic.feature +++ b/features/testbot/basic.feature @@ -14,9 +14,9 @@ Feature: Basic Routing | ab | When I route I should get - | from | to | route | - | a | b | ab | - | b | a | ab | + | from | to | route | + | a | b | ab,ab | + | b | a | ab,ab | Scenario: Routing in between two nodes of way Given the node map @@ -27,9 +27,9 @@ Feature: Basic Routing | abcd | When I route I should get - | from | to | route | - | 1 | 2 | abcd | - | 2 | 1 | abcd | + | from | to | route | + | 1 | 2 | abcd,abcd | + | 2 | 1 | abcd,abcd | Scenario: Routing between the middle nodes of way Given the node map @@ -40,19 +40,19 @@ Feature: Basic Routing | abcdef | When I route I should get - | from | to | route | - | b | c | abcdef | - | b | d | abcdef | - | b | e | abcdef | - | c | b | abcdef | - | c | d | abcdef | - | c | e | abcdef | - | d | b | abcdef | - | d | c | abcdef | - | d | e | abcdef | - | e | b | abcdef | - | e | c | abcdef | - | e | d | abcdef | + | from | to | route | + | b | c | abcdef,abcdef | + | b | d | abcdef,abcdef | + | b | e | abcdef,abcdef | + | c | b | abcdef,abcdef | + | c | d | abcdef,abcdef | + | c | e | abcdef,abcdef | + | d | b | abcdef,abcdef | + | d | c | abcdef,abcdef | + | d | e | abcdef,abcdef | + | e | b | abcdef,abcdef | + | e | c | abcdef,abcdef | + | e | d | abcdef,abcdef | Scenario: Two ways connected in a straight line Given the node map @@ -64,13 +64,13 @@ Feature: Basic Routing | bc | When I route I should get - | from | to | route | - | a | c | ab,bc | - | c | a | bc,ab | - | a | b | ab | - | b | a | ab | - | b | c | bc | - | c | b | bc | + | from | to | route | + | a | c | ab,bc,bc | + | c | a | bc,ab,ab | + | a | b | ab,ab | + | b | a | ab,ab | + | b | c | bc,bc | + | c | b | bc,bc | Scenario: 2 unconnected parallel ways Given the node map @@ -83,33 +83,33 @@ Feature: Basic Routing | def | When I route I should get - | from | to | route | - | a | b | abc | - | b | a | abc | - | b | c | abc | - | c | b | abc | - | d | e | def | - | e | d | def | - | e | f | def | - | f | e | def | - | a | d | | - | d | a | | - | b | d | | - | d | b | | - | c | d | | - | d | c | | - | a | e | | - | e | a | | - | b | e | | - | e | b | | - | c | e | | - | e | c | | - | a | f | | - | f | a | | - | b | f | | - | f | b | | - | c | f | | - | f | c | | + | from | to | route | + | a | b | abc,abc | + | b | a | abc,abc | + | b | c | abc,abc | + | c | b | abc,abc | + | d | e | def,def | + | e | d | def,def | + | e | f | def,def | + | f | e | def,def | + | a | d | | + | d | a | | + | b | d | | + | d | b | | + | c | d | | + | d | c | | + | a | e | | + | e | a | | + | b | e | | + | e | b | | + | c | e | | + | e | c | | + | a | f | | + | f | a | | + | b | f | | + | f | b | | + | c | f | | + | f | c | | Scenario: 3 ways connected in a triangle Given the node map @@ -124,13 +124,13 @@ Feature: Basic Routing | ca | When I route I should get - | from | to | route | - | a | b | ab | - | a | c | ca | - | b | c | bc | - | b | a | ab | - | c | a | ca | - | c | b | bc | + | from | to | route | + | a | b | ab,ab | + | a | c | ca,ca | + | b | c | bc,bc | + | b | a | ab,ab | + | c | a | ca,ca | + | c | b | bc,bc | Scenario: 3 connected triangles Given a grid size of 100 meters @@ -157,12 +157,12 @@ Feature: Basic Routing When I route I should get | from | to | route | - | a | b | ab | - | a | c | ca | - | b | c | bc | - | b | a | ab | - | c | a | ca | - | c | b | bc | + | a | b | ab,ab | + | a | c | ca,ca | + | b | c | bc,bc | + | b | a | ab,ab | + | c | a | ca,ca | + | c | b | bc,bc | Scenario: To ways connected at a 45 degree angle Given the node map @@ -176,13 +176,13 @@ Feature: Basic Routing | cde | When I route I should get - | from | to | route | - | b | d | abc,cde | - | a | e | abc,cde | - | a | c | abc | - | c | a | abc | - | c | e | cde | - | e | c | cde | + | from | to | route | + | b | d | abc,cde,cde | + | a | e | abc,cde,cde | + | a | c | abc,abc | + | c | a | abc,abc | + | c | e | cde,cde | + | e | c | cde,cde | Scenario: Grid city center Given the node map @@ -203,11 +203,11 @@ Feature: Basic Routing | dhlp | When I route I should get - | from | to | route | - | f | g | efgh | - | g | f | efgh | - | f | j | bfjn | - | j | f | bfjn | + | from | to | route | + | f | g | efgh,efgh | + | g | f | efgh,efgh | + | f | j | bfjn,bfjn | + | j | f | bfjn,bfjn | Scenario: Grid city periphery Given the node map @@ -228,11 +228,11 @@ Feature: Basic Routing | dhlp | When I route I should get - | from | to | route | - | a | d | abcd | - | d | a | abcd | - | a | m | aeim | - | m | a | aeim | + | from | to | route | + | a | d | abcd,abcd | + | d | a | abcd,abcd | + | a | m | aeim,aeim | + | m | a | aeim,aeim | Scenario: Testbot - Triangle challenge Given the node map @@ -249,5 +249,5 @@ Feature: Basic Routing When I route I should get | from | to | route | - | d | c | de,ce | - | e | d | de | + | d | c | de,ce,ce | + | e | d | de,de | diff --git a/features/testbot/bearing.feature b/features/testbot/bearing.feature index 9b3dc4eb7..76a2819af 100644 --- a/features/testbot/bearing.feature +++ b/features/testbot/bearing.feature @@ -14,8 +14,8 @@ Feature: Compass bearing | ab | When I route I should get - | from | to | route | compass | bearing | - | a | b | ab | NW | 315 | + | from | to | route | bearing | + | a | b | ab,ab | 315,0 | Scenario: Bearing when going west Given the node map @@ -26,8 +26,8 @@ Feature: Compass bearing | ab | When I route I should get - | from | to | route | compass | bearing | - | a | b | ab | W | 270 | + | from | to | route | bearing | + | a | b | ab,ab | 270,0 | Scenario: Bearing af 45 degree intervals Given the node map @@ -47,15 +47,15 @@ Feature: Compass bearing | xh | When I route I should get - | from | to | route | compass | bearing | - | x | a | xa | N | 0 | - | x | b | xb | NW | 315 | - | x | c | xc | W | 270 | - | x | d | xd | SW | 225 | - | x | e | xe | S | 180 | - | x | f | xf | SE | 135 | - | x | g | xg | E | 90 | - | x | h | xh | NE | 45 | + | from | to | route | bearing | + | x | a | xa,xa | 0,0 | + | x | b | xb,xb | 315,0 | + | x | c | xc,xc | 270,0 | + | x | d | xd,xd | 225,0 | + | x | e | xe,xe | 180,0 | + | x | f | xf,xf | 135,0 | + | x | g | xg,xg | 90,0 | + | x | h | xh,xh | 45,0 | Scenario: Bearing in a roundabout Given the node map @@ -76,9 +76,9 @@ Feature: Compass bearing | ha | yes | When I route I should get - | from | to | route | compass | bearing | - | c | b | cd,de,ef,fg,gh,ha,ab | W,SW,S,SE,E,NE,N | 270,225,180,135,90,45,0 | - | g | f | gh,ha,ab,bc,cd,de,ef | E,NE,N,NW,W,SW,S | 90,45,0,315,270,225,180 | + | from | to | route | bearing | + | c | b | cd,de,ef,fg,gh,ha,ab,ab | 270,225,180,135,90,45,0,0 | + | g | f | gh,ha,ab,bc,cd,de,ef,ef | 90,45,0,315,270,225,180,0 | Scenario: Bearing should stay constant when zig-zagging Given the node map @@ -96,8 +96,8 @@ Feature: Compass bearing | gh | When I route I should get - | from | to | route | compass | bearing | - | a | h | ab,bc,cd,de,ef,fg,gh | N,SE,N,SE,N,SE,N | 0,135,0,135,0,135,0 | + | from | to | route | bearing | + | a | h | ab,bc,cd,de,ef,fg,gh,gh | 0,135,0,135,0,135,0,0 | Scenario: Bearings on an east-west way. Given the node map @@ -108,37 +108,37 @@ Feature: Compass bearing | abcdef | When I route I should get - | from | to | route | compass | bearing | - | a | b | abcdef | E | 90 | - | a | c | abcdef | E | 90 | - | a | d | abcdef | E | 90 | - | a | e | abcdef | E | 90 | - | a | f | abcdef | E | 90 | - | b | a | abcdef | W | 270 | - | b | c | abcdef | E | 90 | - | b | d | abcdef | E | 90 | - | b | e | abcdef | E | 90 | - | b | f | abcdef | E | 90 | - | c | a | abcdef | W | 270 | - | c | b | abcdef | W | 270 | - | c | d | abcdef | E | 90 | - | c | e | abcdef | E | 90 | - | c | f | abcdef | E | 90 | - | d | a | abcdef | W | 270 | - | d | b | abcdef | W | 270 | - | d | c | abcdef | W | 270 | - | d | e | abcdef | E | 90 | - | d | f | abcdef | E | 90 | - | e | a | abcdef | W | 270 | - | e | b | abcdef | W | 270 | - | e | c | abcdef | W | 270 | - | e | d | abcdef | W | 270 | - | e | f | abcdef | E | 90 | - | f | a | abcdef | W | 270 | - | f | b | abcdef | W | 270 | - | f | c | abcdef | W | 270 | - | f | d | abcdef | W | 270 | - | f | e | abcdef | W | 270 | + | from | to | route | bearing | + | a | b | abcdef,abcdef | 90,0 | + | a | c | abcdef,abcdef | 90,0 | + | a | d | abcdef,abcdef | 90,0 | + | a | e | abcdef,abcdef | 90,0 | + | a | f | abcdef,abcdef | 90,0 | + | b | a | abcdef,abcdef | 270,0 | + | b | c | abcdef,abcdef | 90,0 | + | b | d | abcdef,abcdef | 90,0 | + | b | e | abcdef,abcdef | 90,0 | + | b | f | abcdef,abcdef | 90,0 | + | c | a | abcdef,abcdef | 270,0 | + | c | b | abcdef,abcdef | 270,0 | + | c | d | abcdef,abcdef | 90,0 | + | c | e | abcdef,abcdef | 90,0 | + | c | f | abcdef,abcdef | 90,0 | + | d | a | abcdef,abcdef | 270,0 | + | d | b | abcdef,abcdef | 270,0 | + | d | c | abcdef,abcdef | 270,0 | + | d | e | abcdef,abcdef | 90,0 | + | d | f | abcdef,abcdef | 90,0 | + | e | a | abcdef,abcdef | 270,0 | + | e | b | abcdef,abcdef | 270,0 | + | e | c | abcdef,abcdef | 270,0 | + | e | d | abcdef,abcdef | 270,0 | + | e | f | abcdef,abcdef | 90,0 | + | f | a | abcdef,abcdef | 270,0 | + | f | b | abcdef,abcdef | 270,0 | + | f | c | abcdef,abcdef | 270,0 | + | f | d | abcdef,abcdef | 270,0 | + | f | e | abcdef,abcdef | 270,0 | Scenario: Bearings at high latitudes # The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html, @@ -161,19 +161,19 @@ Feature: Compass bearing | bd | When I route I should get - | from | to | route | compass | bearing | - | a | b | ab | N | 0 | - | b | c | bc | E | 90 | - | c | d | cd | S | 180 | - | d | a | da | W | 270 | - | b | a | ab | S | 180 | - | c | b | bc | W | 270 | - | d | c | cd | N | 0 | - | a | d | da | E | 90 | - | a | c | ac | NE | 45 | - | c | a | ac | SW | 225 | - | b | d | bd | SE | 135 | - | d | b | bd | NW | 315 | + | from | to | route | bearing | + | a | b | ab,ab | 0,0 | + | b | c | bc,bc | 90,0 | + | c | d | cd,cd | 180,0 | + | d | a | da,da | 270,0 | + | b | a | ab,ab | 180,0 | + | c | b | bc,bc | 270,0 | + | d | c | cd,cd | 0,0 | + | a | d | da,da | 90,0 | + | a | c | ac,ac | 45,0 | + | c | a | ac,ac | 225,0 | + | b | d | bd,bd | 135,0 | + | d | b | bd,bd | 315,0 | Scenario: Bearings at high negative latitudes # The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html, @@ -196,16 +196,16 @@ Feature: Compass bearing | bd | When I route I should get - | from | to | route | compass | bearing | - | a | b | ab | S | 180 | - | b | c | bc | E | 90 | - | c | d | cd | N | 0 | - | d | a | da | W | 270 | - | b | a | ab | N | 0 | - | c | b | bc | W | 270 | - | d | c | cd | S | 180 | - | a | d | da | E | 90 | - | a | c | ac | SE | 135 | - | c | a | ac | NW | 315 | - | b | d | bd | NE | 45 | - | d | b | bd | SW | 225 | + | from | to | route | bearing | + | a | b | ab,ab | 180,0 | + | b | c | bc,bc | 90,0 | + | c | d | cd,cd | 0,0 | + | d | a | da,da | 270,0 | + | b | a | ab,ab | 0,0 | + | c | b | bc,bc | 270,0 | + | d | c | cd,cd | 180,0 | + | a | d | da,da | 90,0 | + | a | c | ac,ac | 135,0 | + | c | a | ac,ac | 315,0 | + | b | d | bd,bd | 45,0 | + | d | b | bd,bd | 225,0 | diff --git a/features/testbot/bearing_param.feature b/features/testbot/bearing_param.feature index 1efa81742..ea60856f0 100644 --- a/features/testbot/bearing_param.feature +++ b/features/testbot/bearing_param.feature @@ -15,11 +15,11 @@ Feature: Bearing parameter When I route I should get | from | to | bearings | route | bearing | - | b | c | 90 90 | ad | 90 | + | b | c | 90 90 | ad,ad | 90,0 | | b | c | 180 90 | | | - | b | c | 80 100 | ad | 90 | + | b | c | 80 100 | ad,ad | 90,0 | | b | c | 79 100 | | | - | b | c | 79,11 100 | ad | 90 | + | b | c | 79,11 100 | ad,ad | 90,0 | Scenario: Testbot - Intial bearing in simple case Given the node map @@ -35,10 +35,10 @@ Feature: Bearing parameter When I route I should get | from | to | bearings | route | bearing | | 0 | c | 0 0 | | | - | 0 | c | 45 45 | bc | 45 ~3% | + | 0 | c | 45 45 | bc,bc | 45 ~3% | | 0 | c | 85 85 | | | | 0 | c | 95 95 | | | - | 0 | c | 135 135 | ac | 135 ~1% | + | 0 | c | 135 135 | ac,ac | 135 ~1% | | 0 | c | 180 180 | | | Scenario: Testbot - Initial bearing on split way @@ -55,19 +55,19 @@ Feature: Bearing parameter When I route I should get | from | to | bearings | route | bearing | - | 0 | b | 10 10 | bc | 0 | - | 0 | b | 90 90 | ab | 90 | + | 0 | b | 10 10 | bc,bc | 0,0 | + | 0 | b | 90 90 | ab,ab | 90,0 | # The returned bearing is wrong here, it's based on the snapped # coordinates, not the acutal edge bearing. This should be - # fixed one day, but it's only a problem when we snap too vias + # fixed one day, but it's only a problem when we snap two vias # to the same point - DP #| 0 | b | 170 170 | da | 180 | #| 0 | b | 189 189 | da | 180 | - | 0 | 1 | 90 270 | ab,bc,cd | 90,0,270 | - | 1 | d | 10 10 | bc | 0 | - | 1 | d | 90 90 | ab,bc,cd,da | 90,0,270,180 | - | 1 | 0 | 189 189 | da | 180 | - | 1 | d | 270 270 | cd | 270 | + | 0 | 1 | 90 270 | ab,bc,cd,cd | 90,0,270,0 | + | 1 | d | 10 10 | bc,bc | 0,0 | + | 1 | d | 90 90 | ab,bc,cd,da,da | 90,0,270,180,0 | + | 1 | 0 | 189 189 | da,da | 180,0 | + | 1 | d | 270 270 | cd,cd | 270,0 | | 1 | d | 349 349 | | | Scenario: Testbot - Initial bearing in all direction @@ -100,12 +100,12 @@ Feature: Bearing parameter | ha | yes | When I route I should get - | from | to | bearings | route | bearing | - | 0 | q | 0 90 | ia,ab,bc,cd,de,ef,fg,gh,ha | 0,90,180,180,270,270,0,0,90 | - | 0 | a | 45 90 | jb,bc,cd,de,ef,fg,gh,ha | 45,180,180,270,270,0,0,90 | - | 0 | q | 90 90 | kc,cd,de,ef,fg,gh,ha | 90,180,270,270,0,0,90 | - | 0 | a | 135 90 | ld,de,ef,fg,gh,ha | 135,270,270,0,0,90 | - | 0 | a | 180 90 | me,ef,fg,gh,ha | 180,270,0,0,90 | - | 0 | a | 225 90 | nf,fg,gh,ha | 225,0,0,90 | - | 0 | a | 270 90 | og,gh,ha | 270,0,90 | - | 0 | a | 315 90 | ph,ha | 315,90 | + | from | to | bearings | route | bearing | + | 0 | q | 0 90 | ia,ab,bc,cd,de,ef,fg,gh,ha,ha | 0,90,180,180,270,270,0,0,90,0 | + | 0 | a | 45 90 | jb,bc,cd,de,ef,fg,gh,ha,ha | 45,180,180,270,270,0,0,90,0 | + | 0 | q | 90 90 | kc,cd,de,ef,fg,gh,ha,ha | 90,180,270,270,0,0,90,0 | + | 0 | a | 135 90 | ld,de,ef,fg,gh,ha,ha | 135,270,270,0,0,90,0 | + | 0 | a | 180 90 | me,ef,fg,gh,ha,ha | 180,270,0,0,90,0 | + | 0 | a | 225 90 | nf,fg,gh,ha,ha | 225,0,0,90,0 | + | 0 | a | 270 90 | og,gh,ha,ha | 270,0,90,0 | + | 0 | a | 315 90 | ph,ha,ha | 315,90,0 | diff --git a/features/testbot/compression.feature b/features/testbot/compression.feature index 4c0121d6f..79823ceed 100644 --- a/features/testbot/compression.feature +++ b/features/testbot/compression.feature @@ -17,6 +17,6 @@ Feature: Geometry Compression | fg | When I route I should get - | from | to | route | distance | speed | - | b | e | abcdef | 589m | 36 km/h | - | e | b | abcdef | 589m | 36 km/h | + | from | to | route | distance | speed | + | b | e | abcdef,abcdef | 589m | 36 km/h | + | e | b | abcdef,abcdef | 589m | 36 km/h | diff --git a/features/testbot/uturn.feature b/features/testbot/continue_straight.feature similarity index 57% rename from features/testbot/uturn.feature rename to features/testbot/continue_straight.feature index 26c681897..40fcbb4fd 100644 --- a/features/testbot/uturn.feature +++ b/features/testbot/continue_straight.feature @@ -1,10 +1,10 @@ -@routing @uturn @via @testbot +@routing @continue_straight @via @testbot Feature: U-turns at via points Background: Given the profile "testbot" - Scenario: U-turns at via points disabled by default + Scenario: Continue straight at waypoints enabled by default Given the node map | a | b | c | d | | | e | f | g | @@ -20,16 +20,16 @@ Feature: U-turns at via points | fg | When I route I should get - | waypoints | route | turns | - | a,e,c | ab,be,be,ef,fg,dg,cd | head,right,via,left,straight,left,left,destination | + | waypoints | route | + | a,e,c | ab,be,be,ef,fg,dg,cd,cd | - Scenario: Query param to allow U-turns at all via points + Scenario: Query parameter to disallow changing direction at all waypoints Given the node map | a | b | c | d | | | e | f | g | And the query options - | uturns | true | + | continue_straight | false | And the ways | nodes | @@ -42,17 +42,16 @@ Feature: U-turns at via points | fg | When I route I should get - | waypoints | route | - | a,e,c | ab,be,be,bc | + | waypoints | route | + | a,e,c | ab,be,be,be,bc,bc | - @todo - Scenario: Instructions at via points at u-turns + Scenario: Instructions at waypoints at u-turns Given the node map | a | b | c | d | | | e | f | g | And the query options - | uturns | true | + | continue_straight | false | And the ways | nodes | @@ -65,8 +64,8 @@ Feature: U-turns at via points | fg | When I route I should get - | waypoints | route | turns | - | a,e,c | ab,be,be,bc | head,right,uturn,right,destination | + | waypoints | route | + | a,e,c | ab,be,be,be,bc,bc | Scenario: u-turn mixed with non-uturn vias Given the node map @@ -75,19 +74,19 @@ Feature: U-turns at via points | | | e | | f | | g | And the query options - | uturns | true | + | continue_straight | false | And the ways - | nodes | - | ab | - | bc | - | cd | - | be | - | dg | - | ef | - | fg | + | nodes | oneway | + | ab | no | + | bc | no | + | cd | no | + | be | yes | + | dg | no | + | ef | no | + | fg | no | When I route I should get - | waypoints | route | - | 1,2,3,4,5 | ab,be,be,bc,bc,cd,dg,dg,cd | + | waypoints | route | + | 1,2,3,4,5 | ab,be,be,be,ef,fg,dg,cd,bc,bc,bc,cd,dg,dg,dg,cd,cd | diff --git a/features/testbot/datastore.feature b/features/testbot/datastore.feature index 568ae7aa4..70a754bdf 100644 --- a/features/testbot/datastore.feature +++ b/features/testbot/datastore.feature @@ -14,8 +14,8 @@ Feature: Temporary tests related to osrm-datastore When I route I should get | from | to | route | - | a | b | ab | - | b | a | ab | + | a | b | ab,ab | + | b | a | ab,ab | Scenario: Scenaria xy Given the node map @@ -27,5 +27,5 @@ Feature: Temporary tests related to osrm-datastore When I route I should get | from | to | route | - | x | y | xy | - | y | x | xy | + | x | y | xy,xy | + | y | x | xy,xy | diff --git a/features/testbot/distance.feature b/features/testbot/distance.feature index 55b5f6416..b8e571fef 100644 --- a/features/testbot/distance.feature +++ b/features/testbot/distance.feature @@ -15,7 +15,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | a | b | ab | 100m +- 2 | + | a | b | ab,ab | 100m +- 2 | Scenario: Distance should equal sum of segments, leftwinded Given the node map @@ -28,8 +28,8 @@ Feature: Distance calculation | abcde | When I route I should get - | from | to | route | distance | - | a | d | abcde | 300m +-2 | + | from | to | route | distance | + | a | d | abcde,abcde | 300m +-2 | Scenario: Distance should equal sum of segments, rightwinded Given the node map @@ -42,8 +42,8 @@ Feature: Distance calculation | abcde | When I route I should get - | from | to | route | distance | - | a | d | abcde | 300m +-2 | + | from | to | route | distance | + | a | d | abcde,abcde | 300m +-2 | Scenario: 10m distances Given a grid size of 10 meters @@ -56,13 +56,13 @@ Feature: Distance calculation | abc | When I route I should get - | from | to | route | distance | - | a | b | abc | 10m +-2 | - | b | a | abc | 10m +-2 | - | b | c | abc | 10m +-2 | - | c | b | abc | 10m +-2 | - | a | c | abc | 20m +-4 | - | c | a | abc | 20m +-4 | + | from | to | route | distance | + | a | b | abc,abc | 10m +-2 | + | b | a | abc,abc | 10m +-2 | + | b | c | abc,abc | 10m +-2 | + | c | b | abc,abc | 10m +-2 | + | a | c | abc,abc | 20m +-4 | + | c | a | abc,abc | 20m +-4 | Scenario: 100m distances Given a grid size of 100 meters @@ -75,13 +75,13 @@ Feature: Distance calculation | abc | When I route I should get - | from | to | route | distance | - | a | b | abc | 100m +-2 | - | b | a | abc | 100m +-2 | - | b | c | abc | 100m +-2 | - | c | b | abc | 100m +-2 | - | a | c | abc | 200m +-4 | - | c | a | abc | 200m +-4 | + | from | to | route | distance | + | a | b | abc,abc | 100m +-2 | + | b | a | abc,abc | 100m +-2 | + | b | c | abc,abc | 100m +-2 | + | c | b | abc,abc | 100m +-2 | + | a | c | abc,abc | 200m +-4 | + | c | a | abc,abc | 200m +-4 | Scenario: 1km distance Given a grid size of 1000 meters @@ -94,13 +94,13 @@ Feature: Distance calculation | abc | When I route I should get - | from | to | route | distance | - | a | b | abc | 1000m +-2 | - | b | a | abc | 1000m +-2 | - | b | c | abc | 1000m +-2 | - | c | b | abc | 1000m +-2 | - | a | c | abc | 2000m +-4 | - | c | a | abc | 2000m +-4 | + | from | to | route | distance | + | a | b | abc,abc | 1000m +-2 | + | b | a | abc,abc | 1000m +-2 | + | b | c | abc,abc | 1000m +-2 | + | c | b | abc,abc | 1000m +-2 | + | a | c | abc,abc | 2000m +-4 | + | c | a | abc,abc | 2000m +-4 | Scenario: Distance of a winding south-north path Given a grid size of 10 meters @@ -115,14 +115,14 @@ Feature: Distance calculation | abcdefgh | When I route I should get - | from | to | route | distance | - | a | b | abcdefgh | 10m +-2 | - | a | c | abcdefgh | 20m +-4 | - | a | d | abcdefgh | 30m +-6 | - | a | e | abcdefgh | 40m +-8 | - | a | f | abcdefgh | 50m +-10 | - | a | g | abcdefgh | 60m +-12 | - | a | h | abcdefgh | 70m +-14 | + | from | to | route | distance | + | a | b | abcdefgh,abcdefgh | 10m +-2 | + | a | c | abcdefgh,abcdefgh | 20m +-4 | + | a | d | abcdefgh,abcdefgh | 30m +-6 | + | a | e | abcdefgh,abcdefgh | 40m +-8 | + | a | f | abcdefgh,abcdefgh | 50m +-10 | + | a | g | abcdefgh,abcdefgh | 60m +-12 | + | a | h | abcdefgh,abcdefgh | 70m +-14 | Scenario: Distance of a winding east-west path Given a grid size of 10 meters @@ -135,14 +135,14 @@ Feature: Distance calculation | abcdefgh | When I route I should get - | from | to | route | distance | - | a | b | abcdefgh | 10m +-2 | - | a | c | abcdefgh | 20m +-4 | - | a | d | abcdefgh | 30m +-6 | - | a | e | abcdefgh | 40m +-8 | - | a | f | abcdefgh | 50m +-10 | - | a | g | abcdefgh | 60m +-12 | - | a | h | abcdefgh | 70m +-14 | + | from | to | route | distance | + | a | b | abcdefgh,abcdefgh | 10m +-2 | + | a | c | abcdefgh,abcdefgh | 20m +-4 | + | a | d | abcdefgh,abcdefgh | 30m +-6 | + | a | e | abcdefgh,abcdefgh | 40m +-8 | + | a | f | abcdefgh,abcdefgh | 50m +-10 | + | a | g | abcdefgh,abcdefgh | 60m +-12 | + | a | h | abcdefgh,abcdefgh | 70m +-14 | Scenario: Geometric distances Given a grid size of 100 meters @@ -184,30 +184,30 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | x | a | xa | 300m +-2 | - | x | b | xb | 316m +-2 | - | x | c | xc | 360m +-2 | - | x | d | xd | 424m +-2 | - | x | e | xe | 360m +-2 | - | x | f | xf | 316m +-2 | - | x | g | xg | 300m +-2 | - | x | h | xh | 316m +-2 | - | x | i | xi | 360m +-2 | - | x | j | xj | 424m +-2 | - | x | k | xk | 360m +-2 | - | x | l | xl | 316m +-2 | - | x | m | xm | 300m +-2 | - | x | n | xn | 316m +-2 | - | x | o | xo | 360m +-2 | - | x | p | xp | 424m +-2 | - | x | q | xq | 360m +-2 | - | x | r | xr | 316m +-2 | - | x | s | xs | 300m +-2 | - | x | t | xt | 316m +-2 | - | x | u | xu | 360m +-2 | - | x | v | xv | 424m +-2 | - | x | w | xw | 360m +-2 | - | x | y | xy | 316m +-2 | + | x | a | xa,xa | 300m +-2 | + | x | b | xb,xb | 316m +-2 | + | x | c | xc,xc | 360m +-2 | + | x | d | xd,xd | 424m +-2 | + | x | e | xe,xe | 360m +-2 | + | x | f | xf,xf | 316m +-2 | + | x | g | xg,xg | 300m +-2 | + | x | h | xh,xh | 316m +-2 | + | x | i | xi,xi | 360m +-2 | + | x | j | xj,xj | 424m +-2 | + | x | k | xk,xk | 360m +-2 | + | x | l | xl,xl | 316m +-2 | + | x | m | xm,xm | 300m +-2 | + | x | n | xn,xn | 316m +-2 | + | x | o | xo,xo | 360m +-2 | + | x | p | xp,xp | 424m +-2 | + | x | q | xq,xq | 360m +-2 | + | x | r | xr,xr | 316m +-2 | + | x | s | xs,xs | 300m +-2 | + | x | t | xt,xt | 316m +-2 | + | x | u | xu,xu | 360m +-2 | + | x | v | xv,xv | 424m +-2 | + | x | w | xw,xw | 360m +-2 | + | x | y | xy,xy | 316m +-2 | @maze Scenario: Distance of a maze of short segments @@ -224,5 +224,5 @@ Feature: Distance calculation | abcdefghijklmnopqrst | When I route I should get - | from | to | route | distance | - | a | t | abcdefghijklmnopqrst | 133m +-2 | + | from | to | route | distance | + | a | t | abcdefghijklmnopqrst,abcdefghijklmnopqrst | 133m +-2 | diff --git a/features/testbot/distance_matrix.feature b/features/testbot/distance_matrix.feature index 491012623..4afa30708 100644 --- a/features/testbot/distance_matrix.feature +++ b/features/testbot/distance_matrix.feature @@ -16,9 +16,9 @@ Feature: Basic Distance Matrix | ab | When I request a travel time matrix I should get - | | a | b | - | a | 0 | 100 | - | b | 100 | 0 | + | | a | b | + | a | 0 | 10 | + | b | 10 | 0 | Scenario: Testbot - Travel time matrix with different way speeds Given the node map @@ -29,13 +29,13 @@ Feature: Basic Distance Matrix | ab | primary | | bc | secondary | | cd | tertiary | - + When I request a travel time matrix I should get - | | a | b | c | d | - | a | 0 | 100 | 300 | 600 | - | b | 100 | 0 | 200 | 500 | - | c | 300 | 200 | 0 | 300 | - | d | 600 | 500 | 300 | 0 | + | | a | b | c | d | + | a | 0 | 10 | 30 | 60 | + | b | 10 | 0 | 20 | 50 | + | c | 30 | 20 | 0 | 30 | + | d | 60 | 50 | 30 | 0 | Scenario: Testbot - Travel time matrix with fuzzy match Given the node map @@ -47,9 +47,9 @@ Feature: Basic Distance Matrix When I request a travel time matrix I should get | | a | b | - | a | 0 | 95 +- 10 | - | b | 95 ~10% | 0 | - + | a | 0 | 9 +- 2 | + | b | 9 ~15% | 0 | + Scenario: Testbot - Travel time matrix of small grid Given the node map | a | b | c | @@ -64,11 +64,11 @@ Feature: Basic Distance Matrix | cf | When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 100 | 200 | 300 | - | b | 100 | 0 | 100 | 200 | - | e | 200 | 100 | 0 | 100 | - | f | 300 | 200 | 100 | 0 | + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + | b | 10 | 0 | 10 | 20 | + | e | 20 | 10 | 0 | 10 | + | f | 30 | 20 | 10 | 0 | Scenario: Testbot - Travel time matrix of network with unroutable parts Given the node map @@ -79,10 +79,10 @@ Feature: Basic Distance Matrix | ab | yes | When I request a travel time matrix I should get - | | a | b | - | a | 0 | 100 | - | b | | 0 | - + | | a | b | + | a | 0 | 10 | + | b | | 0 | + Scenario: Testbot - Travel time matrix of network with oneways Given the node map | x | a | b | y | @@ -95,11 +95,11 @@ Feature: Basic Distance Matrix | by | | When I request a travel time matrix I should get - | | x | y | d | e | - | x | 0 | 300 | 400 | 300 | - | y | 500 | 0 | 300 | 200 | - | d | 200 | 300 | 0 | 300 | - | e | 300 | 400 | 100 | 0 | + | | x | y | d | e | + | x | 0 | 30 | 40 | 30 | + | y | 50 | 0 | 30 | 20 | + | d | 20 | 30 | 0 | 30 | + | e | 30 | 40 | 10 | 0 | Scenario: Testbot - Travel time matrix and with only one source Given the node map @@ -115,8 +115,8 @@ Feature: Basic Distance Matrix | cf | When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 100 | 200 | 300 | + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | Scenario: Testbot - Travel time 3x2 matrix Given the node map @@ -132,11 +132,11 @@ Feature: Basic Distance Matrix | cf | When I request a travel time matrix I should get - | | b | e | f | - | a | 100 | 200 | 300 | - | b | 0 | 100 | 200 | + | | b | e | f | + | a | 10 | 20 | 30 | + | b | 0 | 10 | 20 | - Scenario: Testbog - All coordinates are from same small component + Scenario: Testbot - All coordinates are from same small component Given a grid size of 300 meters Given the extract extra arguments "--small-component-size 4" Given the node map @@ -152,11 +152,11 @@ Feature: Basic Distance Matrix | fg | When I request a travel time matrix I should get - | | f | g | - | f | 0 | 300 | - | g | 300 | 0 | + | | f | g | + | f | 0 | 30 | + | g | 30 | 0 | - Scenario: Testbog - Coordinates are from different small component and snap to big CC + Scenario: Testbot - Coordinates are from different small component and snap to big CC Given a grid size of 300 meters Given the extract extra arguments "--small-component-size 4" Given the node map @@ -173,9 +173,27 @@ Feature: Basic Distance Matrix | hi | When I request a travel time matrix I should get - | | f | g | h | i | - | f | 0 | 300 | 0 | 300 | - | g | 300 | 0 | 300 | 0 | - | h | 0 | 300 | 0 | 300 | - | i | 300 | 0 | 300 | 0 | + | | f | g | h | i | + | f | 0 | 30 | 0 | 30 | + | g | 30 | 0 | 30 | 0 | + | h | 0 | 30 | 0 | 30 | + | i | 30 | 0 | 30 | 0 | + Scenario: Testbot - Travel time matrix with loops + Given the node map + | a | 1 | 2 | b | + | d | 4 | 3 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I request a travel time matrix I should get + | | 1 | 2 | 3 | 4 | + | 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 | + | 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 | + | 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 | + | 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 | diff --git a/features/testbot/duration.feature b/features/testbot/duration.feature index d5c6e46ec..bb158db1c 100644 --- a/features/testbot/duration.feature +++ b/features/testbot/duration.feature @@ -20,11 +20,11 @@ Feature: Durations When I route I should get | from | to | route | distance | time | - | a | b | ab | 100m +-1 | 60s +-1 | - | b | c | bc | 200m +-1 | 600s +-1 | - | c | d | cd | 300m +-1 | 3600s +-1 | - | d | e | de | 141m +-2 | 36000s +-1 | - | e | f | ef | 224m +-2 | 3723s +-1 | + | a | b | ab,ab | 100m +-1 | 60s +-1 | + | b | c | bc,bc | 200m +-1 | 600s +-1 | + | c | d | cd,cd | 300m +-1 | 3600s +-1 | + | d | e | de,de | 141m +-2 | 36000s +-1 | + | e | f | ef,ef | 224m +-2 | 3723s +-1 | @todo Scenario: Partial duration of ways @@ -36,7 +36,7 @@ Feature: Durations | abc | primary | 0:01 | When I route I should get - | from | to | route | distance | time | - | a | c | abc | 300m +-1 | 60s +-1 | - | a | b | ab | 100m +-1 | 20s +-1 | - | b | c | bc | 200m +-1 | 40s +-1 | + | from | to | route | distance | time | + | a | c | abc,abc | 300m +-1 | 60s +-1 | + | a | b | ab,ab | 100m +-1 | 20s +-1 | + | b | c | bc,bc | 200m +-1 | 40s +-1 | diff --git a/features/testbot/example.feature b/features/testbot/example.feature index c2aa1e926..075a22e56 100644 --- a/features/testbot/example.feature +++ b/features/testbot/example.feature @@ -20,18 +20,18 @@ Feature: Testbot - Walkthrough | de | primary | | When I route I should get - | from | to | route | - | a | b | abc | - | a | c | abc | - | a | d | abc,cd | - | a | e | abc,ce | - | b | a | abc | - | b | c | abc | - | b | d | abc,cd | - | b | e | abc,ce | - | d | a | de,ce,abc | - | d | b | de,ce,abc | - | d | e | de | - | e | a | ce,abc | - | e | b | ce,abc | - | e | c | ce | + | from | to | route | + | a | b | abc,abc | + | a | c | abc,abc | + | a | d | abc,cd,cd | + | a | e | abc,ce,ce | + | b | a | abc,abc | + | b | c | abc,abc | + | b | d | abc,cd,cd | + | b | e | abc,ce,ce | + | d | a | de,ce,abc,abc | + | d | b | de,ce,abc,abc | + | d | e | de,de | + | e | a | ce,abc,abc | + | e | b | ce,abc,abc | + | e | c | ce,ce | diff --git a/features/testbot/fastest.feature b/features/testbot/fastest.feature index 8c24a5334..5576c738c 100644 --- a/features/testbot/fastest.feature +++ b/features/testbot/fastest.feature @@ -18,9 +18,9 @@ Feature: Choosing fastest route | asb | primary | When I route I should get - | from | to | route | - | x | y | xa,atb,by | - | y | x | by,atb,xa | + | from | to | route | + | x | y | xa,atb,by,by | + | y | x | by,atb,xa,xa | Scenario: Pick the fastest route, even when it's longer Given the node map @@ -33,6 +33,6 @@ Feature: Choosing fastest route | asb | secondary | When I route I should get - | from | to | route | - | a | b | apb | - | b | a | apb | + | from | to | route | + | a | b | apb,apb | + | b | a | apb,apb | diff --git a/features/testbot/ferry.feature b/features/testbot/ferry.feature index 21cefcb6f..6dd896b95 100644 --- a/features/testbot/ferry.feature +++ b/features/testbot/ferry.feature @@ -32,11 +32,11 @@ Feature: Testbot - Handle ferry routes When I route I should get | from | to | route | time | - | b | c | bc | 60s +-1 | - | f | g | fg | 600s +-1 | - | j | k | jk | 3600s +-1 | - | n | o | no | 86400s +-1 | - | r | s | rs | 345600s +-1 | + | b | c | bc,bc | 60s +-1 | + | f | g | fg,fg | 600s +-1 | + | j | k | jk,jk | 3600s +-1 | + | n | o | no,no | 86400s +-1 | + | r | s | rs,rs | 345600s +-1 | @todo Scenario: Testbot - Week long ferry routes @@ -59,9 +59,9 @@ Feature: Testbot - Handle ferry routes When I route I should get | from | to | route | time | - | b | c | bc | 86400s +-1 | - | f | g | fg | 604800s +-1 | - | j | k | jk | 259200s +-1 | + | b | c | bc,bc | 86400s +-1 | + | f | g | fg,fg | 604800s +-1 | + | j | k | jk,jk | 259200s +-1 | Scenario: Testbot - Ferry duration, multiple nodes Given the node map @@ -76,8 +76,8 @@ Feature: Testbot - Handle ferry routes When I route I should get | from | to | route | time | - | a | d | ad | 3600s +-1 | - | d | a | ad | 3600s +-1 | + | a | d | ad,ad | 3600s +-1 | + | d | a | ad,ad | 3600s +-1 | @todo Scenario: Testbot - Ferry duration, individual parts, fast @@ -95,11 +95,11 @@ Feature: Testbot - Handle ferry routes | abcd | | ferry | 0:06 | When I route I should get - | from | to | route | time | - | a | d | abcd | 360s +-1 | - | a | b | abcd | 60s +-1 | - | b | c | abcd | 120s +-1 | - | c | d | abcd | 180s +-1 | + | from | to | route | time | + | a | d | abcd,abcd | 360s +-1 | + | a | b | abcd,abcd | 60s +-1 | + | b | c | abcd,abcd | 120s +-1 | + | c | d | abcd,abcd | 180s +-1 | @todo Scenario: Testbot - Ferry duration, individual parts, slow @@ -116,11 +116,11 @@ Feature: Testbot - Handle ferry routes | abcd | | ferry | 1:00 | When I route I should get - | from | to | route | time | - | a | d | abcd | 3600s ~1% | - | a | b | abcd | 600s ~1% | - | b | c | abcd | 1200s ~1% | - | c | d | abcd | 1800s ~1% | + | from | to | route | time | + | a | d | abcd,abcd | 3600s ~1% | + | a | b | abcd,abcd | 600s ~1% | + | b | c | abcd,abcd | 1200s ~1% | + | c | d | abcd,abcd | 1800s ~1% | Scenario: Testbot - Ferry duration, connected routes Given the node map @@ -135,9 +135,9 @@ Feature: Testbot - Handle ferry routes | defg | | ferry | 0:30 | When I route I should get - | from | to | route | time | - | a | g | abcd,defg | 3600s +-1 | - | g | a | defg,abcd | 3600s +-1 | + | from | to | route | time | + | a | g | abcd,defg,defg | 3600s +-1 | + | g | a | defg,abcd,abcd | 3600s +-1 | Scenario: Testbot - Prefer road when faster than ferry Given the node map @@ -154,9 +154,9 @@ Feature: Testbot - Handle ferry routes | defg | | ferry | 0:01 | When I route I should get - | from | to | route | time | - | a | g | xa,xy,yg | 60s +-25% | - | g | a | yg,xy,xa | 60s +-25% | + | from | to | route | time | + | a | g | xa,xy,yg,yg | 60s +-25% | + | g | a | yg,xy,xa,xa | 60s +-25% | Scenario: Testbot - Long winding ferry route Given the node map @@ -170,9 +170,9 @@ Feature: Testbot - Handle ferry routes | abcdefg | | ferry | 6:30 | When I route I should get - | from | to | route | time | - | a | g | abcdefg | 23400s +-2 | - | g | a | abcdefg | 23400s +-2 | + | from | to | route | time | + | a | g | abcdefg,abcdefg | 23400s +-2 | + | g | a | abcdefg,abcdefg | 23400s +-2 | @todo Scenario: Testbot - Ferry duration formats @@ -194,12 +194,12 @@ Feature: Testbot - Handle ferry routes When I route I should get | from | to | route | time | - | a | b | ab | 60s +-1 | - | c | d | cd | 60s +-1 | - | e | f | ef | 3600s +-1 | - | g | h | gh | 3600s +-1 | - | i | j | ij | 8400s +-1 | - | k | l | kl | 36000s +-1 | - | m | n | mn | 360000s +-1 | - | o | p | mn | 3600000s +-1 | - | q | r | mn | 36000000s +-1 | + | a | b | ab,ab | 60s +-1 | + | c | d | cd,cd | 60s +-1 | + | e | f | ef,ef | 3600s +-1 | + | g | h | gh,gh | 3600s +-1 | + | i | j | ij,ij | 8400s +-1 | + | k | l | kl,kl | 36000s +-1 | + | m | n | mn,mn | 360000s +-1 | + | o | p | mn,mn | 3600000s +-1 | + | q | r | mn,mn | 36000000s +-1 | diff --git a/features/testbot/fixed.feature b/features/testbot/fixed.feature index 0edf25c15..dc09eaa91 100644 --- a/features/testbot/fixed.feature +++ b/features/testbot/fixed.feature @@ -22,5 +22,5 @@ Feature: Fixed bugs, kept to check for regressions | cdec | When I route I should get - | from | to | route | turns | - | x | y | abc | head,destination | + | from | to | route | + | x | y | abc,abc | diff --git a/features/testbot/geometry.feature b/features/testbot/geometry.feature index 8b38c06f8..24f3d235f 100644 --- a/features/testbot/geometry.feature +++ b/features/testbot/geometry.feature @@ -21,10 +21,8 @@ Feature: Retrieve geometry | cd | When I route I should get - | from | to | route | geometry | - | a | c | ab,bc | _c`\|@_upzA_c`\|@_c`\|@_c`\|@_c`\|@ | - | b | d | bc,cd | _gayB_yqwC_c`\|@_c`\|@_c`\|@_c`\|@ | + | from | to | route | geometry | + | a | c | ab,bc,bc | _ibE_~cH_seK_seK | + | b | d | bc,cd,cd | _seK_hgN_seK_seK | # Mind the \ before the pipes -# polycodec.rb decode2 '_c`|@_upzA_c`|@_c`|@_c`|@_c`|@' [[1.0, 1.5], [2.0, 2.5], [3.0, 3.5]] -# polycodec.rb decode2 '_gayB_yqwC_c`|@_c`|@_c`|@_c`|@' [[2.0, 2.5], [3.0, 3.5], [4.0, 4.5]] diff --git a/features/testbot/graph.feature b/features/testbot/graph.feature index adff99868..81741aa93 100644 --- a/features/testbot/graph.feature +++ b/features/testbot/graph.feature @@ -17,8 +17,8 @@ Feature: Basic Routing | dce | When I route I should get - | from | to | route | - | a | e | abc,dce | + | from | to | route | + | a | e | abc,dce,dce | Scenario: Turn instructions on compressed road network geometry Given the node map @@ -36,5 +36,5 @@ Feature: Basic Routing | fy | last | When I route I should get - | from | to | route | turns | - | x | y | first,compr,last | head,right,left,destination | + | from | to | route | + | x | y | first,compr,last,last | diff --git a/features/testbot/load.feature b/features/testbot/load.feature index cf5e470bd..b29fdc7ed 100644 --- a/features/testbot/load.feature +++ b/features/testbot/load.feature @@ -17,8 +17,8 @@ Feature: Ways of loading data When I route I should get | from | to | route | - | a | b | ab | - | b | a | ab | + | a | b | ab,ab | + | b | a | ab,ab | Scenario: Load data directly - st Given data is loaded directly @@ -31,10 +31,10 @@ Feature: Ways of loading data When I route I should get | from | to | route | - | s | t | st | - | t | s | st | + | s | t | st,st | + | t | s | st,st | - Scenario: Load data datstore - xy + Scenario: Load data datastore - xy Given data is loaded with datastore Given the node map | x | y | @@ -45,8 +45,8 @@ Feature: Ways of loading data When I route I should get | from | to | route | - | x | y | xy | - | y | x | xy | + | x | y | xy,xy | + | y | x | xy,xy | Scenario: Load data directly - cd Given data is loaded directly @@ -59,5 +59,5 @@ Feature: Ways of loading data When I route I should get | from | to | route | - | c | d | cd | - | d | c | cd | + | c | d | cd,cd | + | d | c | cd,cd | diff --git a/features/testbot/loop.feature b/features/testbot/loop.feature index c69e7a251..f035ab0fb 100644 --- a/features/testbot/loop.feature +++ b/features/testbot/loop.feature @@ -4,7 +4,7 @@ Feature: Avoid weird loops caused by rounding errors Background: Given the profile "testbot" - Scenario: Weired sidestreet loops + Scenario: Weird sidestreet loops Given the node map | a | 1 | b | 2 | c | 3 | d | | | | | | | | | @@ -18,10 +18,10 @@ Feature: Avoid weird loops caused by rounding errors | cg | When I route I should get - | waypoints | route | turns | - | a,1,d | abcd,abcd | head,via,destination | - | a,2,d | abcd,abcd | head,via,destination | - | a,3,d | abcd,abcd | head,via,destination | + | waypoints | route | + | a,1,d | abcd,abcd,abcd,abcd | + | a,2,d | abcd,abcd,abcd,abcd | + | a,3,d | abcd,abcd,abcd,abcd | Scenario: Avoid weird loops 1 Given the node locations @@ -47,8 +47,8 @@ Feature: Avoid weird loops caused by rounding errors | ie | When I route I should get - | from | to | route | turns | - | x | y | hfgd | head,destination | + | from | to | route | + | x | y | hfgd,hfgd | Scenario: Avoid weird loops 2 Given the node locations @@ -67,8 +67,8 @@ Feature: Avoid weird loops caused by rounding errors | cdec | When I route I should get - | from | to | route | turns | - | x | y | abc | head,destination | + | from | to | route | + | x | y | abc,abc | @412 Scenario: Avoid weird loops 3 @@ -92,6 +92,6 @@ Feature: Avoid weird loops caused by rounding errors | cf | primary | When I route I should get - | waypoints | route | turns | - | a,2,d | ab,be,ef,ef,cf,cd | head,left,right,via,right,left,destination | - | a,1,d | ab,be,ef,ef,cf,cd | head,left,right,via,right,left,destination | + | waypoints | route | + | a,2,d | ab,be,ef,ef,ef,cf,cd,cd | + | a,1,d | ab,be,ef,ef,ef,cf,cd,cd | diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature index 91bf928fa..71ac0776c 100644 --- a/features/testbot/matching.feature +++ b/features/testbot/matching.feature @@ -5,6 +5,23 @@ Feature: Basic Map Matching Given the profile "testbot" Given a grid size of 10 meters + Scenario: Testbot - Map matching with outlier that has no candidate + Given a grid size of 100 meters + Given the node map + | a | b | c | d | + | | | | | + | | | | | + | | | | | + | | | 1 | | + + And the ways + | nodes | oneway | + | abcd | no | + + When I match I should get + | trace | timestamps | matchings | + | ab1d | 0 1 2 3 | abcd | + Scenario: Testbot - Map matching with trace splitting Given the node map | a | b | c | d | @@ -18,6 +35,20 @@ Feature: Basic Map Matching | trace | timestamps | matchings | | abcd | 0 1 62 63 | ab,cd | + Scenario: Testbot - Map matching with core factor + Given the contract extra arguments "--core 0.8" + Given the node map + | a | b | c | d | + | | | e | | + + And the ways + | nodes | oneway | + | abcd | no | + + When I match I should get + | trace | timestamps | matchings | + | abcd | 0 1 2 3 | abcd | + Scenario: Testbot - Map matching with small distortion Given the node map | a | b | c | d | e | @@ -54,3 +85,22 @@ Feature: Basic Map Matching | trace | matchings | | dcba | hgfe | + Scenario: Testbot - Matching with oneway streets + Given a grid size of 10 meters + Given the node map + | a | b | c | d | + | e | f | g | h | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | hg | yes | + | gf | yes | + | fe | yes | + + When I match I should get + | trace | matchings | + | dcba | hg,gf,fe | + | efgh | ab,bc,cd | diff --git a/features/testbot/matching_turns.feature b/features/testbot/matching_turns.feature deleted file mode 100644 index 8bca4a274..000000000 --- a/features/testbot/matching_turns.feature +++ /dev/null @@ -1,121 +0,0 @@ -@routing @turns @testbot -Feature: Turn directions/codes - - Background: - Given the profile "testbot" - - Scenario: Turn directions - Given the query options - | instructions | true | - Given the node map - | o | p | a | b | c | - | n | | | | d | - | m | | x | | e | - | l | | | | f | - | k | j | i | h | g | - - And the ways - | nodes | - | xa | - | xb | - | xc | - | xd | - | xe | - | xf | - | xg | - | xh | - | xi | - | xj | - | xk | - | xl | - | xm | - | xn | - | xo | - | xp | - - When I match I should get - | trace | route | turns | matchings | - | im | xi,xm | head,left,destination | im | - | io | xi,xo | head,slight_left,destination | io | - | ia | xi,xa | head,straight,destination | ia | - | ic | xi,xc | head,slight_right,destination | ic | - | ie | xi,xe | head,right,destination | ie | - - | ko | xk,xo | head,left,destination | ko | - | ka | xk,xa | head,slight_left,destination | ka | - | kc | xk,xc | head,straight,destination | kc | - | ke | xk,xe | head,slight_right,destination | ke | - | kg | xk,xg | head,right,destination | kg | - - | ma | xm,xa | head,left,destination | ma | - | mc | xm,xc | head,slight_left,destination | mc | - | me | xm,xe | head,straight,destination | me | - | mg | xm,xg | head,slight_right,destination | mg | - | mi | xm,xi | head,right,destination | mi | - - | oc | xo,xc | head,left,destination | oc | - | oe | xo,xe | head,slight_left,destination | oe | - | og | xo,xg | head,straight,destination | og | - | oi | xo,xi | head,slight_right,destination | oi | - | ok | xo,xk | head,right,destination | ok | - - | ae | xa,xe | head,left,destination | ae | - | ag | xa,xg | head,slight_left,destination | ag | - | ai | xa,xi | head,straight,destination | ai | - | ak | xa,xk | head,slight_right,destination | ak | - | am | xa,xm | head,right,destination | am | - - | cg | xc,xg | head,left,destination | cg | - | ci | xc,xi | head,slight_left,destination | ci | - | ck | xc,xk | head,straight,destination | ck | - | cm | xc,xm | head,slight_right,destination | cm | - | co | xc,xo | head,right,destination | co | - - | ei | xe,xi | head,left,destination | ei | - | ek | xe,xk | head,slight_left,destination | ek | - | em | xe,xm | head,straight,destination | em | - | eo | xe,xo | head,slight_right,destination | eo | - | ea | xe,xa | head,right,destination | ea | - - | gk | xg,xk | head,left,destination | gk | - | gm | xg,xm | head,slight_left,destination | gm | - | go | xg,xo | head,straight,destination | go | - | ga | xg,xa | head,slight_right,destination | ga | - | gc | xg,xc | head,right,destination | gc | - - Scenario: Turn directions - Given the query options - | instructions | true | - Given the node map - | o | p | a | b | c | - | n | | | | d | - | m | | x | | e | - | l | | | | f | - | k | j | i | h | g | - - And the ways - | nodes | - | xa | - | xb | - | xc | - | xd | - | xe | - | xf | - | xg | - | xh | - | xi | - | xj | - | xk | - | xl | - | xm | - | xn | - | xo | - | xp | - - When I match I should get - | trace | route | turns | matchings | duration | - | im | xi,xm | head,left,destination | im | 80 | - | io | xi,xo | head,slight_left,destination | io | 88 | - | ia | xi,xa | head,straight,destination | ia | 80 | - | ic | xi,xc | head,slight_right,destination | ic | 88 | - | ie | xi,xe | head,right,destination | ie | 60 | diff --git a/features/testbot/mode.feature b/features/testbot/mode.feature index ef8cd257e..3f2463d7d 100644 --- a/features/testbot/mode.feature +++ b/features/testbot/mode.feature @@ -2,7 +2,7 @@ Feature: Testbot - Travel mode # testbot modes: -# 1 normal +# 1 driving # 2 route # 3 river downstream # 4 river upstream @@ -12,6 +12,7 @@ Feature: Testbot - Travel mode Background: Given the profile "testbot" + @mokob @2166 Scenario: Testbot - Always announce mode change Given the node map | a | b | c | d | @@ -23,9 +24,27 @@ Feature: Testbot - Travel mode | cd | residential | foo | When I route I should get - | from | to | route | modes | - | a | d | foo,foo,foo | 1,3,1 | + | from | to | route | modes | + | a | d | foo,foo,foo,foo | driving,river downstream,driving,driving | + | b | d | foo,foo,foo | river downstream,driving,driving | + @mokob @2166 + Scenario: Testbot - Compressed Modes + Given the node map + | a | b | c | d | e | f | g | + + And the ways + | nodes | highway | name | + | abc | residential | road | + | cde | river | liquid | + | efg | residential | solid | + + When I route I should get + | from | to | route | modes | + | a | g | road,liquid,solid,solid | driving,river downstream,driving,driving | + | c | g | liquid,solid,solid | river downstream,driving,driving | + + @mokob @2166 Scenario: Testbot - Modes in each direction, different forward/backward speeds Given the node map | | 0 | 1 | | @@ -36,15 +55,15 @@ Feature: Testbot - Travel mode | ab | river | | When I route I should get - | from | to | route | modes | - | a | 0 | ab | 3 | - | a | b | ab | 3 | - | 0 | 1 | ab | 3 | - | 0 | b | ab | 3 | - | b | 1 | ab | 4 | - | b | a | ab | 4 | - | 1 | 0 | ab | 4 | - | 1 | a | ab | 4 | + | from | to | route | modes | + | a | 0 | ab,ab | river downstream,river downstream | + | a | b | ab,ab | river downstream,river downstream | + | 0 | 1 | ab,ab | river downstream,river downstream | + | 0 | b | ab,ab | river downstream,river downstream | + | b | 1 | ab,ab | river upstream,river upstream | + | b | a | ab,ab | river upstream,river upstream | + | 1 | 0 | ab,ab | river upstream,river upstream | + | 1 | a | ab,ab | river upstream,river upstream | Scenario: Testbot - Modes in each direction, same forward/backward speeds Given the node map @@ -56,11 +75,11 @@ Feature: Testbot - Travel mode | ab | steps | When I route I should get - | from | to | route | modes | time | - | 0 | 1 | ab | 5 | 60s +-1 | - | 1 | 0 | ab | 6 | 60s +-1 | + | from | to | route | modes | time | + | 0 | 1 | ab,ab | steps down,steps down | 60s +-1 | + | 1 | 0 | ab,ab | steps up,steps up | 60s +-1 | - @oneway + @oneway @mokob @2166 Scenario: Testbot - Modes for oneway, different forward/backward speeds Given the node map | a | b | @@ -70,9 +89,9 @@ Feature: Testbot - Travel mode | ab | river | yes | When I route I should get - | from | to | route | modes | - | a | b | ab | 3 | - | b | a | | | + | from | to | route | modes | + | a | b | ab,ab | river downstream,river downstream | + | b | a | | | @oneway Scenario: Testbot - Modes for oneway, same forward/backward speeds @@ -84,11 +103,11 @@ Feature: Testbot - Travel mode | ab | steps | yes | When I route I should get - | from | to | route | modes | - | a | b | ab | 5 | - | b | a | | | + | from | to | route | modes | + | a | b | ab,ab | steps down,steps down | + | b | a | | | - @oneway + @oneway @mokob @2166 Scenario: Testbot - Modes for reverse oneway, different forward/backward speeds Given the node map | a | b | @@ -98,9 +117,9 @@ Feature: Testbot - Travel mode | ab | river | -1 | When I route I should get - | from | to | route | modes | - | a | b | | | - | b | a | ab | 4 | + | from | to | route | modes | + | a | b | | | + | b | a | ab,ab | river upstream,river upstream | @oneway Scenario: Testbot - Modes for reverse oneway, same forward/backward speeds @@ -112,11 +131,11 @@ Feature: Testbot - Travel mode | ab | steps | -1 | When I route I should get - | from | to | route | modes | - | a | b | | | - | b | a | ab | 6 | + | from | to | route | modes | + | a | b | | | + | b | a | ab,ab | steps up,steps up | - @via + @via @mokob @2166 Scenario: Testbot - Mode should be set at via points Given the node map | a | 1 | b | @@ -126,10 +145,11 @@ Feature: Testbot - Travel mode | ab | river | When I route I should get - | waypoints | route | modes | turns | - | a,1,b | ab,ab | 3,3 | head,via,destination | - | b,1,a | ab,ab | 4,4 | head,via,destination | + | waypoints | route | modes | + | a,1,b | ab,ab,ab,ab | river downstream,river downstream,river downstream,river downstream | + | b,1,a | ab,ab,ab,ab | river upstream,river upstream,river upstream,river upstream | + @mokob @2166 Scenario: Testbot - Starting at a tricky node Given the node map | | a | | | | @@ -141,9 +161,10 @@ Feature: Testbot - Travel mode | bc | primary | When I route I should get - | from | to | route | modes | - | b | a | ab | 4 | + | from | to | route | modes | + | b | a | ab,ab | river upstream,river upstream | + @mokob @2166 Scenario: Testbot - Mode changes on straight way without name change Given the node map | a | 1 | b | 2 | c | @@ -154,11 +175,11 @@ Feature: Testbot - Travel mode | bc | river | Avenue | When I route I should get - | from | to | route | modes | turns | - | a | c | Avenue,Avenue | 1,3 | head,straight,destination | - | c | a | Avenue,Avenue | 4,1 | head,straight,destination | - | 1 | 2 | Avenue,Avenue | 1,3 | head,straight,destination | - | 2 | 1 | Avenue,Avenue | 4,1 | head,straight,destination | + | from | to | route | modes | + | a | c | Avenue,Avenue,Avenue | driving,river downstream,river downstream | + | c | a | Avenue,Avenue,Avenue | river upstream,driving,driving | + | 1 | 2 | Avenue,Avenue,Avenue | driving,river downstream,river downstream | + | 2 | 1 | Avenue,Avenue,Avenue | river upstream,driving,driving | Scenario: Testbot - Mode for routes Given the node map @@ -174,15 +195,16 @@ Feature: Testbot - Travel mode | ef | primary | | | When I route I should get - | from | to | route | turns | modes | - | a | d | ab,bc,cd | head,right,left,destination | 1,2,1 | - | d | a | cd,bc,ab | head,right,left,destination | 1,2,1 | - | c | a | bc,ab | head,left,destination | 2,1 | - | d | b | cd,bc | head,right,destination | 1,2 | - | a | c | ab,bc | head,right,destination | 1,2 | - | b | d | bc,cd | head,left,destination | 2,1 | - | a | f | ab,bc,cd,de,ef | head,right,left,straight,straight,destination | 1,2,1,1,1 | + | from | to | route | modes | + | a | d | ab,bc,cd,cd | driving,route,driving,driving | + | d | a | cd,bc,ab,ab | driving,route,driving,driving | + | c | a | bc,ab,ab | route,driving,driving | + | d | b | cd,bc,bc | driving,route,route | + | a | c | ab,bc,bc | driving,route,route | + | b | d | bc,cd,cd | route,driving,driving | + | a | f | ab,bc,cd,de,ef,ef | driving,route,driving,driving,driving,driving | + @mokob @2166 Scenario: Testbot - Modes, triangle map Given the node map | | | | | | | d | @@ -201,22 +223,23 @@ Feature: Testbot - Travel mode | de | primary | | When I route I should get - | from | to | route | modes | - | 0 | 1 | abc,ce,de | 1,3,1 | - | 1 | 0 | de,ce,abc | 1,4,1 | - | 0 | 2 | abc,cd | 1,1 | - | 2 | 0 | cd,de,ce,abc | 1,1,4,1 | - | 0 | 3 | abc,ce | 1,3 | - | 3 | 0 | ce,abc | 4,1 | - | 4 | 3 | ce | 3 | - | 3 | 4 | ce | 4 | - | 3 | 1 | ce,de | 3,1 | - | 1 | 3 | de,ce | 1,4 | - | a | e | abc,ce | 1,3 | - | e | a | ce,abc | 4,1 | - | a | d | abc,cd | 1,1 | - | d | a | de,ce,abc | 1,4,1 | + | from | to | route | modes | + | 0 | 1 | abc,ce,de,de | driving,river downstream,driving,driving | + | 1 | 0 | de,ce,abc,abc | driving,river upstream,driving,driving | + | 0 | 2 | abc,cd,cd | driving,driving,driving | + | 2 | 0 | cd,de,ce,abc,abc | driving,driving,river upstream,driving,driving | + | 0 | 3 | abc,ce,ce | driving,river downstream,river downstream | + | 3 | 0 | ce,abc,abc | river upstream,driving,driving | + | 4 | 3 | ce,ce | river downstream,river downstream | + | 3 | 4 | ce,ce | river upstream,river upstream | + | 3 | 1 | ce,de,de | river downstream,driving,driving | + | 1 | 3 | de,ce,ce | driving,river upstream,river upstream | + | a | e | abc,ce,ce | driving,river downstream,river downstream | + | e | a | ce,abc,abc | river upstream,driving,driving | + | a | d | abc,cd,cd | driving,driving,driving | + | d | a | de,ce,abc,abc | driving,river upstream,driving,driving | + @mokob @2166 Scenario: Testbot - River in the middle Given the node map | a | b | c | | | @@ -230,12 +253,12 @@ Feature: Testbot - Travel mode | efg | primary | When I route I should get - | from | to | route | modes | - | a | g | abc,cde,efg | 1,3,1 | - | b | f | abc,cde,efg | 1,3,1 | - | e | c | cde | 4 | - | e | b | cde,abc | 4,1 | - | e | a | cde,abc | 4,1 | - | c | e | cde | 3 | - | c | f | cde,efg | 3,1 | - | c | g | cde,efg | 3,1 | + | from | to | route | modes | + | a | g | abc,cde,efg,efg | driving,river downstream,driving,driving | + | b | f | abc,cde,efg,efg | driving,river downstream,driving,driving | + | e | c | cde,cde | river upstream,river upstream | + | e | b | cde,abc,abc | river upstream,driving,driving | + | e | a | cde,abc,abc | river upstream,driving,driving | + | c | e | cde,cde | river downstream,river downstream | + | c | f | cde,efg,efg | river downstream,driving,driving | + | c | g | cde,efg,efg | river downstream,driving,driving | diff --git a/features/testbot/oneway.feature b/features/testbot/oneway.feature index 766bf8b3b..6e2326d54 100644 --- a/features/testbot/oneway.feature +++ b/features/testbot/oneway.feature @@ -29,23 +29,23 @@ Feature: Testbot - oneways | xe | yes | When I route I should get - | from | to | route | - | a | b | ab | - | b | c | bc | - | c | d | cd | - | d | e | de | - | e | f | ef | - | f | g | fg | - | g | h | gh | - | h | a | ha | - | b | a | bc,cd,de,ef,fg,gh,ha | - | c | b | cd,de,ef,fg,gh,ha,ab | - | d | c | de,ef,fg,gh,ha,ab,bc | - | e | d | ef,fg,gh,ha,ab,bc,cd | - | f | e | fg,gh,ha,ab,bc,cd,de | - | g | f | gh,ha,ab,bc,cd,de,ef | - | h | g | ha,ab,bc,cd,de,ef,fg | - | a | h | ab,bc,cd,de,ef,fg,gh | + | from | to | route | + | a | b | ab,ab | + | b | c | bc,bc | + | c | d | cd,cd | + | d | e | de,de | + | e | f | ef,ef | + | f | g | fg,fg | + | g | h | gh,gh | + | h | a | ha,ha | + | b | a | bc,cd,de,ef,fg,gh,ha,ha | + | c | b | cd,de,ef,fg,gh,ha,ab,ab | + | d | c | de,ef,fg,gh,ha,ab,bc,bc | + | e | d | ef,fg,gh,ha,ab,bc,cd,cd | + | f | e | fg,gh,ha,ab,bc,cd,de,de | + | g | f | gh,ha,ab,bc,cd,de,ef,ef | + | h | g | ha,ab,bc,cd,de,ef,fg,fg | + | a | h | ab,bc,cd,de,ef,fg,gh,gh | Scenario: Testbot - Simple oneway Then routability should be @@ -70,9 +70,9 @@ Feature: Testbot - oneways | da | | no | When I route I should get - | from | to | route | - | a | b | ab | - | b | a | bc,cd,da | + | from | to | route | + | a | b | ab,ab | + | b | a | bc,cd,da,da | Scenario: Testbot - Handle various oneway tag values Then routability should be @@ -98,5 +98,5 @@ Feature: Testbot - oneways When I route I should get - | from | to | route | - | a | c | ab,bc | + | from | to | route | + | a | c | ab,bc,bc | diff --git a/features/testbot/opposite.feature b/features/testbot/opposite.feature index c19755cdc..de11e5ec1 100644 --- a/features/testbot/opposite.feature +++ b/features/testbot/opposite.feature @@ -13,6 +13,6 @@ Feature: Separate settings for forward/backward direction | abcd | river | When I route I should get - | from | to | route | distance | speed | - | a | d | abcd | 300 +- 1m | 36 km/h | - | d | a | abcd | 300 +- 1m | 16 km/h +- 1 | + | from | to | route | distance | speed | + | a | d | abcd,abcd | 300 +- 1m | 36 km/h | + | d | a | abcd,abcd | 300 +- 1m | 16 km/h +- 1 | diff --git a/features/testbot/origin.feature b/features/testbot/origin.feature index b5b194502..e56e94dc8 100644 --- a/features/testbot/origin.feature +++ b/features/testbot/origin.feature @@ -17,9 +17,9 @@ Feature: Routing close to the [0,0] origin | abcd | yes | When I route I should get - | from | to | route | distance | - | b | c | abcd | 100m +-1 | - | c | b | | | + | from | to | route | distance | + | b | c | abcd,abcd | 100m +-1 | + | c | b | | | Scenario: North-south oneways close to the origin Given the node locations @@ -34,9 +34,9 @@ Feature: Routing close to the [0,0] origin | abcd | yes | When I route I should get - | from | to | route | distance | - | b | c | abcd | 100m +-1 | - | c | b | | | + | from | to | route | distance | + | b | c | abcd,abcd | 100m +-1 | + | c | b | | | Scenario: East-west oneways crossing the origin Given the node locations @@ -52,9 +52,9 @@ Feature: Routing close to the [0,0] origin | abcde | yes | When I route I should get - | from | to | route | distance | - | b | d | abcde | 200m +-2 | - | d | b | | | + | from | to | route | distance | + | b | d | abcde,abcde | 200m +-2 | + | d | b | | | Scenario: North-south oneways crossing the origin Given the node locations @@ -70,6 +70,6 @@ Feature: Routing close to the [0,0] origin | abcde | yes | When I route I should get - | from | to | route | distance | - | b | d | abcde | 200m +-2 | - | d | b | | | + | from | to | route | distance | + | b | d | abcde,abcde | 200m +-2 | + | d | b | | | diff --git a/features/testbot/overlap.feature b/features/testbot/overlap.feature index 509867fb5..c76cff270 100644 --- a/features/testbot/overlap.feature +++ b/features/testbot/overlap.feature @@ -1,10 +1,9 @@ -@routing @testbot @overlap +@routing @testbot @overlap @todo Feature: Testbot - overlapping ways - + Background: Given the profile "testbot" - @bug @610 Scenario: Testbot - multiple way between same nodes Note that cb is connecting the same two nodes as bc Given the node map @@ -18,11 +17,10 @@ Feature: Testbot - overlapping ways | cb | secondary | When I route I should get - | from | to | route | - | a | d | ab,bc,cd | - | d | a | cd,bc,ab | - - @bug @610 + | from | to | route | + | a | d | ab,bc,cd,cd | + | d | a | cd,bc,ab,ab | + Scenario: Testbot - area on top of way Given the node map | x | a | b | y | @@ -34,6 +32,6 @@ Feature: Testbot - overlapping ways | abcda | secondary | yes | When I route I should get - | from | to | route | - | x | y | xaby | - | y | x | xaby | + | from | to | route | + | x | y | xaby,xaby | + | y | x | xaby,xaby | diff --git a/features/testbot/penalty.feature b/features/testbot/penalty.feature index e4f3cb632..8d86a17ca 100644 --- a/features/testbot/penalty.feature +++ b/features/testbot/penalty.feature @@ -20,9 +20,9 @@ Feature: Penalties | def | When I route I should get - | from | to | route | time | distance | - | a | c | abc | 20s +-1 | 200m +-1 | - | d | f | def | 27s +-1 | 200m +-1 | + | from | to | route | time | distance | + | a | c | abc,abc | 20s +-1 | 200m +-1 | + | d | f | def,def | 27s +-1 | 200m +-1 | Scenario: Signal penalty should not depend on way type Given the node map @@ -43,10 +43,10 @@ Feature: Penalties | ghi | tertiary | When I route I should get - | from | to | route | time | - | a | c | abc | 27s +-1 | - | d | f | def | 47s +-1 | - | g | i | ghi | 67s +-1 | + | from | to | route | time | + | a | c | abc,abc | 27s +-1 | + | d | f | def,def | 47s +-1 | + | g | i | ghi,ghi | 67s +-1 | Scenario: Passing multiple traffic signals should incur a accumulated delay Given the node map @@ -63,8 +63,8 @@ Feature: Penalties | abcde | When I route I should get - | from | to | route | time | - | a | e | abcde | 61s +-1 | + | from | to | route | time | + | a | e | abcde,abcde | 61s +-1 | @todo Scenario: Signal penalty should not depend on way type @@ -86,13 +86,13 @@ Feature: Penalties | ghi | tertiary | When I route I should get - | from | to | route | time | - | a | b | abc | 10s +-1 | - | a | c | abc | 27s +-1 | - | d | e | def | 20s +-1 | - | d | f | def | 47s +-1 | - | g | h | ghi | 30s +-1 | - | g | i | ghi | 67s +-1 | + | from | to | route | time | + | a | b | abc,abc | 10s +-1 | + | a | c | abc,abc | 27s +-1 | + | d | e | def,def | 20s +-1 | + | d | f | def,def | 47s +-1 | + | g | h | ghi,ghi | 30s +-1 | + | g | i | ghi,ghi | 67s +-1 | Scenario: Passing multiple traffic signals should incur a accumulated delay Given the node map @@ -109,8 +109,8 @@ Feature: Penalties | abcde | When I route I should get - | from | to | route | time | - | a | e | abcde | 61s +-1 | + | from | to | route | time | + | a | e | abcde,abcde | 61s +-1 | @todo Scenario: Starting or ending at a traffic signal should not incur a delay @@ -144,9 +144,9 @@ Feature: Penalties | abcd | primary | When I route I should get - | from | to | route | time | - | b | c | abcd | 10s +-1 | - | c | b | abcd | 10s +-1 | + | from | to | route | time | + | b | c | abcd,abcd | 10s +-1 | + | c | b | abcd,abcd | 10s +-1 | Scenario: Prefer faster route without traffic signals Given a grid size of 50 meters @@ -164,5 +164,5 @@ Feature: Penalties | adc | primary | When I route I should get - | from | to | route | - | a | c | adc | + | from | to | route | + | a | c | adc,adc | diff --git a/features/testbot/planetary.feature b/features/testbot/planetary.feature index 19925fe27..1fff4bded 100644 --- a/features/testbot/planetary.feature +++ b/features/testbot/planetary.feature @@ -13,7 +13,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | + | a | b | ab,ab | 8905559m ~0.1% | Scenario: Approximated Longitudinal distances at latitude 45 Given the node locations @@ -27,7 +27,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | c | d | cd | 6028844m ~4.5% | + | c | d | cd,cd | 6028844m ~4.5% | Scenario: Approximated Longitudinal distances at latitude 80 Given the node locations @@ -41,7 +41,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | c | d | cd | 1431469m ~9.5% | + | c | d | cd,cd | 1431469m ~9.5% | Scenario: Approximated Latitudinal distances at longitude 0 Given the node locations @@ -55,7 +55,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | + | a | b | ab,ab | 8905559m ~0.1% | Scenario: Approximated Latitudinal distances at longitude 45 Given the node locations @@ -69,7 +69,7 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | + | a | b | ab,ab | 8905559m ~0.1% | Scenario: Approximated Latitudinal distances at longitude 80 Given the node locations @@ -83,4 +83,4 @@ Feature: Distance calculation When I route I should get | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | + | a | b | ab,ab | 8905559m ~0.1% | diff --git a/features/testbot/post.feature b/features/testbot/post.feature deleted file mode 100644 index ac53177c5..000000000 --- a/features/testbot/post.feature +++ /dev/null @@ -1,83 +0,0 @@ -@post @testbot -Feature: POST request - - Background: - Given the profile "testbot" - And the HTTP method "POST" - - Scenario: Testbot - viaroute POST request - Given the node locations - | node | lat | lon | - | a | 55.68740 | 12.52430 | - | b | 55.68745 | 12.52409 | - | c | 55.68711 | 12.52383 | - | x | -55.68740 | 12.52430 | - | y | -55.68745 | 12.52409 | - | z | -55.68711 | 12.52383 | - - And the ways - | nodes | - | ab | - | bc | - | xy | - | yz | - - When I route I should get - | from | to | route | turns | - | a | c | ab,bc | head,left,destination | - | c | a | bc,ab | head,right,destination | - | x | z | xy,yz | head,right,destination | - | z | x | yz,xy | head,left,destination | - - Scenario: Testbot - match POST request - Given a grid size of 10 meters - Given the node map - | a | b | c | d | - | e | f | g | h | - - And the ways - | nodes | oneway | - | abcd | yes | - | hgfe | yes | - - When I match I should get - | trace | matchings | - | dcba | hgfe | - - Scenario: Testbot - table POST request - Given the node map - | x | a | b | y | - | | d | e | | - - And the ways - | nodes | oneway | - | abeda | yes | - | xa | | - | by | | - - When I request a travel time matrix I should get - | | x | y | d | e | - | x | 0 | 300 | 400 | 300 | - | y | 500 | 0 | 300 | 200 | - | d | 200 | 300 | 0 | 300 | - | e | 300 | 400 | 100 | 0 | - - Scenario: Testbot - nearest POST request - Given the node locations - | node | lat | lon | - | a | -85 | -180 | - | b | -85 | -160 | - | c | -85 | -140 | - | x | -84.999 | -180 | - | y | -84.999 | -160 | - | z | -84.999 | -140 | - - And the ways - | nodes | - | abc | - - When I request nearest I should get - | in | out | - | x | a | - | y | b | - | z | c | diff --git a/features/testbot/projection.feature b/features/testbot/projection.feature index d7a0b8993..ad4a32b5d 100644 --- a/features/testbot/projection.feature +++ b/features/testbot/projection.feature @@ -23,16 +23,16 @@ Feature: Projection to nearest point on road Scenario: Projection onto way at high latitudes, 1km distance When I route I should get - | from | to | route | compass | bearing | distance | - | b | a | abc | SW | 225 | 1000m +-7 | - | b | c | abc | NE | 45 | 1000m +-7 | - | a | d | abc | NE | 45 | 1000m +-7 | - | d | a | abc | SW | 225 | 1000m +-7 | - | c | d | abc | SW | 225 | 1000m +-8 | - | d | c | abc | NE | 45 +-5 | 1000m +-8 | + | from | to | route | bearing | distance | + | b | a | abc,abc | 225,0 +-1 | 1000m +- 7 | + | b | c | abc,abc | 45,0 +-1 | 1000m +- 7 | + | a | d | abc,abc | 45,0 +-1 | 1000m +- 7 | + | d | a | abc,abc | 225,0 +-1 | 1000m +- 7 | + | c | d | abc,abc | 225,0 +-1 | 1000m +- 8 | + | d | c | abc,abc | 45 +-1 | 1000m +- 8 | Scenario: Projection onto way at high latitudes, no distance When I route I should get - | from | to | route | distance | - | d | b | abc | 0m +-5 | - | b | d | abc | 0m +-5 | + | from | to | route | distance | + | d | b | abc,abc | 0m +-5 | + | b | d | abc,abc | 0m +-5 | diff --git a/features/testbot/protobuffer.feature b/features/testbot/protobuffer.feature deleted file mode 100644 index e32de26c2..000000000 --- a/features/testbot/protobuffer.feature +++ /dev/null @@ -1,156 +0,0 @@ -@routing @pbf @testbot -Feature: Importing protobuffer (.pbf) format -# Test normally read .osm, which is faster than .pbf files, -# since we don't need to use osmosis to first convert to .pbf -# The scenarios in this file test the ability to import .pbf files, -# including nodes, way, restictions, and a various special situations. - - Background: - Given the profile "testbot" - And the import format "pbf" - - Scenario: Testbot - Protobuffer import, nodes and ways - Given the node map - | | | | d | - | a | b | c | | - | | | | e | - - And the ways - | nodes | highway | oneway | - | abc | primary | | - | cd | primary | yes | - | ce | river | | - | de | primary | | - - When I route I should get - | from | to | route | - | d | c | de,ce | - | e | d | de | - - - Scenario: Testbot - Protobuffer import, turn restiction relations - Given the node map - | | n | | - | w | j | e | - | | s | | - - And the ways - | nodes | oneway | - | sj | yes | - | nj | -1 | - | wj | -1 | - | ej | -1 | - - And the relations - | type | way:from | way:to | node:via | restriction | - | restriction | sj | wj | j | no_left_turn | - - When I route I should get - | from | to | route | - | s | w | | - | s | n | sj,nj | - | s | e | sj,ej | - - - Scenario: Testbot - Protobuffer import, distances at longitude 45 - Given the node locations - | node | lat | lon | - | a | 80 | 45 | - | b | 0 | 45 | - - And the ways - | nodes | - | ab | - - When I route I should get - | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | - - Scenario: Testbot - Protobuffer import, distances at longitude 80 - Given the node locations - | node | lat | lon | - | a | 80 | 80 | - | b | 0 | 80 | - - And the ways - | nodes | - | ab | - - When I route I should get - | from | to | route | distance | - | a | b | ab | 8905559m ~0.1% | - - Scenario: Testbot - Protobuffer import, empty dataset - Given the node map - | | - - Given the ways - | nodes | - - When the data has been prepared - Then "osrm-extract" should return code 1 - - - Scenario: Testbot - Protobuffer import, streetnames with UTF characters - Given the node map - | a | b | c | d | - - And the ways - | nodes | name | - | ab | Scandinavian København | - | bc | Japanese 東京 | - | cd | Cyrillic Москва | - - When I route I should get - | from | to | route | - | a | b | Scandinavian København | - | b | c | Japanese 東京 | - | c | d | Cyrillic Москва | - - Scenario: Testbot - Protobuffer import, bearing af 45 degree intervals - Given the node map - | b | a | h | - | c | x | g | - | d | e | f | - - And the ways - | nodes | - | xa | - | xb | - | xc | - | xd | - | xe | - | xf | - | xg | - | xh | - - When I route I should get - | from | to | route | compass | bearing | - | x | a | xa | N | 0 | - | x | b | xb | NW | 315 | - | x | c | xc | W | 270 | - | x | d | xd | SW | 225 | - | x | e | xe | S | 180 | - | x | f | xf | SE | 135 | - | x | g | xg | E | 90 | - | x | h | xh | NE | 45 | - - - Scenario: Testbot - Protobuffer import, rraffic signals should incur a delay - Given the node map - | a | b | c | - | d | e | f | - - And the nodes - | node | highway | - | e | traffic_signals | - - And the ways - | nodes | - | abc | - | def | - - When I route I should get - | from | to | route | time | distance | - | a | c | abc | 20s +-1 | 200m +-1 | - | d | f | def | 27s +-1 | 200m +-1 | diff --git a/features/testbot/roundabout.feature b/features/testbot/roundabout.feature deleted file mode 100644 index 570b14bed..000000000 --- a/features/testbot/roundabout.feature +++ /dev/null @@ -1,76 +0,0 @@ -@routing @testbot @roundabout @instruction -Feature: Roundabout Instructions - - Background: - Given the profile "testbot" - - Scenario: Testbot - Roundabout - Given the node map - | | | v | | | - | | | d | | | - | s | a | | c | u | - | | | b | | | - | | | t | | | - - And the ways - | nodes | junction | - | sa | | - | tb | | - | uc | | - | vd | | - | abcda | roundabout | - - When I route I should get - | from | to | route | turns | - | s | t | sa,tb | head,enter_roundabout-1,destination | - | s | u | sa,uc | head,enter_roundabout-2,destination | - | s | v | sa,vd | head,enter_roundabout-3,destination | - | t | u | tb,uc | head,enter_roundabout-1,destination | - | t | v | tb,vd | head,enter_roundabout-2,destination | - | t | s | tb,sa | head,enter_roundabout-3,destination | - | u | v | uc,vd | head,enter_roundabout-1,destination | - | u | s | uc,sa | head,enter_roundabout-2,destination | - | u | t | uc,tb | head,enter_roundabout-3,destination | - | v | s | vd,sa | head,enter_roundabout-1,destination | - | v | t | vd,tb | head,enter_roundabout-2,destination | - | v | u | vd,uc | head,enter_roundabout-3,destination | - - Scenario: Testbot - Roundabout with oneway links - Given the node map - | | | p | o | | | - | | | h | g | | | - | i | a | | | f | n | - | j | b | | | e | m | - | | | c | d | | | - | | | k | l | | | - - And the ways - | nodes | junction | oneway | - | ai | | yes | - | jb | | yes | - | ck | | yes | - | ld | | yes | - | em | | yes | - | nf | | yes | - | go | | yes | - | ph | | yes | - | abcdefgha | roundabout | | - - When I route I should get - | from | to | route | turns | - | j | k | jb,ck | head,enter_roundabout-1,destination | - | j | m | jb,em | head,enter_roundabout-2,destination | - | j | o | jb,go | head,enter_roundabout-3,destination | - | j | i | jb,ai | head,enter_roundabout-4,destination | - | l | m | ld,em | head,enter_roundabout-1,destination | - | l | o | ld,go | head,enter_roundabout-2,destination | - | l | i | ld,ai | head,enter_roundabout-3,destination | - | l | k | ld,ck | head,enter_roundabout-4,destination | - | n | o | nf,go | head,enter_roundabout-1,destination | - | n | i | nf,ai | head,enter_roundabout-2,destination | - | n | k | nf,ck | head,enter_roundabout-3,destination | - | n | m | nf,em | head,enter_roundabout-4,destination | - | p | i | ph,ai | head,enter_roundabout-1,destination | - | p | k | ph,ck | head,enter_roundabout-2,destination | - | p | m | ph,em | head,enter_roundabout-3,destination | - | p | o | ph,go | head,enter_roundabout-4,destination | diff --git a/features/testbot/snap.feature b/features/testbot/snap.feature index 9b7bcbd0b..80c44bb88 100644 --- a/features/testbot/snap.feature +++ b/features/testbot/snap.feature @@ -21,14 +21,14 @@ Feature: Snap start/end point to the nearest way When I route I should get | from | to | route | - | 1 | c | nc | - | 2 | c | nc | - | 3 | c | ec | - | 4 | c | ec | - | 5 | c | sc | - | 6 | c | sc | - | 7 | c | wc | - | 8 | c | wc | + | 1 | c | nc,nc | + | 2 | c | nc,nc | + | 3 | c | ec,ec | + | 4 | c | ec,ec | + | 5 | c | sc,sc | + | 6 | c | sc,sc | + | 7 | c | wc,wc | + | 8 | c | wc,wc | Scenario: Snap to nearest edge of a square Given the node map @@ -43,11 +43,11 @@ Feature: Snap start/end point to the nearest way | adb | When I route I should get - | from | to | route | - | 1 | b | adb | - | 2 | b | adb | - | 6 | b | aub | - | 7 | b | aub | + | from | to | route | + | 1 | b | adb,adb | + | 2 | b | adb,adb | + | 6 | b | aub,aub | + | 7 | b | aub,aub | Scenario: Snap to edge right under start/end point Given the node map @@ -64,17 +64,17 @@ Feature: Snap start/end point to the nearest way | jkla | When I route I should get - | from | to | route | - | a | b | abcd | - | a | c | abcd | - | a | d | abcd | - | a | e | abcd,defg | - | a | f | abcd,defg | - | a | h | jkla,ghij | - | a | i | jkla,ghij | - | a | j | jkla | - | a | k | jkla | - | a | l | jkla | + | from | to | route | + | a | b | abcd,abcd | + | a | c | abcd,abcd | + | a | d | abcd,abcd | + | a | e | abcd,defg,defg | + | a | f | abcd,defg,defg | + | a | h | jkla,ghij,ghij | + | a | i | jkla,ghij,ghij | + | a | j | jkla,jkla | + | a | k | jkla,jkla | + | a | l | jkla,jkla | Scenario: Snapping in viaroute Given the extract extra arguments "--small-component-size 4" @@ -92,9 +92,9 @@ Feature: Snap start/end point to the nearest way When I route I should get | from | to | route | - | a | b | ab | - | a | d | cd | - | c | d | cd | + | a | b | ab,ab | + | a | d | cd,cd | + | c | d | cd,cd | Scenario: Snap to correct way at large scales Given a grid size of 1000 meters @@ -111,12 +111,12 @@ Feature: Snap start/end point to the nearest way When I route I should get | from | to | route | - | x | a | xa | - | x | b | xb | - | x | c | xc | - | a | x | xa | - | b | x | xb | - | c | x | xc | + | x | a | xa,xa | + | x | b | xb,xb | + | x | c | xc,xc | + | a | x | xa,xa | + | b | x | xb,xb | + | c | x | xc,xc | Scenario: Find edges within 100m, and the same from 1km Given a grid size of 100 meters @@ -152,19 +152,19 @@ Feature: Snap start/end point to the nearest way When I route I should get | from | to | route | - | x | 1 | xa | - | x | 2 | xb | - | x | 3 | xc | - | x | 4 | xd | - | x | 5 | xe | - | x | 6 | xf | - | x | 7 | xg | - | x | 8 | xh | - | x | i | xa | - | x | j | xb | - | x | k | xc | - | x | l | xd | - | x | m | xe | - | x | n | xf | - | x | o | xg | - | x | p | xh | + | x | 1 | xa,xa | + | x | 2 | xb,xb | + | x | 3 | xc,xc | + | x | 4 | xd,xd | + | x | 5 | xe,xe | + | x | 6 | xf,xf | + | x | 7 | xg,xg | + | x | 8 | xh,xh | + | x | i | xa,xa | + | x | j | xb,xb | + | x | k | xc,xc | + | x | l | xd,xd | + | x | m | xe,xe | + | x | n | xf,xf | + | x | o | xg,xg | + | x | p | xh,xh | diff --git a/features/testbot/speed.feature b/features/testbot/speed.feature index 2eb3724f5..041641962 100644 --- a/features/testbot/speed.feature +++ b/features/testbot/speed.feature @@ -26,6 +26,6 @@ Feature: Testbot - speeds | ab | river | When I route I should get - | from | to | route | speed | time | distance | - | a | b | ab | 36 km/h | 10s | 100m | - | b | a | ab | 16 km/h +- 1 | 23s | 100m | + | from | to | route | speed | time | distance | + | a | b | ab,ab | 36 km/h | 10s +-1 | 100m +- 1 | + | b | a | ab,ab | 16 km/h +- 1 | 23s +-1 | 100m +- 1 | diff --git a/features/testbot/status.feature b/features/testbot/status.feature index 86d825921..2221461a6 100644 --- a/features/testbot/status.feature +++ b/features/testbot/status.feature @@ -14,8 +14,8 @@ Feature: Status messages When I route I should get | from | to | route | status | message | - | a | b | ab | 200 | Found route between points | - | b | a | ab | 200 | Found route between points | + | a | b | ab,ab | 200 | | + | b | a | ab,ab | 200 | | Scenario: No route found Given the node map @@ -30,38 +30,38 @@ Feature: Status messages When I route I should get | from | to | route | status | message | - | a | b | ab | 200 | Found route between points | - | c | d | cd | 200 | Found route between points | - | a | c | | 207 | Impossible route between points | - | b | d | | 207 | Impossible route between points | + | a | b | ab,ab | 200 | | + | c | d | cd,cd | 200 | | + | a | c | | 400 | Impossible route between points | + | b | d | | 400 | Impossible route between points | Scenario: Malformed requests Given the node locations | node | lat | lon | | a | 1.00 | 1.00 | - | b | 1.01 | 1.00 | + | b | 2.00 | 1.00 | And the ways | nodes | | ab | When I route I should get - | request | status | message | - | viaroute?loc=1,1&loc=1.01,1 | 200 | Found route between points | - | nonsense | 400 | Service not found | - | nonsense?loc=1,1&loc=1.01,1 | 400 | Service not found | - | | 400 | Query string malformed close to position 0 | - | / | 400 | Query string malformed close to position 0 | - | ? | 400 | Query string malformed close to position 0 | - | viaroute?loc= | 400 | Query string malformed close to position 9 | - | viaroute?loc=1 | 400 | Query string malformed close to position 9 | - | viaroute?loc=1,1 | 400 | Invalid coordinates | - | viaroute?loc=1,1,1 | 400 | Query string malformed close to position 17 | - | viaroute?loc=x | 400 | Query string malformed close to position 9 | - | viaroute?loc=x,y | 400 | Query string malformed close to position 9 | - | viaroute?loc=1,1&loc= | 400 | Query string malformed close to position 17 | - | viaroute?loc=1,1&loc=1 | 400 | Query string malformed close to position 17 | - | viaroute?loc=1,1&loc=1,1 | 200 | Found route between points | - | viaroute?loc=1,1&loc=1,1,1 | 400 | Query string malformed close to position 25 | - | viaroute?loc=1,1&loc=x | 400 | Query string malformed close to position 17 | - | viaroute?loc=1,1&loc=x,y | 400 | Query string malformed close to position 17 | + | request | status | message | + | route/v1/driving/1,1;1,2 | 200 | | + | nonsense | 400 | URL string malformed close to position 9: "nse" | + | nonsense/v1/driving/1,1;1,2 | 400 | Service nonsense not found! | + | | 400 | URL string malformed close to position 1: "/" | + | / | 400 | URL string malformed close to position 1: "//" | + | ? | 400 | URL string malformed close to position 1: "/?" | + | route/v1/driving | 400 | URL string malformed close to position 17: "ing" | + | route/v1/driving/ | 400 | URL string malformed close to position 18: "ng/" | + | route/v1/driving/1 | 400 | Query string malformed close to position 1 | + | route/v1/driving/1,1 | 400 | Number of coordinates needs to be at least two. | + | route/v1/driving/1,1,1 | 400 | Query string malformed close to position 3 | + | route/v1/driving/x | 400 | Query string malformed close to position 0 | + | route/v1/driving/x,y | 400 | Query string malformed close to position 0 | + | route/v1/driving/1,1; | 400 | Query string malformed close to position 3 | + | route/v1/driving/1,1;1 | 400 | Query string malformed close to position 5 | + | route/v1/driving/1,1;1,1,1 | 400 | Query string malformed close to position 7 | + | route/v1/driving/1,1;x | 400 | Query string malformed close to position 3 | + | route/v1/driving/1,1;x,y | 400 | Query string malformed close to position 3 | diff --git a/features/testbot/time.feature b/features/testbot/time.feature index c838796f2..834b13b20 100644 --- a/features/testbot/time.feature +++ b/features/testbot/time.feature @@ -27,15 +27,15 @@ Feature: Estimation of travel time | xh | primary | When I route I should get - | from | to | route | time | - | x | a | xa | 1s +-1 | - | x | b | xb | 1s +-1 | - | x | c | xc | 1s +-1 | - | x | d | xd | 1s +-1 | - | x | e | xe | 1s +-1 | - | x | f | xf | 1s +-1 | - | x | g | xg | 1s +-1 | - | x | h | xh | 1s +-1 | + | from | to | route | time | + | x | a | xa,xa | 1s +-1 | + | x | b | xb,xb | 1s +-1 | + | x | c | xc,xc | 1s +-1 | + | x | d | xd,xd | 1s +-1 | + | x | e | xe,xe | 1s +-1 | + | x | f | xf,xf | 1s +-1 | + | x | g | xg,xg | 1s +-1 | + | x | h | xh,xh | 1s +-1 | Scenario: Basic travel time, 100m scale Given a grid size of 100 meters @@ -56,15 +56,15 @@ Feature: Estimation of travel time | xh | primary | When I route I should get - | from | to | route | time | - | x | a | xa | 10s +-1 | - | x | b | xb | 14s +-1 | - | x | c | xc | 10s +-1 | - | x | d | xd | 14s +-1 | - | x | e | xe | 10s +-1 | - | x | f | xf | 14s +-1 | - | x | g | xg | 10s +-1 | - | x | h | xh | 14s +-1 | + | from | to | route | time | + | x | a | xa,xa | 10s +-1 | + | x | b | xb,xb | 14s +-1 | + | x | c | xc,xc | 10s +-1 | + | x | d | xd,xd | 14s +-1 | + | x | e | xe,xe | 10s +-1 | + | x | f | xf,xf | 14s +-1 | + | x | g | xg,xg | 10s +-1 | + | x | h | xh,xh | 14s +-1 | Scenario: Basic travel time, 1km scale Given a grid size of 1000 meters @@ -85,15 +85,15 @@ Feature: Estimation of travel time | xh | primary | When I route I should get - | from | to | route | time | - | x | a | xa | 100s +-1 | - | x | b | xb | 141s +-1 | - | x | c | xc | 100s +-1 | - | x | d | xd | 141s +-1 | - | x | e | xe | 100s +-1 | - | x | f | xf | 141s +-1 | - | x | g | xg | 100s +-1 | - | x | h | xh | 141s +-1 | + | from | to | route | time | + | x | a | xa,xa | 100s +-1 | + | x | b | xb,xb | 141s +-1 | + | x | c | xc,xc | 100s +-1 | + | x | d | xd,xd | 141s +-1 | + | x | e | xe,xe | 100s +-1 | + | x | f | xf,xf | 141s +-1 | + | x | g | xg,xg | 100s +-1 | + | x | h | xh,xh | 141s +-1 | Scenario: Basic travel time, 10km scale Given a grid size of 10000 meters @@ -114,15 +114,15 @@ Feature: Estimation of travel time | xh | primary | When I route I should get - | from | to | route | time | - | x | a | xa | 1000s +-1 | - | x | b | xb | 1414s +-1 | - | x | c | xc | 1000s +-1 | - | x | d | xd | 1414s +-1 | - | x | e | xe | 1000s +-1 | - | x | f | xf | 1414s +-1 | - | x | g | xg | 1000s +-1 | - | x | h | xh | 1414s +-1 | + | from | to | route | time | + | x | a | xa,xa | 1000s +-1 | + | x | b | xb,xb | 1414s +-1 | + | x | c | xc,xc | 1000s +-1 | + | x | d | xd,xd | 1414s +-1 | + | x | e | xe,xe | 1000s +-1 | + | x | f | xf,xf | 1414s +-1 | + | x | g | xg,xg | 1000s +-1 | + | x | h | xh,xh | 1414s +-1 | Scenario: Time of travel depending on way type Given the node map @@ -138,10 +138,10 @@ Feature: Estimation of travel time | ace | something | When I route I should get - | from | to | route | time | - | a | b | ab | 10s +-1 | - | c | d | cd | 20s +-1 | - | e | f | ef | 30s +-1 | + | from | to | route | time | + | a | b | ab,ab | 10s +-1 | + | c | d | cd,cd | 20s +-1 | + | e | f | ef,ef | 30s +-1 | Scenario: Time of travel on a series of ways Given the node map @@ -155,10 +155,10 @@ Feature: Estimation of travel time | cd | primary | When I route I should get - | from | to | route | time | - | a | b | ab | 10s +-1 | - | a | c | ab,bc | 20s +-1 | - | a | d | ab,bc,cd | 30s +-1 | + | from | to | route | time | + | a | b | ab,ab | 10s +-1 | + | a | c | ab,bc,bc | 20s +-1 | + | a | d | ab,bc,cd,cd | 30s +-1 | Scenario: Time of travel on a winding way Given the node map @@ -171,10 +171,10 @@ Feature: Estimation of travel time | abcdefghi | primary | When I route I should get - | from | to | route | time | - | a | b | abcdefghi | 10s +-1 | - | a | e | abcdefghi | 40s +-1 | - | a | i | abcdefghi | 80s +-1 | + | from | to | route | time | + | a | b | abcdefghi,abcdefghi | 10s +-1 | + | a | e | abcdefghi,abcdefghi | 40s +-1 | + | a | i | abcdefghi,abcdefghi | 80s +-1 | Scenario: Time of travel on combination of road types Given the node map @@ -188,11 +188,11 @@ Feature: Estimation of travel time | cde | tertiary | When I route I should get - | from | to | route | time | - | b | c | abc | 10s +-1 | - | c | e | cde | 60s +-1 | - | b | d | abc,cde | 40s +-1 | - | a | e | abc,cde | 80s +-1 | + | from | to | route | time | + | b | c | abc,abc | 10s +-1 | + | c | e | cde,cde | 60s +-1 | + | b | d | abc,cde,cde | 40s +-1 | + | a | e | abc,cde,cde | 80s +-1 | Scenario: Time of travel on part of a way Given the node map @@ -206,13 +206,13 @@ Feature: Estimation of travel time | ab | primary | When I route I should get - | from | to | route | time | - | 1 | 2 | ab | 10s +-1 | - | 1 | 3 | ab | 20s +-1 | - | 1 | 4 | ab | 30s +-1 | - | 4 | 3 | ab | 10s +-1 | - | 4 | 2 | ab | 20s +-1 | - | 4 | 1 | ab | 30s +-1 | + | from | to | route | time | + | 1 | 2 | ab,ab | 10s +-1 | + | 1 | 3 | ab,ab | 20s +-1 | + | 1 | 4 | ab,ab | 30s +-1 | + | 4 | 3 | ab,ab | 10s +-1 | + | 4 | 2 | ab,ab | 20s +-1 | + | 4 | 1 | ab,ab | 30s +-1 | Scenario: Total travel time should match sum of times of individual ways Given a grid size of 1000 meters @@ -228,10 +228,10 @@ Feature: Estimation of travel time | cd | primary | When I route I should get - | from | to | route | distances | distance | times | time | - | a | b | ab | 1000m +-1 | 1000m +-1 | 100s +-1 | 100s +-1 | - | b | c | bc | 2000m +-1 | 2000m +-1 | 200s +-1 | 200s +-1 | - | c | d | cd | 3000m +-1 | 3000m +-1 | 300s +-1 | 300s +-1 | - | a | c | ab,bc | 1000m,2000m +-1 | 3000m +-1 | 100s,200s +-1 | 300s +-1 | - | b | d | bc,cd | 2000m,3000m +-1 | 5000m +-1 | 200s,300s +-1 | 500s +-1 | - | a | d | ab,bc,cd | 1000m,2000m,3000m +-1 | 6000m +-1 | 100s,200s,300s +-1 | 600s +-1 | + | from | to | route | distances | distance | times | time | + | a | b | ab,ab | 1000m +-1 | 1000m +-1 | 100s +-1 | 100s +-1 | + | b | c | bc,bc | 2000m +-1 | 2000m +-1 | 200s +-1 | 200s +-1 | + | c | d | cd,cd | 3000m +-1 | 3000m +-1 | 300s +-1 | 300s +-1 | + | a | c | ab,bc,bc | 1000m,2000m +-1 | 3000m +-1 | 100s,200s +-1 | 300s +-1 | + | b | d | bc,cd,cd | 2000m,3000m +-1 | 5000m +-1 | 200s,300s +-1 | 500s +-1 | + | a | d | ab,bc,cd,cd | 1000m,2000m,3000m +-1 | 6000m +-1 | 100s,200s,300s +-1 | 600s +-1 | diff --git a/features/testbot/trip.feature b/features/testbot/trip.feature index c9f944b26..5f3376369 100644 --- a/features/testbot/trip.feature +++ b/features/testbot/trip.feature @@ -82,5 +82,20 @@ Feature: Basic trip planning | waypoints | trips | | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p | cbalkjihgfedc,ponm | + # Test single node in each component #1850 + Scenario: Testbot - Trip planning with less than 10 nodes + Given the node map + | a | 1 | b | + | | | | + | c | 2 | d | + + And the ways + | nodes | + | ab | + | cd | + + When I plan a trip I should get + | waypoints | trips | + | 1,2 | | diff --git a/features/testbot/turns.feature b/features/testbot/turns.feature deleted file mode 100644 index 6eba4f9e9..000000000 --- a/features/testbot/turns.feature +++ /dev/null @@ -1,123 +0,0 @@ -@routing @turns @testbot -Feature: Turn directions/codes - - Background: - Given the profile "testbot" - - Scenario: Turn directions - Given the node map - | o | p | a | b | c | - | n | | | | d | - | m | | x | | e | - | l | | | | f | - | k | j | i | h | g | - - And the ways - | nodes | - | xa | - | xb | - | xc | - | xd | - | xe | - | xf | - | xg | - | xh | - | xi | - | xj | - | xk | - | xl | - | xm | - | xn | - | xo | - | xp | - - When I route I should get - | from | to | route | turns | - | i | k | xi,xk | head,sharp_left,destination | - | i | m | xi,xm | head,left,destination | - | i | o | xi,xo | head,slight_left,destination | - | i | a | xi,xa | head,straight,destination | - | i | c | xi,xc | head,slight_right,destination | - | i | e | xi,xe | head,right,destination | - | i | g | xi,xg | head,sharp_right,destination | - - | k | m | xk,xm | head,sharp_left,destination | - | k | o | xk,xo | head,left,destination | - | k | a | xk,xa | head,slight_left,destination | - | k | c | xk,xc | head,straight,destination | - | k | e | xk,xe | head,slight_right,destination | - | k | g | xk,xg | head,right,destination | - | k | i | xk,xi | head,sharp_right,destination | - - | m | o | xm,xo | head,sharp_left,destination | - | m | a | xm,xa | head,left,destination | - | m | c | xm,xc | head,slight_left,destination | - | m | e | xm,xe | head,straight,destination | - | m | g | xm,xg | head,slight_right,destination | - | m | i | xm,xi | head,right,destination | - | m | k | xm,xk | head,sharp_right,destination | - - | o | a | xo,xa | head,sharp_left,destination | - | o | c | xo,xc | head,left,destination | - | o | e | xo,xe | head,slight_left,destination | - | o | g | xo,xg | head,straight,destination | - | o | i | xo,xi | head,slight_right,destination | - | o | k | xo,xk | head,right,destination | - | o | m | xo,xm | head,sharp_right,destination | - - | a | c | xa,xc | head,sharp_left,destination | - | a | e | xa,xe | head,left,destination | - | a | g | xa,xg | head,slight_left,destination | - | a | i | xa,xi | head,straight,destination | - | a | k | xa,xk | head,slight_right,destination | - | a | m | xa,xm | head,right,destination | - | a | o | xa,xo | head,sharp_right,destination | - - | c | e | xc,xe | head,sharp_left,destination | - | c | g | xc,xg | head,left,destination | - | c | i | xc,xi | head,slight_left,destination | - | c | k | xc,xk | head,straight,destination | - | c | m | xc,xm | head,slight_right,destination | - | c | o | xc,xo | head,right,destination | - | c | a | xc,xa | head,sharp_right,destination | - - | e | g | xe,xg | head,sharp_left,destination | - | e | i | xe,xi | head,left,destination | - | e | k | xe,xk | head,slight_left,destination | - | e | m | xe,xm | head,straight,destination | - | e | o | xe,xo | head,slight_right,destination | - | e | a | xe,xa | head,right,destination | - | e | c | xe,xc | head,sharp_right,destination | - - | g | i | xg,xi | head,sharp_left,destination | - | g | k | xg,xk | head,left,destination | - | g | m | xg,xm | head,slight_left,destination | - | g | o | xg,xo | head,straight,destination | - | g | a | xg,xa | head,slight_right,destination | - | g | c | xg,xc | head,right,destination | - | g | e | xg,xe | head,sharp_right,destination | - - Scenario: Turn instructions at high latitude - # https://github.com/DennisOSRM/Project-OSRM/issues/532 - Given the node locations - | node | lat | lon | - | a | 55.68740 | 12.52430 | - | b | 55.68745 | 12.52409 | - | c | 55.68711 | 12.52383 | - | x | -55.68740 | 12.52430 | - | y | -55.68745 | 12.52409 | - | z | -55.68711 | 12.52383 | - - And the ways - | nodes | - | ab | - | bc | - | xy | - | yz | - - When I route I should get - | from | to | route | turns | - | a | c | ab,bc | head,left,destination | - | c | a | bc,ab | head,right,destination | - | x | z | xy,yz | head,right,destination | - | z | x | yz,xy | head,left,destination | diff --git a/features/testbot/utf.feature b/features/testbot/utf.feature index d979e9f92..b537163ee 100644 --- a/features/testbot/utf.feature +++ b/features/testbot/utf.feature @@ -15,7 +15,7 @@ Feature: Handling of UTF characters | cd | Cyrillic Москва | When I route I should get - | from | to | route | - | a | b | Scandinavian København | - | b | c | Japanese 東京 | - | c | d | Cyrillic Москва | + | from | to | route | + | a | b | Scandinavian København,Scandinavian København | + | b | c | Japanese 東京,Japanese 東京 | + | c | d | Cyrillic Москва,Cyrillic Москва | diff --git a/features/testbot/via.feature b/features/testbot/via.feature index a88b0f4b4..84b939bfc 100644 --- a/features/testbot/via.feature +++ b/features/testbot/via.feature @@ -13,9 +13,23 @@ Feature: Via points | abc | When I route I should get - | waypoints | route | - | a,b,c | abc,abc | - | c,b,a | abc,abc | + | waypoints | route | + | a,b,c | abc,abc,abc,abc | + + Scenario: Simple via point with core factor + Given the contract extra arguments "--core 0.8" + Given the node map + | a | b | c | + + And the ways + | nodes | + | abc | + + When I route I should get + | waypoints | route | + | a,b,c | abc,abc,abc,abc | + | c,b,a | abc,abc,abc,abc | + | c,b,a | abc,abc,abc,abc | Scenario: Via point at a dead end Given the node map @@ -28,10 +42,11 @@ Feature: Via points | bd | When I route I should get - | waypoints | route | - | a,d,c | abc,bd,bd,bd,abc | - | c,d,a | abc,bd,bd,bd,abc | + | waypoints | route | + | a,d,c | abc,bd,bd,bd,abc,abc | + | c,d,a | abc,bd,bd,bd,abc,abc | + @mokob Scenario: Multiple via points Given the node map | a | | | | e | f | g | | @@ -48,9 +63,9 @@ Feature: Via points | dh | When I route I should get - | waypoints | route | - | a,c,f | ab,bcd,bcd,de,efg | - | a,c,f,h | ab,bcd,bcd,de,efg,efg,gh | + | waypoints | route | + | a,c,f | ab,bcd,bcd,bcd,de,efg,efg | + | a,c,f,h | ab,bcd,bcd,bcd,de,efg,efg,efg,gh,gh | Scenario: Duplicate via point @@ -65,8 +80,8 @@ Feature: Via points | ab | When I route I should get - | waypoints | route | turns | - | 1,1,4 | ab,ab | head,via,destination | + | waypoints | route | + | 1,1,4 | ab,ab,ab,ab | Scenario: Via points on ring of oneways # xa it to avoid only having a single ring, which cna trigger edge cases @@ -86,12 +101,12 @@ Feature: Via points | fa | yes | When I route I should get - | waypoints | route | distance | turns | - | 1,3 | ab,bc,cd | 400m +-1 | head,straight,straight,destination | - | 3,1 | cd,de,ef,fa,ab | 1000m +-1 | head,right,right,right,right,destination | - | 1,2,3 | ab,bc,bc,cd | 400m +-1 | head,straight,via,straight,destination | - | 1,3,2 | ab,bc,cd,cd,de,ef,fa,ab,bc | 1600m +-1 | head,straight,straight,via,right,right,right,right,straight,destination | - | 3,2,1 | cd,de,ef,fa,ab,bc,bc,cd,de,ef,fa,ab | 2400m +-1 | head,right,right,right,right,straight,via,straight,right,right,right,right,destination | + | waypoints | route | distance | + | 1,3 | ab,bc,cd,cd | 400m +-1 | + | 3,1 | cd,de,ef,fa,ab,ab | 1000m +-1 | + | 1,2,3 | ab,bc,bc,bc,cd,cd | 400m +-1 | + | 1,3,2 | ab,bc,cd,cd,cd,de,ef,fa,ab,bc,bc | 1600m +-1 | + | 3,2,1 | cd,de,ef,fa,ab,bc,bc,bc,cd,de,ef,fa,ab,ab | 2400m +-1 | Scenario: Via points on ring on the same oneway # xa it to avoid only having a single ring, which cna trigger edge cases @@ -109,9 +124,95 @@ Feature: Via points | da | yes | When I route I should get - | waypoints | route | distance | turns | - | 1,3 | ab | 200m +-1 | head,destination | - | 3,1 | ab,bc,cd,da,ab | 800m +-1 | head,right,right,right,right,destination | - | 1,2,3 | ab,ab | 200m +-1 | head,via,destination | - | 1,3,2 | ab,ab,bc,cd,da,ab | 1100m +-1 | head,via,right,right,right,right,destination | - | 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 1800m | head,right,right,right,right,via,right,right,right,right,destination | + | waypoints | route | distance | + | 1,3 | ab,ab | 200m +-1 | + | 3,1 | ab,bc,cd,da,ab,ab | 800m +-1 | + | 1,2,3 | ab,ab,ab,ab | 200m +-1 | + | 1,3,2 | ab,ab,ab,bc,cd,da,ab,ab | 1100m +-1 | + | 3,2,1 | ab,bc,cd,da,ab,ab,ab,bc,cd,da,ab,ab | 1800m +-1 | + + # See issue #1896 + Scenario: Via point at a dead end with oneway + Given the node map + | a | b | c | + | | d | | + | | e | | + + And the ways + | nodes | oneway | + | abc | no | + | bd | no | + | de | yes | + + When I route I should get + | waypoints | route | + | a,d,c | abc,bd,bd,bd,abc,abc | + | c,d,a | abc,bd,bd,bd,abc,abc | + + # See issue #1896 + Scenario: Via point at a dead end with barrier + Given the profile "car" + Given the node map + | a | b | c | + | | 1 | | + | | d | | + | | | | + | | | | + | f | e | | + + And the nodes + | node | barrier | + | d | bollard | + + And the ways + | nodes | + | abc | + | bd | + | afed | + + When I route I should get + | waypoints | route | + | a,1,c | abc,bd,bd,bd,bd,abc,abc | + | c,1,a | abc,bd,bd,bd,bd,abc,abc | + + Scenario: Via points on ring on the same oneway, forces one of the vertices to be top node + Given the node map + | a | 1 | 2 | b | + | 8 | | | 3 | + | 7 | | | 4 | + | d | 6 | 5 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I route I should get + | waypoints | route | distance | + | 2,1 | ab,bc,cd,da,ab,ab | 1100m +-1 | + | 4,3 | bc,cd,da,ab,bc,bc | 1100m +-1 | + | 6,5 | cd,da,ab,bc,cd,cd | 1100m +-1 | + | 8,7 | da,ab,bc,cd,da,da | 1100m +-1 | + + Scenario: Multiple Via points on ring on the same oneway, forces one of the vertices to be top node + Given the node map + | a | 1 | 2 | 3 | b | + | | | | | 4 | + | | | | | 5 | + | | | | | 6 | + | d | 9 | 8 | 7 | c | + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I route I should get + | waypoints | route | distance | + | 3,2,1 | ab,bc,cd,da,ab,ab,ab,bc,cd,da,ab,ab | 3000m +-1 | + | 6,5,4 | bc,cd,da,ab,bc,bc,bc,cd,da,ab,bc,bc | 3000m +-1 | + | 9,8,7 | cd,da,ab,bc,cd,cd,cd,da,ab,bc,cd,cd | 3000m +-1 | diff --git a/features/timestamp/timestamp.feature b/features/timestamp/timestamp.feature deleted file mode 100644 index 70ef91c2b..000000000 --- a/features/timestamp/timestamp.feature +++ /dev/null @@ -1,11 +0,0 @@ -@timestamp -Feature: Timestamp - - Scenario: Request timestamp - Given the node map - | a | b | - And the ways - | nodes | - | ab | - When I request /timestamp - Then I should get a valid timestamp diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp new file mode 100644 index 000000000..7cfc3dc66 --- /dev/null +++ b/include/contractor/contractor.hpp @@ -0,0 +1,96 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef CONTRACTOR_CONTRACTOR_HPP +#define CONTRACTOR_CONTRACTOR_HPP + +#include "contractor/contractor_config.hpp" +#include "contractor/query_edge.hpp" +#include "extractor/edge_based_edge.hpp" +#include "extractor/edge_based_node.hpp" +#include "util/typedefs.hpp" +#include "util/deallocating_vector.hpp" + +#include +#include + +#include + +namespace osrm +{ +namespace contractor +{ + +/// Base class of osrm-contract +class Contractor +{ + public: + using EdgeData = QueryEdge::EdgeData; + + explicit Contractor(const ContractorConfig &config_) : config{config_} {} + + Contractor(const Contractor &) = delete; + Contractor &operator=(const Contractor &) = delete; + + int Run(); + + protected: + void ContractGraph(const unsigned max_edge_id, + util::DeallocatingVector &edge_based_edge_list, + util::DeallocatingVector &contracted_edge_list, + std::vector &&node_weights, + std::vector &is_core_node, + std::vector &inout_node_levels) const; + void WriteCoreNodeMarker(std::vector &&is_core_node) const; + void WriteNodeLevels(std::vector &&node_levels) const; + void ReadNodeLevels(std::vector &contraction_order) const; + std::size_t + WriteContractedGraph(unsigned number_of_edge_based_nodes, + const util::DeallocatingVector &contracted_edge_list); + void FindComponents(unsigned max_edge_id, + const util::DeallocatingVector &edges, + std::vector &nodes) const; + + private: + ContractorConfig config; + + std::size_t + LoadEdgeExpandedGraph(const std::string &edge_based_graph_path, + util::DeallocatingVector &edge_based_edge_list, + const std::string &edge_segment_lookup_path, + const std::string &edge_penalty_path, + const std::vector &segment_speed_path, + const std::string &nodes_filename, + const std::string &geometry_filename, + const std::string &datasource_names_filename, + const std::string &datasource_indexes_filename, + const std::string &rtree_leaf_filename); +}; +} +} + +#endif // PROCESSING_CHAIN_HPP diff --git a/contractor/contractor_options.hpp b/include/contractor/contractor_config.hpp similarity index 56% rename from contractor/contractor_options.hpp rename to include/contractor/contractor_config.hpp index 5932b78c6..f557279ca 100644 --- a/contractor/contractor_options.hpp +++ b/include/contractor/contractor_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -32,20 +32,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -enum class return_code : unsigned +namespace osrm +{ +namespace contractor { - ok, - fail, - exit -}; struct ContractorConfig { - ContractorConfig() noexcept : requested_num_threads(0) {} + ContractorConfig() : requested_num_threads(0) {} + + // Infer the output names from the path of the .osrm file + void UseDefaultOutputNames() + { + level_output_path = osrm_input_path.string() + ".level"; + core_output_path = osrm_input_path.string() + ".core"; + graph_output_path = osrm_input_path.string() + ".hsgr"; + edge_based_graph_path = osrm_input_path.string() + ".ebg"; + edge_segment_lookup_path = osrm_input_path.string() + ".edge_segment_lookup"; + edge_penalty_path = osrm_input_path.string() + ".edge_penalties"; + node_based_graph_path = osrm_input_path.string() + ".nodes"; + geometry_path = osrm_input_path.string() + ".geometry"; + rtree_leaf_path = osrm_input_path.string() + ".fileIndex"; + datasource_names_path = osrm_input_path.string() + ".datasource_names"; + datasource_indexes_path = osrm_input_path.string() + ".datasource_indexes"; + } boost::filesystem::path config_file_path; boost::filesystem::path osrm_input_path; - boost::filesystem::path profile_path; std::string level_output_path; std::string core_output_path; @@ -54,28 +67,24 @@ struct ContractorConfig std::string edge_segment_lookup_path; std::string edge_penalty_path; + std::string node_based_graph_path; + std::string geometry_path; + std::string rtree_leaf_path; bool use_cached_priority; unsigned requested_num_threads; - //A percentage of vertices that will be contracted for the hierarchy. - //Offers a trade-off between preprocessing and query time. - //The remaining vertices form the core of the hierarchy + // A percentage of vertices that will be contracted for the hierarchy. + // Offers a trade-off between preprocessing and query time. + // The remaining vertices form the core of the hierarchy //(e.g. 0.8 contracts 80 percent of the hierarchy, leaving a core of 20%) double core_factor; - std::string segment_speed_lookup_path; - -#ifdef DEBUG_GEOMETRY - std::string debug_geometry_path; -#endif -}; - -struct ContractorOptions -{ - static return_code ParseArguments(int argc, char *argv[], ContractorConfig &extractor_config); - - static void GenerateOutputFilesNames(ContractorConfig &extractor_config); + std::vector segment_speed_lookup_paths; + std::string datasource_indexes_path; + std::string datasource_names_path; }; +} +} #endif // EXTRACTOR_OPTIONS_HPP diff --git a/algorithms/crc32_processor.hpp b/include/contractor/crc32_processor.hpp similarity index 71% rename from algorithms/crc32_processor.hpp rename to include/contractor/crc32_processor.hpp index a31b4ad23..eaef76c8e 100644 --- a/algorithms/crc32_processor.hpp +++ b/include/contractor/crc32_processor.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef ITERATOR_BASED_CRC32_H #define ITERATOR_BASED_CRC32_H @@ -36,6 +9,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +namespace osrm +{ +namespace contractor +{ + class IteratorbasedCRC32 { public: @@ -141,5 +119,7 @@ struct RangebasedCRC32 private: IteratorbasedCRC32 crc32; }; +} +} #endif /* ITERATOR_BASED_CRC32_H */ diff --git a/contractor/contractor.hpp b/include/contractor/graph_contractor.hpp similarity index 68% rename from contractor/contractor.hpp rename to include/contractor/graph_contractor.hpp index 07a21dc50..ce33d268a 100644 --- a/contractor/contractor.hpp +++ b/include/contractor/graph_contractor.hpp @@ -1,44 +1,17 @@ -/* +#ifndef GRAPH_CONTRACTOR_HPP +#define GRAPH_CONTRACTOR_HPP -Copyright (c) 2015, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef CONTRACTOR_HPP -#define CONTRACTOR_HPP - -#include "../data_structures/binary_heap.hpp" -#include "../data_structures/deallocating_vector.hpp" -#include "../data_structures/dynamic_graph.hpp" -#include "../data_structures/percent.hpp" -#include "../data_structures/query_edge.hpp" -#include "../data_structures/xor_fast_hash.hpp" -#include "../data_structures/xor_fast_hash_storage.hpp" -#include "../util/integer_range.hpp" -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" -#include "../typedefs.h" +#include "util/binary_heap.hpp" +#include "util/deallocating_vector.hpp" +#include "util/dynamic_graph.hpp" +#include "util/percent.hpp" +#include "contractor/query_edge.hpp" +#include "util/xor_fast_hash.hpp" +#include "util/xor_fast_hash_storage.hpp" +#include "util/integer_range.hpp" +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" +#include "util/typedefs.hpp" #include @@ -53,9 +26,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -class Contractor +namespace osrm +{ +namespace contractor { +class GraphContractor +{ private: struct ContractorEdgeData { @@ -86,18 +63,22 @@ class Contractor struct ContractorHeapData { - short hop; - bool target; - ContractorHeapData() : hop(0), target(false) {} - ContractorHeapData(short h, bool t) : hop(h), target(t) {} + ContractorHeapData() {} + ContractorHeapData(short hop_, bool target_) : hop(hop_), target(target_) {} + + short hop = 0; + bool target = false; }; - using ContractorGraph = DynamicGraph; - // using ContractorHeap = BinaryHeap; + // using ContractorHeap = util::BinaryHeap // >; - using ContractorHeap = - BinaryHeap>; + using ContractorHeap = util::BinaryHeap>; using ContractorEdge = ContractorGraph::InputEdge; struct ContractorThreadData @@ -108,11 +89,7 @@ class Contractor explicit ContractorThreadData(NodeID nodes) : heap(nodes) {} }; - struct NodePriorityData - { - int depth; - NodePriorityData() : depth(0) {} - }; + using NodeDepth = int; struct ContractionStats { @@ -157,16 +134,18 @@ class Contractor }; public: - template Contractor(int nodes, ContainerT &input_edge_list) - : Contractor(nodes, input_edge_list, {}, {}) + template + GraphContractor(int nodes, ContainerT &input_edge_list) + : GraphContractor(nodes, input_edge_list, {}, {}) { } template - Contractor(int nodes, - ContainerT &input_edge_list, - std::vector &&node_levels_) - : node_levels(std::move(node_levels_)) + GraphContractor(int nodes, + ContainerT &input_edge_list, + std::vector &&node_levels_, + std::vector &&node_weights_) + : node_levels(std::move(node_levels_)), node_weights(std::move(node_weights_)) { std::vector edges; edges.reserve(input_edge_list.size() * 2); @@ -179,7 +158,7 @@ class Contractor #ifndef NDEBUG if (static_cast(std::max(diter->weight, 1)) > 24 * 60 * 60 * 10) { - SimpleLogger().Write(logWARNING) + util::SimpleLogger().Write(logWARNING) << "Edge weight large -> " << static_cast(std::max(diter->weight, 1)) << " : " << static_cast(diter->source) << " -> " @@ -223,8 +202,7 @@ class Contractor forward_edge.data.shortcut = reverse_edge.data.shortcut = false; forward_edge.data.id = reverse_edge.data.id = id; forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1; - forward_edge.data.distance = reverse_edge.data.distance = - std::numeric_limits::max(); + forward_edge.data.distance = reverse_edge.data.distance = INVALID_EDGE_WEIGHT; // remove parallel edges while (i < edges.size() && edges[i].source == source && edges[i].target == target) { @@ -243,7 +221,7 @@ class Contractor // merge edges (s,t) and (t,s) into bidirectional edge if (forward_edge.data.distance == reverse_edge.data.distance) { - if ((int)forward_edge.data.distance != std::numeric_limits::max()) + if ((int)forward_edge.data.distance != INVALID_EDGE_WEIGHT) { forward_edge.data.backward = true; edges[edge++] = forward_edge; @@ -251,70 +229,47 @@ class Contractor } else { // insert seperate edges - if (((int)forward_edge.data.distance) != std::numeric_limits::max()) + if (((int)forward_edge.data.distance) != INVALID_EDGE_WEIGHT) { edges[edge++] = forward_edge; } - if ((int)reverse_edge.data.distance != std::numeric_limits::max()) + if ((int)reverse_edge.data.distance != INVALID_EDGE_WEIGHT) { edges[edge++] = reverse_edge; } } } - std::cout << "merged " << edges.size() - edge << " edges out of " << edges.size() - << std::endl; + util::SimpleLogger().Write() << "merged " << edges.size() - edge << " edges out of " + << edges.size(); edges.resize(edge); contractor_graph = std::make_shared(nodes, edges); edges.clear(); edges.shrink_to_fit(); BOOST_ASSERT(0 == edges.capacity()); - // unsigned maxdegree = 0; - // NodeID highestNode = 0; - // - // for(unsigned i = 0; i < contractor_graph->GetNumberOfNodes(); ++i) { - // unsigned degree = contractor_graph->EndEdges(i) - - // contractor_graph->BeginEdges(i); - // if(degree > maxdegree) { - // maxdegree = degree; - // highestNode = i; - // } - // } - // - // SimpleLogger().Write() << "edges at node with id " << highestNode << " has degree - // " << maxdegree; - // for(unsigned i = contractor_graph->BeginEdges(highestNode); i < - // contractor_graph->EndEdges(highestNode); ++i) { - // SimpleLogger().Write() << " ->(" << highestNode << "," << - // contractor_graph->GetTarget(i) - // << "); via: " << contractor_graph->GetEdgeData(i).via; - // } - - std::cout << "contractor finished initalization" << std::endl; + util::SimpleLogger().Write() << "contractor finished initalization"; } - ~Contractor() {} - void Run(double core_factor = 1.0) { // for the preperation we can use a big grain size, which is much faster (probably cache) - constexpr size_t InitGrainSize = 100000; - constexpr size_t PQGrainSize = 100000; + const constexpr size_t InitGrainSize = 100000; + const constexpr size_t PQGrainSize = 100000; // auto_partitioner will automatically increase the blocksize if we have // a lot of data. It is *important* for the last loop iterations // (which have a very small dataset) that it is devisible. - constexpr size_t IndependentGrainSize = 1; - constexpr size_t ContractGrainSize = 1; - constexpr size_t NeighboursGrainSize = 1; - constexpr size_t DeleteGrainSize = 1; + const constexpr size_t IndependentGrainSize = 1; + const constexpr size_t ContractGrainSize = 1; + const constexpr size_t NeighboursGrainSize = 1; + const constexpr size_t DeleteGrainSize = 1; const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes(); - Percent p(number_of_nodes); + util::Percent p(number_of_nodes); ThreadDataContainer thread_data_list(number_of_nodes); NodeID number_of_contracted_nodes = 0; - std::vector node_data; + std::vector node_depth; std::vector node_priorities; is_core_node.resize(number_of_nodes, false); @@ -338,20 +293,20 @@ class Contractor } else { - node_data.resize(number_of_nodes); + node_depth.resize(number_of_nodes, 0); node_priorities.resize(number_of_nodes); node_levels.resize(number_of_nodes); std::cout << "initializing elimination PQ ..." << std::flush; tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, PQGrainSize), - [this, &node_priorities, &node_data, &thread_data_list]( - const tbb::blocked_range &range) + [this, &node_priorities, &node_depth, + &thread_data_list](const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); for (int x = range.begin(), end = range.end(); x != end; ++x) { node_priorities[x] = - this->EvaluateNodePriority(data, &node_data[x], x); + this->EvaluateNodePriority(data, node_depth[x], x); } }); std::cout << "ok" << std::endl; @@ -368,9 +323,10 @@ class Contractor if (!flushed_contractor && (number_of_contracted_nodes > static_cast(number_of_nodes * 0.65 * core_factor))) { - DeallocatingVector new_edge_set; // this one is not explicitely - // cleared since it goes out of - // scope anywa + util::DeallocatingVector + new_edge_set; // this one is not explicitely + // cleared since it goes out of + // scope anywa std::cout << " [flush " << number_of_contracted_nodes << " nodes] " << std::flush; // Delete old heap data to free memory that we need for the coming operations @@ -378,24 +334,27 @@ class Contractor // Create new priority array std::vector new_node_priority(remaining_nodes.size()); + std::vector new_node_weights(remaining_nodes.size()); // this map gives the old IDs from the new ones, necessary to get a consistent graph // at the end of contraction orig_node_id_from_new_node_id_map.resize(remaining_nodes.size()); // this map gives the new IDs from the old ones, necessary to remap targets from the // remaining graph - std::vector new_node_id_from_orig_id_map(number_of_nodes, UINT_MAX); + std::vector new_node_id_from_orig_id_map(number_of_nodes, SPECIAL_NODEID); - for (const auto new_node_id : osrm::irange(0, remaining_nodes.size())) + for (const auto new_node_id : util::irange(0UL, remaining_nodes.size())) { - auto& node = remaining_nodes[new_node_id]; + auto &node = remaining_nodes[new_node_id]; BOOST_ASSERT(node_priorities.size() > node.id); new_node_priority[new_node_id] = node_priorities[node.id]; + BOOST_ASSERT(node_weights.size() > node.id); + new_node_weights[new_node_id] = node_weights[node.id]; } // build forward and backward renumbering map and remap ids in remaining_nodes - for (const auto new_node_id : osrm::irange(0, remaining_nodes.size())) + for (const auto new_node_id : util::irange(0UL, remaining_nodes.size())) { - auto& node = remaining_nodes[new_node_id]; + auto &node = remaining_nodes[new_node_id]; // create renumbering maps in both directions orig_node_id_from_new_node_id_map[new_node_id] = node.id; new_node_id_from_orig_id_map[node.id] = new_node_id; @@ -403,7 +362,7 @@ class Contractor } // walk over all nodes for (const auto source : - osrm::irange(0, contractor_graph->GetNumberOfNodes())) + util::irange(0UL, contractor_graph->GetNumberOfNodes())) { for (auto current_edge : contractor_graph->GetAdjacentEdgeRange(source)) { @@ -417,15 +376,14 @@ class Contractor else { // node is not yet contracted. - // add (renumbered) outgoing edges to new DynamicGraph. + // add (renumbered) outgoing edges to new util::DynamicGraph. ContractorEdge new_edge = {new_node_id_from_orig_id_map[source], - new_node_id_from_orig_id_map[target], - data}; + new_node_id_from_orig_id_map[target], data}; new_edge.data.is_original_via_node_ID = true; - BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[source], + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_node_id_from_orig_id_map[source], "new source id not resolveable"); - BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[target], + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_node_id_from_orig_id_map[target], "new target id not resolveable"); new_edge_set.push_back(new_edge); } @@ -439,8 +397,12 @@ class Contractor // Replace old priorities array by new one node_priorities.swap(new_node_priority); // Delete old node_priorities vector + // Due to the scope, these should get cleared automatically? @daniel-j-h do you + // agree? new_node_priority.clear(); new_node_priority.shrink_to_fit(); + + node_weights.swap(new_node_weights); // old Graph is removed contractor_graph.reset(); @@ -457,39 +419,44 @@ class Contractor thread_data_list.number_of_nodes = contractor_graph->GetNumberOfNodes(); } - tbb::parallel_for(tbb::blocked_range(0, remaining_nodes.size(), IndependentGrainSize), - [this, &node_priorities, &remaining_nodes, &thread_data_list]( - const tbb::blocked_range &range) - { - ContractorThreadData *data = thread_data_list.getThreadData(); - // determine independent node set - for (auto i = range.begin(), end = range.end(); i != end; ++i) - { - const NodeID node = remaining_nodes[i].id; - remaining_nodes[i].is_independent = - this->IsNodeIndependent(node_priorities, data, node); - } - }); + tbb::parallel_for( + tbb::blocked_range(0, remaining_nodes.size(), IndependentGrainSize), + [this, &node_priorities, &remaining_nodes, + &thread_data_list](const tbb::blocked_range &range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + // determine independent node set + for (auto i = range.begin(), end = range.end(); i != end; ++i) + { + const NodeID node = remaining_nodes[i].id; + remaining_nodes[i].is_independent = + this->IsNodeIndependent(node_priorities, data, node); + } + }); // sort all remaining nodes to the beginning of the sequence - const auto begin_independent_nodes = stable_partition(remaining_nodes.begin(), remaining_nodes.end(), - [](RemainingNodeData node_data) - { - return !node_data.is_independent; - }); - auto begin_independent_nodes_idx = std::distance(remaining_nodes.begin(), begin_independent_nodes); + const auto begin_independent_nodes = stable_partition( + remaining_nodes.begin(), remaining_nodes.end(), [](RemainingNodeData node_data) + { + return !node_data.is_independent; + }); + auto begin_independent_nodes_idx = + std::distance(remaining_nodes.begin(), begin_independent_nodes); auto end_independent_nodes_idx = remaining_nodes.size(); if (!use_cached_node_priorities) { // write out contraction level tbb::parallel_for( - tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize), - [this, remaining_nodes, flushed_contractor, current_level](const tbb::blocked_range &range) + tbb::blocked_range(begin_independent_nodes_idx, + end_independent_nodes_idx, ContractGrainSize), + [this, remaining_nodes, flushed_contractor, + current_level](const tbb::blocked_range &range) { if (flushed_contractor) { - for (int position = range.begin(), end = range.end(); position != end; ++position) + for (int position = range.begin(), end = range.end(); position != end; + ++position) { const NodeID x = remaining_nodes[position].id; node_levels[orig_node_id_from_new_node_id_map[x]] = current_level; @@ -497,7 +464,8 @@ class Contractor } else { - for (int position = range.begin(), end = range.end(); position != end; ++position) + for (int position = range.begin(), end = range.end(); position != end; + ++position) { const NodeID x = remaining_nodes[position].id; node_levels[x] = current_level; @@ -507,24 +475,29 @@ class Contractor } // contract independent nodes - tbb::parallel_for( - tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize), - [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range &range) - { - ContractorThreadData *data = thread_data_list.getThreadData(); - for (int position = range.begin(), end = range.end(); position != end; ++position) - { - const NodeID x = remaining_nodes[position].id; - this->ContractNode(data, x); - } - }); + tbb::parallel_for(tbb::blocked_range(begin_independent_nodes_idx, + end_independent_nodes_idx, + ContractGrainSize), + [this, &remaining_nodes, + &thread_data_list](const tbb::blocked_range &range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int position = range.begin(), end = range.end(); + position != end; ++position) + { + const NodeID x = remaining_nodes[position].id; + this->ContractNode(data, x); + } + }); tbb::parallel_for( - tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, DeleteGrainSize), + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, + DeleteGrainSize), [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); - for (int position = range.begin(), end = range.end(); position != end; ++position) + for (int position = range.begin(), end = range.end(); position != end; + ++position) { const NodeID x = remaining_nodes[position].id; this->DeleteIncomingEdges(data, x); @@ -569,15 +542,17 @@ class Contractor if (!use_cached_node_priorities) { tbb::parallel_for( - tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize), - [this, &node_priorities, &remaining_nodes, &node_data, &thread_data_list]( - const tbb::blocked_range &range) + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, + NeighboursGrainSize), + [this, &node_priorities, &remaining_nodes, &node_depth, + &thread_data_list](const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); - for (int position = range.begin(), end = range.end(); position != end; ++position) + for (int position = range.begin(), end = range.end(); position != end; + ++position) { NodeID x = remaining_nodes[position].id; - this->UpdateNodeNeighbours(node_priorities, node_data, data, x); + this->UpdateNodeNeighbours(node_priorities, node_depth, data, x); } }); } @@ -585,30 +560,6 @@ class Contractor // remove contracted nodes from the pool number_of_contracted_nodes += end_independent_nodes_idx - begin_independent_nodes_idx; remaining_nodes.resize(begin_independent_nodes_idx); - // unsigned maxdegree = 0; - // unsigned avgdegree = 0; - // unsigned mindegree = UINT_MAX; - // unsigned quaddegree = 0; - // - // for(unsigned i = 0; i < remaining_nodes.size(); ++i) { - // unsigned degree = contractor_graph->EndEdges(remaining_nodes[i].id) - // - - // contractor_graph->BeginEdges(remaining_nodes[i].first); - // if(degree > maxdegree) - // maxdegree = degree; - // if(degree < mindegree) - // mindegree = degree; - // - // avgdegree += degree; - // quaddegree += (degree*degree); - // } - // - // avgdegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() ); - // quaddegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() ); - // - // SimpleLogger().Write() << "rest: " << remaining_nodes.size() << ", max: " - // << maxdegree << ", min: " << mindegree << ", avg: " << avgdegree << ", - // quad: " << quaddegree; p.printStatus(number_of_contracted_nodes); ++current_level; @@ -616,32 +567,31 @@ class Contractor if (remaining_nodes.size() > 2) { - if (orig_node_id_from_new_node_id_map.size() > 0) - { - tbb::parallel_for( - tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), - [this, &remaining_nodes](const tbb::blocked_range &range) - { - for (int x = range.begin(), end = range.end(); x != end; ++x) - { - const auto orig_id = remaining_nodes[x].id; - is_core_node[orig_node_id_from_new_node_id_map[orig_id]] = true; - } - }); - } - else - { - tbb::parallel_for( - tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), - [this, &remaining_nodes](const tbb::blocked_range &range) - { - for (int x = range.begin(), end = range.end(); x != end; ++x) - { - const auto orig_id = remaining_nodes[x].id; - is_core_node[orig_id] = true; - } - }); - } + if (orig_node_id_from_new_node_id_map.size() > 0) + { + tbb::parallel_for(tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), + [this, &remaining_nodes](const tbb::blocked_range &range) + { + for (int x = range.begin(), end = range.end(); x != end; ++x) + { + const auto orig_id = remaining_nodes[x].id; + is_core_node[orig_node_id_from_new_node_id_map[orig_id]] = + true; + } + }); + } + else + { + tbb::parallel_for(tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), + [this, &remaining_nodes](const tbb::blocked_range &range) + { + for (int x = range.begin(), end = range.end(); x != end; ++x) + { + const auto orig_id = remaining_nodes[x].id; + is_core_node[orig_id] = true; + } + }); + } } else { @@ -650,8 +600,9 @@ class Contractor is_core_node.clear(); } - SimpleLogger().Write() << "[core] " << remaining_nodes.size() << " nodes " - << contractor_graph->GetNumberOfEdges() << " edges." << std::endl; + util::SimpleLogger().Write() << "[core] " << remaining_nodes.size() << " nodes " + << contractor_graph->GetNumberOfEdges() << " edges." + << std::endl; thread_data_list.data.clear(); } @@ -666,15 +617,15 @@ class Contractor out_node_levels.swap(node_levels); } - template inline void GetEdges(DeallocatingVector &edges) + template inline void GetEdges(util::DeallocatingVector &edges) { - Percent p(contractor_graph->GetNumberOfNodes()); - SimpleLogger().Write() << "Getting edges of minimized graph"; + util::Percent p(contractor_graph->GetNumberOfNodes()); + util::SimpleLogger().Write() << "Getting edges of minimized graph"; const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes(); if (contractor_graph->GetNumberOfNodes()) { Edge new_edge; - for (const auto node : osrm::irange(0u, number_of_nodes)) + for (const auto node : util::irange(0u, number_of_nodes)) { p.printStatus(node); for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) @@ -691,8 +642,8 @@ class Contractor new_edge.source = node; new_edge.target = target; } - BOOST_ASSERT_MSG(UINT_MAX != new_edge.source, "Source id invalid"); - BOOST_ASSERT_MSG(UINT_MAX != new_edge.target, "Target id invalid"); + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.source, "Source id invalid"); + BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid"); new_edge.data.distance = data.distance; new_edge.data.shortcut = data.shortcut; if (!data.is_original_via_node_ID && !orig_node_id_from_new_node_id_map.empty()) @@ -723,23 +674,55 @@ class Contractor } private: + inline void RelaxNode(const NodeID node, + const NodeID forbidden_node, + const int distance, + ContractorHeap &heap) + { + const short current_hop = heap.GetData(node).hop + 1; + for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge); + if (!data.forward) + { + continue; + } + const NodeID to = contractor_graph->GetTarget(edge); + if (forbidden_node == to) + { + continue; + } + const int to_distance = distance + data.distance; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_distance, ContractorHeapData{current_hop, false}); + } + // Found a shorter Path -> Update distance + else if (to_distance < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_distance); + heap.GetData(to).hop = current_hop; + } + } + } + inline void Dijkstra(const int max_distance, const unsigned number_of_targets, const int maxNodes, - ContractorThreadData *const data, + ContractorThreadData &data, const NodeID middleNode) { - ContractorHeap &heap = data->heap; + ContractorHeap &heap = data.heap; int nodes = 0; unsigned number_of_targets_found = 0; while (!heap.Empty()) { const NodeID node = heap.DeleteMin(); - const int distance = heap.GetKey(node); - const short current_hop = heap.GetData(node).hop + 1; - + const auto distance = heap.GetKey(node); if (++nodes > maxNodes) { return; @@ -759,38 +742,12 @@ class Contractor } } - // iterate over all edges of node - for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) - { - const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge); - if (!data.forward) - { - continue; - } - const NodeID to = contractor_graph->GetTarget(edge); - if (middleNode == to) - { - continue; - } - const int to_distance = distance + data.distance; - - // New Node discovered -> Add to Heap + Node Info Storage - if (!heap.WasInserted(to)) - { - heap.Insert(to, to_distance, ContractorHeapData(current_hop, false)); - } - // Found a shorter Path -> Update distance - else if (to_distance < heap.GetKey(to)) - { - heap.DecreaseKey(to, to_distance); - heap.GetData(to).hop = current_hop; - } - } + RelaxNode(node, middleNode, distance, heap); } } inline float EvaluateNodePriority(ContractorThreadData *const data, - NodePriorityData *const node_data, + const NodeDepth node_depth, const NodeID node) { ContractionStats stats; @@ -802,14 +759,14 @@ class Contractor float result; if (0 == (stats.edges_deleted_count * stats.original_edges_deleted_count)) { - result = 1.f * node_data->depth; + result = 1.f * node_depth; } else { result = 2.f * (((float)stats.edges_added_count) / stats.edges_deleted_count) + 4.f * (((float)stats.original_edges_added_count) / stats.original_edges_deleted_count) + - 1.f * node_data->depth; + 1.f * node_depth; } BOOST_ASSERT(result >= 0); return result; @@ -820,13 +777,21 @@ class Contractor ContractNode(ContractorThreadData *data, const NodeID node, ContractionStats *stats = nullptr) { ContractorHeap &heap = data->heap; - int inserted_edges_size = data->inserted_edges.size(); + std::size_t inserted_edges_size = data->inserted_edges.size(); std::vector &inserted_edges = data->inserted_edges; + const constexpr bool SHORTCUT_ARC = true; + const constexpr bool FORWARD_DIRECTION_ENABLED = true; + const constexpr bool FORWARD_DIRECTION_DISABLED = false; + const constexpr bool REVERSE_DIRECTION_ENABLED = true; + const constexpr bool REVERSE_DIRECTION_DISABLED = false; for (auto in_edge : contractor_graph->GetAdjacentEdgeRange(node)) { const ContractorEdgeData &in_data = contractor_graph->GetEdgeData(in_edge); const NodeID source = contractor_graph->GetTarget(in_edge); + if (source == node) + continue; + if (RUNSIMULATION) { BOOST_ASSERT(stats != nullptr); @@ -839,7 +804,7 @@ class Contractor } heap.Clear(); - heap.Insert(source, 0, ContractorHeapData()); + heap.Insert(source, 0, ContractorHeapData{}); int max_distance = 0; unsigned number_of_targets = 0; @@ -851,22 +816,64 @@ class Contractor continue; } const NodeID target = contractor_graph->GetTarget(out_edge); - const int path_distance = in_data.distance + out_data.distance; + if (node == target) + continue; + + const EdgeWeight path_distance = in_data.distance + out_data.distance; + if (target == source) + { + if (path_distance < node_weights[node]) + { + if (RUNSIMULATION) + { + // make sure to prune better, but keep inserting this loop if it should + // still be the best + // CAREFUL: This only works due to the independent node-setting. This + // guarantees that source is not connected to another node that is + // contracted + node_weights[source] = path_distance + 1; + BOOST_ASSERT(stats != nullptr); + stats->edges_added_count += 2; + stats->original_edges_added_count += + 2 * (out_data.originalEdges + in_data.originalEdges); + } + else + { + // CAREFUL: This only works due to the independent node-setting. This + // guarantees that source is not connected to another node that is + // contracted + node_weights[source] = path_distance; // make sure to prune better + inserted_edges.emplace_back( + source, target, path_distance, + out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC, + FORWARD_DIRECTION_ENABLED, REVERSE_DIRECTION_DISABLED); + + inserted_edges.emplace_back( + target, source, path_distance, + out_data.originalEdges + in_data.originalEdges, node, SHORTCUT_ARC, + FORWARD_DIRECTION_DISABLED, REVERSE_DIRECTION_ENABLED); + } + } + continue; + } max_distance = std::max(max_distance, path_distance); if (!heap.WasInserted(target)) { - heap.Insert(target, INT_MAX, ContractorHeapData(0, true)); + heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true}); ++number_of_targets; } } if (RUNSIMULATION) { - Dijkstra(max_distance, number_of_targets, 1000, data, node); + const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000; + Dijkstra(max_distance, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, *data, + node); } else { - Dijkstra(max_distance, number_of_targets, 2000, data, node); + const int constexpr FULL_SEARCH_SPACE_SIZE = 2000; + Dijkstra(max_distance, number_of_targets, FULL_SEARCH_SPACE_SIZE, *data, node); } for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node)) { @@ -876,6 +883,8 @@ class Contractor continue; } const NodeID target = contractor_graph->GetTarget(out_edge); + if (target == node) + continue; const int path_distance = in_data.distance + out_data.distance; const int distance = heap.GetKey(target); if (path_distance < distance) @@ -891,22 +900,26 @@ class Contractor { inserted_edges.emplace_back(source, target, path_distance, out_data.originalEdges + in_data.originalEdges, - node, true, true, false); + node, SHORTCUT_ARC, FORWARD_DIRECTION_ENABLED, + REVERSE_DIRECTION_DISABLED); inserted_edges.emplace_back(target, source, path_distance, out_data.originalEdges + in_data.originalEdges, - node, true, false, true); + node, SHORTCUT_ARC, FORWARD_DIRECTION_DISABLED, + REVERSE_DIRECTION_ENABLED); } } } } + // Check For One-Way Streets to decide on the creation of self-loops + if (!RUNSIMULATION) { - int iend = inserted_edges.size(); - for (int i = inserted_edges_size; i < iend; ++i) + std::size_t iend = inserted_edges.size(); + for (std::size_t i = inserted_edges_size; i < iend; ++i) { bool found = false; - for (int other = i + 1; other < iend; ++other) + for (std::size_t other = i + 1; other < iend; ++other) { if (inserted_edges[other].source != inserted_edges[i].source) { @@ -957,14 +970,14 @@ class Contractor std::sort(neighbours.begin(), neighbours.end()); neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin()); - for (const auto i : osrm::irange(0, neighbours.size())) + for (const auto i : util::irange(0, neighbours.size())) { contractor_graph->DeleteEdgesTo(neighbours[i], node); } } inline bool UpdateNodeNeighbours(std::vector &priorities, - std::vector &node_data, + std::vector &node_depth, ContractorThreadData *const data, const NodeID node) { @@ -980,7 +993,7 @@ class Contractor continue; } neighbours.push_back(u); - node_data[u].depth = (std::max)(node_data[node].depth + 1, node_data[u].depth); + node_depth[u] = std::max(node_depth[node] + 1, node_depth[u]); } // eliminate duplicate entries ( forward + backward edges ) std::sort(neighbours.begin(), neighbours.end()); @@ -989,7 +1002,7 @@ class Contractor // re-evaluate priorities of neighboring nodes for (const NodeID u : neighbours) { - priorities[u] = EvaluateNodePriority(data, &(node_data)[u], u); + priorities[u] = EvaluateNodePriority(data, node_depth[u], u); } return true; } @@ -1076,8 +1089,17 @@ class Contractor stxxl::vector external_edge_list; std::vector orig_node_id_from_new_node_id_map; std::vector node_levels; + + // A list of weights for every node in the graph. + // The weight represents the cost for a u-turn on the segment in the base-graph in addition to + // its traversal. + // During contraction, self-loops are checked against this node weight to ensure that necessary + // self-loops are added. + std::vector node_weights; std::vector is_core_node; - XORFastHash fast_hash; + util::XORFastHash<> fast_hash; }; +} +} #endif // CONTRACTOR_HPP diff --git a/data_structures/query_edge.hpp b/include/contractor/query_edge.hpp similarity index 50% rename from data_structures/query_edge.hpp rename to include/contractor/query_edge.hpp index 1c4af03d9..a3265b882 100644 --- a/data_structures/query_edge.hpp +++ b/include/contractor/query_edge.hpp @@ -1,37 +1,15 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef QUERYEDGE_HPP #define QUERYEDGE_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" #include +namespace osrm +{ +namespace contractor +{ + struct QueryEdge { NodeID source; @@ -75,5 +53,7 @@ struct QueryEdge data.id == right.data.id); } }; +} +} #endif // QUERYEDGE_HPP diff --git a/include/engine/api/base_api.hpp b/include/engine/api/base_api.hpp new file mode 100644 index 000000000..8cac72b55 --- /dev/null +++ b/include/engine/api/base_api.hpp @@ -0,0 +1,64 @@ +#ifndef ENGINE_API_BASE_API_HPP +#define ENGINE_API_BASE_API_HPP + +#include "engine/api/base_parameters.hpp" +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/api/json_factory.hpp" +#include "engine/hint.hpp" + +#include +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class BaseAPI +{ + public: + BaseAPI(const datafacade::BaseDataFacade &facade_, const BaseParameters ¶meters_) + : facade(facade_), parameters(parameters_) + { + } + + util::json::Array MakeWaypoints(const std::vector &segment_end_coordinates) const + { + BOOST_ASSERT(parameters.coordinates.size() > 0); + BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1); + + util::json::Array waypoints; + waypoints.values.resize(parameters.coordinates.size()); + waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom); + + auto out_iter = std::next(waypoints.values.begin()); + boost::range::transform(segment_end_coordinates, out_iter, + [this](const PhantomNodes &phantom_pair) + { + return MakeWaypoint(phantom_pair.target_phantom); + }); + return waypoints; + } + + // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions + // protected: + util::json::Object MakeWaypoint(const PhantomNode &phantom) const + { + return json::makeWaypoint(phantom.location, facade.GetNameForID(phantom.name_id), + Hint{phantom, facade.GetCheckSum()}); + } + + const datafacade::BaseDataFacade &facade; + const BaseParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp new file mode 100644 index 000000000..63239b716 --- /dev/null +++ b/include/engine/api/base_parameters.hpp @@ -0,0 +1,90 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ENGINE_API_BASE_PARAMETERS_HPP +#define ENGINE_API_BASE_PARAMETERS_HPP + +#include "engine/hint.hpp" +#include "engine/bearing.hpp" +#include "util/coordinate.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +/** + * General parameters for OSRM service queries. + * + * Holds member attributes: + * - coordinates: for specifying location(s) to services + * - hints: hint for the service to derive the position(s) in the road network more efficiently, + * optional per coordinate + * - radiuses: limits the search for segments in the road network to given radius(es) in meter, + * optional per coordinate + * - bearings: limits the search for segments in the road network to given bearing(s) in degree + * towards true north in clockwise direction, optional per coordinate + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct BaseParameters +{ + std::vector coordinates; + std::vector> hints; + std::vector> radiuses; + std::vector> bearings; + + // FIXME add validation for invalid bearing values + bool IsValid() const + { + return (hints.empty() || hints.size() == coordinates.size()) && + (bearings.empty() || bearings.size() == coordinates.size()) && + (radiuses.empty() || radiuses.size() == coordinates.size()) && + std::all_of(bearings.begin(), bearings.end(), + [](const boost::optional bearing_and_range) + { + if (bearing_and_range) + { + return bearing_and_range->IsValid(); + } + return true; + }); + } +}; +} +} +} + +#endif // ROUTE_PARAMETERS_HPP diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp new file mode 100644 index 000000000..398fb684b --- /dev/null +++ b/include/engine/api/json_factory.hpp @@ -0,0 +1,94 @@ +#ifndef ENGINE_RESPONSE_OBJECTS_HPP_ +#define ENGINE_RESPONSE_OBJECTS_HPP_ + +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/travel_mode.hpp" +#include "engine/polyline_compressor.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/step_maneuver.hpp" +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "util/coordinate.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +struct Hint; + +namespace api +{ +namespace json +{ +namespace detail +{ + +std::string instructionTypeToString(extractor::guidance::TurnType type); +std::string instructionModifierToString(extractor::guidance::DirectionModifier modifier); + +util::json::Array coordinateToLonLat(const util::Coordinate coordinate); + +std::string modeToString(const extractor::TravelMode mode); + +} // namespace detail + +template util::json::String makePolyline(ForwardIter begin, ForwardIter end) +{ + return {encodePolyline(begin, end)}; +} + +template +util::json::Object makeGeoJSONGeometry(ForwardIter begin, ForwardIter end) +{ + auto num_coordinates = std::distance(begin, end); + BOOST_ASSERT(num_coordinates != 0); + util::json::Object geojson; + if (num_coordinates > 1) + { + geojson.values["type"] = "LineString"; + util::json::Array coordinates; + std::transform(begin, end, std::back_inserter(coordinates.values), &detail::coordinateToLonLat); + geojson.values["coordinates"] = std::move(coordinates); + } + else if (num_coordinates > 0) + { + geojson.values["type"] = "Point"; + util::json::Array coordinates; + coordinates.values.push_back(detail::coordinateToLonLat(*begin)); + geojson.values["coordinates"] = std::move(coordinates); + } + return geojson; +} + +util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver); + +util::json::Object makeRouteStep(guidance::RouteStep step, + boost::optional geometry); + +util::json::Object makeRoute(const guidance::Route &route, + util::json::Array legs, + boost::optional geometry); + +util::json::Object +makeWaypoint(const util::Coordinate location, std::string name, const Hint &hint); + +util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps); + +util::json::Array makeRouteLegs(std::vector legs, + std::vector step_geometries); +} +} +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_ diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp new file mode 100644 index 000000000..46b26ae61 --- /dev/null +++ b/include/engine/api/match_api.hpp @@ -0,0 +1,119 @@ +#ifndef ENGINE_API_MATCH_HPP +#define ENGINE_API_MATCH_HPP + +#include "engine/api/route_api.hpp" +#include "engine/api/match_parameters.hpp" + +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/internal_route_result.hpp" +#include "engine/map_matching/sub_matching.hpp" + +#include "util/integer_range.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class MatchAPI final : public RouteAPI +{ + public: + MatchAPI(const datafacade::BaseDataFacade &facade_, const MatchParameters ¶meters_) + : RouteAPI(facade_, parameters_), parameters(parameters_) + { + } + + void MakeResponse(const std::vector &sub_matchings, + const std::vector &sub_routes, + util::json::Object &response) const + { + auto number_of_routes = sub_matchings.size(); + util::json::Array routes; + routes.values.reserve(number_of_routes); + BOOST_ASSERT(sub_matchings.size() == sub_routes.size()); + for (auto index : util::irange(0UL, sub_matchings.size())) + { + auto route = MakeRoute(sub_routes[index].segment_end_coordinates, + sub_routes[index].unpacked_path_segments, + sub_routes[index].source_traversed_in_reverse, + sub_routes[index].target_traversed_in_reverse); + route.values["confidence"] = sub_matchings[index].confidence; + routes.values.push_back(std::move(route)); + } + response.values["tracepoints"] = MakeTracepoints(sub_matchings); + response.values["matchings"] = std::move(routes); + response.values["code"] = "Ok"; + } + + // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions + // protected: + + // FIXME this logic is a little backwards. We should change the output format of the + // map_matching + // routing algorithm to be easier to consume here. + util::json::Array + MakeTracepoints(const std::vector &sub_matchings) const + { + util::json::Array waypoints; + waypoints.values.reserve(parameters.coordinates.size()); + + struct MatchingIndex + { + MatchingIndex() = default; + MatchingIndex(unsigned sub_matching_index_, unsigned point_index_) + : sub_matching_index(sub_matching_index_), point_index(point_index_) + { + } + + unsigned sub_matching_index = std::numeric_limits::max(); + unsigned point_index = std::numeric_limits::max(); + + bool NotMatched() + { + return sub_matching_index == std::numeric_limits::max() && + point_index == std::numeric_limits::max(); + } + }; + + std::vector trace_idx_to_matching_idx(parameters.coordinates.size()); + for (auto sub_matching_index : + util::irange(0u, static_cast(sub_matchings.size()))) + { + for (auto point_index : util::irange( + 0u, static_cast(sub_matchings[sub_matching_index].indices.size()))) + { + trace_idx_to_matching_idx[sub_matchings[sub_matching_index].indices[point_index]] = + MatchingIndex{sub_matching_index, point_index}; + } + } + + for (auto trace_index : util::irange(0UL, parameters.coordinates.size())) + { + auto matching_index = trace_idx_to_matching_idx[trace_index]; + if (matching_index.NotMatched()) + { + waypoints.values.push_back(util::json::Null()); + continue; + } + const auto &phantom = + sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index]; + auto waypoint = BaseAPI::MakeWaypoint(phantom); + waypoint.values["matchings_index"] = matching_index.sub_matching_index; + waypoint.values["waypoint_index"] = matching_index.point_index; + waypoints.values.push_back(std::move(waypoint)); + } + + return waypoints; + } + + const MatchParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/algorithms/trip_tabu_search.hpp b/include/engine/api/match_parameters.hpp similarity index 50% rename from algorithms/trip_tabu_search.hpp rename to include/engine/api/match_parameters.hpp index 32f50fc59..0cb4741ea 100644 --- a/algorithms/trip_tabu_search.hpp +++ b/include/engine/api/match_parameters.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,40 +25,55 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TRIP_BRUTE_FORCE_HPP -#define TRIP_BRUTE_FORCE_HPP +#ifndef ENGINE_API_MATCH_PARAMETERS_HPP +#define ENGINE_API_MATCH_PARAMETERS_HPP -#include "../data_structures/search_engine.hpp" -#include "../util/simple_logger.hpp" +#include "engine/api/route_parameters.hpp" -#include - -#include -#include -#include #include -#include namespace osrm { -namespace trip +namespace engine +{ +namespace api { -// todo: yet to be implemented -void TabuSearchTrip(std::vector &location, - const PhantomNodeArray &phantom_node_vector, - const std::vector &dist_table, - InternalRouteResult &min_route, - std::vector &min_loc_permutation) +/** + * Parameters specific to the OSRM Match service. + * + * Holds member attributes: + * - timestamps: timestamp(s) for the corresponding input coordinate(s) + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct MatchParameters : public RouteParameters { + MatchParameters() + : RouteParameters(false, + false, + RouteParameters::GeometriesType::Polyline, + RouteParameters::OverviewType::Simplified, + {}) + { + } + + template + MatchParameters(std::vector timestamps_, Args... args_) + : RouteParameters{std::forward(args_)...}, timestamps{std::move(timestamps_)} + { + } + + std::vector timestamps; + bool IsValid() const + { + return RouteParameters::IsValid() && + (timestamps.empty() || timestamps.size() == coordinates.size()); + } +}; +} +} } -void TabuSearchTrip(const PhantomNodeArray &phantom_node_vector, - const std::vector &dist_table, - InternalRouteResult &min_route, - std::vector &min_loc_permutation) -{ -} -} -} -#endif // TRIP_BRUTE_FORCE_HPP \ No newline at end of file +#endif diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp new file mode 100644 index 000000000..ed09f6217 --- /dev/null +++ b/include/engine/api/nearest_api.hpp @@ -0,0 +1,57 @@ +#ifndef ENGINE_API_NEAREST_API_HPP +#define ENGINE_API_NEAREST_API_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/nearest_parameters.hpp" + +#include "engine/api/json_factory.hpp" +#include "engine/phantom_node.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class NearestAPI final : public BaseAPI +{ + public: + NearestAPI(const datafacade::BaseDataFacade &facade_, const NearestParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } + + void MakeResponse(const std::vector> &phantom_nodes, + util::json::Object &response) const + { + BOOST_ASSERT(phantom_nodes.size() == 1); + BOOST_ASSERT(parameters.coordinates.size() == 1); + + util::json::Array waypoints; + waypoints.values.resize(phantom_nodes.front().size()); + std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(), + waypoints.values.begin(), + [this](const PhantomNodeWithDistance &phantom_with_distance) + { + auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node); + waypoint.values["distance"] = phantom_with_distance.distance; + return waypoint; + }); + + response.values["code"] = "Ok"; + response.values["waypoints"] = std::move(waypoints); + } + + const NearestParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/util/osrm_exception.hpp b/include/engine/api/nearest_parameters.hpp similarity index 62% rename from util/osrm_exception.hpp rename to include/engine/api/nearest_parameters.hpp index 369433f34..6a6dad7f1 100644 --- a/util/osrm_exception.hpp +++ b/include/engine/api/nearest_parameters.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,28 +25,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OSRM_EXCEPTION_HPP -#define OSRM_EXCEPTION_HPP +#ifndef ENGINE_API_NEAREST_PARAMETERS_HPP +#define ENGINE_API_NEAREST_PARAMETERS_HPP -#include -#include -#include +#include "engine/api/base_parameters.hpp" namespace osrm { -class exception final : public std::exception +namespace engine +{ +namespace api { - public: - explicit exception(const char *message) : message(message) {} - explicit exception(std::string message) : message(std::move(message)) {} - private: - // This function exists to 'anchor' the class, and stop the compiler from - // copying vtable and RTTI info into every object file that includes - // this header. (Caught by -Wweak-vtables under Clang.) - virtual void anchor() const; - const char *what() const noexcept override { return message.c_str(); } - const std::string message; +/** + * Parameters specific to the OSRM Nearest service. + * + * Holds member attributes: + * - number of results: number of nearest segments that should be returned + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct NearestParameters : public BaseParameters +{ + unsigned number_of_results = 1; + + bool IsValid() const { return BaseParameters::IsValid() && number_of_results >= 1; } }; } -#endif /* OSRM_EXCEPTION_HPP */ +} +} + +#endif // ENGINE_API_NEAREST_PARAMETERS_HPP diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp new file mode 100644 index 000000000..1907bee00 --- /dev/null +++ b/include/engine/api/route_api.hpp @@ -0,0 +1,190 @@ +#ifndef ENGINE_API_ROUTE_HPP +#define ENGINE_API_ROUTE_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/json_factory.hpp" +#include "engine/api/route_parameters.hpp" + +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/guidance/assemble_geometry.hpp" +#include "engine/guidance/assemble_leg.hpp" +#include "engine/guidance/assemble_overview.hpp" +#include "engine/guidance/assemble_route.hpp" +#include "engine/guidance/assemble_steps.hpp" +#include "engine/guidance/post_processing.hpp" + +#include "engine/internal_route_result.hpp" + +#include "util/coordinate.hpp" +#include "util/integer_range.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class RouteAPI : public BaseAPI +{ + public: + RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } + + void MakeResponse(const InternalRouteResult &raw_route, util::json::Object &response) const + { + auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL; + util::json::Array routes; + routes.values.resize(number_of_routes); + routes.values[0] = + MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments, + raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse); + if (raw_route.has_alternative()) + { + std::vector> wrapped_leg(1); + wrapped_leg.front() = std::move(raw_route.unpacked_alternative); + routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg, + raw_route.alt_source_traversed_in_reverse, + raw_route.alt_target_traversed_in_reverse); + } + response.values["waypoints"] = BaseAPI::MakeWaypoints(raw_route.segment_end_coordinates); + response.values["routes"] = std::move(routes); + response.values["code"] = "Ok"; + } + + // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions + // protected: + template + util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const + { + if (parameters.geometries == RouteParameters::GeometriesType::Polyline) + { + return json::makePolyline(begin, end); + } + + BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON); + return json::makeGeoJSONGeometry(begin, end); + } + + util::json::Object MakeRoute(const std::vector &segment_end_coordinates, + const std::vector> &unpacked_path_segments, + const std::vector &source_traversed_in_reverse, + const std::vector &target_traversed_in_reverse) const + { + std::vector legs; + std::vector leg_geometries; + auto number_of_legs = segment_end_coordinates.size(); + legs.reserve(number_of_legs); + leg_geometries.reserve(number_of_legs); + + for (auto idx : util::irange(0UL, number_of_legs)) + { + const auto &phantoms = segment_end_coordinates[idx]; + const auto &path_data = unpacked_path_segments[idx]; + + const bool reversed_source = source_traversed_in_reverse[idx]; + const bool reversed_target = target_traversed_in_reverse[idx]; + + auto leg_geometry = guidance::assembleGeometry( + BaseAPI::facade, path_data, phantoms.source_phantom, phantoms.target_phantom); + auto leg = guidance::assembleLeg(facade, path_data, leg_geometry, phantoms.source_phantom, + phantoms.target_phantom, reversed_target, parameters.steps); + + if (parameters.steps) + { + auto steps = guidance::assembleSteps( + BaseAPI::facade, path_data, leg_geometry, phantoms.source_phantom, + phantoms.target_phantom, reversed_source, reversed_target); + + /* Perform step-based post-processing. + * + * Using post-processing on basis of route-steps for a single leg at a time + * comes at the cost that we cannot count the correct exit for roundabouts. + * We can only emit the exit nr/intersections up to/starting at a part of the leg. + * If a roundabout is not terminated in a leg, we will end up with a + *enter-roundabout + * and exit-roundabout-nr where the exit nr is out of sync with the previous enter. + * + * | S | + * * * + * ----* * ---- + * T + * ----* * ---- + * V * * + * | | + * | | + * + * Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to + *take + * the second exit, even though counting from S it would be the third. + * For S, we only emit `roundabout` without an exit number, showing that we enter a + *roundabout + * to find a via point. + * The same exit will be emitted, though, if we should start routing at S, making + * the overall response consistent. + */ + + guidance::trimShortSegments(steps, leg_geometry); + leg.steps = guidance::postProcess(std::move(steps)); + leg.steps = guidance::assignRelativeLocations(std::move(leg.steps), leg_geometry, + phantoms.source_phantom, + phantoms.target_phantom); + leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps); + } + + leg_geometries.push_back(std::move(leg_geometry)); + legs.push_back(std::move(leg)); + } + + auto route = guidance::assembleRoute(legs); + boost::optional json_overview; + if (parameters.overview != RouteParameters::OverviewType::False) + { + const auto use_simplification = + parameters.overview == RouteParameters::OverviewType::Simplified; + BOOST_ASSERT(use_simplification || + parameters.overview == RouteParameters::OverviewType::Full); + + auto overview = guidance::assembleOverview(leg_geometries, use_simplification); + json_overview = MakeGeometry(overview.begin(), overview.end()); + } + + std::vector step_geometries; + for (const auto idx : util::irange(0UL, legs.size())) + { + auto &leg_geometry = leg_geometries[idx]; + std::transform( + legs[idx].steps.begin(), legs[idx].steps.end(), std::back_inserter(step_geometries), + [this, &leg_geometry](const guidance::RouteStep &step) { + if (parameters.geometries == RouteParameters::GeometriesType::Polyline) + { + return static_cast( + json::makePolyline(leg_geometry.locations.begin() + step.geometry_begin, + leg_geometry.locations.begin() + step.geometry_end)); + } + BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON); + return static_cast(json::makeGeoJSONGeometry( + leg_geometry.locations.begin() + step.geometry_begin, + leg_geometry.locations.begin() + step.geometry_end)); + }); + } + + return json::makeRoute(route, + json::makeRouteLegs(std::move(legs), std::move(step_geometries)), + std::move(json_overview)); + } + + const RouteParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp new file mode 100644 index 000000000..59040205a --- /dev/null +++ b/include/engine/api/route_parameters.hpp @@ -0,0 +1,96 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ENGINE_API_ROUTE_PARAMETERS_HPP +#define ENGINE_API_ROUTE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +/** + * Parameters specific to the OSRM Route service. + * + * Holds member attributes: + * - steps: return route step for each route leg + * - alternatives: tries to find alternative routes + * - geometries: route geometry encoded in Polyline or GeoJSON + * - overview: adds overview geometry either Full, Simplified (according to highest zoom level) or + * False (not at all) + * - continue_straight: enable or disable continue_straight (disabled by default) + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct RouteParameters : public BaseParameters +{ + enum class GeometriesType + { + Polyline, + GeoJSON + }; + enum class OverviewType + { + Simplified, + Full, + False + }; + + RouteParameters() = default; + + template + RouteParameters(const bool steps_, + const bool alternatives_, + const GeometriesType geometries_, + const OverviewType overview_, + const boost::optional continue_straight_, + Args... args_) + : BaseParameters{std::forward(args_)...}, steps{steps_}, alternatives{alternatives_}, + geometries{geometries_}, overview{overview_}, continue_straight{continue_straight_} + { + } + + bool steps = false; + bool alternatives = false; + GeometriesType geometries = GeometriesType::Polyline; + OverviewType overview = OverviewType::Simplified; + boost::optional continue_straight; + + bool IsValid() const { return coordinates.size() >= 2 && BaseParameters::IsValid(); } +}; +} +} +} + +#endif diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp new file mode 100644 index 000000000..1655ce357 --- /dev/null +++ b/include/engine/api/table_api.hpp @@ -0,0 +1,135 @@ +#ifndef ENGINE_API_TABLE_HPP +#define ENGINE_API_TABLE_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/table_parameters.hpp" +#include "engine/api/json_factory.hpp" + +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/guidance/assemble_leg.hpp" +#include "engine/guidance/assemble_route.hpp" +#include "engine/guidance/assemble_geometry.hpp" +#include "engine/guidance/assemble_overview.hpp" +#include "engine/guidance/assemble_steps.hpp" + +#include "engine/internal_route_result.hpp" + +#include "util/integer_range.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class TableAPI final : public BaseAPI +{ + public: + TableAPI(const datafacade::BaseDataFacade &facade_, const TableParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } + + virtual void MakeResponse(const std::vector &durations, + const std::vector &phantoms, + util::json::Object &response) const + { + auto number_of_sources = parameters.sources.size(); + auto number_of_destinations = parameters.destinations.size(); + ; + + // symmetric case + if (parameters.sources.empty()) + { + response.values["sources"] = MakeWaypoints(phantoms); + number_of_sources = phantoms.size(); + } + else + { + response.values["sources"] = MakeWaypoints(phantoms, parameters.sources); + } + + if (parameters.destinations.empty()) + { + response.values["destinations"] = MakeWaypoints(phantoms); + number_of_destinations = phantoms.size(); + } + else + { + response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations); + } + + response.values["durations"] = + MakeTable(durations, number_of_sources, number_of_destinations); + response.values["code"] = "Ok"; + } + + // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions + // protected: + virtual util::json::Array MakeWaypoints(const std::vector &phantoms) const + { + util::json::Array json_waypoints; + json_waypoints.values.reserve(phantoms.size()); + BOOST_ASSERT(phantoms.size() == parameters.coordinates.size()); + + boost::range::transform(phantoms, std::back_inserter(json_waypoints.values), + [this](const PhantomNode &phantom) + { + return BaseAPI::MakeWaypoint(phantom); + }); + return json_waypoints; + } + + virtual util::json::Array MakeWaypoints(const std::vector &phantoms, + const std::vector &indices) const + { + util::json::Array json_waypoints; + json_waypoints.values.reserve(indices.size()); + boost::range::transform(indices, std::back_inserter(json_waypoints.values), + [this, phantoms](const std::size_t idx) + { + BOOST_ASSERT(idx < phantoms.size()); + return BaseAPI::MakeWaypoint(phantoms[idx]); + }); + return json_waypoints; + } + + virtual util::json::Array MakeTable(const std::vector &values, + std::size_t number_of_rows, + std::size_t number_of_columns) const + { + util::json::Array json_table; + for (const auto row : util::irange(0UL, number_of_rows)) + { + util::json::Array json_row; + auto row_begin_iterator = values.begin() + (row * number_of_columns); + auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns); + json_row.values.resize(number_of_columns); + std::transform(row_begin_iterator, row_end_iterator, json_row.values.begin(), + [](const EdgeWeight duration) + { + if (duration == INVALID_EDGE_WEIGHT) + { + return util::json::Value(util::json::Null()); + } + return util::json::Value(util::json::Number(duration / 10.)); + }); + json_table.values.push_back(std::move(json_row)); + } + return json_table; + } + + const TableParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/include/engine/api/table_parameters.hpp b/include/engine/api/table_parameters.hpp new file mode 100644 index 000000000..d4d3fd356 --- /dev/null +++ b/include/engine/api/table_parameters.hpp @@ -0,0 +1,110 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ENGINE_API_TABLE_PARAMETERS_HPP +#define ENGINE_API_TABLE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +#include + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +/** + * Parameters specific to the OSRM Table service. + * + * Holds member attributes: + * - sources: indices into coordinates indicating sources for the Table service, no sources means + * use all coordinates as sources + * - destinations: indices into coordinates indicating destinations for the Table service, no + * destinations means use all coordinates as destinations + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct TableParameters : public BaseParameters +{ + std::vector sources; + std::vector destinations; + + TableParameters() = default; + template + TableParameters(std::vector sources_, + std::vector destinations_, + Args... args_) + : BaseParameters{std::forward(args_)...}, sources{std::move(sources_)}, + destinations{std::move(destinations_)} + { + } + + bool IsValid() const + { + if (!BaseParameters::IsValid()) + return false; + + // Distance Table makes only sense with 2+ coodinates + if (coordinates.size() < 2) + return false; + + // 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault + + // 2/ len(srcs) and len(dsts) smaller or equal to len(locations) + if (sources.size() > coordinates.size()) + return false; + + if (destinations.size() > coordinates.size()) + return false; + + // 3/ 0 <= index < len(locations) + const auto not_in_range = [this](const std::size_t x) + { + return x >= coordinates.size(); + }; + + if (std::any_of(begin(sources), end(sources), not_in_range)) + return false; + + if (std::any_of(begin(destinations), end(destinations), not_in_range)) + return false; + + return true; + } +}; +} +} +} + +#endif // ENGINE_API_TABLE_PARAMETERS_HPP diff --git a/include/engine/api/tile_parameters.hpp b/include/engine/api/tile_parameters.hpp new file mode 100644 index 000000000..34c949a89 --- /dev/null +++ b/include/engine/api/tile_parameters.hpp @@ -0,0 +1,76 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ENGINE_API_TILE_PARAMETERS_HPP +#define ENGINE_API_TILE_PARAMETERS_HPP + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +/** + * Parameters specific to the OSRM Tile service. + * + * Holds member attributes: + * - x: the x location for the tile + * - y: the y location for the tile + * - z: the zoom level for the tile + * + * The parameters x,y and z have to conform to the Slippy Map Tilenames specification: + * - https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels + * - https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#X_and_Y + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct TileParameters final +{ + unsigned x; + unsigned y; + unsigned z; + + bool IsValid() const + { + // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels + // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#X_and_Y + const auto valid_x = x <= static_cast(std::pow(2., z)) - 1; + const auto valid_y = y <= static_cast(std::pow(2., z)) - 1; + const auto valid_z = z < 20; + + return valid_x && valid_y && valid_z; + }; +}; +} +} +} + +#endif diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp new file mode 100644 index 000000000..9ed72ef65 --- /dev/null +++ b/include/engine/api/trip_api.hpp @@ -0,0 +1,111 @@ +#ifndef ENGINE_API_TRIP_HPP +#define ENGINE_API_TRIP_HPP + +#include "engine/api/route_api.hpp" +#include "engine/api/trip_parameters.hpp" + +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/internal_route_result.hpp" + +#include "util/integer_range.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class TripAPI final : public RouteAPI +{ + public: + TripAPI(const datafacade::BaseDataFacade &facade_, const TripParameters ¶meters_) + : RouteAPI(facade_, parameters_), parameters(parameters_) + { + } + + void MakeResponse(const std::vector> &sub_trips, + const std::vector &sub_routes, + const std::vector &phantoms, + util::json::Object &response) const + { + auto number_of_routes = sub_trips.size(); + util::json::Array routes; + routes.values.reserve(number_of_routes); + BOOST_ASSERT(sub_trips.size() == sub_routes.size()); + for (auto index : util::irange(0UL, sub_trips.size())) + { + auto route = MakeRoute(sub_routes[index].segment_end_coordinates, + sub_routes[index].unpacked_path_segments, + sub_routes[index].source_traversed_in_reverse, + sub_routes[index].target_traversed_in_reverse); + routes.values.push_back(std::move(route)); + } + response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms); + response.values["trips"] = std::move(routes); + response.values["code"] = "Ok"; + } + + // FIXME gcc 4.8 doesn't support for lambdas to call protected member functions + // protected: + + // FIXME this logic is a little backwards. We should change the output format of the + // trip plugin routing algorithm to be easier to consume here. + util::json::Array MakeWaypoints(const std::vector> &sub_trips, + const std::vector &phantoms) const + { + util::json::Array waypoints; + waypoints.values.reserve(parameters.coordinates.size()); + + struct TripIndex + { + TripIndex() = default; + TripIndex(unsigned sub_trip_index_, unsigned point_index_) + : sub_trip_index(sub_trip_index_), point_index(point_index_) + { + } + + unsigned sub_trip_index = std::numeric_limits::max(); + unsigned point_index = std::numeric_limits::max(); + + bool NotUsed() + { + return sub_trip_index == std::numeric_limits::max() && + point_index == std::numeric_limits::max(); + } + }; + + std::vector input_idx_to_trip_idx(parameters.coordinates.size()); + for (auto sub_trip_index : util::irange(0u, sub_trips.size())) + { + for (auto point_index : + util::irange(0u, sub_trips[sub_trip_index].size())) + { + input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] = + TripIndex{sub_trip_index, point_index}; + } + } + + for (auto input_index : util::irange(0UL, parameters.coordinates.size())) + { + auto trip_index = input_idx_to_trip_idx[input_index]; + BOOST_ASSERT(!trip_index.NotUsed()); + + auto waypoint = BaseAPI::MakeWaypoint(phantoms[input_index]); + waypoint.values["trips_index"] = trip_index.sub_trip_index; + waypoint.values["waypoint_index"] = trip_index.point_index; + waypoints.values.push_back(std::move(waypoint)); + } + + return waypoints; + } + + const TripParameters ¶meters; +}; + +} // ns api +} // ns engine +} // ns osrm + +#endif diff --git a/algorithms/polyline_formatter.hpp b/include/engine/api/trip_parameters.hpp similarity index 69% rename from algorithms/polyline_formatter.hpp rename to include/engine/api/trip_parameters.hpp index 68cc70293..53c9f4274 100644 --- a/algorithms/polyline_formatter.hpp +++ b/include/engine/api/trip_parameters.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2014, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,21 +25,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef POLYLINE_FORMATTER_HPP -#define POLYLINE_FORMATTER_HPP +#ifndef ENGINE_API_TRIP_PARAMETERS_HPP +#define ENGINE_API_TRIP_PARAMETERS_HPP -struct SegmentInformation; +#include "engine/api/route_parameters.hpp" -#include - -#include #include -struct PolylineFormatter +namespace osrm +{ +namespace engine +{ +namespace api { - osrm::json::String printEncodedString(const std::vector &polyline) const; - osrm::json::Array printUnencodedString(const std::vector &polyline) const; +/** + * Parameters specific to the OSRM Trip service. + * + * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * NearestParameters, TripParameters, MatchParameters and TileParameters + */ +struct TripParameters : public RouteParameters +{ + // bool IsValid() const; Falls back to base class }; +} +} +} -#endif /* POLYLINE_FORMATTER_HPP */ +#endif diff --git a/include/engine/base64.hpp b/include/engine/base64.hpp new file mode 100644 index 000000000..648296183 --- /dev/null +++ b/include/engine/base64.hpp @@ -0,0 +1,141 @@ +#ifndef OSRM_BASE64_HPP +#define OSRM_BASE64_HPP + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +// RFC 4648 "The Base16, Base32, and Base64 Data Encodings" +// See: https://tools.ietf.org/html/rfc4648 + +namespace detail +{ +// The C++ standard guarantees none of this by default, but we need it in the following. +static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits"); +static_assert(sizeof(char) == 1u, "we assume a char is one byte large"); + +using Base64FromBinary = boost::archive::iterators::base64_from_binary< + boost::archive::iterators::transform_width>; + +using BinaryFromBase64 = boost::archive::iterators::transform_width< + boost::archive::iterators::binary_from_base64, + 8, // get a view of 8 bit + 6 // from a sequence of 6 bit + >; +} // ns detail + +namespace osrm +{ +namespace engine +{ + +// Encoding Implementation + +// Encodes a chunk of memory to Base64. +inline std::string encodeBase64(const unsigned char *first, std::size_t size) +{ + std::vector bytes{first, first + size}; + BOOST_ASSERT(!bytes.empty()); + + std::size_t bytes_to_pad{0}; + + while (bytes.size() % 3 != 0) + { + bytes_to_pad += 1; + bytes.push_back(0); + } + + BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2); + BOOST_ASSERT_MSG(0 == bytes.size() % 3, "base64 input data size is not a multiple of 3"); + + std::string encoded{detail::Base64FromBinary{bytes.data()}, + detail::Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}}; + + return encoded.append(bytes_to_pad, '='); +} + +// C++11 standard 3.9.1/1: Plain char, signed char, and unsigned char are three distinct types + +// Overload for signed char catches (not only but also) C-string literals. +inline std::string encodeBase64(const signed char *first, std::size_t size) +{ + return encodeBase64(reinterpret_cast(first), size); +} + +// Overload for char catches (not only but also) C-string literals. +inline std::string encodeBase64(const char *first, std::size_t size) +{ + return encodeBase64(reinterpret_cast(first), size); +} + +// Convenience specialization, encoding from string instead of byte-dumping it. +inline std::string encodeBase64(const std::string &x) { return encodeBase64(x.data(), x.size()); } + +// Encode any sufficiently trivial object to Base64. +template std::string encodeBase64Bytewise(const T &x) +{ +#if not defined __GNUC__ or __GNUC__ > 4 + static_assert(std::is_trivially_copyable::value, "requires a trivially copyable type"); +#endif + + return encodeBase64(reinterpret_cast(&x), sizeof(T)); +} + +// Decoding Implementation + +// Decodes into a chunk of memory that is at least as large as the input. +template void decodeBase64(const std::string &encoded, OutputIter out) +{ + auto unpadded = encoded; + + const auto num_padded = std::count(begin(encoded), end(encoded), '='); + std::replace(begin(unpadded), end(unpadded), '=', 'A'); // A_64 == \0 + + std::string decoded{detail::BinaryFromBase64{begin(unpadded)}, + detail::BinaryFromBase64{begin(unpadded) + unpadded.length()}}; + + decoded.erase(end(decoded) - num_padded, end(decoded)); + std::copy(begin(decoded), end(decoded), out); +} + +// Convenience specialization, filling string instead of byte-dumping into it. +inline std::string decodeBase64(const std::string &encoded) +{ + std::string rv; + + decodeBase64(encoded, std::back_inserter(rv)); + + return rv; +} + +// Decodes from Base 64 to any sufficiently trivial object. +template T decodeBase64Bytewise(const std::string &encoded) +{ +#if not defined __GNUC__ or __GNUC__ > 4 + static_assert(std::is_trivially_copyable::value, "requires a trivially copyable type"); +#endif + + T x; + + decodeBase64(encoded, reinterpret_cast(&x)); + + return x; +} + +} // ns engine +} // ns osrm + +#endif /* OSRM_BASE64_HPP */ diff --git a/util/range_algorithms.hpp b/include/engine/bearing.hpp similarity index 72% rename from util/range_algorithms.hpp rename to include/engine/bearing.hpp index 4d01d29c8..3b999622b 100644 --- a/util/range_algorithms.hpp +++ b/include/engine/bearing.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,25 +25,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RANGE_ALGORITHMS_HPP -#define RANGE_ALGORITHMS_HPP - -#include +#ifndef OSRM_ENGINE_BEARING_HPP +#define OSRM_ENGINE_BEARING_HPP namespace osrm { - -template -auto max_element(const Container &c) -> decltype(std::max_element(c.begin(), c.end())) +namespace engine { - return std::max_element(c.begin(), c.end()); -} -template -auto max_element(const Container &c) -> decltype(std::max_element(c.cbegin(), c.cend())) +struct Bearing { - return std::max_element(c.cbegin(), c.cend()); + short bearing; + short range; + + bool IsValid() const { return bearing >= 0 && bearing <= 360 && range >= 0 && range <= 180; } +}; + +inline bool operator==(const Bearing lhs, const Bearing rhs) +{ + return lhs.bearing == rhs.bearing && lhs.range == rhs.range; +} +inline bool operator!=(const Bearing lhs, const Bearing rhs) { return !(lhs == rhs); } } } -#endif // RANGE_ALGORITHMS_HPP +#endif diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp new file mode 100644 index 000000000..1f608d947 --- /dev/null +++ b/include/engine/datafacade/datafacade_base.hpp @@ -0,0 +1,153 @@ +#ifndef DATAFACADE_BASE_HPP +#define DATAFACADE_BASE_HPP + +// Exposes all data access interfaces to the algorithms via base class ptr + +#include "extractor/edge_based_node.hpp" +#include "extractor/external_memory_node.hpp" +#include "contractor/query_edge.hpp" +#include "engine/phantom_node.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "util/integer_range.hpp" +#include "util/exception.hpp" +#include "util/string_util.hpp" +#include "util/typedefs.hpp" + +#include "osrm/coordinate.hpp" + +#include + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace datafacade +{ + +using EdgeRange = util::range; + +class BaseDataFacade +{ + public: + using EdgeData = contractor::QueryEdge::EdgeData; + using RTreeLeaf = extractor::EdgeBasedNode; + BaseDataFacade() {} + virtual ~BaseDataFacade() {} + + // search graph access + virtual unsigned GetNumberOfNodes() const = 0; + + virtual unsigned GetNumberOfEdges() const = 0; + + virtual unsigned GetOutDegree(const NodeID n) const = 0; + + virtual NodeID GetTarget(const EdgeID e) const = 0; + + virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0; + + virtual EdgeID BeginEdges(const NodeID n) const = 0; + + virtual EdgeID EndEdges(const NodeID n) const = 0; + + virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; + + // searches for a specific edge + virtual EdgeID FindEdge(const NodeID from, const NodeID to) const = 0; + + virtual EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const = 0; + + virtual EdgeID + FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0; + + // node and edge information access + virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0; + + virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0; + + virtual void GetUncompressedGeometry(const EdgeID id, + std::vector &result_nodes) const = 0; + + // Gets the weight values for each segment in an uncompressed geometry. + // Should always be 1 shorter than GetUncompressedGeometry + virtual void GetUncompressedWeights(const EdgeID id, + std::vector &result_weights) const = 0; + + // Returns the data source ids that were used to supply the edge + // weights. Will return an empty array when only the base profile is used. + virtual void GetUncompressedDatasources(const EdgeID id, + std::vector &data_sources) const = 0; + + // Gets the name of a datasource + virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const = 0; + + virtual extractor::guidance::TurnInstruction + GetTurnInstructionForEdgeID(const unsigned id) const = 0; + + virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0; + + virtual std::vector GetEdgesInBox(const util::Coordinate south_west, + const util::Coordinate north_east) = 0; + + virtual std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance, + const int bearing, + const int bearing_range) = 0; + virtual std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance) = 0; + + virtual std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) = 0; + virtual std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) = 0; + virtual std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) = 0; + virtual std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance) = 0; + + virtual std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) = 0; + virtual std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance) = 0; + virtual std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) = 0; + virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::Coordinate input_coordinate, const int bearing, const int bearing_range) = 0; + + virtual unsigned GetCheckSum() const = 0; + + virtual bool IsCoreNode(const NodeID id) const = 0; + + virtual unsigned GetNameIndexFromEdgeID(const unsigned id) const = 0; + + virtual std::string GetNameForID(const unsigned name_id) const = 0; + + virtual std::size_t GetCoreSize() const = 0; + + virtual std::string GetTimestamp() const = 0; + + virtual bool GetContinueStraightDefault() const = 0; +}; +} +} +} + +#endif // DATAFACADE_BASE_HPP diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp new file mode 100644 index 000000000..bda4869b8 --- /dev/null +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -0,0 +1,658 @@ +#ifndef INTERNAL_DATAFACADE_HPP +#define INTERNAL_DATAFACADE_HPP + +// implements all data storage when shared memory is _NOT_ used + +#include "engine/datafacade/datafacade_base.hpp" + +#include "extractor/guidance/turn_instruction.hpp" + +#include "engine/geospatial_query.hpp" +#include "extractor/original_edge_data.hpp" +#include "extractor/profile_properties.hpp" +#include "extractor/query_node.hpp" +#include "contractor/query_edge.hpp" +#include "util/shared_memory_vector_wrapper.hpp" +#include "util/static_graph.hpp" +#include "util/static_rtree.hpp" +#include "util/range_table.hpp" +#include "util/graph_loader.hpp" +#include "util/simple_logger.hpp" +#include "util/rectangle.hpp" +#include "extractor/compressed_edge_container.hpp" + +#include "osrm/coordinate.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace datafacade +{ + +class InternalDataFacade final : public BaseDataFacade +{ + + private: + using super = BaseDataFacade; + using QueryGraph = util::StaticGraph; + using InputEdge = typename QueryGraph::InputEdge; + using RTreeLeaf = typename super::RTreeLeaf; + using InternalRTree = + util::StaticRTree::vector, false>; + using InternalGeospatialQuery = GeospatialQuery; + + InternalDataFacade() {} + + unsigned m_check_sum; + unsigned m_number_of_nodes; + std::unique_ptr m_query_graph; + std::string m_timestamp; + + std::shared_ptr::vector> m_coordinate_list; + util::ShM::vector m_via_node_list; + util::ShM::vector m_name_ID_list; + util::ShM::vector m_turn_instruction_list; + util::ShM::vector m_travel_mode_list; + util::ShM::vector m_names_char_list; + util::ShM::vector m_geometry_indices; + util::ShM::vector m_geometry_list; + util::ShM::vector m_is_core_node; + util::ShM::vector m_segment_weights; + util::ShM::vector m_datasource_list; + util::ShM::vector m_datasource_names; + extractor::ProfileProperties m_profile_properties; + + boost::thread_specific_ptr m_static_rtree; + boost::thread_specific_ptr m_geospatial_query; + boost::filesystem::path ram_index_path; + boost::filesystem::path file_index_path; + util::RangeTable<16, false> m_name_table; + + void LoadProfileProperties(const boost::filesystem::path &properties_path) + { + boost::filesystem::ifstream in_stream(properties_path); + if (!in_stream) + { + throw util::exception("Could not open " + properties_path.string() + " for reading."); + } + + in_stream.read(reinterpret_cast(&m_profile_properties), sizeof(m_profile_properties)); + } + + void LoadTimestamp(const boost::filesystem::path ×tamp_path) + { + util::SimpleLogger().Write() << "Loading Timestamp"; + boost::filesystem::ifstream timestamp_stream(timestamp_path); + if (!timestamp_stream) + { + throw util::exception("Could not open " + timestamp_path.string() + " for reading."); + } + getline(timestamp_stream, m_timestamp); + } + + void LoadGraph(const boost::filesystem::path &hsgr_path) + { + typename util::ShM::vector node_list; + typename util::ShM::vector edge_list; + + util::SimpleLogger().Write() << "loading graph from " << hsgr_path.string(); + + m_number_of_nodes = readHSGRFromStream(hsgr_path, node_list, edge_list, &m_check_sum); + + BOOST_ASSERT_MSG(0 != node_list.size(), "node list empty"); + // BOOST_ASSERT_MSG(0 != edge_list.size(), "edge list empty"); + util::SimpleLogger().Write() << "loaded " << node_list.size() << " nodes and " + << edge_list.size() << " edges"; + m_query_graph = std::unique_ptr(new QueryGraph(node_list, edge_list)); + + BOOST_ASSERT_MSG(0 == node_list.size(), "node list not flushed"); + BOOST_ASSERT_MSG(0 == edge_list.size(), "edge list not flushed"); + util::SimpleLogger().Write() << "Data checksum is " << m_check_sum; + } + + void LoadNodeAndEdgeInformation(const boost::filesystem::path &nodes_file, + const boost::filesystem::path &edges_file) + { + boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary); + + extractor::QueryNode current_node; + unsigned number_of_coordinates = 0; + nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned)); + m_coordinate_list = std::make_shared>(number_of_coordinates); + for (unsigned i = 0; i < number_of_coordinates; ++i) + { + nodes_input_stream.read((char *)¤t_node, sizeof(extractor::QueryNode)); + m_coordinate_list->at(i) = util::Coordinate(current_node.lon, current_node.lat); + BOOST_ASSERT(m_coordinate_list->at(i).IsValid()); + } + + boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary); + unsigned number_of_edges = 0; + edges_input_stream.read((char *)&number_of_edges, sizeof(unsigned)); + m_via_node_list.resize(number_of_edges); + m_name_ID_list.resize(number_of_edges); + m_turn_instruction_list.resize(number_of_edges); + m_travel_mode_list.resize(number_of_edges); + + extractor::OriginalEdgeData current_edge_data; + for (unsigned i = 0; i < number_of_edges; ++i) + { + edges_input_stream.read((char *)&(current_edge_data), + sizeof(extractor::OriginalEdgeData)); + m_via_node_list[i] = current_edge_data.via_node; + m_name_ID_list[i] = current_edge_data.name_id; + m_turn_instruction_list[i] = current_edge_data.turn_instruction; + m_travel_mode_list[i] = current_edge_data.travel_mode; + } + } + + void LoadCoreInformation(const boost::filesystem::path &core_data_file) + { + std::ifstream core_stream(core_data_file.string().c_str(), std::ios::binary); + unsigned number_of_markers; + core_stream.read((char *)&number_of_markers, sizeof(unsigned)); + + std::vector unpacked_core_markers(number_of_markers); + core_stream.read((char *)unpacked_core_markers.data(), sizeof(char) * number_of_markers); + + // in this case we have nothing to do + if (number_of_markers <= 0) + { + return; + } + + m_is_core_node.resize(number_of_markers); + for (auto i = 0u; i < number_of_markers; ++i) + { + BOOST_ASSERT(unpacked_core_markers[i] == 0 || unpacked_core_markers[i] == 1); + m_is_core_node[i] = unpacked_core_markers[i] == 1; + } + } + + void LoadGeometries(const boost::filesystem::path &geometry_file) + { + std::ifstream geometry_stream(geometry_file.string().c_str(), std::ios::binary); + unsigned number_of_indices = 0; + unsigned number_of_compressed_geometries = 0; + + geometry_stream.read((char *)&number_of_indices, sizeof(unsigned)); + + m_geometry_indices.resize(number_of_indices); + if (number_of_indices > 0) + { + geometry_stream.read((char *)&(m_geometry_indices[0]), + number_of_indices * sizeof(unsigned)); + } + + geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned)); + + BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries); + m_geometry_list.resize(number_of_compressed_geometries); + + if (number_of_compressed_geometries > 0) + { + geometry_stream.read((char *)&(m_geometry_list[0]), + number_of_compressed_geometries * + sizeof(extractor::CompressedEdgeContainer::CompressedEdge)); + } + } + + void LoadDatasourceInfo(const boost::filesystem::path &datasource_names_file, + const boost::filesystem::path &datasource_indexes_file) + { + boost::filesystem::ifstream datasources_stream(datasource_indexes_file, std::ios::binary); + if (!datasources_stream) + { + throw util::exception("Could not open " + datasource_indexes_file.string() + " for reading!"); + } + BOOST_ASSERT(datasources_stream); + + std::size_t number_of_datasources = 0; + datasources_stream.read(reinterpret_cast(&number_of_datasources), + sizeof(std::size_t)); + if (number_of_datasources > 0) + { + m_datasource_list.resize(number_of_datasources); + datasources_stream.read(reinterpret_cast(&(m_datasource_list[0])), + number_of_datasources * sizeof(uint8_t)); + } + + boost::filesystem::ifstream datasourcenames_stream(datasource_names_file, std::ios::binary); + if (!datasourcenames_stream) + { + throw util::exception("Could not open " + datasource_names_file.string() + " for reading!"); + } + BOOST_ASSERT(datasourcenames_stream); + std::string name; + while (std::getline(datasourcenames_stream, name)) + { + m_datasource_names.push_back(std::move(name)); + } + } + + void LoadRTree() + { + BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree"); + + m_static_rtree.reset(new InternalRTree(ram_index_path, file_index_path, m_coordinate_list)); + m_geospatial_query.reset( + new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this)); + } + + void LoadStreetNames(const boost::filesystem::path &names_file) + { + boost::filesystem::ifstream name_stream(names_file, std::ios::binary); + + name_stream >> m_name_table; + + unsigned number_of_chars = 0; + name_stream.read((char *)&number_of_chars, sizeof(unsigned)); + BOOST_ASSERT_MSG(0 != number_of_chars, "name file broken"); + m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element + name_stream.read((char *)&m_names_char_list[0], number_of_chars * sizeof(char)); + if (0 == m_names_char_list.size()) + { + util::SimpleLogger().Write(logWARNING) << "list of street names is empty"; + } + } + + public: + virtual ~InternalDataFacade() + { + m_static_rtree.reset(); + m_geospatial_query.reset(); + } + + explicit InternalDataFacade(const storage::StorageConfig& config) + { + ram_index_path = config.ram_index_path; + file_index_path = config.file_index_path; + + util::SimpleLogger().Write() << "loading graph data"; + LoadGraph(config.hsgr_data_path); + + util::SimpleLogger().Write() << "loading edge information"; + LoadNodeAndEdgeInformation(config.nodes_data_path, config.edges_data_path); + + util::SimpleLogger().Write() << "loading core information"; + LoadCoreInformation(config.core_data_path); + + util::SimpleLogger().Write() << "loading geometries"; + LoadGeometries(config.geometries_path); + + util::SimpleLogger().Write() << "loading datasource info"; + LoadDatasourceInfo(config.datasource_names_path, + config.datasource_indexes_path); + + util::SimpleLogger().Write() << "loading timestamp"; + LoadTimestamp(config.timestamp_path); + + util::SimpleLogger().Write() << "loading profile properties"; + LoadProfileProperties(config.properties_path); + + util::SimpleLogger().Write() << "loading street names"; + LoadStreetNames(config.names_data_path); + } + + // search graph access + unsigned GetNumberOfNodes() const override final { return m_query_graph->GetNumberOfNodes(); } + + unsigned GetNumberOfEdges() const override final { return m_query_graph->GetNumberOfEdges(); } + + unsigned GetOutDegree(const NodeID n) const override final + { + return m_query_graph->GetOutDegree(n); + } + + NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } + + EdgeData &GetEdgeData(const EdgeID e) const override final + { + return m_query_graph->GetEdgeData(e); + } + + EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph->BeginEdges(n); } + + EdgeID EndEdges(const NodeID n) const override final { return m_query_graph->EndEdges(n); } + + EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final + { + return m_query_graph->GetAdjacentEdgeRange(node); + } + + // searches for a specific edge + EdgeID FindEdge(const NodeID from, const NodeID to) const override final + { + return m_query_graph->FindEdge(from, to); + } + + EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const override final + { + return m_query_graph->FindEdgeInEitherDirection(from, to); + } + + EdgeID + FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const override final + { + return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); + } + + // node and edge information access + util::Coordinate GetCoordinateOfNode(const unsigned id) const override final + { + return m_coordinate_list->at(id); + } + + extractor::guidance::TurnInstruction + GetTurnInstructionForEdgeID(const unsigned id) const override final + { + return m_turn_instruction_list.at(id); + } + + extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const override final + { + return m_travel_mode_list.at(id); + } + + std::vector GetEdgesInBox(const util::Coordinate south_west, + const util::Coordinate north_east) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat, + north_east.lat}; + return m_geospatial_query->Search(bbox); + } + + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance); + } + + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance, + bearing, bearing_range); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing, + bearing_range); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance, + bearing, bearing_range); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::Coordinate input_coordinate) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance, bearing, bearing_range); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, bearing, bearing_range); + } + + unsigned GetCheckSum() const override final { return m_check_sum; } + + unsigned GetNameIndexFromEdgeID(const unsigned id) const override final + { + return m_name_ID_list.at(id); + } + + std::string GetNameForID(const unsigned name_id) const override final + { + if (std::numeric_limits::max() == name_id) + { + return ""; + } + auto range = m_name_table.GetRange(name_id); + + std::string result; + result.reserve(range.size()); + if (range.begin() != range.end()) + { + result.resize(range.back() - range.front() + 1); + std::copy(m_names_char_list.begin() + range.front(), + m_names_char_list.begin() + range.back() + 1, result.begin()); + } + return result; + } + + virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final + { + return m_via_node_list.at(id); + } + + virtual std::size_t GetCoreSize() const override final { return m_is_core_node.size(); } + + virtual bool IsCoreNode(const NodeID id) const override final + { + if (m_is_core_node.size() > 0) + { + return m_is_core_node[id]; + } + else + { + return false; + } + } + + virtual void GetUncompressedGeometry(const EdgeID id, + std::vector &result_nodes) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_nodes.clear(); + result_nodes.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) + { + result_nodes.emplace_back(edge.node_id); + }); + } + + virtual void + GetUncompressedWeights(const EdgeID id, + std::vector &result_weights) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_weights.clear(); + result_weights.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) + { + result_weights.emplace_back(edge.weight); + }); + } + + // Returns the data source ids that were used to supply the edge + // weights. + virtual void + GetUncompressedDatasources(const EdgeID id, + std::vector &result_datasources) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_datasources.clear(); + result_datasources.reserve(end - begin); + + // If there was no datasource info, return an array of 0's. + if (m_datasource_list.empty()) + { + for (unsigned i = 0; i < end - begin; ++i) + { + result_datasources.push_back(0); + } + } + else + { + std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end, + [&](const uint8_t &datasource_id) + { + result_datasources.push_back(datasource_id); + }); + } + } + + virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final + { + BOOST_ASSERT(m_datasource_names.size() >= 1); + BOOST_ASSERT(m_datasource_names.size() > datasource_name_id); + return m_datasource_names[datasource_name_id]; + } + + std::string GetTimestamp() const override final { return m_timestamp; } + + bool GetContinueStraightDefault() const override final { return m_profile_properties.continue_straight_at_waypoint; } +}; +} +} +} + +#endif // INTERNAL_DATAFACADE_HPP diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp new file mode 100644 index 000000000..bd70e835e --- /dev/null +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -0,0 +1,718 @@ +#ifndef SHARED_DATAFACADE_HPP +#define SHARED_DATAFACADE_HPP + +// implements all data storage when shared memory _IS_ used + +#include "engine/datafacade/datafacade_base.hpp" +#include "storage/shared_datatype.hpp" +#include "storage/shared_memory.hpp" + +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/profile_properties.hpp" + +#include "engine/geospatial_query.hpp" +#include "util/range_table.hpp" +#include "util/static_graph.hpp" +#include "util/static_rtree.hpp" +#include "util/make_unique.hpp" +#include "util/simple_logger.hpp" +#include "util/rectangle.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace datafacade +{ + +class SharedDataFacade final : public BaseDataFacade +{ + + private: + using super = BaseDataFacade; + using QueryGraph = util::StaticGraph; + using GraphNode = typename QueryGraph::NodeArrayEntry; + using GraphEdge = typename QueryGraph::EdgeArrayEntry; + using NameIndexBlock = typename util::RangeTable<16, true>::BlockT; + using InputEdge = typename QueryGraph::InputEdge; + using RTreeLeaf = typename super::RTreeLeaf; + using SharedRTree = + util::StaticRTree::vector, true>; + using SharedGeospatialQuery = GeospatialQuery; + using TimeStampedRTreePair = std::pair>; + using RTreeNode = typename SharedRTree::TreeNode; + + storage::SharedDataLayout *data_layout; + char *shared_memory; + storage::SharedDataTimestamp *data_timestamp_ptr; + + storage::SharedDataType CURRENT_LAYOUT; + storage::SharedDataType CURRENT_DATA; + unsigned CURRENT_TIMESTAMP; + + unsigned m_check_sum; + std::unique_ptr m_query_graph; + std::unique_ptr m_layout_memory; + std::unique_ptr m_large_memory; + std::string m_timestamp; + extractor::ProfileProperties* m_profile_properties; + + std::shared_ptr::vector> m_coordinate_list; + util::ShM::vector m_via_node_list; + util::ShM::vector m_name_ID_list; + util::ShM::vector m_turn_instruction_list; + util::ShM::vector m_travel_mode_list; + util::ShM::vector m_names_char_list; + util::ShM::vector m_name_begin_indices; + util::ShM::vector m_geometry_indices; + util::ShM::vector m_geometry_list; + util::ShM::vector m_is_core_node; + util::ShM::vector m_datasource_list; + + util::ShM::vector m_datasource_name_data; + util::ShM::vector m_datasource_name_offsets; + util::ShM::vector m_datasource_name_lengths; + + boost::thread_specific_ptr>> m_static_rtree; + boost::thread_specific_ptr m_geospatial_query; + boost::filesystem::path file_index_path; + + std::shared_ptr> m_name_table; + + void LoadChecksum() + { + m_check_sum = *data_layout->GetBlockPtr(shared_memory, + storage::SharedDataLayout::HSGR_CHECKSUM); + util::SimpleLogger().Write() << "set checksum: " << m_check_sum; + } + + void LoadProfileProperties() + { + m_profile_properties = + data_layout->GetBlockPtr(shared_memory, storage::SharedDataLayout::PROPERTIES); + } + + void LoadTimestamp() + { + auto timestamp_ptr = + data_layout->GetBlockPtr(shared_memory, storage::SharedDataLayout::TIMESTAMP); + m_timestamp.resize(data_layout->GetBlockSize(storage::SharedDataLayout::TIMESTAMP)); + std::copy(timestamp_ptr, + timestamp_ptr + data_layout->GetBlockSize(storage::SharedDataLayout::TIMESTAMP), + m_timestamp.begin()); + } + + void LoadRTree() + { + BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree"); + + auto tree_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::R_SEARCH_TREE); + m_static_rtree.reset(new TimeStampedRTreePair( + CURRENT_TIMESTAMP, + util::make_unique( + tree_ptr, data_layout->num_entries[storage::SharedDataLayout::R_SEARCH_TREE], + file_index_path, m_coordinate_list))); + m_geospatial_query.reset( + new SharedGeospatialQuery(*m_static_rtree->second, m_coordinate_list, *this)); + } + + void LoadGraph() + { + auto graph_nodes_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::GRAPH_NODE_LIST); + + auto graph_edges_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::GRAPH_EDGE_LIST); + + typename util::ShM::vector node_list( + graph_nodes_ptr, data_layout->num_entries[storage::SharedDataLayout::GRAPH_NODE_LIST]); + typename util::ShM::vector edge_list( + graph_edges_ptr, data_layout->num_entries[storage::SharedDataLayout::GRAPH_EDGE_LIST]); + m_query_graph.reset(new QueryGraph(node_list, edge_list)); + } + + void LoadNodeAndEdgeInformation() + { + auto coordinate_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::COORDINATE_LIST); + m_coordinate_list = util::make_unique::vector>( + coordinate_list_ptr, + data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]); + + auto travel_mode_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::TRAVEL_MODE); + typename util::ShM::vector travel_mode_list( + travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]); + m_travel_mode_list = std::move(travel_mode_list); + + auto turn_instruction_list_ptr = + data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION); + typename util::ShM::vector + turn_instruction_list( + turn_instruction_list_ptr, + data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]); + m_turn_instruction_list = std::move(turn_instruction_list); + + auto name_id_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::NAME_ID_LIST); + typename util::ShM::vector name_id_list( + name_id_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_ID_LIST]); + m_name_ID_list = std::move(name_id_list); + } + + void LoadViaNodeList() + { + auto via_node_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::VIA_NODE_LIST); + typename util::ShM::vector via_node_list( + via_node_list_ptr, data_layout->num_entries[storage::SharedDataLayout::VIA_NODE_LIST]); + m_via_node_list = std::move(via_node_list); + } + + void LoadNames() + { + auto offsets_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::NAME_OFFSETS); + auto blocks_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::NAME_BLOCKS); + typename util::ShM::vector name_offsets( + offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]); + typename util::ShM::vector name_blocks( + blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]); + + auto names_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::NAME_CHAR_LIST); + typename util::ShM::vector names_char_list( + names_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_CHAR_LIST]); + m_name_table = util::make_unique>( + name_offsets, name_blocks, static_cast(names_char_list.size())); + + m_names_char_list = std::move(names_char_list); + } + + void LoadCoreInformation() + { + if (data_layout->num_entries[storage::SharedDataLayout::CORE_MARKER] <= 0) + { + return; + } + + auto core_marker_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::CORE_MARKER); + typename util::ShM::vector is_core_node( + core_marker_ptr, data_layout->num_entries[storage::SharedDataLayout::CORE_MARKER]); + m_is_core_node = std::move(is_core_node); + } + + void LoadGeometries() + { + auto geometries_index_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::GEOMETRIES_INDEX); + typename util::ShM::vector geometry_begin_indices( + geometries_index_ptr, + data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_INDEX]); + m_geometry_indices = std::move(geometry_begin_indices); + + auto geometries_list_ptr = + data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST); + typename util::ShM::vector + geometry_list(geometries_list_ptr, + data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]); + m_geometry_list = std::move(geometry_list); + + auto datasources_list_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::DATASOURCES_LIST); + typename util::ShM::vector datasources_list( + datasources_list_ptr, + data_layout->num_entries[storage::SharedDataLayout::DATASOURCES_LIST]); + m_datasource_list = std::move(datasources_list); + + auto datasource_name_data_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_DATA); + typename util::ShM::vector datasource_name_data( + datasource_name_data_ptr, + data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_DATA]); + m_datasource_name_data = std::move(datasource_name_data); + + auto datasource_name_offsets_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS); + typename util::ShM::vector datasource_name_offsets( + datasource_name_offsets_ptr, + data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS]); + m_datasource_name_offsets = std::move(datasource_name_offsets); + + auto datasource_name_lengths_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS); + typename util::ShM::vector datasource_name_lengths( + datasource_name_lengths_ptr, + data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS]); + m_datasource_name_lengths = std::move(datasource_name_lengths); + } + + public: + virtual ~SharedDataFacade() {} + + boost::shared_mutex data_mutex; + + SharedDataFacade() + { + if (!storage::SharedMemory::RegionExists(storage::CURRENT_REGIONS)) + { + throw util::exception( + "No shared memory blocks found, have you forgotten to run osrm-datastore?"); + } + data_timestamp_ptr = static_cast( + storage::makeSharedMemory(storage::CURRENT_REGIONS, + sizeof(storage::SharedDataTimestamp), false, false) + ->Ptr()); + CURRENT_LAYOUT = storage::LAYOUT_NONE; + CURRENT_DATA = storage::DATA_NONE; + CURRENT_TIMESTAMP = 0; + + // load data + CheckAndReloadFacade(); + } + + void CheckAndReloadFacade() + { + if (CURRENT_LAYOUT != data_timestamp_ptr->layout || + CURRENT_DATA != data_timestamp_ptr->data || + CURRENT_TIMESTAMP != data_timestamp_ptr->timestamp) + { + // Get exclusive lock + util::SimpleLogger().Write(logDEBUG) << "Updates available, getting exclusive lock"; + const boost::lock_guard lock(data_mutex); + + if (CURRENT_LAYOUT != data_timestamp_ptr->layout || + CURRENT_DATA != data_timestamp_ptr->data) + { + // release the previous shared memory segments + storage::SharedMemory::Remove(CURRENT_LAYOUT); + storage::SharedMemory::Remove(CURRENT_DATA); + + CURRENT_LAYOUT = data_timestamp_ptr->layout; + CURRENT_DATA = data_timestamp_ptr->data; + CURRENT_TIMESTAMP = 0; // Force trigger a reload + + util::SimpleLogger().Write(logDEBUG) + << "Current layout was different to new layout, swapping"; + } + else + { + util::SimpleLogger().Write(logDEBUG) + << "Current layout was same to new layout, not swapping"; + } + + if (CURRENT_TIMESTAMP != data_timestamp_ptr->timestamp) + { + CURRENT_TIMESTAMP = data_timestamp_ptr->timestamp; + + util::SimpleLogger().Write(logDEBUG) << "Performing data reload"; + m_layout_memory.reset(storage::makeSharedMemory(CURRENT_LAYOUT)); + + data_layout = static_cast(m_layout_memory->Ptr()); + + m_large_memory.reset(storage::makeSharedMemory(CURRENT_DATA)); + shared_memory = (char *)(m_large_memory->Ptr()); + + const auto file_index_ptr = data_layout->GetBlockPtr( + shared_memory, storage::SharedDataLayout::FILE_INDEX_PATH); + file_index_path = boost::filesystem::path(file_index_ptr); + if (!boost::filesystem::exists(file_index_path)) + { + util::SimpleLogger().Write(logDEBUG) << "Leaf file name " + << file_index_path.string(); + throw util::exception("Could not load leaf index file. " + "Is any data loaded into shared memory?"); + } + + LoadGraph(); + LoadChecksum(); + LoadNodeAndEdgeInformation(); + LoadGeometries(); + LoadTimestamp(); + LoadViaNodeList(); + LoadNames(); + LoadCoreInformation(); + LoadProfileProperties(); + + util::SimpleLogger().Write() << "number of geometries: " + << m_coordinate_list->size(); + for (unsigned i = 0; i < m_coordinate_list->size(); ++i) + { + if (!GetCoordinateOfNode(i).IsValid()) + { + util::SimpleLogger().Write() << "coordinate " << i << " not valid"; + } + } + } + util::SimpleLogger().Write(logDEBUG) << "Releasing exclusive lock"; + } + } + + // search graph access + unsigned GetNumberOfNodes() const override final { return m_query_graph->GetNumberOfNodes(); } + + unsigned GetNumberOfEdges() const override final { return m_query_graph->GetNumberOfEdges(); } + + unsigned GetOutDegree(const NodeID n) const override final + { + return m_query_graph->GetOutDegree(n); + } + + NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } + + EdgeData &GetEdgeData(const EdgeID e) const override final + { + return m_query_graph->GetEdgeData(e); + } + + EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph->BeginEdges(n); } + + EdgeID EndEdges(const NodeID n) const override final { return m_query_graph->EndEdges(n); } + + EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final + { + return m_query_graph->GetAdjacentEdgeRange(node); + } + + // searches for a specific edge + EdgeID FindEdge(const NodeID from, const NodeID to) const override final + { + return m_query_graph->FindEdge(from, to); + } + + EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const override final + { + return m_query_graph->FindEdgeInEitherDirection(from, to); + } + + EdgeID + FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const override final + { + return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); + } + + // node and edge information access + util::Coordinate GetCoordinateOfNode(const NodeID id) const override final + { + return m_coordinate_list->at(id); + } + + virtual void GetUncompressedGeometry(const EdgeID id, + std::vector &result_nodes) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_nodes.clear(); + result_nodes.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) + { + result_nodes.emplace_back(edge.node_id); + }); + } + + virtual void + GetUncompressedWeights(const EdgeID id, + std::vector &result_weights) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_weights.clear(); + result_weights.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) + { + result_weights.emplace_back(edge.weight); + }); + } + + virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final + { + return m_via_node_list.at(id); + } + + extractor::guidance::TurnInstruction + GetTurnInstructionForEdgeID(const unsigned id) const override final + { + return m_turn_instruction_list.at(id); + } + + extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const override final + { + return m_travel_mode_list.at(id); + } + + std::vector GetEdgesInBox(const util::Coordinate south_west, + const util::Coordinate north_east) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat, + north_east.lat}; + return m_geospatial_query->Search(bbox); + } + + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance); + } + + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const float max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance, + bearing, bearing_range); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing, + bearing_range); + } + + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance, + bearing, bearing_range); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::Coordinate input_coordinate) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance, bearing, bearing_range); + } + + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, bearing, bearing_range); + } + + unsigned GetCheckSum() const override final { return m_check_sum; } + + unsigned GetNameIndexFromEdgeID(const unsigned id) const override final + { + return m_name_ID_list.at(id); + } + + std::string GetNameForID(const unsigned name_id) const override final + { + if (std::numeric_limits::max() == name_id) + { + return ""; + } + auto range = m_name_table->GetRange(name_id); + + std::string result; + result.reserve(range.size()); + if (range.begin() != range.end()) + { + result.resize(range.back() - range.front() + 1); + std::copy(m_names_char_list.begin() + range.front(), + m_names_char_list.begin() + range.back() + 1, result.begin()); + } + return result; + } + + bool IsCoreNode(const NodeID id) const override final + { + if (m_is_core_node.size() > 0) + { + return m_is_core_node.at(id); + } + + return false; + } + + virtual std::size_t GetCoreSize() const override final { return m_is_core_node.size(); } + + // Returns the data source ids that were used to supply the edge + // weights. + virtual void + GetUncompressedDatasources(const EdgeID id, + std::vector &result_datasources) const override final + { + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1); + + result_datasources.clear(); + result_datasources.reserve(end - begin); + + // If there was no datasource info, return an array of 0's. + if (m_datasource_list.empty()) + { + for (unsigned i = 0; i < end - begin; ++i) + { + result_datasources.push_back(0); + } + } + else + { + std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end, + [&](const uint8_t &datasource_id) + { + result_datasources.push_back(datasource_id); + }); + } + } + + virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final + { + BOOST_ASSERT(m_datasource_name_offsets.size() >= 1); + BOOST_ASSERT(m_datasource_name_offsets.size() > datasource_name_id); + + std::string result; + result.reserve(m_datasource_name_lengths[datasource_name_id]); + std::copy(m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id], + m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id] + + m_datasource_name_lengths[datasource_name_id], + std::back_inserter(result)); + + return result; + } + + std::string GetTimestamp() const override final { return m_timestamp; } + + bool GetContinueStraightDefault() const override final { return m_profile_properties->continue_straight_at_waypoint; } +}; +} +} +} + +#endif // SHARED_DATAFACADE_HPP diff --git a/include/engine/douglas_peucker.hpp b/include/engine/douglas_peucker.hpp new file mode 100644 index 000000000..e95d0cc16 --- /dev/null +++ b/include/engine/douglas_peucker.hpp @@ -0,0 +1,81 @@ +#ifndef DOUGLAS_PEUCKER_HPP_ +#define DOUGLAS_PEUCKER_HPP_ + +#include "util/coordinate.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace detail +{ + +// This is derived from the following formular: +// x = b * (1 + lon/180) => dx = b * dlon/180 +// y = b * (1 - lat/180) => dy = b * dlat/180 +// dx^2 + dy^2 < min_pixel^2 +// => dlon^2 + dlat^2 < min_pixel^2 / b^2 * 180^2 +inline std::vector generateThreshold(double min_pixel, unsigned number_of_zoomlevels) +{ + std::vector thresholds(number_of_zoomlevels); + for (unsigned zoom = 0; zoom < number_of_zoomlevels; ++zoom) + { + const double shift = (1u << zoom) * 256; + const double b = shift / 2.0; + const double pixel_to_deg = 180. / b; + const std::uint64_t min_deg = min_pixel * pixel_to_deg * COORDINATE_PRECISION; + thresholds[zoom] = min_deg * min_deg; + } + + return thresholds; +} + +const constexpr std::uint64_t DOUGLAS_PEUCKER_THRESHOLDS[19] = { + 49438476562500, // z0 + 12359619140625, // z1 + 3089903027344, // z2 + 772475756836, // z3 + 193118939209, // z4 + 48279515076, // z5 + 12069878769, // z6 + 3017414761, // z7 + 754326225, // z8 + 188567824, // z9 + 47141956, // z10 + 11785489, // z11 + 2944656, // z12 + 736164, // z13 + 184041, // z14 + 45796, // z15 + 11449, // z16 + 2809, // z17 + 676, // z18 +}; + +const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE = + sizeof(DOUGLAS_PEUCKER_THRESHOLDS) / sizeof(*DOUGLAS_PEUCKER_THRESHOLDS); +} // ns detail + +// These functions compute the bitvector of indicating generalized input +// points according to the (Ramer-)Douglas-Peucker algorithm. +// +// Input is vector of pairs. Each pair consists of the point information and a +// bit indicating if the points is present in the generalization. +// Note: points may also be pre-selected*/ +std::vector douglasPeucker(std::vector::const_iterator begin, + std::vector::const_iterator end, + const unsigned zoom_level); + +// Convenience range-based function +inline std::vector douglasPeucker(const std::vector &geometry, + const unsigned zoom_level) +{ + return douglasPeucker(begin(geometry), end(geometry), zoom_level); +} +} +} + +#endif /* DOUGLAS_PEUCKER_HPP_ */ diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp new file mode 100644 index 000000000..027320ba3 --- /dev/null +++ b/include/engine/engine.hpp @@ -0,0 +1,88 @@ +#ifndef ENGINE_HPP +#define ENGINE_HPP + +#include "engine/status.hpp" +#include "storage/shared_barriers.hpp" +#include "util/json_container.hpp" + +#include +#include +#include + +namespace osrm +{ + +namespace util +{ +namespace json +{ +struct Object; +} +} + +// Fwd decls +namespace engine +{ +struct EngineConfig; +namespace api +{ +struct RouteParameters; +struct TableParameters; +struct NearestParameters; +struct TripParameters; +struct MatchParameters; +struct TileParameters; +} +namespace plugins +{ +class ViaRoutePlugin; +class TablePlugin; +class NearestPlugin; +class TripPlugin; +class MatchPlugin; +class TilePlugin; +} +// End fwd decls + +namespace datafacade +{ +class BaseDataFacade; +} + +class Engine final +{ + public: + // Needs to be public + struct EngineLock; + + explicit Engine(EngineConfig &config); + + Engine(Engine &&) noexcept; + Engine &operator=(Engine &&) noexcept; + + // Impl. in cpp since for unique_ptr of incomplete types + ~Engine(); + + Status Route(const api::RouteParameters ¶meters, util::json::Object &result); + Status Table(const api::TableParameters ¶meters, util::json::Object &result); + Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result); + Status Trip(const api::TripParameters ¶meters, util::json::Object &result); + Status Match(const api::MatchParameters ¶meters, util::json::Object &result); + Status Tile(const api::TileParameters ¶meters, std::string &result); + + private: + std::unique_ptr lock; + + std::unique_ptr route_plugin; + std::unique_ptr table_plugin; + std::unique_ptr nearest_plugin; + std::unique_ptr trip_plugin; + std::unique_ptr match_plugin; + std::unique_ptr tile_plugin; + + std::unique_ptr query_data_facade; +}; +} +} + +#endif // OSRM_IMPL_HPP diff --git a/include/osrm/libosrm_config.hpp b/include/engine/engine_config.hpp similarity index 68% rename from include/osrm/libosrm_config.hpp rename to include/engine/engine_config.hpp index 677450a96..eb627198c 100644 --- a/include/osrm/libosrm_config.hpp +++ b/include/engine/engine_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,22 +25,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef LIBOSRM_CONFIG_HPP -#define LIBOSRM_CONFIG_HPP +#ifndef ENGINE_CONFIG_HPP +#define ENGINE_CONFIG_HPP + +#include "storage/storage_config.hpp" #include -#include #include -struct LibOSRMConfig +namespace osrm { - std::unordered_map server_paths; + +namespace engine +{ + +/** + * Configures an OSRM instance. + * + * You can customize the storage OSRM uses for auxiliary files specifying a storage config. + * + * You can further set service constraints. + * These are the maximum number of allowed locations (-1 for unlimited) for the services: + * - Trip + * - Route + * - Table + * - Match + * + * In addition, shared memory can be used for datasets loaded with osrm-datastore. + * + * \see OSRM, StorageConfig + */ +struct EngineConfig final +{ + bool IsValid() const; + + storage::StorageConfig storage_config; int max_locations_trip = -1; int max_locations_viaroute = -1; int max_locations_distance_table = -1; int max_locations_map_matching = -1; bool use_shared_memory = true; }; +} +} #endif // SERVER_CONFIG_HPP diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp new file mode 100644 index 000000000..d2e90b887 --- /dev/null +++ b/include/engine/geospatial_query.hpp @@ -0,0 +1,469 @@ +#ifndef GEOSPATIAL_QUERY_HPP +#define GEOSPATIAL_QUERY_HPP + +#include "util/coordinate_calculation.hpp" +#include "util/typedefs.hpp" +#include "engine/phantom_node.hpp" +#include "util/bearing.hpp" +#include "util/rectangle.hpp" +#include "util/web_mercator.hpp" + +#include "osrm/coordinate.hpp" + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +// Implements complex queries on top of an RTree and builds PhantomNodes from it. +// +// Only holds a weak reference on the RTree! +template class GeospatialQuery +{ + using EdgeData = typename RTreeT::EdgeData; + using CoordinateList = typename RTreeT::CoordinateList; + using CandidateSegment = typename RTreeT::CandidateSegment; + + public: + GeospatialQuery(RTreeT &rtree_, + std::shared_ptr coordinates_, + DataFacadeT &datafacade_) + : rtree(rtree_), coordinates(std::move(coordinates_)), datafacade(datafacade_) + { + } + + std::vector Search(const util::RectangleInt2D &bbox) + { + return rtree.SearchInBox(bbox); + } + + // Returns nearest PhantomNodes in the given bearing range within max_distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, const double max_distance) + { + auto results = + rtree.Nearest(input_coordinate, + [](const CandidateSegment &) + { + return std::make_pair(true, true); + }, + [this, max_distance, input_coordinate](const std::size_t, + const CandidateSegment &segment) + { + return checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns nearest PhantomNodes in the given bearing range within max_distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodesInRange(const util::Coordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) + { + auto results = rtree.Nearest( + input_coordinate, + [this, bearing, bearing_range, max_distance](const CandidateSegment &segment) + { + return checkSegmentBearing(segment, bearing, bearing_range); + }, + [this, max_distance, input_coordinate](const std::size_t, + const CandidateSegment &segment) + { + return checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes in the given bearing range. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const int bearing, + const int bearing_range) + { + auto results = + rtree.Nearest(input_coordinate, + [this, bearing, bearing_range](const CandidateSegment &segment) + { + return checkSegmentBearing(segment, bearing, bearing_range); + }, + [max_results](const std::size_t num_results, const CandidateSegment &) + { + return num_results >= max_results; + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes in the given bearing range within the maximum + // distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance, + const int bearing, + const int bearing_range) + { + auto results = + rtree.Nearest(input_coordinate, + [this, bearing, bearing_range](const CandidateSegment &segment) + { + return checkSegmentBearing(segment, bearing, bearing_range); + }, + [this, max_distance, max_results, input_coordinate]( + const std::size_t num_results, const CandidateSegment &segment) + { + return num_results >= max_results || + checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) + { + auto results = + rtree.Nearest(input_coordinate, + [](const CandidateSegment &) + { + return std::make_pair(true, true); + }, + [max_results](const std::size_t num_results, const CandidateSegment &) + { + return num_results >= max_results; + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns max_results nearest PhantomNodes in the given max distance. + // Does not filter by small/big component! + std::vector + NearestPhantomNodes(const util::Coordinate input_coordinate, + const unsigned max_results, + const double max_distance) + { + auto results = + rtree.Nearest(input_coordinate, + [](const CandidateSegment &) + { + return std::make_pair(true, true); + }, + [this, max_distance, max_results, input_coordinate]( + const std::size_t num_results, const CandidateSegment &segment) + { + return num_results >= max_results || + checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + return MakePhantomNodes(input_coordinate, results); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [&has_big_component, &has_small_component](const CandidateSegment &segment) + { + auto use_segment = (!has_small_component || + (!has_big_component && !segment.data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + has_big_component = has_big_component || !segment.data.component.is_tiny; + has_small_component = has_small_component || segment.data.component.is_tiny; + + return use_directions; + }, + [this, &has_big_component, max_distance, + input_coordinate](const std::size_t num_results, const CandidateSegment &segment) + { + return (num_results > 0 && has_big_component) || + checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() == 1 || results.size() == 2); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [&has_big_component, &has_small_component](const CandidateSegment &segment) + { + auto use_segment = (!has_small_component || + (!has_big_component && !segment.data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + has_big_component = has_big_component || !segment.data.component.is_tiny; + has_small_component = has_small_component || segment.data.component.is_tiny; + + return use_directions; + }, + [&has_big_component](const std::size_t num_results, const CandidateSegment &) + { + return num_results > 0 && has_big_component; + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() == 1 || results.size() == 2); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::Coordinate input_coordinate, const int bearing, const int bearing_range) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [this, bearing, bearing_range, &has_big_component, + &has_small_component](const CandidateSegment &segment) + { + auto use_segment = (!has_small_component || + (!has_big_component && !segment.data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + if (use_segment) + { + use_directions = checkSegmentBearing(segment, bearing, bearing_range); + if (use_directions.first || use_directions.second) + { + has_big_component = has_big_component || !segment.data.component.is_tiny; + has_small_component = has_small_component || segment.data.component.is_tiny; + } + } + + return use_directions; + }, + [&has_big_component](const std::size_t num_results, const CandidateSegment &) + { + return num_results > 0 && has_big_component; + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() > 0); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair + NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [this, bearing, bearing_range, &has_big_component, + &has_small_component](const CandidateSegment &segment) + { + auto use_segment = (!has_small_component || + (!has_big_component && !segment.data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + if (use_segment) + { + use_directions = checkSegmentBearing(segment, bearing, bearing_range); + if (use_directions.first || use_directions.second) + { + has_big_component = has_big_component || !segment.data.component.is_tiny; + has_small_component = has_small_component || segment.data.component.is_tiny; + } + } + + return use_directions; + }, + [this, &has_big_component, max_distance, + input_coordinate](const std::size_t num_results, const CandidateSegment &segment) + { + return (num_results > 0 && has_big_component) || + checkSegmentDistance(input_coordinate, segment, max_distance); + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() > 0); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + private: + std::vector + MakePhantomNodes(const util::Coordinate input_coordinate, + const std::vector &results) const + { + std::vector distance_and_phantoms(results.size()); + std::transform(results.begin(), results.end(), distance_and_phantoms.begin(), + [this, &input_coordinate](const EdgeData &data) + { + return MakePhantomNode(input_coordinate, data); + }); + return distance_and_phantoms; + } + + PhantomNodeWithDistance MakePhantomNode(const util::Coordinate input_coordinate, + const EdgeData &data) const + { + util::Coordinate point_on_segment; + double ratio; + const auto current_perpendicular_distance = + util::coordinate_calculation::perpendicularDistance( + coordinates->at(data.u), coordinates->at(data.v), input_coordinate, + point_on_segment, ratio); + + // Find the node-based-edge that this belongs to, and directly + // calculate the forward_weight, forward_offset, reverse_weight, reverse_offset + + int forward_offset = 0, forward_weight = 0; + int reverse_offset = 0, reverse_weight = 0; + + if (data.forward_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector forward_weight_vector; + datafacade.GetUncompressedWeights(data.forward_packed_geometry_id, + forward_weight_vector); + for (std::size_t i = 0; i < data.fwd_segment_position; i++) + { + forward_offset += forward_weight_vector[i]; + } + forward_weight = forward_weight_vector[data.fwd_segment_position]; + } + + if (data.reverse_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector reverse_weight_vector; + datafacade.GetUncompressedWeights(data.reverse_packed_geometry_id, + reverse_weight_vector); + + BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size()); + + for (std::size_t i = 0; + i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++) + { + reverse_offset += reverse_weight_vector[i]; + } + reverse_weight = + reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1]; + } + + ratio = std::min(1.0, std::max(0.0, ratio)); + if (data.forward_segment_id.id != SPECIAL_SEGMENTID) + { + forward_weight *= ratio; + } + if (data.reverse_segment_id.id != SPECIAL_SEGMENTID) + { + reverse_weight *= 1.0 - ratio; + } + + auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, forward_offset, + reverse_weight, reverse_offset, + point_on_segment, input_coordinate}, + current_perpendicular_distance}; + + return transformed; + } + + bool checkSegmentDistance(const Coordinate input_coordinate, + const CandidateSegment &segment, + const double max_distance) + { + BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID || + !segment.data.forward_segment_id.enabled); + BOOST_ASSERT(segment.data.reverse_segment_id.id != SPECIAL_SEGMENTID || + !segment.data.reverse_segment_id.enabled); + + Coordinate wsg84_coordinate = + util::web_mercator::toWGS84(segment.fixed_projected_coordinate); + + return util::coordinate_calculation::haversineDistance(input_coordinate, wsg84_coordinate) > + max_distance; + } + + std::pair checkSegmentBearing(const CandidateSegment &segment, + const int filter_bearing, + const int filter_bearing_range) + { + BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID || + !segment.data.forward_segment_id.enabled); + BOOST_ASSERT(segment.data.reverse_segment_id.id != SPECIAL_SEGMENTID || + !segment.data.reverse_segment_id.enabled); + + const double forward_edge_bearing = util::coordinate_calculation::bearing( + coordinates->at(segment.data.u), coordinates->at(segment.data.v)); + + const double backward_edge_bearing = (forward_edge_bearing + 180) > 360 + ? (forward_edge_bearing - 180) + : (forward_edge_bearing + 180); + + const bool forward_bearing_valid = + util::bearing::CheckInBounds(std::round(forward_edge_bearing), filter_bearing, + filter_bearing_range) && + segment.data.forward_segment_id.enabled; + const bool backward_bearing_valid = + util::bearing::CheckInBounds(std::round(backward_edge_bearing), filter_bearing, + filter_bearing_range) && + segment.data.reverse_segment_id.enabled; + return std::make_pair(forward_bearing_valid, backward_bearing_valid); + } + + RTreeT &rtree; + const std::shared_ptr coordinates; + DataFacadeT &datafacade; +}; +} +} + +#endif diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp new file mode 100644 index 000000000..4fd84cbdd --- /dev/null +++ b/include/engine/guidance/assemble_geometry.hpp @@ -0,0 +1,81 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP + +#include "engine/internal_route_result.hpp" +#include "engine/phantom_node.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "engine/guidance/toolkit.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/coordinate.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/travel_mode.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// Extracts the geometry for each segment and calculates the traveled distance +// Combines the geometry form the phantom node with the PathData +// to the full route geometry. +// +// turn 0 1 2 3 4 +// s...x...y...z...t +// |---|segment 0 +// |---| segment 1 +// |---| segment 2 +// |---| segment 3 +template +LegGeometry assembleGeometry(const DataFacadeT &facade, + const std::vector &leg_data, + const PhantomNode &source_node, + const PhantomNode &target_node) +{ + LegGeometry geometry; + + // segment 0 first and last + geometry.segment_offsets.push_back(0); + geometry.locations.push_back(source_node.location); + + auto current_distance = 0.; + auto prev_coordinate = geometry.locations.front(); + for (const auto &path_point : leg_data) + { + auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node); + current_distance += + util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate); + + // all changes to this check have to be matched with assemble_steps + if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) + { + geometry.segment_distances.push_back(current_distance); + geometry.segment_offsets.push_back(geometry.locations.size()); + current_distance = 0.; + } + + prev_coordinate = coordinate; + geometry.locations.push_back(std::move(coordinate)); + } + current_distance += + util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location); + // segment leading to the target node + geometry.segment_distances.push_back(current_distance); + geometry.segment_offsets.push_back(geometry.locations.size()); + geometry.locations.push_back(target_node.location); + + BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1); + BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size()); + + return geometry; +} +} +} +} + +#endif diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp new file mode 100644 index 000000000..6f8e6cba0 --- /dev/null +++ b/include/engine/guidance/assemble_leg.hpp @@ -0,0 +1,174 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_ +#define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_ + +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/internal_route_result.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace detail +{ +const constexpr std::size_t MAX_USED_SEGMENTS = 2; +struct NamedSegment +{ + EdgeWeight duration; + std::uint32_t position; + std::uint32_t name_id; +}; + +template +std::array summarizeRoute(const std::vector &route_data) +{ + // merges segments with same name id + const auto collapse_segments = [](std::vector &segments) + { + auto out = segments.begin(); + auto end = segments.end(); + for (auto in = segments.begin(); in != end; ++in) + { + if (in->name_id == out->name_id) + { + out->duration += in->duration; + } + else + { + ++out; + BOOST_ASSERT(out != end); + *out = *in; + } + } + return out; + }; + + std::vector segments(route_data.size()); + std::uint32_t index = 0; + std::transform( + route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point) + { + return NamedSegment{point.duration_until_turn, index++, point.name_id}; + }); + // this makes sure that the segment with the lowest position comes first + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.name_id < rhs.name_id || + (lhs.name_id == rhs.name_id && lhs.position < rhs.position); + }); + auto new_end = collapse_segments(segments); + segments.resize(new_end - segments.begin()); + // sort descending + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.duration > rhs.duration; + }); + + // make sure the segments are sorted by position + segments.resize(std::min(segments.size(), SegmentNumber)); + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.position < rhs.position; + }); + + std::array summary; + std::fill(summary.begin(), summary.end(), 0); + std::transform(segments.begin(), segments.end(), summary.begin(), + [](const NamedSegment &segment) + { + return segment.name_id; + }); + return summary; +} +} + +inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, + const std::vector &route_data, + const LegGeometry &leg_geometry, + const PhantomNode &source_node, + const PhantomNode &target_node, + const bool target_traversed_in_reverse, + const bool needs_summary) +{ + const auto target_duration = + (target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight) / + 10.; + + auto distance = std::accumulate(leg_geometry.segment_distances.begin(), + leg_geometry.segment_distances.end(), 0.); + auto duration = std::accumulate(route_data.begin(), route_data.end(), 0., + [](const double sum, const PathData &data) { + return sum + data.duration_until_turn; + }) / + 10.; + + // s + // | + // Given a route a---b---c where there is a right turn at c. + // | + // d + // |--t + // e + // (a, b, c) gets compressed to (a,c) + // (c, d, e) gets compressed to (c,e) + // The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration + // of (a,b,c)). + // The phantom node of s will contain: + // `forward_weight`: duration of (a,s) + // `forward_offset`: 0 (its the first segment) + // The phantom node of t will contain: + // `forward_weight`: duration of (d,t) + // `forward_offset`: duration of (c, d) + // path_data will have entries for (s,b), (b, c), (c, d) but (d, t) is only + // caputed by the phantom node. So we need to add the target duration here. + // On local segments, the target duration is already part of the duration, however. + + duration = duration + target_duration; + if (route_data.empty()) + { + duration -= (target_traversed_in_reverse ? source_node.reverse_weight + : source_node.forward_weight) / + 10.0; + } + + std::string summary; + if (needs_summary) + { + auto summary_array = detail::summarizeRoute(route_data); + + BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0); + BOOST_ASSERT(summary_array.begin() != summary_array.end()); + summary = std::accumulate(std::next(summary_array.begin()), summary_array.end(), + facade.GetNameForID(summary_array.front()), + [&facade](std::string previous, const std::uint32_t name_id) + { + if (name_id != 0) + { + previous += ", " + facade.GetNameForID(name_id); + } + return previous; + }); + } + + return RouteLeg{duration, distance, summary, {}}; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ diff --git a/include/engine/guidance/assemble_overview.hpp b/include/engine/guidance/assemble_overview.hpp new file mode 100644 index 000000000..f801d6c22 --- /dev/null +++ b/include/engine/guidance/assemble_overview.hpp @@ -0,0 +1,24 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP + +#include "engine/guidance/leg_geometry.hpp" + +#include "util/coordinate.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +std::vector assembleOverview(const std::vector &leg_geometries, + const bool use_simplification); + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/include/engine/guidance/assemble_route.hpp b/include/engine/guidance/assemble_route.hpp new file mode 100644 index 000000000..ff1c905cf --- /dev/null +++ b/include/engine/guidance/assemble_route.hpp @@ -0,0 +1,22 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP + +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +Route assembleRoute(const std::vector &route_legs); + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp new file mode 100644 index 000000000..a32811a41 --- /dev/null +++ b/include/engine/guidance/assemble_steps.hpp @@ -0,0 +1,145 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_ +#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_ + +#include "engine/guidance/leg_geometry.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/step_maneuver.hpp" +#include "engine/guidance/toolkit.hpp" +#include "engine/internal_route_result.hpp" +#include "engine/phantom_node.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/travel_mode.hpp" +#include "util/bearing.hpp" +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace detail +{ +StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction, + const LegGeometry &leg_geometry, + const std::size_t segment_index); + +StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction, + const WaypointType waypoint_type, + const LegGeometry &leg_geometry); + +} // ns detail + +template +std::vector assembleSteps(const DataFacadeT &facade, + const std::vector &leg_data, + const LegGeometry &leg_geometry, + const PhantomNode &source_node, + const PhantomNode &target_node, + const bool source_traversed_in_reverse, + const bool target_traversed_in_reverse) +{ + const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.; + const constexpr char *NO_ROTARY_NAME = ""; + const EdgeWeight source_duration = + source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight; + const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode + : source_node.forward_travel_mode; + + const EdgeWeight target_duration = + target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight; + const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode + : target_node.forward_travel_mode; + + const auto number_of_segments = leg_geometry.GetNumberOfSegments(); + + std::vector steps; + steps.reserve(number_of_segments); + + std::size_t segment_index = 0; + BOOST_ASSERT(leg_geometry.locations.size() >= 2); + + if (leg_data.size() > 0) + { + + StepManeuver maneuver = detail::stepManeuverFromGeometry( + extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry); + maneuver.location = source_node.location; + + // PathData saves the information we need of the segment _before_ the turn, + // but a RouteStep is with regard to the segment after the turn. + // We need to skip the first segment because it is already covered by the + // initial start of a route + int segment_duration = 0; + for (const auto &path_point : leg_data) + { + segment_duration += path_point.duration_until_turn; + + // all changes to this check have to be matched with assemble_geometry + if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) + { + BOOST_ASSERT(segment_duration >= 0); + const auto name = facade.GetNameForID(path_point.name_id); + const auto distance = leg_geometry.segment_distances[segment_index]; + steps.push_back(RouteStep{path_point.name_id, name, NO_ROTARY_NAME, + segment_duration / 10.0, distance, path_point.travel_mode, + maneuver, leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); + maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction, + leg_geometry, segment_index); + segment_index++; + segment_duration = 0; + } + } + const auto distance = leg_geometry.segment_distances[segment_index]; + const int duration = segment_duration + target_duration; + BOOST_ASSERT(duration >= 0); + steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), + NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver, + leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); + } + // In this case the source + target are on the same edge segment + else + { + BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position); + // s t + // u-------------v + // |---| source_duration + // |---------| target_duration + + StepManeuver maneuver = detail::stepManeuverFromGeometry( + extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry); + int duration = target_duration - source_duration; + BOOST_ASSERT(duration >= 0); + + steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id), + NO_ROTARY_NAME, duration / 10., + leg_geometry.segment_distances[segment_index], source_mode, + std::move(maneuver), leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); + } + + BOOST_ASSERT(segment_index == number_of_segments - 1); + // This step has length zero, the only reason we need it is the target location + auto final_maneuver = detail::stepManeuverFromGeometry( + extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry); + + BOOST_ASSERT(!leg_geometry.locations.empty()); + steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), + NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, + final_maneuver, leg_geometry.locations.size()-1, + leg_geometry.locations.size()}); + + return steps; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ diff --git a/include/engine/guidance/leg_geometry.hpp b/include/engine/guidance/leg_geometry.hpp new file mode 100644 index 000000000..622e40f52 --- /dev/null +++ b/include/engine/guidance/leg_geometry.hpp @@ -0,0 +1,54 @@ +#ifndef ENGINE_GUIDANCE_LEG_GEOMETRY_HPP +#define ENGINE_GUIDANCE_LEG_GEOMETRY_HPP + +#include "util/coordinate.hpp" +#include "util/integer_range.hpp" + +#include + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// locations 0---1---2-...-n-1---n +// turns s x y t +// segment | 0 | 1 | 2 | sentinel +// offsets 0 2 n-1 n +struct LegGeometry +{ + std::vector locations; + // segment_offset[i] .. segment_offset[i+1] (inclusive) + // contains the geometry of segment i + std::vector segment_offsets; + // length of the segment in meters + std::vector segment_distances; + + std::size_t FrontIndex(std::size_t segment_index) const + { + return segment_offsets[segment_index]; + } + + std::size_t BackIndex(std::size_t segment_index) const + { + return segment_offsets[segment_index + 1]; + } + + std::size_t GetNumberOfSegments() const + { + BOOST_ASSERT(segment_offsets.size() > 0); + return segment_offsets.size() - 1; + } +}; +} +} +} + +#endif diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp new file mode 100644 index 000000000..32f6ee5ca --- /dev/null +++ b/include/engine/guidance/post_processing.hpp @@ -0,0 +1,44 @@ +#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP +#define ENGINE_GUIDANCE_POST_PROCESSING_HPP + +#include "engine/phantom_node.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/leg_geometry.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// passed as none-reference to modify in-place and move out again +std::vector postProcess(std::vector steps); + +// trim initial/final segment of very short length. +// This function uses in/out parameter passing to modify both steps and geometry in place. +// We use this method since both steps and geometry are closely coupled logically but +// are not coupled in the same way in the background. To avoid the additional overhead +// of introducing intermediate structions, we resolve to the in/out scheme at this point. +void trimShortSegments(std::vector &steps, LegGeometry &geometry); + +// assign relative locations to depart/arrive instructions +std::vector assignRelativeLocations(std::vector steps, + const LegGeometry &geometry, + const PhantomNode &source_node, + const PhantomNode &target_node); + +// postProcess will break the connection between the leg geometry +// for which a segment is supposed to represent exactly the coordinates +// between routing maneuvers and the route steps itself. +// If required, we can get both in sync again using this function. +// Move in LegGeometry for modification in place. +LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector &steps); + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_POST_PROCESSING_HPP diff --git a/include/engine/guidance/route.hpp b/include/engine/guidance/route.hpp new file mode 100644 index 000000000..bde2d5fca --- /dev/null +++ b/include/engine/guidance/route.hpp @@ -0,0 +1,20 @@ +#ifndef ROUTE_HPP +#define ROUTE_HPP + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +struct Route +{ + double duration; + double distance; +}; +} +} +} + +#endif diff --git a/include/engine/guidance/route_leg.hpp b/include/engine/guidance/route_leg.hpp new file mode 100644 index 000000000..5ecca4faa --- /dev/null +++ b/include/engine/guidance/route_leg.hpp @@ -0,0 +1,29 @@ +#ifndef ROUTE_LEG_HPP +#define ROUTE_LEG_HPP + +#include "engine/guidance/route_step.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +struct RouteLeg +{ + double duration; + double distance; + std::string summary; + std::vector steps; +}; +} +} +} + +#endif diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp new file mode 100644 index 000000000..d5950a61d --- /dev/null +++ b/include/engine/guidance/route_step.hpp @@ -0,0 +1,41 @@ +#ifndef ROUTE_STEP_HPP +#define ROUTE_STEP_HPP + +#include "engine/guidance/step_maneuver.hpp" +#include "extractor/travel_mode.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +// Given the following turn from a,b to b,c over b: +// a --> b --> c +// this struct saves the information of the segment b,c. +// Notable exceptions are Departure and Arrival steps. +// Departue: s --> a --> b. Represents the segment s,a with location being s. +// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment. +struct RouteStep +{ + unsigned name_id; + std::string name; + std::string rotary_name; + double duration; + double distance; + extractor::TravelMode mode; + StepManeuver maneuver; + // indices into the locations array stored the LegGeometry + std::size_t geometry_begin; + std::size_t geometry_end; +}; +} +} +} + +#endif diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp new file mode 100644 index 000000000..5ae70f9f6 --- /dev/null +++ b/include/engine/guidance/step_maneuver.hpp @@ -0,0 +1,45 @@ +#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP +#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP + +#include "util/coordinate.hpp" +#include "extractor/guidance/turn_instruction.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +enum class WaypointType : std::uint8_t +{ + None, + Arrive, + Depart, +}; + +//A represenetation of intermediate intersections +struct IntermediateIntersection +{ + double duration; + double distance; + util::Coordinate location; +}; + +struct StepManeuver +{ + util::Coordinate location; + double bearing_before; + double bearing_after; + extractor::guidance::TurnInstruction instruction; + WaypointType waypoint_type; + unsigned exit; + std::vector intersections; +}; +} // namespace guidance +} // namespace engine +} // namespace osrmn +#endif diff --git a/include/engine/guidance/toolkit.hpp b/include/engine/guidance/toolkit.hpp new file mode 100644 index 000000000..e9cbcf5e3 --- /dev/null +++ b/include/engine/guidance/toolkit.hpp @@ -0,0 +1,63 @@ +#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ +#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ + +#include "extractor/guidance/turn_instruction.hpp" +#include "util/bearing.hpp" + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// Silent Turn Instructions are not to be mentioned to the outside world but +inline bool isSilent(const extractor::guidance::TurnInstruction instruction) +{ + return instruction.type == extractor::guidance::TurnType::NoTurn || + instruction.type == extractor::guidance::TurnType::Suppressed || + instruction.type == extractor::guidance::TurnType::StayOnRoundabout; +} + +inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction) +{ + return (instruction.type == extractor::guidance::TurnType::EnterRoundabout || + instruction.type == extractor::guidance::TurnType::EnterRotary || + instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit || + instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit || + instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout || + instruction.type == extractor::guidance::TurnType::EnterAndExitRotary); +} + +inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction) +{ + return (instruction.type == extractor::guidance::TurnType::ExitRoundabout || + instruction.type == extractor::guidance::TurnType::ExitRotary || + instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout || + instruction.type == extractor::guidance::TurnType::EnterAndExitRotary); +} + +inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction) +{ + return instruction.type == extractor::guidance::TurnType::StayOnRoundabout; +} + +inline extractor::guidance::DirectionModifier angleToDirectionModifier(const double bearing) +{ + if (bearing < 135) + { + return extractor::guidance::DirectionModifier::Right; + } + + if (bearing <= 225) + { + return extractor::guidance::DirectionModifier::Straight; + } + return extractor::guidance::DirectionModifier::Left; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */ diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp new file mode 100644 index 000000000..56dfbc4d6 --- /dev/null +++ b/include/engine/hint.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ENGINE_HINT_HPP +#define ENGINE_HINT_HPP + +#include "engine/phantom_node.hpp" + +#include "util/coordinate.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +// Fwd. decls. +namespace datafacade +{ +class BaseDataFacade; +} + +// Is returned as a temporary identifier for snapped coodinates +struct Hint +{ + PhantomNode phantom; + std::uint32_t data_checksum; + + bool IsValid(const util::Coordinate new_input_coordinates, + const datafacade::BaseDataFacade &facade) const; + + std::string ToBase64() const; + static Hint FromBase64(const std::string &base64Hint); + + friend bool operator==(const Hint &, const Hint &); + friend std::ostream &operator<<(std::ostream &, const Hint &); +}; + +#ifndef _MSC_VER +static_assert(sizeof(Hint) == 60 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_HINT_SIZE = 88; +static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), + "ENCODED_HINT_SIZE does not match size of Hint"); +#else +// PhantomNode is bigger under windows because MSVC does not support bit packing +static_assert(sizeof(Hint) == 72 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_HINT_SIZE = 104; +static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), + "ENCODED_HINT_SIZE does not match size of Hint"); +#endif +} +} + +#endif diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp new file mode 100644 index 000000000..d1666e164 --- /dev/null +++ b/include/engine/internal_route_result.hpp @@ -0,0 +1,63 @@ +#ifndef RAW_ROUTE_DATA_H +#define RAW_ROUTE_DATA_H + +#include "engine/phantom_node.hpp" +#include "extractor/travel_mode.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "util/typedefs.hpp" + +#include "osrm/coordinate.hpp" + +#include + +namespace osrm +{ +namespace engine +{ + +const constexpr unsigned INVALID_EXIT_NR = 0; + +struct PathData +{ + // id of via node of the turn + NodeID turn_via_node; + // name of the street that leads to the turn + unsigned name_id; + // duration that is traveled on the segment until the turn is reached + EdgeWeight duration_until_turn; + // instruction to execute at the turn + extractor::guidance::TurnInstruction turn_instruction; + // travel mode of the street that leads to the turn + extractor::TravelMode travel_mode : 4; +}; + +struct InternalRouteResult +{ + std::vector> unpacked_path_segments; + std::vector unpacked_alternative; + std::vector segment_end_coordinates; + std::vector source_traversed_in_reverse; + std::vector target_traversed_in_reverse; + std::vector alt_source_traversed_in_reverse; + std::vector alt_target_traversed_in_reverse; + int shortest_path_length; + int alternative_path_length; + + bool is_valid() const { return INVALID_EDGE_WEIGHT != shortest_path_length; } + + bool has_alternative() const { return INVALID_EDGE_WEIGHT != alternative_path_length; } + + bool is_via_leg(const std::size_t leg) const + { + return (leg != unpacked_path_segments.size() - 1); + } + + InternalRouteResult() + : shortest_path_length(INVALID_EDGE_WEIGHT), alternative_path_length(INVALID_EDGE_WEIGHT) + { + } +}; +} +} + +#endif // RAW_ROUTE_DATA_H diff --git a/algorithms/bayes_classifier.hpp b/include/engine/map_matching/bayes_classifier.hpp similarity index 63% rename from algorithms/bayes_classifier.hpp rename to include/engine/map_matching/bayes_classifier.hpp index ea300c1a9..c77fa843e 100644 --- a/algorithms/bayes_classifier.hpp +++ b/include/engine/map_matching/bayes_classifier.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef BAYES_CLASSIFIER_HPP #define BAYES_CLASSIFIER_HPP @@ -33,6 +6,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + +namespace osrm +{ +namespace engine +{ +namespace map_matching +{ + struct NormalDistribution { NormalDistribution(const double mean, const double standard_deviation) @@ -40,11 +22,13 @@ struct NormalDistribution { } - // FIXME implement log-probability version since its faster + // FIXME implement log-probability version since it's faster double density_function(const double val) const { + using namespace boost::math::constants; + const double x = val - mean; - return 1.0 / (std::sqrt(2. * M_PI) * standard_deviation) * + return 1.0 / (std::sqrt(two_pi()) * standard_deviation) * std::exp(-x * x / (standard_deviation * standard_deviation)); } @@ -59,7 +43,7 @@ struct LaplaceDistribution { } - // FIXME implement log-probability version since its faster + // FIXME implement log-probability version since it's faster double density_function(const double val) const { const double x = std::abs(val - location); @@ -114,5 +98,8 @@ class BayesClassifier double positive_apriori_probability; double negative_apriori_probability; }; +} +} +} #endif // BAYES_CLASSIFIER_HPP diff --git a/include/engine/map_matching/hidden_markov_model.hpp b/include/engine/map_matching/hidden_markov_model.hpp new file mode 100644 index 000000000..9b3634313 --- /dev/null +++ b/include/engine/map_matching/hidden_markov_model.hpp @@ -0,0 +1,140 @@ +#ifndef HIDDEN_MARKOV_MODEL +#define HIDDEN_MARKOV_MODEL + +#include "util/integer_range.hpp" + +#include +#include + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace map_matching +{ + +static const double log_2_pi = std::log(2. * boost::math::constants::pi()); +static const double IMPOSSIBLE_LOG_PROB = -std::numeric_limits::infinity(); +static const double MINIMAL_LOG_PROB = std::numeric_limits::lowest(); +static const std::size_t INVALID_STATE = std::numeric_limits::max(); + +// closures to precompute log -> only simple floating point operations +struct EmissionLogProbability +{ + double sigma_z; + double log_sigma_z; + + EmissionLogProbability(const double sigma_z) : sigma_z(sigma_z), log_sigma_z(std::log(sigma_z)) + { + } + + double operator()(const double distance) const + { + return -0.5 * (log_2_pi + (distance / sigma_z) * (distance / sigma_z)) - log_sigma_z; + } +}; + +struct TransitionLogProbability +{ + double beta; + double log_beta; + TransitionLogProbability(const double beta) : beta(beta), log_beta(std::log(beta)) {} + + double operator()(const double d_t) const { return -log_beta - d_t / beta; } +}; + +template struct HiddenMarkovModel +{ + std::vector> viterbi; + std::vector>> parents; + std::vector> path_distances; + std::vector> pruned; + std::vector breakage; + + const CandidateLists &candidates_list; + const std::vector> &emission_log_probabilities; + + HiddenMarkovModel(const CandidateLists &candidates_list, + const std::vector> &emission_log_probabilities) + : breakage(candidates_list.size()), candidates_list(candidates_list), + emission_log_probabilities(emission_log_probabilities) + { + viterbi.resize(candidates_list.size()); + parents.resize(candidates_list.size()); + path_distances.resize(candidates_list.size()); + pruned.resize(candidates_list.size()); + breakage.resize(candidates_list.size()); + for (const auto i : util::irange(0UL, candidates_list.size())) + { + const auto &num_candidates = candidates_list[i].size(); + // add empty vectors + if (num_candidates > 0) + { + viterbi[i].resize(num_candidates); + parents[i].resize(num_candidates); + path_distances[i].resize(num_candidates); + pruned[i].resize(num_candidates); + } + } + + clear(0); + } + + void clear(std::size_t initial_timestamp) + { + BOOST_ASSERT(viterbi.size() == parents.size() && parents.size() == path_distances.size() && + path_distances.size() == pruned.size() && pruned.size() == breakage.size()); + + for (const auto t : util::irange(initial_timestamp, viterbi.size())) + { + std::fill(viterbi[t].begin(), viterbi[t].end(), IMPOSSIBLE_LOG_PROB); + std::fill(parents[t].begin(), parents[t].end(), std::make_pair(0u, 0u)); + std::fill(path_distances[t].begin(), path_distances[t].end(), 0); + std::fill(pruned[t].begin(), pruned[t].end(), true); + } + std::fill(breakage.begin() + initial_timestamp, breakage.end(), true); + } + + std::size_t initialize(std::size_t initial_timestamp) + { + auto num_points = candidates_list.size(); + do + { + BOOST_ASSERT(initial_timestamp < num_points); + + for (const auto s : util::irange(0UL, viterbi[initial_timestamp].size())) + { + viterbi[initial_timestamp][s] = emission_log_probabilities[initial_timestamp][s]; + parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s); + pruned[initial_timestamp][s] = viterbi[initial_timestamp][s] < MINIMAL_LOG_PROB; + + breakage[initial_timestamp] = + breakage[initial_timestamp] && pruned[initial_timestamp][s]; + } + + ++initial_timestamp; + } while (initial_timestamp < num_points && breakage[initial_timestamp - 1]); + + if (initial_timestamp >= num_points) + { + return INVALID_STATE; + } + + BOOST_ASSERT(initial_timestamp > 0); + --initial_timestamp; + + BOOST_ASSERT(breakage[initial_timestamp] == false); + + return initial_timestamp; + } +}; +} +} +} + +#endif // HIDDEN_MARKOV_MODEL diff --git a/include/engine/map_matching/matching_confidence.hpp b/include/engine/map_matching/matching_confidence.hpp new file mode 100644 index 000000000..03613b048 --- /dev/null +++ b/include/engine/map_matching/matching_confidence.hpp @@ -0,0 +1,58 @@ +#ifndef ENGINE_MAP_MATCHING_CONFIDENCE_HPP +#define ENGINE_MAP_MATCHING_CONFIDENCE_HPP + +#include "engine/map_matching/bayes_classifier.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace map_matching +{ + +struct MatchingConfidence +{ + private: + using ClassifierT = BayesClassifier; + using TraceClassification = ClassifierT::ClassificationT; + + public: + MatchingConfidence() + : // the values were derived from fitting a laplace distribution + // to the values of manually classified traces + classifier(map_matching::LaplaceDistribution(0.005986, 0.016646), + map_matching::LaplaceDistribution(0.054385, 0.458432), + 0.696774) // valid apriori probability + { + } + + double operator()(const float trace_length, const float matched_length) const + { + const double distance_feature = -std::log(trace_length) + std::log(matched_length); + + // matched to the same point + if (!std::isfinite(distance_feature)) + { + return 0; + } + + const auto label_with_confidence = classifier.classify(distance_feature); + if (label_with_confidence.first == ClassifierT::ClassLabel::POSITIVE) + { + return label_with_confidence.second; + } + + BOOST_ASSERT(label_with_confidence.first == ClassifierT::ClassLabel::NEGATIVE); + return 1 - label_with_confidence.second; + } + + private: + ClassifierT classifier; +}; +} +} +} + +#endif diff --git a/include/engine/map_matching/sub_matching.hpp b/include/engine/map_matching/sub_matching.hpp new file mode 100644 index 000000000..22c180312 --- /dev/null +++ b/include/engine/map_matching/sub_matching.hpp @@ -0,0 +1,25 @@ +#ifndef MAP_MATCHING_SUB_MATCHING_HPP +#define MAP_MATCHING_SUB_MATCHING_HPP + +#include "engine/phantom_node.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace map_matching +{ + +struct SubMatching +{ + std::vector nodes; + std::vector indices; + double confidence; +}; +} +} +} + +#endif diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp new file mode 100644 index 000000000..88724b171 --- /dev/null +++ b/include/engine/phantom_node.hpp @@ -0,0 +1,214 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef PHANTOM_NODES_H +#define PHANTOM_NODES_H + +#include "extractor/travel_mode.hpp" +#include "util/typedefs.hpp" + +#include "util/coordinate.hpp" + +#include + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +struct PhantomNode +{ + PhantomNode(SegmentID forward_segment_id, + SegmentID reverse_segment_id, + unsigned name_id, + int forward_weight, + int reverse_weight, + int forward_offset, + int reverse_offset, + unsigned forward_packed_geometry_id_, + unsigned reverse_packed_geometry_id_, + bool is_tiny_component, + unsigned component_id, + util::Coordinate location, + util::Coordinate input_location, + unsigned short fwd_segment_position, + extractor::TravelMode forward_travel_mode, + extractor::TravelMode backward_travel_mode) + : forward_segment_id(forward_segment_id), reverse_segment_id(reverse_segment_id), + name_id(name_id), forward_weight(forward_weight), reverse_weight(reverse_weight), + forward_offset(forward_offset), reverse_offset(reverse_offset), + forward_packed_geometry_id(forward_packed_geometry_id_), + reverse_packed_geometry_id(reverse_packed_geometry_id_), + component{component_id, is_tiny_component}, location(std::move(location)), + input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position), + forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode) + { + } + + PhantomNode() + : forward_segment_id{SPECIAL_SEGMENTID, false}, + reverse_segment_id{SPECIAL_SEGMENTID, false}, + name_id(std::numeric_limits::max()), forward_weight(INVALID_EDGE_WEIGHT), + reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0), + forward_packed_geometry_id(SPECIAL_EDGEID), reverse_packed_geometry_id(SPECIAL_EDGEID), + component{INVALID_COMPONENTID, false}, fwd_segment_position(0), + forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), + backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) + { + } + + int GetForwardWeightPlusOffset() const + { + BOOST_ASSERT(forward_segment_id.enabled); + return forward_offset + forward_weight; + } + + int GetReverseWeightPlusOffset() const + { + BOOST_ASSERT(reverse_segment_id.enabled); + return reverse_offset + reverse_weight; + } + + bool IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; } + + bool IsValid(const unsigned number_of_nodes) const + { + return location.IsValid() && ((forward_segment_id.id < number_of_nodes) || + (reverse_segment_id.id < number_of_nodes)) && + ((forward_weight != INVALID_EDGE_WEIGHT) || + (reverse_weight != INVALID_EDGE_WEIGHT)) && + (component.id != INVALID_COMPONENTID) && (name_id != INVALID_NAMEID); + } + + bool IsValid(const unsigned number_of_nodes, const util::Coordinate queried_coordinate) const + { + return queried_coordinate == input_location && IsValid(number_of_nodes); + } + + bool IsValid() const { return location.IsValid() && (name_id != INVALID_NAMEID); } + + bool operator==(const PhantomNode &other) const { return location == other.location; } + + template + explicit PhantomNode(const OtherT &other, + int forward_weight_, + int forward_offset_, + int reverse_weight_, + int reverse_offset_, + const util::Coordinate location_, + const util::Coordinate input_location_) + : forward_segment_id{other.forward_segment_id}, + reverse_segment_id{other.reverse_segment_id}, name_id{other.name_id}, + forward_weight{forward_weight_}, reverse_weight{reverse_weight_}, + forward_offset{forward_offset_}, reverse_offset{reverse_offset_}, + forward_packed_geometry_id{other.forward_packed_geometry_id}, + reverse_packed_geometry_id{other.reverse_packed_geometry_id}, + component{other.component.id, other.component.is_tiny}, location{location_}, + input_location{input_location_}, fwd_segment_position{other.fwd_segment_position}, + forward_travel_mode{other.forward_travel_mode}, + backward_travel_mode{other.backward_travel_mode} + { + } + + SegmentID forward_segment_id; + SegmentID reverse_segment_id; + unsigned name_id; + int forward_weight; + int reverse_weight; + int forward_offset; + int reverse_offset; + unsigned forward_packed_geometry_id; + unsigned reverse_packed_geometry_id; + struct ComponentType + { + uint32_t id : 31; + bool is_tiny : 1; + } component; +// bit-fields are broken on Windows +#ifndef _MSC_VER + static_assert(sizeof(ComponentType) == 4, "ComponentType needs to be 4 bytes big"); +#endif + util::Coordinate location; + util::Coordinate input_location; + unsigned short fwd_segment_position; + // note 4 bits would suffice for each, + // but the saved byte would be padding anyway + extractor::TravelMode forward_travel_mode; + extractor::TravelMode backward_travel_mode; +}; + +#ifndef _MSC_VER +static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected"); +#else +static_assert(sizeof(PhantomNode) == 72, "PhantomNode has more padding then expected"); +#endif + +using PhantomNodePair = std::pair; + +struct PhantomNodeWithDistance +{ + PhantomNode phantom_node; + double distance; +}; + +struct PhantomNodes +{ + PhantomNode source_phantom; + PhantomNode target_phantom; +}; + +inline std::ostream &operator<<(std::ostream &out, const PhantomNodes &pn) +{ + out << "source_coord: " << pn.source_phantom.location << "\n"; + out << "target_coord: " << pn.target_phantom.location << std::endl; + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const PhantomNode &pn) +{ + out << "node1: " << pn.forward_segment_id.id << ", " + << "node2: " << pn.reverse_segment_id.id << ", " + << "name: " << pn.name_id << ", " + << "fwd-w: " << pn.forward_weight << ", " + << "rev-w: " << pn.reverse_weight << ", " + << "fwd-o: " << pn.forward_offset << ", " + << "rev-o: " << pn.reverse_offset << ", " + << "fwd_geom: " << pn.forward_packed_geometry_id << ", " + << "rev_geom: " << pn.reverse_packed_geometry_id << ", " + << "comp: " << pn.component.is_tiny << " / " << pn.component.id << ", " + << "pos: " << pn.fwd_segment_position << ", " + << "loc: " << pn.location; + return out; +} +} +} + +#endif // PHANTOM_NODES_H diff --git a/include/engine/plugins/match.hpp b/include/engine/plugins/match.hpp new file mode 100644 index 000000000..57f556a9b --- /dev/null +++ b/include/engine/plugins/match.hpp @@ -0,0 +1,48 @@ +#ifndef MATCH_HPP +#define MATCH_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/api/match_parameters.hpp" + +#include "engine/map_matching/bayes_classifier.hpp" +#include "engine/routing_algorithms/map_matching.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "util/json_util.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class MatchPlugin : public BasePlugin +{ + public: + using SubMatching = map_matching::SubMatching; + using SubMatchingList = routing_algorithms::SubMatchingList; + using CandidateLists = routing_algorithms::CandidateLists; + static const constexpr double DEFAULT_GPS_PRECISION = 5; + static const constexpr double RADIUS_MULTIPLIER = 3; + + MatchPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_map_matching) + : BasePlugin(facade_), map_matching(&facade_, heaps, DEFAULT_GPS_PRECISION), + shortest_path(&facade_, heaps), max_locations_map_matching(max_locations_map_matching) + { + } + + Status HandleRequest(const api::MatchParameters ¶meters, util::json::Object &json_result); + + private: + SearchEngineData heaps; + routing_algorithms::MapMatching map_matching; + routing_algorithms::ShortestPathRouting shortest_path; + int max_locations_map_matching; +}; +} +} +} + +#endif // MATCH_HPP diff --git a/include/engine/plugins/nearest.hpp b/include/engine/plugins/nearest.hpp new file mode 100644 index 000000000..103b789c0 --- /dev/null +++ b/include/engine/plugins/nearest.hpp @@ -0,0 +1,26 @@ +#ifndef NEAREST_HPP +#define NEAREST_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/api/nearest_parameters.hpp" +#include "osrm/json_container.hpp" + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class NearestPlugin final : public BasePlugin +{ + public: + explicit NearestPlugin(datafacade::BaseDataFacade &facade); + + Status HandleRequest(const api::NearestParameters ¶ms, util::json::Object &result); +}; +} +} +} + +#endif /* NEAREST_HPP */ diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp new file mode 100644 index 000000000..e030ff815 --- /dev/null +++ b/include/engine/plugins/plugin_base.hpp @@ -0,0 +1,285 @@ +#ifndef BASE_PLUGIN_HPP +#define BASE_PLUGIN_HPP + +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/api/base_parameters.hpp" +#include "engine/phantom_node.hpp" +#include "engine/status.hpp" + +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/json_container.hpp" +#include "util/integer_range.hpp" + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class BasePlugin +{ + protected: + datafacade::BaseDataFacade &facade; + BasePlugin(datafacade::BaseDataFacade &facade_) : facade(facade_) {} + + bool CheckAllCoordinates(const std::vector &coordinates) + { + return !std::any_of(std::begin(coordinates), std::end(coordinates), + [](const util::Coordinate coordinate) + { + return !coordinate.IsValid(); + }); + } + + Status Error(const std::string &code, + const std::string &message, + util::json::Object &json_result) const + { + json_result.values["code"] = code; + json_result.values["message"] = message; + return Status::Error; + } + + // Decides whether to use the phantom node from a big or small component if both are found. + // Returns true if all phantom nodes are in the same component after snapping. + std::vector + SnapPhantomNodes(const std::vector &phantom_node_pair_list) const + { + const auto check_component_id_is_tiny = + [](const std::pair &phantom_pair) + { + return phantom_pair.first.component.is_tiny; + }; + + // are all phantoms from a tiny cc? + const auto check_all_in_same_component = + [](const std::vector> &nodes) + { + const auto component_id = nodes.front().first.component.id; + + return std::all_of(std::begin(nodes), std::end(nodes), + [component_id](const PhantomNodePair &phantom_pair) + { + return component_id == phantom_pair.first.component.id; + }); + }; + + const auto fallback_to_big_component = + [](const std::pair &phantom_pair) + { + if (phantom_pair.first.component.is_tiny && phantom_pair.second.IsValid() && + !phantom_pair.second.component.is_tiny) + { + return phantom_pair.second; + } + return phantom_pair.first; + }; + + const auto use_closed_phantom = [](const std::pair &phantom_pair) + { + return phantom_pair.first; + }; + + const bool every_phantom_is_in_tiny_cc = + std::all_of(std::begin(phantom_node_pair_list), std::end(phantom_node_pair_list), + check_component_id_is_tiny); + auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list); + + std::vector snapped_phantoms; + snapped_phantoms.reserve(phantom_node_pair_list.size()); + + // The only case we don't snap to the big component if all phantoms are in the same small + // component + if (every_phantom_is_in_tiny_cc && all_in_same_component) + { + std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(), + std::back_inserter(snapped_phantoms), use_closed_phantom); + } + else + { + std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(), + std::back_inserter(snapped_phantoms), fallback_to_big_component); + } + + return snapped_phantoms; + } + + // Falls back to default_radius for non-set radii + std::vector> + GetPhantomNodesInRange(const api::BaseParameters ¶meters, + const std::vector radiuses) const + { + std::vector> phantom_nodes( + parameters.coordinates.size()); + BOOST_ASSERT(radiuses.size() == parameters.coordinates.size()); + + const bool use_hints = !parameters.hints.empty(); + const bool use_bearings = !parameters.bearings.empty(); + + for (const auto i : util::irange(0UL, parameters.coordinates.size())) + { + if (use_hints && parameters.hints[i] && + parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) + { + phantom_nodes[i].push_back(PhantomNodeWithDistance{ + parameters.hints[i]->phantom, + util::coordinate_calculation::haversineDistance( + parameters.coordinates[i], parameters.hints[i]->phantom.location), + }); + continue; + } + if (use_bearings && parameters.bearings[i]) + { + phantom_nodes[i] = facade.NearestPhantomNodesInRange( + parameters.coordinates[i], radiuses[i], parameters.bearings[i]->bearing, + parameters.bearings[i]->range); + } + else + { + phantom_nodes[i] = + facade.NearestPhantomNodesInRange(parameters.coordinates[i], radiuses[i]); + } + } + + return phantom_nodes; + } + + std::vector> + GetPhantomNodes(const api::BaseParameters ¶meters, unsigned number_of_results) + { + std::vector> phantom_nodes( + parameters.coordinates.size()); + + const bool use_hints = !parameters.hints.empty(); + const bool use_bearings = !parameters.bearings.empty(); + const bool use_radiuses = !parameters.radiuses.empty(); + + BOOST_ASSERT(parameters.IsValid()); + for (const auto i : util::irange(0UL, parameters.coordinates.size())) + { + if (use_hints && parameters.hints[i] && + parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) + { + phantom_nodes[i].push_back(PhantomNodeWithDistance{ + parameters.hints[i]->phantom, + util::coordinate_calculation::haversineDistance( + parameters.coordinates[i], parameters.hints[i]->phantom.location), + }); + continue; + } + + if (use_bearings && parameters.bearings[i]) + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_nodes[i] = facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, *parameters.radiuses[i], + parameters.bearings[i]->bearing, parameters.bearings[i]->range); + } + else + { + phantom_nodes[i] = facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, + parameters.bearings[i]->bearing, parameters.bearings[i]->range); + } + } + else + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_nodes[i] = facade.NearestPhantomNodes( + parameters.coordinates[i], number_of_results, *parameters.radiuses[i]); + } + else + { + phantom_nodes[i] = + facade.NearestPhantomNodes(parameters.coordinates[i], number_of_results); + } + } + + // we didn't find a fitting node, return error + if (phantom_nodes[i].empty()) + { + break; + } + } + return phantom_nodes; + } + + std::vector GetPhantomNodes(const api::BaseParameters ¶meters) + { + std::vector phantom_node_pairs(parameters.coordinates.size()); + + const bool use_hints = !parameters.hints.empty(); + const bool use_bearings = !parameters.bearings.empty(); + const bool use_radiuses = !parameters.radiuses.empty(); + + BOOST_ASSERT(parameters.IsValid()); + for (const auto i : util::irange(0UL, parameters.coordinates.size())) + { + if (use_hints && parameters.hints[i] && + parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) + { + phantom_node_pairs[i].first = parameters.hints[i]->phantom; + // we don't set the second one - it will be marked as invalid + continue; + } + + if (use_bearings && parameters.bearings[i]) + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], *parameters.radiuses[i], + parameters.bearings[i]->bearing, parameters.bearings[i]->range); + } + else + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], parameters.bearings[i]->bearing, + parameters.bearings[i]->range); + } + } + else + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], *parameters.radiuses[i]); + } + else + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i]); + } + } + + // we didn't find a fitting node, return error + if (!phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes())) + { + // TODO document why? + phantom_node_pairs.pop_back(); + break; + } + BOOST_ASSERT(phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes())); + BOOST_ASSERT(phantom_node_pairs[i].second.IsValid(facade.GetNumberOfNodes())); + } + return phantom_node_pairs; + } +}; +} +} +} + +#endif /* BASE_PLUGIN_HPP */ diff --git a/include/engine/plugins/table.hpp b/include/engine/plugins/table.hpp new file mode 100644 index 000000000..09f9761ae --- /dev/null +++ b/include/engine/plugins/table.hpp @@ -0,0 +1,35 @@ +#ifndef TABLE_HPP +#define TABLE_HPP + +#include "engine/plugins/plugin_base.hpp" + +#include "engine/api/table_parameters.hpp" +#include "engine/routing_algorithms/many_to_many.hpp" +#include "engine/search_engine_data.hpp" +#include "util/json_container.hpp" + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class TablePlugin final : public BasePlugin +{ + public: + explicit TablePlugin(datafacade::BaseDataFacade &facade, + const int max_locations_distance_table); + + Status HandleRequest(const api::TableParameters ¶ms, util::json::Object &result); + + private: + SearchEngineData heaps; + routing_algorithms::ManyToManyRouting distance_table; + int max_locations_distance_table; +}; +} +} +} + +#endif // TABLE_HPP diff --git a/include/engine/plugins/tile.hpp b/include/engine/plugins/tile.hpp new file mode 100644 index 000000000..77ba357ac --- /dev/null +++ b/include/engine/plugins/tile.hpp @@ -0,0 +1,35 @@ +#ifndef TILEPLUGIN_HPP +#define TILEPLUGIN_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/api/tile_parameters.hpp" + +#include + +/* + * This plugin generates Mapbox Vector tiles that show the internal + * routing geometry and speed values on all road segments. + * You can use this along with a vector-tile viewer, like Mapbox GL, + * to display maps that show the exact road network that + * OSRM is routing. This is very useful for debugging routing + * errors + */ +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class TilePlugin final : public BasePlugin +{ + public: + TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade) {} + + Status HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer); +}; +} +} +} + +#endif /* TILEPLUGIN_HPP */ diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp new file mode 100644 index 000000000..cf368840b --- /dev/null +++ b/include/engine/plugins/trip.hpp @@ -0,0 +1,54 @@ +#ifndef TRIP_HPP +#define TRIP_HPP + +#include "engine/plugins/plugin_base.hpp" + +#include "engine/api/trip_parameters.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "engine/routing_algorithms/many_to_many.hpp" + +#include "osrm/json_container.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class TripPlugin final : public BasePlugin +{ + private: + SearchEngineData heaps; + routing_algorithms::ShortestPathRouting shortest_path; + routing_algorithms::ManyToManyRouting duration_table; + int max_locations_trip; + + InternalRouteResult ComputeRoute(const std::vector &phantom_node_list, + const api::TripParameters ¶meters, + const std::vector &trip); + + public: + explicit TripPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_trip_) + : BasePlugin(facade_), shortest_path(&facade_, heaps), duration_table(&facade_, heaps), + max_locations_trip(max_locations_trip_) + { + } + + Status HandleRequest(const api::TripParameters ¶meters, util::json::Object &json_result); +}; +} +} +} + +#endif // TRIP_HPP diff --git a/include/engine/plugins/viaroute.hpp b/include/engine/plugins/viaroute.hpp new file mode 100644 index 000000000..f73dcc612 --- /dev/null +++ b/include/engine/plugins/viaroute.hpp @@ -0,0 +1,47 @@ +#ifndef VIA_ROUTE_HPP +#define VIA_ROUTE_HPP + +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/plugins/plugin_base.hpp" +#include "engine/api/route_api.hpp" + +#include "engine/search_engine_data.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "engine/routing_algorithms/alternative_path.hpp" +#include "engine/routing_algorithms/direct_shortest_path.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +class ViaRoutePlugin final : public BasePlugin +{ + private: + SearchEngineData heaps; + routing_algorithms::ShortestPathRouting shortest_path; + routing_algorithms::AlternativeRouting alternative_path; + routing_algorithms::DirectShortestPathRouting direct_shortest_path; + int max_locations_viaroute; + + public: + explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute); + + Status HandleRequest(const api::RouteParameters &route_parameters, + util::json::Object &json_result); +}; +} +} +} + +#endif // VIA_ROUTE_HPP diff --git a/include/engine/polyline_compressor.hpp b/include/engine/polyline_compressor.hpp new file mode 100644 index 000000000..4ecf94105 --- /dev/null +++ b/include/engine/polyline_compressor.hpp @@ -0,0 +1,31 @@ +#ifndef POLYLINECOMPRESSOR_H_ +#define POLYLINECOMPRESSOR_H_ + +#include "util/coordinate.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace detail +{ +constexpr double POLYLINE_PRECISION = 1e5; +constexpr double COORDINATE_TO_POLYLINE = POLYLINE_PRECISION / COORDINATE_PRECISION; +constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_PRECISION; +} + +using CoordVectorForwardIter = std::vector::const_iterator; +// Encodes geometry into polyline format. +// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm +std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end); + +// Decodes geometry from polyline format +// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm +std::vector decodePolyline(const std::string &polyline); +} +} + +#endif /* POLYLINECOMPRESSOR_H_ */ diff --git a/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp similarity index 85% rename from routing_algorithms/alternative_path.hpp rename to include/engine/routing_algorithms/alternative_path.hpp index 59b772edb..493d62f0d 100644 --- a/routing_algorithms/alternative_path.hpp +++ b/include/engine/routing_algorithms/alternative_path.hpp @@ -1,45 +1,26 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef ALTERNATIVE_PATH_ROUTING_HPP #define ALTERNATIVE_PATH_ROUTING_HPP -#include "routing_base.hpp" -#include "../data_structures/search_engine_data.hpp" -#include "../util/integer_range.hpp" -#include "../util/container.hpp" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/integer_range.hpp" #include +#include +#include #include #include #include +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ + const double VIAPATH_ALPHA = 0.10; const double VIAPATH_EPSILON = 0.15; // alternative at most 15% longer const double VIAPATH_GAMMA = 0.75; // alternative shares at most 75% with the shortest. @@ -103,45 +84,45 @@ class AlternativeRouting final int upper_bound_to_shortest_path_distance = INVALID_EDGE_WEIGHT; NodeID middle_node = SPECIAL_NODEID; const EdgeWeight min_edge_offset = - std::min(-phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), - -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); + std::min(phantom_node_pair.source_phantom.forward_segment_id.enabled + ? -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset() + : 0, + phantom_node_pair.source_phantom.reverse_segment_id.enabled + ? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset() + : 0); - if (phantom_node_pair.source_phantom.forward_node_id != SPECIAL_NODEID) + if (phantom_node_pair.source_phantom.forward_segment_id.enabled) { - // SimpleLogger().Write(logDEBUG) << "fwd-a insert: " << - // phantom_node_pair.source_phantom.forward_node_id << ", w: " << - // -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(); - forward_heap1.Insert(phantom_node_pair.source_phantom.forward_node_id, + BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id != + SPECIAL_SEGMENTID); + forward_heap1.Insert(phantom_node_pair.source_phantom.forward_segment_id.id, -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.source_phantom.forward_node_id); + phantom_node_pair.source_phantom.forward_segment_id.id); } - if (phantom_node_pair.source_phantom.reverse_node_id != SPECIAL_NODEID) + if (phantom_node_pair.source_phantom.reverse_segment_id.enabled) { - // SimpleLogger().Write(logDEBUG) << "fwd-b insert: " << - // phantom_node_pair.source_phantom.reverse_node_id << ", w: " << - // -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(); - forward_heap1.Insert(phantom_node_pair.source_phantom.reverse_node_id, + BOOST_ASSERT(phantom_node_pair.source_phantom.reverse_segment_id.id != + SPECIAL_SEGMENTID); + forward_heap1.Insert(phantom_node_pair.source_phantom.reverse_segment_id.id, -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(), - phantom_node_pair.source_phantom.reverse_node_id); + phantom_node_pair.source_phantom.reverse_segment_id.id); } - if (phantom_node_pair.target_phantom.forward_node_id != SPECIAL_NODEID) + if (phantom_node_pair.target_phantom.forward_segment_id.enabled) { - // SimpleLogger().Write(logDEBUG) << "rev-a insert: " << - // phantom_node_pair.target_phantom.forward_node_id << ", w: " << - // phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(); - reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_node_id, + BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id != + SPECIAL_SEGMENTID); + reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_segment_id.id, phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.target_phantom.forward_node_id); + phantom_node_pair.target_phantom.forward_segment_id.id); } - if (phantom_node_pair.target_phantom.reverse_node_id != SPECIAL_NODEID) + if (phantom_node_pair.target_phantom.reverse_segment_id.enabled) { - // SimpleLogger().Write(logDEBUG) << "rev-b insert: " << - // phantom_node_pair.target_phantom.reverse_node_id << ", w: " << - // phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(); - reverse_heap1.Insert(phantom_node_pair.target_phantom.reverse_node_id, + BOOST_ASSERT(phantom_node_pair.target_phantom.reverse_segment_id.id != + SPECIAL_SEGMENTID); + reverse_heap1.Insert(phantom_node_pair.target_phantom.reverse_segment_id.id, phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(), - phantom_node_pair.target_phantom.reverse_node_id); + phantom_node_pair.target_phantom.reverse_segment_id.id); } // search from s and t till new_min/(1+epsilon) > length_of_shortest_path @@ -168,13 +149,32 @@ class AlternativeRouting final return; } - osrm::sort_unique_resize(via_node_candidate_list); + std::sort(begin(via_node_candidate_list), end(via_node_candidate_list)); + auto unique_end = std::unique(begin(via_node_candidate_list), end(via_node_candidate_list)); + via_node_candidate_list.resize(unique_end - begin(via_node_candidate_list)); std::vector packed_forward_path; std::vector packed_reverse_path; - super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node, packed_forward_path); - super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node, packed_reverse_path); + const bool path_is_a_loop = + upper_bound_to_shortest_path_distance != + forward_heap1.GetKey(middle_node) + reverse_heap1.GetKey(middle_node); + if (path_is_a_loop) + { + // Self Loop + BOOST_ASSERT(forward_heap1.GetData(middle_node).parent == middle_node && + reverse_heap1.GetData(middle_node).parent == middle_node); + packed_forward_path.push_back(middle_node); + packed_forward_path.push_back(middle_node); + } + else + { + + super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node, + packed_forward_path); + super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node, + packed_reverse_path); + } // this set is is used as an indicator if a node is on the shortest path std::unordered_set nodes_in_path(packed_forward_path.size() + @@ -231,16 +231,18 @@ class AlternativeRouting final } } - // SimpleLogger().Write(logDEBUG) << "fwd_search_space size: " << + // util::SimpleLogger().Write(logDEBUG) << "fwd_search_space size: " << // forward_search_space.size() << ", marked " << approximated_forward_sharing.size() << " // nodes"; - // SimpleLogger().Write(logDEBUG) << "rev_search_space size: " << + // util::SimpleLogger().Write(logDEBUG) << "rev_search_space size: " << // reverse_search_space.size() << ", marked " << approximated_reverse_sharing.size() << " // nodes"; std::vector preselected_node_list; for (const NodeID node : via_node_candidate_list) { + if (node == middle_node) + continue; const auto fwd_iterator = approximated_forward_sharing.find(node); const int fwd_sharing = (fwd_iterator != approximated_forward_sharing.end()) ? fwd_iterator->second : 0; @@ -267,10 +269,13 @@ class AlternativeRouting final } std::vector &packed_shortest_path = packed_forward_path; - std::reverse(packed_shortest_path.begin(), packed_shortest_path.end()); - packed_shortest_path.emplace_back(middle_node); - packed_shortest_path.insert(packed_shortest_path.end(), packed_reverse_path.begin(), - packed_reverse_path.end()); + if (!path_is_a_loop) + { + std::reverse(packed_shortest_path.begin(), packed_shortest_path.end()); + packed_shortest_path.emplace_back(middle_node); + packed_shortest_path.insert(packed_shortest_path.end(), packed_reverse_path.begin(), + packed_reverse_path.end()); + } std::vector ranked_candidates_list; // prioritizing via nodes for deep inspection @@ -311,9 +316,11 @@ class AlternativeRouting final BOOST_ASSERT(!packed_shortest_path.empty()); raw_route_data.unpacked_path_segments.resize(1); raw_route_data.source_traversed_in_reverse.push_back( - (packed_shortest_path.front() != phantom_node_pair.source_phantom.forward_node_id)); + (packed_shortest_path.front() != + phantom_node_pair.source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( - (packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_node_id)); + (packed_shortest_path.back() != + phantom_node_pair.target_phantom.forward_segment_id.id)); super::UnpackPath( // -- packed input @@ -332,10 +339,12 @@ class AlternativeRouting final RetrievePackedAlternatePath(forward_heap1, reverse_heap1, forward_heap2, reverse_heap2, s_v_middle, v_t_middle, packed_alternate_path); - raw_route_data.alt_source_traversed_in_reverse.push_back(( - packed_alternate_path.front() != phantom_node_pair.source_phantom.forward_node_id)); + raw_route_data.alt_source_traversed_in_reverse.push_back( + (packed_alternate_path.front() != + phantom_node_pair.source_phantom.forward_segment_id.id)); raw_route_data.alt_target_traversed_in_reverse.push_back( - (packed_alternate_path.back() != phantom_node_pair.target_phantom.forward_node_id)); + (packed_alternate_path.back() != + phantom_node_pair.target_phantom.forward_segment_id.id)); // unpack the alternate path super::UnpackPath(packed_alternate_path.begin(), packed_alternate_path.end(), @@ -399,10 +408,13 @@ class AlternativeRouting final int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT; new_reverse_heap.Insert(via_node, 0, via_node); // compute path by reusing forward search from s + const bool constexpr STALLING_ENABLED = true; + const bool constexpr DO_NOT_FORCE_LOOPS = false; while (!new_reverse_heap.Empty()) { super::RoutingStep(new_reverse_heap, existing_forward_heap, s_v_middle, - upper_bound_s_v_path_length, min_edge_offset, false); + upper_bound_s_v_path_length, min_edge_offset, false, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } // compute path by reusing backward search from node t NodeID v_t_middle = SPECIAL_NODEID; @@ -411,7 +423,8 @@ class AlternativeRouting final while (!new_forward_heap.Empty()) { super::RoutingStep(new_forward_heap, existing_reverse_heap, v_t_middle, - upper_bound_of_v_t_path_length, min_edge_offset, true); + upper_bound_of_v_t_path_length, min_edge_offset, true, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } *real_length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length; @@ -428,9 +441,9 @@ class AlternativeRouting final // partial unpacking, compute sharing // First partially unpack s-->v until paths deviate, note length of common path. - const int64_t s_v_min_path_size = - static_cast(std::min(packed_s_v_path.size(), packed_shortest_path.size())) - 1; - for (const int64_t current_node : osrm::irange(0, s_v_min_path_size)) + const auto s_v_min_path_size = + std::min(packed_s_v_path.size(), packed_shortest_path.size()) - 1; + for (const auto current_node : util::irange(0UL, s_v_min_path_size)) { if (packed_s_v_path[current_node] == packed_shortest_path[current_node] && packed_s_v_path[current_node + 1] == packed_shortest_path[current_node + 1]) @@ -546,7 +559,7 @@ class AlternativeRouting final // //compute forward sharing // while( (packed_alternate_path[aindex] == packed_shortest_path[aindex]) && // (packed_alternate_path[aindex+1] == packed_shortest_path[aindex+1]) ) { - // // SimpleLogger().Write() << "retrieving edge (" << + // // util::SimpleLogger().Write() << "retrieving edge (" << // packed_alternate_path[aindex] << "," << packed_alternate_path[aindex+1] << ")"; // EdgeID edgeID = facade->FindEdgeInEitherDirection(packed_alternate_path[aindex], // packed_alternate_path[aindex+1]); @@ -584,7 +597,8 @@ class AlternativeRouting final const NodeID node = forward_heap.DeleteMin(); const int distance = forward_heap.GetKey(node); // const NodeID parentnode = forward_heap.GetData(node).parent; - // SimpleLogger().Write() << (is_forward_directed ? "[fwd] " : "[rev] ") << "settled edge (" + // util::SimpleLogger().Write() << (is_forward_directed ? "[fwd] " : "[rev] ") << "settled + // edge (" // << parentnode << "," << node << "), dist: " << distance; const int scaled_distance = @@ -608,12 +622,26 @@ class AlternativeRouting final { *middle_node = node; *upper_bound_to_shortest_path_distance = new_distance; - // SimpleLogger().Write() << "accepted middle_node " << *middle_node << " at + // util::SimpleLogger().Write() << "accepted middle_node " << *middle_node + // << " at // distance " << new_distance; // } else { - // SimpleLogger().Write() << "discarded middle_node " << *middle_node << " + // util::SimpleLogger().Write() << "discarded middle_node " << *middle_node + // << " // at distance " << new_distance; } + else + { + // check whether there is a loop present at the node + const auto loop_distance = super::GetLoopWeight(node); + const int new_distance_with_loop = new_distance + loop_distance; + if (loop_distance != INVALID_EDGE_WEIGHT && + new_distance_with_loop <= *upper_bound_to_shortest_path_distance) + { + *middle_node = node; + *upper_bound_to_shortest_path_distance = loop_distance; + } + } } } @@ -669,10 +697,13 @@ class AlternativeRouting final int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT; // compute path by reusing forward search from s new_reverse_heap.Insert(candidate.node, 0, candidate.node); + const bool constexpr STALLING_ENABLED = true; + const bool constexpr DO_NOT_FORCE_LOOPS = false; while (new_reverse_heap.Size() > 0) { super::RoutingStep(new_reverse_heap, existing_forward_heap, *s_v_middle, - upper_bound_s_v_path_length, min_edge_offset, false); + upper_bound_s_v_path_length, min_edge_offset, false, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_length) @@ -687,7 +718,8 @@ class AlternativeRouting final while (new_forward_heap.Size() > 0) { super::RoutingStep(new_forward_heap, existing_reverse_heap, *v_t_middle, - upper_bound_of_v_t_path_length, min_edge_offset, true); + upper_bound_of_v_t_path_length, min_edge_offset, true, + STALLING_ENABLED, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_length) @@ -855,16 +887,21 @@ class AlternativeRouting final if (!forward_heap3.Empty()) { super::RoutingStep(forward_heap3, reverse_heap3, middle, upper_bound, - min_edge_offset, true); + min_edge_offset, true, STALLING_ENABLED, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } if (!reverse_heap3.Empty()) { super::RoutingStep(reverse_heap3, forward_heap3, middle, upper_bound, - min_edge_offset, false); + min_edge_offset, false, STALLING_ENABLED, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } } return (upper_bound <= t_test_path_length); } }; +} +} +} #endif /* ALTERNATIVE_PATH_ROUTING_HPP */ diff --git a/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp similarity index 54% rename from routing_algorithms/direct_shortest_path.hpp rename to include/engine/routing_algorithms/direct_shortest_path.hpp index 2237d682a..970949e02 100644 --- a/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -1,41 +1,21 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef DIRECT_SHORTEST_PATH_HPP #define DIRECT_SHORTEST_PATH_HPP #include #include -#include "routing_base.hpp" -#include "../data_structures/search_engine_data.hpp" -#include "../util/integer_range.hpp" -#include "../util/timing_util.hpp" -#include "../typedefs.h" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/integer_range.hpp" +#include "util/timing_util.hpp" +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ /// This is a striped down version of the general shortest path algorithm. /// The general algorithm always computes two queries for each leg. This is only @@ -60,17 +40,15 @@ class DirectShortestPathRouting final ~DirectShortestPathRouting() {} void operator()(const std::vector &phantom_nodes_vector, - const std::vector &uturn_indicators, InternalRouteResult &raw_route_data) const { - (void)uturn_indicators; // unused - // Get distance to next pair of target nodes. BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(), - "Direct Shortest Path Query only accepts a single source and target pair. Multiple ones have been specified."); - const auto& phantom_node_pair = phantom_nodes_vector.front(); - const auto& source_phantom = phantom_node_pair.source_phantom; - const auto& target_phantom = phantom_node_pair.target_phantom; + "Direct Shortest Path Query only accepts a single source and target pair. " + "Multiple ones have been specified."); + const auto &phantom_node_pair = phantom_nodes_vector.front(); + const auto &source_phantom = phantom_node_pair.source_phantom; + const auto &target_phantom = phantom_node_pair.target_phantom; engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); @@ -79,39 +57,42 @@ class DirectShortestPathRouting final forward_heap.Clear(); reverse_heap.Clear(); - BOOST_ASSERT(source_phantom.is_valid()); - BOOST_ASSERT(target_phantom.is_valid()); + BOOST_ASSERT(source_phantom.IsValid()); + BOOST_ASSERT(target_phantom.IsValid()); - if (source_phantom.forward_node_id != SPECIAL_NODEID) + if (source_phantom.forward_segment_id.enabled) { - forward_heap.Insert(source_phantom.forward_node_id, + forward_heap.Insert(source_phantom.forward_segment_id.id, -source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_node_id); + source_phantom.forward_segment_id.id); } - if (source_phantom.reverse_node_id != SPECIAL_NODEID) + if (source_phantom.reverse_segment_id.enabled) { - forward_heap.Insert(source_phantom.reverse_node_id, + forward_heap.Insert(source_phantom.reverse_segment_id.id, -source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_node_id); + source_phantom.reverse_segment_id.id); } - if (target_phantom.forward_node_id != SPECIAL_NODEID) + if (target_phantom.forward_segment_id.enabled) { - reverse_heap.Insert(target_phantom.forward_node_id, + reverse_heap.Insert(target_phantom.forward_segment_id.id, target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_node_id); + target_phantom.forward_segment_id.id); } - if (target_phantom.reverse_node_id != SPECIAL_NODEID) + if (target_phantom.reverse_segment_id.enabled) { - reverse_heap.Insert(target_phantom.reverse_node_id, + reverse_heap.Insert(target_phantom.reverse_segment_id.id, target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_node_id); + target_phantom.reverse_segment_id.id); } int distance = INVALID_EDGE_WEIGHT; std::vector packed_leg; + const bool constexpr DO_NOT_FORCE_LOOPS = + false; // prevents forcing of loops, since offsets are set correctly + if (super::facade->GetCoreSize() > 0) { engine_working_data.InitializeOrClearSecondThreadLocalStorage( @@ -121,13 +102,13 @@ class DirectShortestPathRouting final forward_core_heap.Clear(); reverse_core_heap.Clear(); - super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, - distance, packed_leg); + distance, packed_leg, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); } else { - super::Search(forward_heap, reverse_heap, distance, packed_leg); + super::Search(forward_heap, reverse_heap, distance, packed_leg, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } // No path found for both target nodes? @@ -143,13 +124,16 @@ class DirectShortestPathRouting final raw_route_data.shortest_path_length = distance; raw_route_data.unpacked_path_segments.resize(1); raw_route_data.source_traversed_in_reverse.push_back( - (packed_leg.front() != phantom_node_pair.source_phantom.forward_node_id)); + (packed_leg.front() != phantom_node_pair.source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( - (packed_leg.back() != phantom_node_pair.target_phantom.forward_node_id)); - - super::UnpackPath(packed_leg.begin(), packed_leg.end(), phantom_node_pair, raw_route_data.unpacked_path_segments.front()); + (packed_leg.back() != phantom_node_pair.target_phantom.forward_segment_id.id)); + super::UnpackPath(packed_leg.begin(), packed_leg.end(), phantom_node_pair, + raw_route_data.unpacked_path_segments.front()); } }; +} +} +} #endif /* DIRECT_SHORTEST_PATH_HPP */ diff --git a/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp similarity index 59% rename from routing_algorithms/many_to_many.hpp rename to include/engine/routing_algorithms/many_to_many.hpp index c5dfb7ca5..e09a91dd3 100644 --- a/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -1,36 +1,9 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef MANY_TO_MANY_ROUTING_HPP #define MANY_TO_MANY_ROUTING_HPP -#include "routing_base.hpp" -#include "../data_structures/search_engine_data.hpp" -#include "../typedefs.h" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/typedefs.hpp" #include @@ -39,6 +12,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ + template class ManyToManyRouting final : public BasicRoutingInterface> @@ -56,6 +36,8 @@ class ManyToManyRouting final { } }; + + // FIXME This should be replaced by an std::unordered_multimap, though this needs benchmarking using SearchSpaceWithBuckets = std::unordered_map>; public: @@ -64,16 +46,17 @@ class ManyToManyRouting final { } - ~ManyToManyRouting() {} - - std::shared_ptr> - operator()(const std::vector &phantom_sources_array, const std::vector &phantom_targets_array) const + std::vector operator()(const std::vector &phantom_nodes, + const std::vector &source_indices, + const std::vector &target_indices) const { - const auto number_of_sources = phantom_sources_array.size(); - const auto number_of_targets = phantom_targets_array.size(); - std::shared_ptr> result_table = - std::make_shared>(number_of_targets * number_of_sources, - std::numeric_limits::max()); + const auto number_of_sources = + source_indices.empty() ? phantom_nodes.size() : source_indices.size(); + const auto number_of_targets = + target_indices.empty() ? phantom_nodes.size() : target_indices.size(); + const auto number_of_entries = number_of_sources * number_of_targets; + std::vector result_table(number_of_entries, + std::numeric_limits::max()); engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); @@ -82,71 +65,102 @@ class ManyToManyRouting final SearchSpaceWithBuckets search_space_with_buckets; - unsigned target_id = 0; - for (const auto &phantom : phantom_targets_array) + unsigned column_idx = 0; + const auto search_target_phantom = [&](const PhantomNode &phantom) { query_heap.Clear(); // insert target(s) at distance 0 - if (SPECIAL_NODEID != phantom.forward_node_id) + if (phantom.forward_segment_id.enabled) { - query_heap.Insert(phantom.forward_node_id, + query_heap.Insert(phantom.forward_segment_id.id, phantom.GetForwardWeightPlusOffset(), - phantom.forward_node_id); + phantom.forward_segment_id.id); } - if (SPECIAL_NODEID != phantom.reverse_node_id) + if (phantom.reverse_segment_id.enabled) { - query_heap.Insert(phantom.reverse_node_id, + query_heap.Insert(phantom.reverse_segment_id.id, phantom.GetReverseWeightPlusOffset(), - phantom.reverse_node_id); + phantom.reverse_segment_id.id); } // explore search space while (!query_heap.Empty()) { - BackwardRoutingStep(target_id, query_heap, search_space_with_buckets); + BackwardRoutingStep(column_idx, query_heap, search_space_with_buckets); } - ++target_id; - } + ++column_idx; + }; // for each source do forward search - unsigned source_id = 0; - for (const auto &phantom : phantom_sources_array) + unsigned row_idx = 0; + const auto search_source_phantom = [&](const PhantomNode &phantom) { query_heap.Clear(); // insert target(s) at distance 0 - if (SPECIAL_NODEID != phantom.forward_node_id) + if (phantom.forward_segment_id.enabled) { - query_heap.Insert(phantom.forward_node_id, - -phantom.GetForwardWeightPlusOffset(), - phantom.forward_node_id); + query_heap.Insert(phantom.forward_segment_id.id, + -phantom.GetForwardWeightPlusOffset(), + phantom.forward_segment_id.id); } - if (SPECIAL_NODEID != phantom.reverse_node_id) + if (phantom.reverse_segment_id.enabled) { - query_heap.Insert(phantom.reverse_node_id, - -phantom.GetReverseWeightPlusOffset(), - phantom.reverse_node_id); + query_heap.Insert(phantom.reverse_segment_id.id, + -phantom.GetReverseWeightPlusOffset(), + phantom.reverse_segment_id.id); } // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(source_id, number_of_targets, query_heap, + ForwardRoutingStep(row_idx, number_of_targets, query_heap, search_space_with_buckets, result_table); } + ++row_idx; + }; - ++source_id; + if (target_indices.empty()) + { + for (const auto &phantom : phantom_nodes) + { + search_target_phantom(phantom); + } } - // BOOST_ASSERT(source_id == target_id); + else + { + for (const auto index : target_indices) + { + const auto &phantom = phantom_nodes[index]; + search_target_phantom(phantom); + } + } + + if (source_indices.empty()) + { + for (const auto &phantom : phantom_nodes) + { + search_source_phantom(phantom); + } + } + else + { + for (const auto index : source_indices) + { + const auto &phantom = phantom_nodes[index]; + search_source_phantom(phantom); + } + } + return result_table; } - void ForwardRoutingStep(const unsigned source_id, + void ForwardRoutingStep(const unsigned row_idx, const unsigned number_of_targets, QueryHeap &query_heap, const SearchSpaceWithBuckets &search_space_with_buckets, - std::shared_ptr> result_table) const + std::vector &result_table) const { const NodeID node = query_heap.DeleteMin(); const int source_distance = query_heap.GetKey(node); @@ -160,16 +174,23 @@ class ManyToManyRouting final for (const NodeBucket ¤t_bucket : bucket_list) { // get target id from bucket entry - const unsigned target_id = current_bucket.target_id; + const unsigned column_idx = current_bucket.target_id; const int target_distance = current_bucket.distance; - const EdgeWeight current_distance = - (*result_table)[source_id * number_of_targets + target_id]; + auto ¤t_distance = result_table[row_idx * number_of_targets + column_idx]; // check if new distance is better const EdgeWeight new_distance = source_distance + target_distance; - if (new_distance >= 0 && new_distance < current_distance) + if (new_distance < 0) { - (*result_table)[source_id * number_of_targets + target_id] = - (source_distance + target_distance); + const EdgeWeight loop_weight = super::GetLoopWeight(node); + const int new_distance_with_loop = new_distance + loop_weight; + if (loop_weight != INVALID_EDGE_WEIGHT && new_distance_with_loop >= 0) + { + current_distance = std::min(current_distance, new_distance_with_loop); + } + } + else if (new_distance < current_distance) + { + result_table[row_idx * number_of_targets + column_idx] = new_distance; } } } @@ -180,7 +201,7 @@ class ManyToManyRouting final RelaxOutgoingEdges(node, source_distance, query_heap); } - void BackwardRoutingStep(const unsigned target_id, + void BackwardRoutingStep(const unsigned column_idx, QueryHeap &query_heap, SearchSpaceWithBuckets &search_space_with_buckets) const { @@ -188,7 +209,7 @@ class ManyToManyRouting final const int target_distance = query_heap.GetKey(node); // store settled nodes in search space bucket - search_space_with_buckets[node].emplace_back(target_id, target_distance); + search_space_with_buckets[node].emplace_back(column_idx, target_distance); if (StallAtNode(node, target_distance, query_heap)) { @@ -256,4 +277,8 @@ class ManyToManyRouting final return false; } }; +} +} +} + #endif diff --git a/routing_algorithms/map_matching.hpp b/include/engine/routing_algorithms/map_matching.hpp similarity index 55% rename from routing_algorithms/map_matching.hpp rename to include/engine/routing_algorithms/map_matching.hpp index 430682d92..0b1020a7a 100644 --- a/routing_algorithms/map_matching.hpp +++ b/include/engine/routing_algorithms/map_matching.hpp @@ -1,39 +1,15 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef MAP_MATCHING_HPP #define MAP_MATCHING_HPP -#include "routing_base.hpp" +#include "engine/routing_algorithms/routing_base.hpp" -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/hidden_markov_model.hpp" -#include "../util/json_logger.hpp" -#include "../util/matching_debug_info.hpp" +#include "engine/map_matching/hidden_markov_model.hpp" +#include "engine/map_matching/sub_matching.hpp" +#include "engine/map_matching/matching_confidence.hpp" + +#include "util/coordinate_calculation.hpp" +#include "util/json_logger.hpp" +#include "util/for_each_pair.hpp" #include @@ -46,28 +22,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { -namespace matching +namespace engine { - -struct SubMatching +namespace routing_algorithms { - std::vector nodes; - std::vector indices; - double length; - double confidence; -}; using CandidateList = std::vector; using CandidateLists = std::vector; -using HMM = HiddenMarkovModel; -using SubMatchingList = std::vector; +using HMM = map_matching::HiddenMarkovModel; +using SubMatchingList = std::vector; constexpr static const unsigned MAX_BROKEN_STATES = 10; - constexpr static const double MAX_SPEED = 180 / 3.6; // 180km -> m/s -constexpr static const unsigned SUSPICIOUS_DISTANCE_DELTA = 100; -} -} +static const constexpr double MATCHING_BETA = 10; +constexpr static const double MAX_DISTANCE_DELTA = 2000.; // implements a hidden markov model map matching algorithm template @@ -76,8 +44,11 @@ class MapMatching final : public BasicRoutingInterface>; using QueryHeap = SearchEngineData::QueryHeap; SearchEngineData &engine_working_data; + map_matching::EmissionLogProbability default_emission_log_probability; + map_matching::TransitionLogProbability transition_log_probability; + map_matching::MatchingConfidence confidence; - unsigned GetMedianSampleTime(const std::vector& timestamps) const + unsigned GetMedianSampleTime(const std::vector ×tamps) const { BOOST_ASSERT(timestamps.size() > 1); @@ -87,30 +58,36 @@ class MapMatching final : public BasicRoutingInterface will not be a difference. auto first_elem = std::next(sample_times.begin()); - auto median = first_elem + std::distance(first_elem, sample_times.end())/2; + auto median = first_elem + std::distance(first_elem, sample_times.end()) / 2; std::nth_element(first_elem, median, sample_times.end()); return *median; } public: - MapMatching(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) + MapMatching(DataFacadeT *facade, + SearchEngineData &engine_working_data, + const double default_gps_precision) + : super(facade), engine_working_data(engine_working_data), + default_emission_log_probability(default_gps_precision), + transition_log_probability(MATCHING_BETA) { } - void operator()(const osrm::matching::CandidateLists &candidates_list, - const std::vector &trace_coordinates, - const std::vector &trace_timestamps, - const double matching_beta, - const double gps_precision, - osrm::matching::SubMatchingList &sub_matchings) const + SubMatchingList + operator()(const CandidateLists &candidates_list, + const std::vector &trace_coordinates, + const std::vector &trace_timestamps, + const std::vector> &trace_gps_precision) const { + SubMatchingList sub_matchings; + BOOST_ASSERT(candidates_list.size() == trace_coordinates.size()); BOOST_ASSERT(candidates_list.size() > 1); const bool use_timestamps = trace_timestamps.size() > 1; - const auto median_sample_time = [&]() { + const auto median_sample_time = [&] + { if (use_timestamps) { return std::max(1u, GetMedianSampleTime(trace_timestamps)); @@ -120,40 +97,81 @@ class MapMatching final : public BasicRoutingInterface::max(); + return MAX_DISTANCE_DELTA; } }(); - // TODO replace default values with table lookup based on sampling frequency - EmissionLogProbability emission_log_probability(gps_precision); - TransitionLogProbability transition_log_probability(matching_beta); - - osrm::matching::HMM model(candidates_list, emission_log_probability); - - std::size_t initial_timestamp = model.initialize(0); - if (initial_timestamp == osrm::matching::INVALID_STATE) + std::vector> emission_log_probabilities(trace_coordinates.size()); + if (trace_gps_precision.empty()) { - return; + for (auto t = 0UL; t < candidates_list.size(); ++t) + { + emission_log_probabilities[t].resize(candidates_list[t].size()); + std::transform(candidates_list[t].begin(), candidates_list[t].end(), + emission_log_probabilities[t].begin(), + [this](const PhantomNodeWithDistance &candidate) + { + return default_emission_log_probability(candidate.distance); + }); + } + } + else + { + for (auto t = 0UL; t < candidates_list.size(); ++t) + { + emission_log_probabilities[t].resize(candidates_list[t].size()); + if (trace_gps_precision[t]) + { + map_matching::EmissionLogProbability emission_log_probability( + *trace_gps_precision[t]); + std::transform( + candidates_list[t].begin(), candidates_list[t].end(), + emission_log_probabilities[t].begin(), + [&emission_log_probability](const PhantomNodeWithDistance &candidate) + { + return emission_log_probability(candidate.distance); + }); + } + else + { + std::transform(candidates_list[t].begin(), candidates_list[t].end(), + emission_log_probabilities[t].begin(), + [this](const PhantomNodeWithDistance &candidate) + { + return default_emission_log_probability(candidate.distance); + }); + } + } } - MatchingDebugInfo matching_debug(osrm::json::Logger::get()); - matching_debug.initialize(candidates_list); + HMM model(candidates_list, emission_log_probabilities); + + std::size_t initial_timestamp = model.initialize(0); + if (initial_timestamp == map_matching::INVALID_STATE) + { + return sub_matchings; + } engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage( + super::facade->GetNumberOfNodes()); QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); + QueryHeap &forward_core_heap = *(engine_working_data.forward_heap_2); + QueryHeap &reverse_core_heap = *(engine_working_data.reverse_heap_2); - std::size_t breakage_begin = osrm::matching::INVALID_STATE; + std::size_t breakage_begin = map_matching::INVALID_STATE; std::vector split_points; std::vector prev_unbroken_timestamps; prev_unbroken_timestamps.reserve(candidates_list.size()); @@ -173,17 +191,17 @@ class MapMatching final : public BasicRoutingInterface - osrm::matching::MAX_BROKEN_STATES); + trace_split = + trace_split || (t - prev_unbroken_timestamps.back() > MAX_BROKEN_STATES); } if (trace_split) { std::size_t split_index = t; - if (breakage_begin != osrm::matching::INVALID_STATE) + if (breakage_begin != map_matching::INVALID_STATE) { split_index = breakage_begin; - breakage_begin = osrm::matching::INVALID_STATE; + breakage_begin = map_matching::INVALID_STATE; } split_points.push_back(split_index); @@ -191,7 +209,7 @@ class MapMatching final : public BasicRoutingInterface stop viterbi calculation - if (new_start == osrm::matching::INVALID_STATE) + if (new_start == map_matching::INVALID_STATE) { break; } @@ -214,28 +232,28 @@ class MapMatching final : public BasicRoutingInterface(0u, prev_viterbi.size())) + for (const auto s : util::irange(0UL, prev_viterbi.size())) { if (prev_pruned[s]) { continue; } - for (const auto s_prime : osrm::irange(0u, current_viterbi.size())) + for (const auto s_prime : util::irange(0UL, current_viterbi.size())) { - // how likely is candidate s_prime at time t to be emitted? - // FIXME this can be pre-computed - const double emission_pr = - emission_log_probability(candidates_list[t][s_prime].distance); + const double emission_pr = emission_log_probabilities[t][s_prime]; double new_value = prev_viterbi[s] + emission_pr; if (current_viterbi[s_prime] > new_value) { @@ -245,11 +263,25 @@ class MapMatching final : public BasicRoutingInterfaceGetCoreSize() > 0) + { + forward_core_heap.Clear(); + reverse_core_heap.Clear(); + network_distance = super::GetNetworkDistanceWithCore( + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, + prev_unbroken_timestamps_list[s].phantom_node, + current_timestamps_list[s_prime].phantom_node, duration_uppder_bound); + } + else + { + network_distance = super::GetNetworkDistance( + forward_heap, reverse_heap, + prev_unbroken_timestamps_list[s].phantom_node, + current_timestamps_list[s_prime].phantom_node); + } + // get distance diff between loc1/2 and locs/s_prime const auto d_t = std::abs(network_distance - haversine_distance); // very low probability transition -> prune @@ -261,17 +293,12 @@ class MapMatching final : public BasicRoutingInterface current_viterbi[s_prime]) { current_viterbi[s_prime] = new_value; current_parents[s_prime] = std::make_pair(prev_unbroken_timestamp, s); current_lengths[s_prime] = network_distance; current_pruned[s_prime] = false; - current_suspicious[s_prime] = d_t > osrm::matching::SUSPICIOUS_DISTANCE_DELTA; model.breakage[t] = false; } } @@ -295,8 +322,6 @@ class MapMatching final : public BasicRoutingInterface= sub_matching_begin && @@ -313,8 +338,7 @@ class MapMatching final : public BasicRoutingInterface(0u, reconstructed_indices.size())) + auto matching_distance = 0.0; + auto trace_distance = 0.0; + matching.nodes.reserve(reconstructed_indices.size()); + matching.indices.reserve(reconstructed_indices.size()); + for (const auto idx : reconstructed_indices) { - const auto timestamp_index = reconstructed_indices[i].first; - const auto location_index = reconstructed_indices[i].second; + const auto timestamp_index = idx.first; + const auto location_index = idx.second; - matching.indices[i] = timestamp_index; - matching.nodes[i] = candidates_list[timestamp_index][location_index].phantom_node; - matching.length += model.path_lengths[timestamp_index][location_index]; - - matching_debug.add_chosen(timestamp_index, location_index); + matching.indices.push_back(timestamp_index); + matching.nodes.push_back( + candidates_list[timestamp_index][location_index].phantom_node); + matching_distance += model.path_distances[timestamp_index][location_index]; } + util::for_each_pair( + reconstructed_indices, [&trace_distance, &trace_coordinates]( + const std::pair &prev, + const std::pair &curr) + { + trace_distance += util::coordinate_calculation::haversineDistance( + trace_coordinates[prev.first], trace_coordinates[curr.first]); + }); + + matching.confidence = confidence(trace_distance, matching_distance); sub_matchings.push_back(matching); sub_matching_begin = sub_matching_end; } - matching_debug.add_breakage(model.breakage); + + return sub_matchings; } }; +} +} +} //[1] "Hidden Markov Map Matching Through Noise and Sparseness"; P. Newson and J. Krumm; 2009; ACM // GIS diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp new file mode 100644 index 000000000..0a9b1f28a --- /dev/null +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -0,0 +1,906 @@ +#ifndef ROUTING_BASE_HPP +#define ROUTING_BASE_HPP + +#include "util/coordinate_calculation.hpp" +#include "engine/internal_route_result.hpp" +#include "engine/search_engine_data.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "util/typedefs.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +namespace routing_algorithms +{ + +template class BasicRoutingInterface +{ + private: + using EdgeData = typename DataFacadeT::EdgeData; + + protected: + DataFacadeT *facade; + + public: + explicit BasicRoutingInterface(DataFacadeT *facade) : facade(facade) {} + ~BasicRoutingInterface() {} + + BasicRoutingInterface(const BasicRoutingInterface &) = delete; + BasicRoutingInterface &operator=(const BasicRoutingInterface &) = delete; + + /* + min_edge_offset is needed in case we use multiple + nodes as start/target nodes with different (even negative) offsets. + In that case the termination criterion is not correct + anymore. + + Example: + forward heap: a(-100), b(0), + reverse heap: c(0), d(100) + + a --- d + \ / + / \ + b --- c + + This is equivalent to running a bi-directional Dijkstra on the following graph: + + a --- d + / \ / \ + y x z + \ / \ / + b --- c + + The graph is constructed by inserting nodes y and z that are connected to the initial nodes + using edges (y, a) with weight -100, (y, b) with weight 0 and, + (d, z) with weight 100, (c, z) with weight 0 corresponding. + Since we are dealing with a graph that contains _negative_ edges, + we need to add an offset to the termination criterion. + */ + void RoutingStep(SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + NodeID &middle_node_id, + std::int32_t &upper_bound, + std::int32_t min_edge_offset, + const bool forward_direction, + const bool stalling, + const bool force_loop_forward, + const bool force_loop_reverse) const + { + const NodeID node = forward_heap.DeleteMin(); + const std::int32_t distance = forward_heap.GetKey(node); + + if (reverse_heap.WasInserted(node)) + { + const std::int32_t new_distance = reverse_heap.GetKey(node) + distance; + if (new_distance < upper_bound) + { + // if loops are forced, they are so at the source + if (new_distance >= 0 && + (!force_loop_forward || forward_heap.GetData(node).parent != node) && + (!force_loop_reverse || reverse_heap.GetData(node).parent != node)) + { + middle_node_id = node; + upper_bound = new_distance; + } + else + { + // check whether there is a loop present at the node + for (const auto edge : facade->GetAdjacentEdgeRange(node)) + { + const EdgeData &data = facade->GetEdgeData(edge); + bool forward_directionFlag = + (forward_direction ? data.forward : data.backward); + if (forward_directionFlag) + { + const NodeID to = facade->GetTarget(edge); + if (to == node) + { + const EdgeWeight edge_weight = data.distance; + const std::int32_t loop_distance = new_distance + edge_weight; + if (loop_distance >= 0 && loop_distance < upper_bound) + { + middle_node_id = node; + upper_bound = loop_distance; + } + } + } + } + } + } + } + + // make sure we don't terminate too early if we initialize the distance + // for the nodes in the forward heap with the forward/reverse offset + BOOST_ASSERT(min_edge_offset <= 0); + if (distance + min_edge_offset > upper_bound) + { + forward_heap.DeleteAll(); + return; + } + + // Stalling + if (stalling) + { + for (const auto edge : facade->GetAdjacentEdgeRange(node)) + { + const EdgeData &data = facade->GetEdgeData(edge); + const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward); + if (reverse_flag) + { + const NodeID to = facade->GetTarget(edge); + const EdgeWeight edge_weight = data.distance; + + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + + if (forward_heap.WasInserted(to)) + { + if (forward_heap.GetKey(to) + edge_weight < distance) + { + return; + } + } + } + } + } + + for (const auto edge : facade->GetAdjacentEdgeRange(node)) + { + const EdgeData &data = facade->GetEdgeData(edge); + bool forward_directionFlag = (forward_direction ? data.forward : data.backward); + if (forward_directionFlag) + { + + const NodeID to = facade->GetTarget(edge); + const EdgeWeight edge_weight = data.distance; + + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + const int to_distance = distance + edge_weight; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!forward_heap.WasInserted(to)) + { + forward_heap.Insert(to, to_distance, node); + } + // Found a shorter Path -> Update distance + else if (to_distance < forward_heap.GetKey(to)) + { + // new parent + forward_heap.GetData(to).parent = node; + forward_heap.DecreaseKey(to, to_distance); + } + } + } + } + + inline EdgeWeight GetLoopWeight(NodeID node) const + { + EdgeWeight loop_weight = INVALID_EDGE_WEIGHT; + for (auto edge : facade->GetAdjacentEdgeRange(node)) + { + const auto &data = facade->GetEdgeData(edge); + if (data.forward) + { + const NodeID to = facade->GetTarget(edge); + if (to == node) + { + loop_weight = std::min(loop_weight, data.distance); + } + } + } + return loop_weight; + } + + template + void UnpackPath(RandomIter packed_path_begin, + RandomIter packed_path_end, + const PhantomNodes &phantom_node_pair, + std::vector &unpacked_path) const + { + const bool start_traversed_in_reverse = + (*packed_path_begin != phantom_node_pair.source_phantom.forward_segment_id.id); + const bool target_traversed_in_reverse = + (*std::prev(packed_path_end) != phantom_node_pair.target_phantom.forward_segment_id.id); + + BOOST_ASSERT(std::distance(packed_path_begin, packed_path_end) > 0); + std::stack> recursion_stack; + + // We have to push the path in reverse order onto the stack because it's LIFO. + for (auto current = std::prev(packed_path_end); current != packed_path_begin; + current = std::prev(current)) + { + recursion_stack.emplace(*std::prev(current), *current); + } + + std::pair edge; + while (!recursion_stack.empty()) + { + // edge.first edge.second + // *------------------>* + // edge_id + edge = recursion_stack.top(); + recursion_stack.pop(); + + // Contraction might introduce double edges by inserting shortcuts + // this searching for the smallest upwards edge found by the forward search + EdgeID smaller_edge_id = SPECIAL_EDGEID; + EdgeWeight edge_weight = std::numeric_limits::max(); + for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) + { + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; + if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && + facade->GetEdgeData(edge_id).forward) + { + smaller_edge_id = edge_id; + edge_weight = weight; + } + } + + // edge.first edge.second + // *<------------------* + // edge_id + // if we don't find a forward edge, this edge must have been an downwards edge + // found by the reverse search. + if (SPECIAL_EDGEID == smaller_edge_id) + { + for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) + { + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; + if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && + facade->GetEdgeData(edge_id).backward) + { + smaller_edge_id = edge_id; + edge_weight = weight; + } + } + } + BOOST_ASSERT_MSG(edge_weight != INVALID_EDGE_WEIGHT, "edge id invalid"); + + const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); + if (ed.shortcut) + { // unpack + const NodeID middle_node_id = ed.id; + // again, we need to this in reversed order + recursion_stack.emplace(middle_node_id, edge.second); + recursion_stack.emplace(edge.first, middle_node_id); + } + else + { + BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut"); + unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id); + const auto turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id); + const extractor::TravelMode travel_mode = + (unpacked_path.empty() && start_traversed_in_reverse) + ? phantom_node_pair.source_phantom.backward_travel_mode + : facade->GetTravelModeForEdgeID(ed.id); + + std::vector id_vector; + facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id), + id_vector); + BOOST_ASSERT(id_vector.size() > 0); + + std::vector weight_vector; + facade->GetUncompressedWeights(facade->GetGeometryIndexForEdgeID(ed.id), + weight_vector); + BOOST_ASSERT(weight_vector.size() > 0); + + auto total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0); + + BOOST_ASSERT(weight_vector.size() == id_vector.size()); + // ed.distance should be total_weight + penalties (turn, stop, etc) + BOOST_ASSERT(ed.distance >= total_weight); + const bool is_first_segment = unpacked_path.empty(); + + const std::size_t start_index = + (is_first_segment + ? ((start_traversed_in_reverse) + ? id_vector.size() - + phantom_node_pair.source_phantom.fwd_segment_position - 1 + : phantom_node_pair.source_phantom.fwd_segment_position) + : 0); + const std::size_t end_index = id_vector.size(); + + BOOST_ASSERT(start_index >= 0); + BOOST_ASSERT(start_index < end_index); + for (std::size_t i = start_index; i < end_index; ++i) + { + unpacked_path.push_back( + PathData{id_vector[i], + name_index, + weight_vector[i], + extractor::guidance::TurnInstruction::NO_TURN(), + travel_mode}); + } + BOOST_ASSERT(unpacked_path.size() > 0); + unpacked_path.back().turn_instruction = turn_instruction; + unpacked_path.back().duration_until_turn += (ed.distance - total_weight); + } + } + std::size_t start_index = 0, end_index = 0; + std::vector id_vector; + std::vector weight_vector; + const bool is_local_path = (phantom_node_pair.source_phantom.forward_packed_geometry_id == + phantom_node_pair.target_phantom.forward_packed_geometry_id) && + unpacked_path.empty(); + + if (target_traversed_in_reverse) + { + facade->GetUncompressedGeometry( + phantom_node_pair.target_phantom.reverse_packed_geometry_id, id_vector); + + facade->GetUncompressedWeights( + phantom_node_pair.target_phantom.reverse_packed_geometry_id, weight_vector); + + if (is_local_path) + { + start_index = + id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1; + } + end_index = id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1; + } + else + { + if (is_local_path) + { + start_index = phantom_node_pair.source_phantom.fwd_segment_position; + } + end_index = phantom_node_pair.target_phantom.fwd_segment_position; + facade->GetUncompressedGeometry( + phantom_node_pair.target_phantom.forward_packed_geometry_id, id_vector); + + facade->GetUncompressedWeights( + phantom_node_pair.target_phantom.forward_packed_geometry_id, weight_vector); + } + + // Given the following compressed geometry: + // U---v---w---x---y---Z + // s t + // s: fwd_segment 0 + // t: fwd_segment 3 + // -> (U, v), (v, w), (w, x) + // note that (x, t) is _not_ included but needs to be added later. + BOOST_ASSERT(start_index <= end_index); + for (std::size_t i = start_index; i != end_index; ++i) + { + BOOST_ASSERT(i < id_vector.size()); + BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0); + unpacked_path.push_back( + PathData{id_vector[i], + phantom_node_pair.target_phantom.name_id, + weight_vector[i], + extractor::guidance::TurnInstruction::NO_TURN(), + target_traversed_in_reverse + ? phantom_node_pair.target_phantom.backward_travel_mode + : phantom_node_pair.target_phantom.forward_travel_mode}); + } + + if (unpacked_path.size() > 0) + { + const auto source_weight = start_traversed_in_reverse + ? phantom_node_pair.source_phantom.reverse_weight + : phantom_node_pair.source_phantom.forward_weight; + // The above code will create segments for (v, w), (w,x), (x, y) and (y, Z). + // However the first segment duration needs to be adjusted to the fact that the source + // phantom is in the middle of the segment. We do this by subtracting v--s from the + // duration. + BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight); + unpacked_path.front().duration_until_turn -= source_weight; + } + + // there is no equivalent to a node-based node in an edge-expanded graph. + // two equivalent routes may start (or end) at different node-based edges + // as they are added with the offset how much "distance" on the edge + // has already been traversed. Depending on offset one needs to remove + // the last node. + if (unpacked_path.size() > 1) + { + const std::size_t last_index = unpacked_path.size() - 1; + const std::size_t second_to_last_index = last_index - 1; + + if (unpacked_path[last_index].turn_via_node == + unpacked_path[second_to_last_index].turn_via_node) + { + unpacked_path.pop_back(); + } + BOOST_ASSERT(!unpacked_path.empty()); + } + } + + void UnpackEdge(const NodeID s, const NodeID t, std::vector &unpacked_path) const + { + std::stack> recursion_stack; + recursion_stack.emplace(s, t); + + std::pair edge; + while (!recursion_stack.empty()) + { + edge = recursion_stack.top(); + recursion_stack.pop(); + + EdgeID smaller_edge_id = SPECIAL_EDGEID; + EdgeWeight edge_weight = std::numeric_limits::max(); + for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) + { + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; + if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && + facade->GetEdgeData(edge_id).forward) + { + smaller_edge_id = edge_id; + edge_weight = weight; + } + } + + if (SPECIAL_EDGEID == smaller_edge_id) + { + for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) + { + const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; + if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && + facade->GetEdgeData(edge_id).backward) + { + smaller_edge_id = edge_id; + edge_weight = weight; + } + } + } + BOOST_ASSERT_MSG(edge_weight != std::numeric_limits::max(), + "edge weight invalid"); + + const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); + if (ed.shortcut) + { // unpack + const NodeID middle_node_id = ed.id; + // again, we need to this in reversed order + recursion_stack.emplace(middle_node_id, edge.second); + recursion_stack.emplace(edge.first, middle_node_id); + } + else + { + BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut"); + unpacked_path.emplace_back(edge.first); + } + } + unpacked_path.emplace_back(t); + } + + void RetrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, + const SearchEngineData::QueryHeap &reverse_heap, + const NodeID middle_node_id, + std::vector &packed_path) const + { + RetrievePackedPathFromSingleHeap(forward_heap, middle_node_id, packed_path); + std::reverse(packed_path.begin(), packed_path.end()); + packed_path.emplace_back(middle_node_id); + RetrievePackedPathFromSingleHeap(reverse_heap, middle_node_id, packed_path); + } + + void RetrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &search_heap, + const NodeID middle_node_id, + std::vector &packed_path) const + { + NodeID current_node_id = middle_node_id; + while (current_node_id != search_heap.GetData(current_node_id).parent) + { + current_node_id = search_heap.GetData(current_node_id).parent; + packed_path.emplace_back(current_node_id); + } + } + + // assumes that heaps are already setup correctly. + // ATTENTION: This only works if no additional offset is supplied next to the Phantom Node + // Offsets. + // In case additional offsets are supplied, you might have to force a loop first. + // A forced loop might be necessary, if source and target are on the same segment. + // If this is the case and the offsets of the respective direction are larger for the source + // than the target + // then a force loop is required (e.g. source_phantom.forward_segment_id == + // target_phantom.forward_segment_id + // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) + // requires + // a force loop, if the heaps have been initialized with positive offsets. + void Search(SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + std::int32_t &distance, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse, + const int duration_upper_bound = INVALID_EDGE_WEIGHT) const + { + NodeID middle = SPECIAL_NODEID; + distance = duration_upper_bound; + + // get offset to account for offsets on phantom nodes on compressed edges + const auto min_edge_offset = std::min(0, forward_heap.MinKey()); + BOOST_ASSERT(min_edge_offset <= 0); + // we only every insert negative offsets for nodes in the forward heap + BOOST_ASSERT(reverse_heap.MinKey() >= 0); + + // run two-Target Dijkstra routing step. + const constexpr bool STALLING_ENABLED = true; + while (0 < (forward_heap.Size() + reverse_heap.Size())) + { + if (!forward_heap.Empty()) + { + RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true, + STALLING_ENABLED, force_loop_forward, force_loop_reverse); + } + if (!reverse_heap.Empty()) + { + RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, false, + STALLING_ENABLED, force_loop_reverse, force_loop_forward); + } + } + + // No path found for both target nodes? + if (duration_upper_bound <= distance || SPECIAL_NODEID == middle) + { + distance = INVALID_EDGE_WEIGHT; + return; + } + + // Was a paths over one of the forward/reverse nodes not found? + BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), + "no path found"); + + // make sure to correctly unpack loops + if (distance != forward_heap.GetKey(middle) + reverse_heap.GetKey(middle)) + { + // self loop + BOOST_ASSERT(forward_heap.GetData(middle).parent == middle && + reverse_heap.GetData(middle).parent == middle); + packed_leg.push_back(middle); + packed_leg.push_back(middle); + } + else + { + RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + } + } + + // assumes that heaps are already setup correctly. + // A forced loop might be necessary, if source and target are on the same segment. + // If this is the case and the offsets of the respective direction are larger for the source + // than the target + // then a force loop is required (e.g. source_phantom.forward_segment_id == + // target_phantom.forward_segment_id + // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) + // requires + // a force loop, if the heaps have been initialized with positive offsets. + void SearchWithCore(SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &forward_core_heap, + SearchEngineData::QueryHeap &reverse_core_heap, + int &distance, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse, + int duration_upper_bound = INVALID_EDGE_WEIGHT) const + { + NodeID middle = SPECIAL_NODEID; + distance = duration_upper_bound; + + std::vector> forward_entry_points; + std::vector> reverse_entry_points; + + // get offset to account for offsets on phantom nodes on compressed edges + const auto min_edge_offset = std::min(0, forward_heap.MinKey()); + // we only every insert negative offsets for nodes in the forward heap + BOOST_ASSERT(reverse_heap.MinKey() >= 0); + + const constexpr bool STALLING_ENABLED = true; + // run two-Target Dijkstra routing step. + while (0 < (forward_heap.Size() + reverse_heap.Size())) + { + if (!forward_heap.Empty()) + { + if (facade->IsCoreNode(forward_heap.Min())) + { + const NodeID node = forward_heap.DeleteMin(); + const int key = forward_heap.GetKey(node); + forward_entry_points.emplace_back(node, key); + } + else + { + RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true, + STALLING_ENABLED, force_loop_forward, force_loop_reverse); + } + } + if (!reverse_heap.Empty()) + { + if (facade->IsCoreNode(reverse_heap.Min())) + { + const NodeID node = reverse_heap.DeleteMin(); + const int key = reverse_heap.GetKey(node); + reverse_entry_points.emplace_back(node, key); + } + else + { + RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, + false, STALLING_ENABLED, force_loop_reverse, force_loop_forward); + } + } + } + // TODO check if unordered_set might be faster + // sort by id and increasing by distance + auto entry_point_comparator = + [](const std::pair &lhs, const std::pair &rhs) + { + return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second < rhs.second); + }; + std::sort(forward_entry_points.begin(), forward_entry_points.end(), entry_point_comparator); + std::sort(reverse_entry_points.begin(), reverse_entry_points.end(), entry_point_comparator); + + NodeID last_id = SPECIAL_NODEID; + forward_core_heap.Clear(); + reverse_core_heap.Clear(); + for (const auto &p : forward_entry_points) + { + if (p.first == last_id) + { + continue; + } + forward_core_heap.Insert(p.first, p.second, p.first); + last_id = p.first; + } + last_id = SPECIAL_NODEID; + for (const auto &p : reverse_entry_points) + { + if (p.first == last_id) + { + continue; + } + reverse_core_heap.Insert(p.first, p.second, p.first); + last_id = p.first; + } + + // get offset to account for offsets on phantom nodes on compressed edges + int min_core_edge_offset = 0; + if (forward_core_heap.Size() > 0) + { + min_core_edge_offset = std::min(min_core_edge_offset, forward_core_heap.MinKey()); + } + if (reverse_core_heap.Size() > 0 && reverse_core_heap.MinKey() < 0) + { + min_core_edge_offset = std::min(min_core_edge_offset, reverse_core_heap.MinKey()); + } + BOOST_ASSERT(min_core_edge_offset <= 0); + + // run two-target Dijkstra routing step on core with termination criterion + const constexpr bool STALLING_DISABLED = false; + while (0 < forward_core_heap.Size() && 0 < reverse_core_heap.Size() && + distance > (forward_core_heap.MinKey() + reverse_core_heap.MinKey())) + { + RoutingStep(forward_core_heap, reverse_core_heap, middle, distance, + min_core_edge_offset, true, STALLING_DISABLED, force_loop_forward, + force_loop_reverse); + + RoutingStep(reverse_core_heap, forward_core_heap, middle, distance, + min_core_edge_offset, false, STALLING_DISABLED, force_loop_reverse, + force_loop_forward); + } + + // No path found for both target nodes? + if (duration_upper_bound <= distance || SPECIAL_NODEID == middle) + { + distance = INVALID_EDGE_WEIGHT; + return; + } + + // Was a paths over one of the forward/reverse nodes not found? + BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), + "no path found"); + + // we need to unpack sub path from core heaps + if (facade->IsCoreNode(middle)) + { + if (distance != forward_core_heap.GetKey(middle) + reverse_core_heap.GetKey(middle)) + { + // self loop + BOOST_ASSERT(forward_core_heap.GetData(middle).parent == middle && + reverse_core_heap.GetData(middle).parent == middle); + packed_leg.push_back(middle); + packed_leg.push_back(middle); + } + else + { + std::vector packed_core_leg; + RetrievePackedPathFromHeap(forward_core_heap, reverse_core_heap, middle, + packed_core_leg); + BOOST_ASSERT(packed_core_leg.size() > 0); + RetrievePackedPathFromSingleHeap(forward_heap, packed_core_leg.front(), packed_leg); + std::reverse(packed_leg.begin(), packed_leg.end()); + packed_leg.insert(packed_leg.end(), packed_core_leg.begin(), packed_core_leg.end()); + RetrievePackedPathFromSingleHeap(reverse_heap, packed_core_leg.back(), packed_leg); + } + } + else + { + if (distance != forward_heap.GetKey(middle) + reverse_heap.GetKey(middle)) + { + // self loop + BOOST_ASSERT(forward_heap.GetData(middle).parent == middle && + reverse_heap.GetData(middle).parent == middle); + packed_leg.push_back(middle); + packed_leg.push_back(middle); + } + else + { + RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + } + } + } + + bool NeedsLoopForward(const PhantomNode &source_phantom, + const PhantomNode &target_phantom) const + { + return source_phantom.forward_segment_id.enabled && + target_phantom.forward_segment_id.enabled && + source_phantom.forward_segment_id.id == target_phantom.forward_segment_id.id && + source_phantom.GetForwardWeightPlusOffset() > + target_phantom.GetForwardWeightPlusOffset(); + } + + bool NeedsLoopBackwards(const PhantomNode &source_phantom, + const PhantomNode &target_phantom) const + { + return source_phantom.reverse_segment_id.enabled && + target_phantom.reverse_segment_id.enabled && + source_phantom.reverse_segment_id.id == target_phantom.reverse_segment_id.id && + source_phantom.GetReverseWeightPlusOffset() > + target_phantom.GetReverseWeightPlusOffset(); + } + + double GetPathDistance(const std::vector &packed_path, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom) const + { + std::vector unpacked_path; + PhantomNodes nodes; + nodes.source_phantom = source_phantom; + nodes.target_phantom = target_phantom; + UnpackPath(packed_path.begin(), packed_path.end(), nodes, unpacked_path); + + util::Coordinate previous_coordinate = source_phantom.location; + util::Coordinate current_coordinate; + double distance = 0; + for (const auto &p : unpacked_path) + { + current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node); + distance += util::coordinate_calculation::haversineDistance(previous_coordinate, + current_coordinate); + previous_coordinate = current_coordinate; + } + distance += util::coordinate_calculation::haversineDistance(previous_coordinate, + target_phantom.location); + return distance; + } + + // Requires the heaps for be empty + // If heaps should be adjusted to be initialized outside of this function, + // the addition of force_loop parameters might be required + double GetNetworkDistanceWithCore(SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &forward_core_heap, + SearchEngineData::QueryHeap &reverse_core_heap, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + int duration_upper_bound = INVALID_EDGE_WEIGHT) const + { + BOOST_ASSERT(forward_heap.Empty()); + BOOST_ASSERT(reverse_heap.Empty()); + + if (source_phantom.forward_segment_id.enabled) + { + forward_heap.Insert(source_phantom.forward_segment_id.id, + -source_phantom.GetForwardWeightPlusOffset(), + source_phantom.forward_segment_id.id); + } + if (source_phantom.reverse_segment_id.enabled) + { + forward_heap.Insert(source_phantom.reverse_segment_id.id, + -source_phantom.GetReverseWeightPlusOffset(), + source_phantom.reverse_segment_id.id); + } + + if (target_phantom.forward_segment_id.enabled) + { + reverse_heap.Insert(target_phantom.forward_segment_id.id, + target_phantom.GetForwardWeightPlusOffset(), + target_phantom.forward_segment_id.id); + } + if (target_phantom.reverse_segment_id.enabled) + { + reverse_heap.Insert(target_phantom.reverse_segment_id.id, + target_phantom.GetReverseWeightPlusOffset(), + target_phantom.reverse_segment_id.id); + } + + const bool constexpr DO_NOT_FORCE_LOOPS = + false; // prevents forcing of loops, since offsets are set correctly + + int duration = INVALID_EDGE_WEIGHT; + std::vector packed_path; + SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, duration, + packed_path, DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS, duration_upper_bound); + + double distance = std::numeric_limits::max(); + if (duration != INVALID_EDGE_WEIGHT) + { + return GetPathDistance(packed_path, source_phantom, target_phantom); + } + return distance; + } + + // Requires the heaps for be empty + // If heaps should be adjusted to be initialized outside of this function, + // the addition of force_loop parameters might be required + double GetNetworkDistance(SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + int duration_upper_bound = INVALID_EDGE_WEIGHT) const + { + BOOST_ASSERT(forward_heap.Empty()); + BOOST_ASSERT(reverse_heap.Empty()); + + if (source_phantom.forward_segment_id.enabled) + { + forward_heap.Insert(source_phantom.forward_segment_id.id, + -source_phantom.GetForwardWeightPlusOffset(), + source_phantom.forward_segment_id.id); + } + if (source_phantom.reverse_segment_id.enabled) + { + forward_heap.Insert(source_phantom.reverse_segment_id.id, + -source_phantom.GetReverseWeightPlusOffset(), + source_phantom.reverse_segment_id.id); + } + + if (target_phantom.forward_segment_id.enabled) + { + reverse_heap.Insert(target_phantom.forward_segment_id.id, + target_phantom.GetForwardWeightPlusOffset(), + target_phantom.forward_segment_id.id); + } + if (target_phantom.reverse_segment_id.enabled) + { + reverse_heap.Insert(target_phantom.reverse_segment_id.id, + target_phantom.GetReverseWeightPlusOffset(), + target_phantom.reverse_segment_id.id); + } + + const bool constexpr DO_NOT_FORCE_LOOPS = + false; // prevents forcing of loops, since offsets are set correctly + + int duration = INVALID_EDGE_WEIGHT; + std::vector packed_path; + Search(forward_heap, reverse_heap, duration, packed_path, DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS, duration_upper_bound); + + if (duration == INVALID_EDGE_WEIGHT) + { + return std::numeric_limits::max(); + } + + return GetPathDistance(packed_path, source_phantom, target_phantom); + } +}; +} +} +} + +#endif // ROUTING_BASE_HPP diff --git a/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp similarity index 57% rename from routing_algorithms/shortest_path.hpp rename to include/engine/routing_algorithms/shortest_path.hpp index 601e39460..7b8fd7c6b 100644 --- a/routing_algorithms/shortest_path.hpp +++ b/include/engine/routing_algorithms/shortest_path.hpp @@ -1,41 +1,22 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef SHORTEST_PATH_HPP #define SHORTEST_PATH_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" -#include "routing_base.hpp" +#include "engine/routing_algorithms/routing_base.hpp" -#include "../data_structures/search_engine_data.hpp" -#include "../util/integer_range.hpp" +#include "engine/search_engine_data.hpp" +#include "util/integer_range.hpp" #include +#include + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ template class ShortestPathRouting final @@ -44,6 +25,7 @@ class ShortestPathRouting final using super = BasicRoutingInterface>; using QueryHeap = SearchEngineData::QueryHeap; SearchEngineData &engine_working_data; + const static constexpr bool DO_NOT_FORCE_LOOP = false; public: ShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) @@ -57,6 +39,8 @@ class ShortestPathRouting final // searches source forward/reverse -> target forward/reverse void SearchWithUTurn(QueryHeap &forward_heap, QueryHeap &reverse_heap, + QueryHeap &forward_core_heap, + QueryHeap &reverse_core_heap, const bool search_from_forward_node, const bool search_from_reverse_node, const bool search_to_forward_node, @@ -72,136 +56,57 @@ class ShortestPathRouting final reverse_heap.Clear(); if (search_from_forward_node) { - forward_heap.Insert(source_phantom.forward_node_id, + forward_heap.Insert(source_phantom.forward_segment_id.id, total_distance_to_forward - source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_node_id); + source_phantom.forward_segment_id.id); } if (search_from_reverse_node) { - forward_heap.Insert(source_phantom.reverse_node_id, + forward_heap.Insert(source_phantom.reverse_segment_id.id, total_distance_to_reverse - source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_node_id); + source_phantom.reverse_segment_id.id); } if (search_to_forward_node) { - reverse_heap.Insert(target_phantom.forward_node_id, + reverse_heap.Insert(target_phantom.forward_segment_id.id, target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_node_id); + target_phantom.forward_segment_id.id); } if (search_to_reverse_node) { - reverse_heap.Insert(target_phantom.reverse_node_id, + reverse_heap.Insert(target_phantom.reverse_segment_id.id, target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_node_id); + target_phantom.reverse_segment_id.id); } + BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance, leg_packed_path); - } - // If source and target are reverse on a oneway we need to find a path - // that connects the two. This is _not_ the shortest path in our model, - // as source and target are on the same edge based node. - // We force a detour by inserting "virtaul vias", which means we search a path - // from all nodes that are connected by outgoing edges to all nodes that are connected by - // incoming edges. - // ------^ - // | ^source - // | ^ - // | ^target - // ------^ - void SearchLoop(QueryHeap &forward_heap, - QueryHeap &reverse_heap, - const bool search_forward_node, - const bool search_reverse_node, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - const int total_distance_to_forward, - const int total_distance_to_reverse, - int &new_total_distance_to_forward, - int &new_total_distance_to_reverse, - std::vector &leg_packed_path_forward, - std::vector &leg_packed_path_reverse) const - { - BOOST_ASSERT(source_phantom.forward_node_id == target_phantom.forward_node_id); - BOOST_ASSERT(source_phantom.reverse_node_id == target_phantom.reverse_node_id); - - if (search_forward_node) + // this is only relevent if source and target are on the same compressed edge + auto is_oneway_source = !(search_from_forward_node && search_from_reverse_node); + auto is_oneway_target = !(search_to_forward_node && search_to_reverse_node); + // we only enable loops here if we can't search from forward to backward node + auto needs_loop_forwad = + is_oneway_source && super::NeedsLoopForward(source_phantom, target_phantom); + auto needs_loop_backwards = + is_oneway_target && super::NeedsLoopBackwards(source_phantom, target_phantom); + if (super::facade->GetCoreSize() > 0) { - forward_heap.Clear(); - reverse_heap.Clear(); - - auto node_id = source_phantom.forward_node_id; - - for (const auto edge : super::facade->GetAdjacentEdgeRange(node_id)) - { - const auto& data = super::facade->GetEdgeData(edge); - if (data.forward) - { - auto target = super::facade->GetTarget(edge); - auto offset = total_distance_to_forward + data.distance - source_phantom.GetForwardWeightPlusOffset(); - forward_heap.Insert(target, offset, target); - } - - if (data.backward) - { - auto target = super::facade->GetTarget(edge); - auto offset = data.distance + target_phantom.GetForwardWeightPlusOffset(); - reverse_heap.Insert(target, offset, target); - } - } - - BOOST_ASSERT(forward_heap.Size() > 0); - BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_forward, - leg_packed_path_forward); - - // insert node to both endpoints to close the leg - leg_packed_path_forward.push_back(node_id); - std::reverse(leg_packed_path_forward.begin(), leg_packed_path_forward.end()); - leg_packed_path_forward.push_back(node_id); - std::reverse(leg_packed_path_forward.begin(), leg_packed_path_forward.end()); + forward_core_heap.Clear(); + reverse_core_heap.Clear(); + BOOST_ASSERT(forward_core_heap.Size() == 0); + BOOST_ASSERT(reverse_core_heap.Size() == 0); + super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, + new_total_distance, leg_packed_path, needs_loop_forwad, + needs_loop_backwards); } - - if (search_reverse_node) + else { - forward_heap.Clear(); - reverse_heap.Clear(); - - auto node_id = source_phantom.reverse_node_id; - - for (const auto edge : super::facade->GetAdjacentEdgeRange(node_id)) - { - const auto& data = super::facade->GetEdgeData(edge); - if (data.forward) - { - auto target = super::facade->GetTarget(edge); - auto offset = total_distance_to_reverse + data.distance - source_phantom.GetReverseWeightPlusOffset(); - forward_heap.Insert(target, offset, target); - } - - if (data.backward) - { - auto target = super::facade->GetTarget(edge); - auto offset = data.distance + target_phantom.GetReverseWeightPlusOffset(); - reverse_heap.Insert(target, offset, target); - } - } - - BOOST_ASSERT(forward_heap.Size() > 0); - BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse, - leg_packed_path_reverse); - - // insert node to both endpoints to close the leg - leg_packed_path_reverse.push_back(node_id); - std::reverse(leg_packed_path_reverse.begin(), leg_packed_path_reverse.end()); - leg_packed_path_reverse.push_back(node_id); - std::reverse(leg_packed_path_reverse.begin(), leg_packed_path_reverse.end()); + super::Search(forward_heap, reverse_heap, new_total_distance, leg_packed_path, + needs_loop_forwad, needs_loop_backwards); } - } // searches shortest path between: @@ -209,6 +114,8 @@ class ShortestPathRouting final // source forward/reverse -> target reverse void Search(QueryHeap &forward_heap, QueryHeap &reverse_heap, + QueryHeap &forward_core_heap, + QueryHeap &reverse_core_heap, const bool search_from_forward_node, const bool search_from_reverse_node, const bool search_to_forward_node, @@ -226,55 +133,87 @@ class ShortestPathRouting final { forward_heap.Clear(); reverse_heap.Clear(); - reverse_heap.Insert(target_phantom.forward_node_id, + reverse_heap.Insert(target_phantom.forward_segment_id.id, target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_node_id); + target_phantom.forward_segment_id.id); if (search_from_forward_node) { - forward_heap.Insert(source_phantom.forward_node_id, + forward_heap.Insert(source_phantom.forward_segment_id.id, total_distance_to_forward - source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_node_id); + source_phantom.forward_segment_id.id); } if (search_from_reverse_node) { - forward_heap.Insert(source_phantom.reverse_node_id, + forward_heap.Insert(source_phantom.reverse_segment_id.id, total_distance_to_reverse - source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_node_id); + source_phantom.reverse_segment_id.id); } BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_forward, - leg_packed_path_forward); + + if (super::facade->GetCoreSize() > 0) + { + forward_core_heap.Clear(); + reverse_core_heap.Clear(); + BOOST_ASSERT(forward_core_heap.Size() == 0); + BOOST_ASSERT(reverse_core_heap.Size() == 0); + super::SearchWithCore( + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, + new_total_distance_to_forward, leg_packed_path_forward, + super::NeedsLoopForward(source_phantom, target_phantom), DO_NOT_FORCE_LOOP); + } + else + { + super::Search(forward_heap, reverse_heap, new_total_distance_to_forward, + leg_packed_path_forward, + super::NeedsLoopForward(source_phantom, target_phantom), + DO_NOT_FORCE_LOOP); + } } if (search_to_reverse_node) { forward_heap.Clear(); reverse_heap.Clear(); - reverse_heap.Insert(target_phantom.reverse_node_id, + reverse_heap.Insert(target_phantom.reverse_segment_id.id, target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_node_id); + target_phantom.reverse_segment_id.id); if (search_from_forward_node) { - forward_heap.Insert(source_phantom.forward_node_id, + forward_heap.Insert(source_phantom.forward_segment_id.id, total_distance_to_forward - source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_node_id); + source_phantom.forward_segment_id.id); } if (search_from_reverse_node) { - forward_heap.Insert(source_phantom.reverse_node_id, + forward_heap.Insert(source_phantom.reverse_segment_id.id, total_distance_to_reverse - source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_node_id); + source_phantom.reverse_segment_id.id); } BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse, - leg_packed_path_reverse); + if (super::facade->GetCoreSize() > 0) + { + forward_core_heap.Clear(); + reverse_core_heap.Clear(); + BOOST_ASSERT(forward_core_heap.Size() == 0); + BOOST_ASSERT(reverse_core_heap.Size() == 0); + super::SearchWithCore(forward_heap, reverse_heap, forward_core_heap, + reverse_core_heap, new_total_distance_to_reverse, + leg_packed_path_reverse, DO_NOT_FORCE_LOOP, + super::NeedsLoopBackwards(source_phantom, target_phantom)); + } + else + { + super::Search(forward_heap, reverse_heap, new_total_distance_to_reverse, + leg_packed_path_reverse, DO_NOT_FORCE_LOOP, + super::NeedsLoopBackwards(source_phantom, target_phantom)); + } } } @@ -288,7 +227,7 @@ class ShortestPathRouting final raw_route_data.shortest_path_length = shortest_path_length; - for (const auto current_leg : osrm::irange(0, packed_leg_begin.size() - 1)) + for (const auto current_leg : util::irange(0UL, packed_leg_begin.size() - 1)) { auto leg_begin = total_packed_path.begin() + packed_leg_begin[current_leg]; auto leg_end = total_packed_path.begin() + packed_leg_begin[current_leg + 1]; @@ -297,28 +236,36 @@ class ShortestPathRouting final raw_route_data.unpacked_path_segments[current_leg]); raw_route_data.source_traversed_in_reverse.push_back( - (*leg_begin != phantom_nodes_vector[current_leg].source_phantom.forward_node_id)); + (*leg_begin != + phantom_nodes_vector[current_leg].source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( (*std::prev(leg_end) != - phantom_nodes_vector[current_leg].target_phantom.forward_node_id)); + phantom_nodes_vector[current_leg].target_phantom.forward_segment_id.id)); } } void operator()(const std::vector &phantom_nodes_vector, - const std::vector &uturn_indicators, + const boost::optional continue_straight_at_waypoint, InternalRouteResult &raw_route_data) const { - BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1); + const bool allow_uturn_at_waypoint = !(continue_straight_at_waypoint ? *continue_straight_at_waypoint : super::facade->GetContinueStraightDefault()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage( + super::facade->GetNumberOfNodes()); QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); + QueryHeap &forward_core_heap = *(engine_working_data.forward_heap_2); + QueryHeap &reverse_core_heap = *(engine_working_data.reverse_heap_2); int total_distance_to_forward = 0; int total_distance_to_reverse = 0; - bool search_from_forward_node = phantom_nodes_vector.front().source_phantom.forward_node_id != SPECIAL_NODEID; - bool search_from_reverse_node = phantom_nodes_vector.front().source_phantom.reverse_node_id != SPECIAL_NODEID; + bool search_from_forward_node = + phantom_nodes_vector.front().source_phantom.forward_segment_id.enabled; + bool search_from_reverse_node = + phantom_nodes_vector.front().source_phantom.reverse_segment_id.enabled; std::vector prev_packed_leg_to_forward; std::vector prev_packed_leg_to_reverse; @@ -342,47 +289,34 @@ class ShortestPathRouting final const auto &source_phantom = phantom_node_pair.source_phantom; const auto &target_phantom = phantom_node_pair.target_phantom; + bool search_to_forward_node = target_phantom.forward_segment_id.enabled; + bool search_to_reverse_node = target_phantom.reverse_segment_id.enabled; - BOOST_ASSERT(current_leg + 1 < uturn_indicators.size()); - const bool allow_u_turn_at_via = uturn_indicators[current_leg + 1]; - - bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID; - bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID; - - BOOST_ASSERT(!search_from_forward_node || source_phantom.forward_node_id != SPECIAL_NODEID); - BOOST_ASSERT(!search_from_reverse_node || source_phantom.reverse_node_id != SPECIAL_NODEID); - - if (source_phantom.forward_node_id == target_phantom.forward_node_id && - source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) - { - search_to_forward_node = search_from_reverse_node; - } - if (source_phantom.reverse_node_id == target_phantom.reverse_node_id && - source_phantom.GetReverseWeightPlusOffset() > target_phantom.GetReverseWeightPlusOffset()) - { - search_to_reverse_node = search_from_forward_node; - } + BOOST_ASSERT(!search_from_forward_node || source_phantom.forward_segment_id.enabled); + BOOST_ASSERT(!search_from_reverse_node || source_phantom.reverse_segment_id.enabled); BOOST_ASSERT(search_from_forward_node || search_from_reverse_node); - if(search_to_reverse_node || search_to_forward_node) + if (search_to_reverse_node || search_to_forward_node) { - if (allow_u_turn_at_via) + if (allow_uturn_at_waypoint) { - SearchWithUTurn(forward_heap, reverse_heap, search_from_forward_node, + SearchWithUTurn(forward_heap, reverse_heap, forward_core_heap, + reverse_core_heap, search_from_forward_node, search_from_reverse_node, search_to_forward_node, search_to_reverse_node, source_phantom, target_phantom, total_distance_to_forward, total_distance_to_reverse, new_total_distance_to_forward, packed_leg_to_forward); - // if only the reverse node is valid (e.g. when using the match plugin) we actually need to move - if (target_phantom.forward_node_id == SPECIAL_NODEID) + // if only the reverse node is valid (e.g. when using the match plugin) we + // actually need to move + if (!target_phantom.forward_segment_id.enabled) { - BOOST_ASSERT(target_phantom.reverse_node_id != SPECIAL_NODEID); + BOOST_ASSERT(target_phantom.reverse_segment_id.enabled); new_total_distance_to_reverse = new_total_distance_to_forward; packed_leg_to_reverse = std::move(packed_leg_to_forward); new_total_distance_to_forward = INVALID_EDGE_WEIGHT; } - else if (target_phantom.reverse_node_id != SPECIAL_NODEID) + else if (target_phantom.reverse_segment_id.enabled) { new_total_distance_to_reverse = new_total_distance_to_forward; packed_leg_to_reverse = packed_leg_to_forward; @@ -390,24 +324,14 @@ class ShortestPathRouting final } else { - Search(forward_heap, reverse_heap, search_from_forward_node, - search_from_reverse_node, search_to_forward_node, search_to_reverse_node, - source_phantom, target_phantom, total_distance_to_forward, - total_distance_to_reverse, new_total_distance_to_forward, - new_total_distance_to_reverse, packed_leg_to_forward, packed_leg_to_reverse); + Search(forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, + search_from_forward_node, search_from_reverse_node, + search_to_forward_node, search_to_reverse_node, source_phantom, + target_phantom, total_distance_to_forward, total_distance_to_reverse, + new_total_distance_to_forward, new_total_distance_to_reverse, + packed_leg_to_forward, packed_leg_to_reverse); } } - else - { - search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID; - search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID; - BOOST_ASSERT(search_from_reverse_node == search_to_reverse_node); - BOOST_ASSERT(search_from_forward_node == search_to_forward_node); - SearchLoop(forward_heap, reverse_heap, search_from_forward_node, - search_from_reverse_node, source_phantom, target_phantom, total_distance_to_forward, - total_distance_to_reverse, new_total_distance_to_forward, - new_total_distance_to_reverse, packed_leg_to_forward, packed_leg_to_reverse); - } // No path found for both target nodes? if ((INVALID_EDGE_WEIGHT == new_total_distance_to_forward) && @@ -423,16 +347,16 @@ class ShortestPathRouting final { bool forward_to_forward = (new_total_distance_to_forward != INVALID_EDGE_WEIGHT) && - packed_leg_to_forward.front() == source_phantom.forward_node_id; + packed_leg_to_forward.front() == source_phantom.forward_segment_id.id; bool reverse_to_forward = (new_total_distance_to_forward != INVALID_EDGE_WEIGHT) && - packed_leg_to_forward.front() == source_phantom.reverse_node_id; + packed_leg_to_forward.front() == source_phantom.reverse_segment_id.id; bool forward_to_reverse = (new_total_distance_to_reverse != INVALID_EDGE_WEIGHT) && - packed_leg_to_reverse.front() == source_phantom.forward_node_id; + packed_leg_to_reverse.front() == source_phantom.forward_segment_id.id; bool reverse_to_reverse = (new_total_distance_to_reverse != INVALID_EDGE_WEIGHT) && - packed_leg_to_reverse.front() == source_phantom.reverse_node_id; + packed_leg_to_reverse.front() == source_phantom.reverse_segment_id.id; BOOST_ASSERT(!forward_to_forward || !reverse_to_forward); BOOST_ASSERT(!forward_to_reverse || !reverse_to_reverse); @@ -467,7 +391,7 @@ class ShortestPathRouting final if (new_total_distance_to_forward != INVALID_EDGE_WEIGHT) { - BOOST_ASSERT(target_phantom.forward_node_id != SPECIAL_NODEID); + BOOST_ASSERT(target_phantom.forward_segment_id.enabled); packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size()); total_packed_path_to_forward.insert(total_packed_path_to_forward.end(), @@ -484,7 +408,7 @@ class ShortestPathRouting final if (new_total_distance_to_reverse != INVALID_EDGE_WEIGHT) { - BOOST_ASSERT(target_phantom.reverse_node_id != SPECIAL_NODEID); + BOOST_ASSERT(target_phantom.reverse_segment_id.enabled); packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size()); total_packed_path_to_reverse.insert(total_packed_path_to_reverse.end(), @@ -532,5 +456,8 @@ class ShortestPathRouting final } } }; +} +} +} #endif /* SHORTEST_PATH_HPP */ diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp new file mode 100644 index 000000000..3112a780c --- /dev/null +++ b/include/engine/search_engine_data.hpp @@ -0,0 +1,42 @@ +#ifndef SEARCH_ENGINE_DATA_HPP +#define SEARCH_ENGINE_DATA_HPP + +#include + +#include "util/typedefs.hpp" +#include "util/binary_heap.hpp" + +namespace osrm +{ +namespace engine +{ + +struct HeapData +{ + NodeID parent; + /* explicit */ HeapData(NodeID p) : parent(p) {} +}; + +struct SearchEngineData +{ + using QueryHeap = + util::BinaryHeap>; + using SearchEngineHeapPtr = boost::thread_specific_ptr; + + static SearchEngineHeapPtr forward_heap_1; + static SearchEngineHeapPtr reverse_heap_1; + static SearchEngineHeapPtr forward_heap_2; + static SearchEngineHeapPtr reverse_heap_2; + static SearchEngineHeapPtr forward_heap_3; + static SearchEngineHeapPtr reverse_heap_3; + + void InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes); + + void InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes); + + void InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes); +}; +} +} + +#endif // SEARCH_ENGINE_DATA_HPP diff --git a/util/mercator.hpp b/include/engine/status.hpp similarity index 83% rename from util/mercator.hpp rename to include/engine/status.hpp index e994c8499..4a0fc9a54 100644 --- a/util/mercator.hpp +++ b/include/engine/status.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,14 +25,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MERCATOR_HPP -#define MERCATOR_HPP +#ifndef ENGINE_STATUS_HPP +#define ENGINE_STATUS_HPP -struct mercator +namespace osrm +{ +namespace engine { - static double y2lat(const double value) noexcept; - static double lat2y(const double latitude) noexcept; +/** + * Status for indicating query success or failure. + * \see OSRM + */ +enum class Status +{ + Ok, + Error }; +} +} -#endif // MERCATOR_HPP +#endif diff --git a/algorithms/trip_brute_force.hpp b/include/engine/trip/trip_brute_force.hpp similarity index 57% rename from algorithms/trip_brute_force.hpp rename to include/engine/trip/trip_brute_force.hpp index 601971cf7..b8fe8ecc8 100644 --- a/algorithms/trip_brute_force.hpp +++ b/include/engine/trip/trip_brute_force.hpp @@ -1,38 +1,11 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TRIP_BRUTE_FORCE_HPP #define TRIP_BRUTE_FORCE_HPP -#include "../data_structures/search_engine.hpp" -#include "../util/dist_table_wrapper.hpp" -#include "../util/simple_logger.hpp" +#include "util/typedefs.hpp" +#include "util/dist_table_wrapper.hpp" +#include "util/simple_logger.hpp" -#include +#include "osrm/json_container.hpp" #include #include @@ -43,11 +16,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { +namespace engine +{ namespace trip { // computes the distance of a given permutation -EdgeWeight ReturnDistance(const DistTableWrapper &dist_table, +EdgeWeight ReturnDistance(const util::DistTableWrapper &dist_table, const std::vector &location_order, const EdgeWeight min_route_dist, const std::size_t component_size) @@ -71,7 +46,7 @@ template std::vector BruteForceTrip(const NodeIDIterator start, const NodeIDIterator end, const std::size_t number_of_locations, - const DistTableWrapper &dist_table) + const util::DistTableWrapper &dist_table) { (void)number_of_locations; // unused @@ -102,7 +77,8 @@ std::vector BruteForceTrip(const NodeIDIterator start, return route; } +} +} +} -} // end namespace trip -} // end namespace osrm #endif // TRIP_BRUTE_FORCE_HPP diff --git a/algorithms/trip_farthest_insertion.hpp b/include/engine/trip/trip_farthest_insertion.hpp similarity index 80% rename from algorithms/trip_farthest_insertion.hpp rename to include/engine/trip/trip_farthest_insertion.hpp index 91f0aa417..c062e72c7 100644 --- a/algorithms/trip_farthest_insertion.hpp +++ b/include/engine/trip/trip_farthest_insertion.hpp @@ -1,37 +1,11 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TRIP_FARTHEST_INSERTION_HPP #define TRIP_FARTHEST_INSERTION_HPP -#include "../data_structures/search_engine.hpp" -#include "../util/dist_table_wrapper.hpp" +#include "util/typedefs.hpp" +#include "util/dist_table_wrapper.hpp" +#include "util/typedefs.hpp" -#include +#include "osrm/json_container.hpp" #include #include @@ -42,6 +16,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { +namespace engine +{ namespace trip { @@ -50,7 +26,7 @@ namespace trip using NodeIDIter = std::vector::iterator; std::pair GetShortestRoundTrip(const NodeID new_loc, - const DistTableWrapper &dist_table, + const util::DistTableWrapper &dist_table, const std::size_t number_of_locations, std::vector &route) { @@ -103,7 +79,7 @@ std::vector FindRoute(const std::size_t &number_of_locations, const std::size_t &component_size, const NodeIDIterator &start, const NodeIDIterator &end, - const DistTableWrapper &dist_table, + const util::DistTableWrapper &dist_table, const NodeID &start1, const NodeID &start2) { @@ -165,7 +141,7 @@ template std::vector FarthestInsertionTrip(const NodeIDIterator &start, const NodeIDIterator &end, const std::size_t number_of_locations, - const DistTableWrapper &dist_table) + const util::DistTableWrapper &dist_table) { ////////////////////////////////////////////////////////////////////////////////////////////////// // START FARTHEST INSERTION HERE @@ -177,6 +153,13 @@ std::vector FarthestInsertionTrip(const NodeIDIterator &start, // 5. DONE! ////////////////////////////////////////////////////////////////////////////////////////////////// + + // Guard against division-by-zero in the code path below. + BOOST_ASSERT(number_of_locations > 0); + + // Guard against dist_table being empty therefore max_element returning the end iterator. + BOOST_ASSERT(dist_table.size() > 0); + const auto component_size = std::distance(start, end); BOOST_ASSERT(component_size >= 0); @@ -194,12 +177,16 @@ std::vector FarthestInsertionTrip(const NodeIDIterator &start, } else { - auto max_dist = 0; + auto max_dist = std::numeric_limits::min(); + for (auto x = start; x != end; ++x) { for (auto y = start; y != end; ++y) { const auto xy_dist = dist_table(*x, *y); + // SCC decomposition done correctly? + BOOST_ASSERT(xy_dist != INVALID_EDGE_WEIGHT); + if (xy_dist > max_dist) { max_dist = xy_dist; @@ -209,14 +196,15 @@ std::vector FarthestInsertionTrip(const NodeIDIterator &start, } } } + BOOST_ASSERT(max_from >= 0); BOOST_ASSERT(max_to >= 0); BOOST_ASSERT_MSG(static_cast(max_from) < number_of_locations, "start node"); BOOST_ASSERT_MSG(static_cast(max_to) < number_of_locations, "start node"); return FindRoute(number_of_locations, component_size, start, end, dist_table, max_from, max_to); } - -} // end namespace trip -} // end namespace osrm +} +} +} #endif // TRIP_FARTHEST_INSERTION_HPP diff --git a/algorithms/trip_nearest_neighbour.hpp b/include/engine/trip/trip_nearest_neighbour.hpp similarity index 64% rename from algorithms/trip_nearest_neighbour.hpp rename to include/engine/trip/trip_nearest_neighbour.hpp index 0ae179284..df70bc4eb 100644 --- a/algorithms/trip_nearest_neighbour.hpp +++ b/include/engine/trip/trip_nearest_neighbour.hpp @@ -1,38 +1,11 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TRIP_NEAREST_NEIGHBOUR_HPP #define TRIP_NEAREST_NEIGHBOUR_HPP -#include "../data_structures/search_engine.hpp" -#include "../util/simple_logger.hpp" -#include "../util/dist_table_wrapper.hpp" +#include "util/typedefs.hpp" +#include "util/simple_logger.hpp" +#include "util/dist_table_wrapper.hpp" -#include +#include "osrm/json_container.hpp" #include #include @@ -42,13 +15,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { +namespace engine +{ namespace trip { + template std::vector NearestNeighbourTrip(const NodeIDIterator &start, const NodeIDIterator &end, const std::size_t number_of_locations, - const DistTableWrapper &dist_table) + const util::DistTableWrapper &dist_table) { ////////////////////////////////////////////////////////////////////////////////////////////////// // START GREEDY NEAREST NEIGHBOUR HERE @@ -116,7 +92,8 @@ std::vector NearestNeighbourTrip(const NodeIDIterator &start, } return route; } +} +} +} -} // end namespace trip -} // end namespace osrm -#endif // TRIP_NEAREST_NEIGHBOUR_HPP \ No newline at end of file +#endif // TRIP_NEAREST_NEIGHBOUR_HPP diff --git a/include/engine/trip/trip_tabu_search.hpp b/include/engine/trip/trip_tabu_search.hpp new file mode 100644 index 000000000..24ff7455d --- /dev/null +++ b/include/engine/trip/trip_tabu_search.hpp @@ -0,0 +1,41 @@ +#ifndef TRIP_BRUTE_FORCE_HPP +#define TRIP_BRUTE_FORCE_HPP + +#include "engine/search_engine.hpp" +#include "util/simple_logger.hpp" + +#include "osrm/json_container.hpp" + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace trip +{ + +// todo: yet to be implemented +void TabuSearchTrip(std::vector &location, + const PhantomNodeArray &phantom_node_vector, + const std::vector &dist_table, + InternalRouteResult &min_route, + std::vector &min_loc_permutation) +{ +} + +void TabuSearchTrip(const PhantomNodeArray &phantom_node_vector, + const std::vector &dist_table, + InternalRouteResult &min_route, + std::vector &min_loc_permutation) +{ +} +} +} +} + +#endif // TRIP_BRUTE_FORCE_HPP diff --git a/include/extractor/compressed_edge_container.hpp b/include/extractor/compressed_edge_container.hpp new file mode 100644 index 000000000..662c18e5d --- /dev/null +++ b/include/extractor/compressed_edge_container.hpp @@ -0,0 +1,57 @@ +#ifndef GEOMETRY_COMPRESSOR_HPP_ +#define GEOMETRY_COMPRESSOR_HPP_ + +#include "util/typedefs.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +class CompressedEdgeContainer +{ + public: + struct CompressedEdge + { + public: + NodeID node_id; // refers to an internal node-based-node + EdgeWeight weight; // the weight of the edge leading to this node + }; + using EdgeBucket = std::vector; + + CompressedEdgeContainer(); + void CompressEdge(const EdgeID surviving_edge_id, + const EdgeID removed_edge_id, + const NodeID via_node_id, + const NodeID target_node, + const EdgeWeight weight1, + const EdgeWeight weight2); + + void + AddUncompressedEdge(const EdgeID edgei_id, const NodeID target_node, const EdgeWeight weight); + + bool HasEntryForID(const EdgeID edge_id) const; + void PrintStatistics() const; + void SerializeInternalVector(const std::string &path) const; + unsigned GetPositionForID(const EdgeID edge_id) const; + const EdgeBucket &GetBucketReference(const EdgeID edge_id) const; + NodeID GetFirstEdgeTargetID(const EdgeID edge_id) const; + NodeID GetLastEdgeSourceID(const EdgeID edge_id) const; + + private: + int free_list_maximum = 0; + + void IncreaseFreeList(); + std::vector m_compressed_geometries; + std::vector m_free_list; + std::unordered_map m_edge_id_to_list_index_map; +}; +} +} + +#endif // GEOMETRY_COMPRESSOR_HPP_ diff --git a/include/extractor/edge_based_edge.hpp b/include/extractor/edge_based_edge.hpp new file mode 100644 index 000000000..e4e5ef3f3 --- /dev/null +++ b/include/extractor/edge_based_edge.hpp @@ -0,0 +1,80 @@ +#ifndef EDGE_BASED_EDGE_HPP +#define EDGE_BASED_EDGE_HPP + +#include "extractor/travel_mode.hpp" +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace extractor +{ + +struct EdgeBasedEdge +{ + public: + EdgeBasedEdge(); + + template explicit EdgeBasedEdge(const EdgeT &other); + + EdgeBasedEdge(const NodeID source, + const NodeID target, + const NodeID edge_id, + const EdgeWeight weight, + const bool forward, + const bool backward); + + bool operator<(const EdgeBasedEdge &other) const; + + NodeID source; + NodeID target; + NodeID edge_id; + EdgeWeight weight : 30; + bool forward : 1; + bool backward : 1; +}; + +// Impl. + +inline EdgeBasedEdge::EdgeBasedEdge() + : source(0), target(0), edge_id(0), weight(0), forward(false), backward(false) +{ +} + +template +inline EdgeBasedEdge::EdgeBasedEdge(const EdgeT &other) + : source(other.source), target(other.target), edge_id(other.data.via), + weight(other.data.distance), forward(other.data.forward), backward(other.data.backward) +{ +} + +inline EdgeBasedEdge::EdgeBasedEdge(const NodeID source, + const NodeID target, + const NodeID edge_id, + const EdgeWeight weight, + const bool forward, + const bool backward) + : source(source), target(target), edge_id(edge_id), weight(weight), forward(forward), + backward(backward) +{ +} + +inline bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const +{ + if (source == other.source) + { + if (target == other.target) + { + if (weight == other.weight) + { + return forward && backward && ((!other.forward) || (!other.backward)); + } + return weight < other.weight; + } + return target < other.target; + } + return source < other.source; +} +} // ns extractor +} // ns osrm + +#endif /* EDGE_BASED_EDGE_HPP */ diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp new file mode 100644 index 000000000..1aff3908f --- /dev/null +++ b/include/extractor/edge_based_graph_factory.hpp @@ -0,0 +1,134 @@ +// This class constructs the edge-expanded routing graph + +#ifndef EDGE_BASED_GRAPH_FACTORY_HPP_ +#define EDGE_BASED_GRAPH_FACTORY_HPP_ + +#include "extractor/edge_based_edge.hpp" +#include "extractor/profile_properties.hpp" +#include "extractor/restriction_map.hpp" +#include "extractor/compressed_edge_container.hpp" +#include "extractor/edge_based_node.hpp" +#include "extractor/original_edge_data.hpp" +#include "extractor/query_node.hpp" +#include "extractor/guidance/turn_analysis.hpp" + +#include "extractor/guidance/turn_instruction.hpp" + +#include "util/node_based_graph.hpp" +#include "util/typedefs.hpp" +#include "util/deallocating_vector.hpp" +#include "util/name_table.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct lua_State; + +namespace osrm +{ +namespace extractor +{ + +class EdgeBasedGraphFactory +{ + public: + EdgeBasedGraphFactory(const EdgeBasedGraphFactory &) = delete; + EdgeBasedGraphFactory &operator=(const EdgeBasedGraphFactory &) = delete; + + explicit EdgeBasedGraphFactory(std::shared_ptr node_based_graph, + const CompressedEdgeContainer &compressed_edge_container, + const std::unordered_set &barrier_nodes, + const std::unordered_set &traffic_lights, + std::shared_ptr restriction_map, + const std::vector &node_info_list, + ProfileProperties profile_properties, + const util::NameTable &name_table); + + void Run(const std::string &original_edge_data_filename, + lua_State *lua_state, + const std::string &edge_segment_lookup_filename, + const std::string &edge_penalty_filename, + const bool generate_edge_lookup); + + // The following get access functions destroy the content in the factory + void GetEdgeBasedEdges(util::DeallocatingVector &edges); + void GetEdgeBasedNodes(std::vector &nodes); + void GetStartPointMarkers(std::vector &node_is_startpoint); + void GetEdgeBasedNodeWeights(std::vector &output_node_weights); + + unsigned GetHighestEdgeID(); + + // Basic analysis of a turn (u --(e1)-- v --(e2)-- w) + // with known angle. + // Handles special cases like u-turns and roundabouts + // For basic turns, the turn based on the angle-classification is returned + guidance::TurnInstruction AnalyzeTurn(const NodeID u, + const EdgeID e1, + const NodeID v, + const EdgeID e2, + const NodeID w, + const double angle) const; + + std::int32_t GetTurnPenalty(double angle, lua_State *lua_state) const; + + private: + using EdgeData = util::NodeBasedDynamicGraph::EdgeData; + + //! maps index from m_edge_based_node_list to ture/false if the node is an entry point to the + //! graph + std::vector m_edge_based_node_is_startpoint; + + //! node weights that indicate the length of the segment (node based) represented by the + //! edge-based node + std::vector m_edge_based_node_weights; + + //! list of edge based nodes (compressed segments) + std::vector m_edge_based_node_list; + util::DeallocatingVector m_edge_based_edge_list; + unsigned m_max_edge_id; + + const std::vector &m_node_info_list; + std::shared_ptr m_node_based_graph; + std::shared_ptr m_restriction_map; + + const std::unordered_set &m_barrier_nodes; + const std::unordered_set &m_traffic_lights; + const CompressedEdgeContainer &m_compressed_edge_container; + + ProfileProperties profile_properties; + + const util::NameTable &name_table; + + void CompressGeometry(); + unsigned RenumberEdges(); + void GenerateEdgeExpandedNodes(); + void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename, + lua_State *lua_state, + const std::string &edge_segment_lookup_filename, + const std::string &edge_fixed_penalties_filename, + const bool generate_edge_lookup); + + void InsertEdgeBasedNode(const NodeID u, const NodeID v); + + void FlushVectorToStream(std::ofstream &edge_data_file, + std::vector &original_edge_data_vector) const; + + std::size_t restricted_turns_counter; + std::size_t skipped_uturns_counter; + std::size_t skipped_barrier_turns_counter; +}; +} // namespace extractor +} // namespace osrm + +#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */ diff --git a/include/extractor/edge_based_node.hpp b/include/extractor/edge_based_node.hpp new file mode 100644 index 000000000..699e9e77c --- /dev/null +++ b/include/extractor/edge_based_node.hpp @@ -0,0 +1,76 @@ +#ifndef EDGE_BASED_NODE_HPP +#define EDGE_BASED_NODE_HPP + +#include "extractor/travel_mode.hpp" +#include "util/typedefs.hpp" + +#include + +#include "osrm/coordinate.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ + +/// This is what util::StaticRTree serialized and stores on disk +/// It is generated in EdgeBasedGraphFactory. +struct EdgeBasedNode +{ + EdgeBasedNode() + : forward_segment_id{SPECIAL_SEGMENTID, false}, + reverse_segment_id{SPECIAL_SEGMENTID, false}, u(SPECIAL_NODEID), + v(SPECIAL_NODEID), name_id(0), forward_packed_geometry_id(SPECIAL_EDGEID), + reverse_packed_geometry_id(SPECIAL_EDGEID), component{INVALID_COMPONENTID, false}, + fwd_segment_position(std::numeric_limits::max()), + forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), + backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) + { + } + + explicit EdgeBasedNode(const SegmentID forward_segment_id_, + const SegmentID reverse_segment_id_, + NodeID u, + NodeID v, + unsigned name_id, + unsigned forward_geometry_id_, + unsigned reverse_geometry_id_, + bool is_tiny_component, + unsigned component_id, + unsigned short fwd_segment_position, + TravelMode forward_travel_mode, + TravelMode backward_travel_mode) + : forward_segment_id(forward_segment_id_), + reverse_segment_id(reverse_segment_id_), u(u), v(v), name_id(name_id), + forward_packed_geometry_id(forward_geometry_id_), + reverse_packed_geometry_id(reverse_geometry_id_), + component{component_id, is_tiny_component}, fwd_segment_position(fwd_segment_position), + forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode) + { + BOOST_ASSERT(forward_segment_id.enabled || + reverse_segment_id.enabled); + } + + SegmentID forward_segment_id; // needed for edge-expanded graph + SegmentID reverse_segment_id; // needed for edge-expanded graph + NodeID u; // indices into the coordinates array + NodeID v; // indices into the coordinates array + unsigned name_id; // id of the edge name + + unsigned forward_packed_geometry_id; + unsigned reverse_packed_geometry_id; + struct + { + unsigned id : 31; + bool is_tiny : 1; + } component; + unsigned short fwd_segment_position; // segment id in a compressed geometry + TravelMode forward_travel_mode : 4; + TravelMode backward_travel_mode : 4; +}; +} +} + +#endif // EDGE_BASED_NODE_HPP diff --git a/include/extractor/external_memory_node.hpp b/include/extractor/external_memory_node.hpp new file mode 100644 index 000000000..d26f7d3ec --- /dev/null +++ b/include/extractor/external_memory_node.hpp @@ -0,0 +1,56 @@ +#ifndef EXTERNAL_MEMORY_NODE_HPP_ +#define EXTERNAL_MEMORY_NODE_HPP_ + +#include "extractor/query_node.hpp" + +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace extractor +{ + +struct ExternalMemoryNode : QueryNode +{ + ExternalMemoryNode(const util::FixedLongitude lon_, + const util::FixedLatitude lat_, + OSMNodeID node_id_, + bool barrier_, + bool traffic_lights_) + : QueryNode(lon_, lat_, node_id_), barrier(barrier_), traffic_lights(traffic_lights_) + { + } + + ExternalMemoryNode() : barrier(false), traffic_lights(false) {} + + static ExternalMemoryNode min_value() + { + return ExternalMemoryNode(util::FixedLongitude(0), util::FixedLatitude(0), MIN_OSM_NODEID, + false, false); + } + + static ExternalMemoryNode max_value() + { + return ExternalMemoryNode(util::FixedLongitude(std::numeric_limits::max()), + util::FixedLatitude(std::numeric_limits::max()), + MAX_OSM_NODEID, false, false); + } + + bool barrier; + bool traffic_lights; +}; + +struct ExternalMemoryNodeSTXXLCompare +{ + using value_type = ExternalMemoryNode; + value_type max_value() { return value_type::max_value(); } + value_type min_value() { return value_type::min_value(); } + bool operator()(const value_type &left, const value_type &right) const + { + return left.node_id < right.node_id; + } +}; +} +} + +#endif /* EXTERNAL_MEMORY_NODE_HPP_ */ diff --git a/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp similarity index 51% rename from extractor/extraction_containers.hpp rename to include/extractor/extraction_containers.hpp index 541ad3537..848e4576e 100644 --- a/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -1,42 +1,20 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef EXTRACTION_CONTAINERS_HPP #define EXTRACTION_CONTAINERS_HPP -#include "internal_extractor_edge.hpp" -#include "first_and_last_segment_of_way.hpp" -#include "scripting_environment.hpp" -#include "../data_structures/external_memory_node.hpp" -#include "../data_structures/restriction.hpp" +#include "extractor/internal_extractor_edge.hpp" +#include "extractor/first_and_last_segment_of_way.hpp" +#include "extractor/scripting_environment.hpp" +#include "extractor/external_memory_node.hpp" +#include "extractor/restriction.hpp" #include #include +namespace osrm +{ +namespace extractor +{ + /** * Uses external memory containers from stxxl to store all the data that * is collected by the extractor callbacks. @@ -56,10 +34,11 @@ class ExtractionContainers void PrepareRestrictions(); void PrepareEdges(lua_State *segment_state); - void WriteNodes(std::ofstream& file_out_stream) const; - void WriteRestrictions(const std::string& restrictions_file_name) const; - void WriteEdges(std::ofstream& file_out_stream) const; - void WriteNames(const std::string& names_file_name) const; + void WriteNodes(std::ofstream &file_out_stream) const; + void WriteRestrictions(const std::string &restrictions_file_name) const; + void WriteEdges(std::ofstream &file_out_stream) const; + void WriteNames(const std::string &names_file_name) const; + public: using STXXLNodeIDVector = stxxl::vector; using STXXLNodeVector = stxxl::vector; @@ -86,5 +65,7 @@ class ExtractionContainers const std::string &names_file_name, lua_State *segment_state); }; +} +} #endif /* EXTRACTION_CONTAINERS_HPP */ diff --git a/extractor/extraction_helper_functions.hpp b/include/extractor/extraction_helper_functions.hpp similarity index 53% rename from extractor/extraction_helper_functions.hpp rename to include/extractor/extraction_helper_functions.hpp index 69ab456af..cbd5a64b5 100644 --- a/extractor/extraction_helper_functions.hpp +++ b/include/extractor/extraction_helper_functions.hpp @@ -1,35 +1,8 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef EXTRACTION_HELPER_FUNCTIONS_HPP #define EXTRACTION_HELPER_FUNCTIONS_HPP -#include "../util/cast.hpp" -#include "../util/iso_8601_duration_parser.hpp" +#include "util/cast.hpp" +#include "util/iso_8601_duration_parser.hpp" #include #include @@ -39,7 +12,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -bool simple_duration_is_valid(const std::string &s) +namespace osrm +{ +namespace extractor +{ + +inline bool simple_duration_is_valid(const std::string &s) { boost::regex simple_format( "((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)", @@ -54,10 +32,10 @@ bool simple_duration_is_valid(const std::string &s) return false; } -bool iso_8601_duration_is_valid(const std::string &s) +inline bool iso_8601_duration_is_valid(const std::string &s) { - iso_8601_grammar iso_parser; - const bool result = qi::parse(s.begin(), s.end(), iso_parser); + util::iso_8601_grammar iso_parser; + const bool result = boost::spirit::qi::parse(s.begin(), s.end(), iso_parser); // check if the was an error with the request if (result && (0 != iso_parser.get_duration())) @@ -67,12 +45,12 @@ bool iso_8601_duration_is_valid(const std::string &s) return false; } -bool durationIsValid(const std::string &s) +inline bool durationIsValid(const std::string &s) { return simple_duration_is_valid(s) || iso_8601_duration_is_valid(s); } -unsigned parseDuration(const std::string &s) +inline unsigned parseDuration(const std::string &s) { if (simple_duration_is_valid(s)) { @@ -108,13 +86,15 @@ unsigned parseDuration(const std::string &s) } else if (iso_8601_duration_is_valid(s)) { - iso_8601_grammar iso_parser; - qi::parse(s.begin(), s.end(), iso_parser); + util::iso_8601_grammar iso_parser; + boost::spirit::qi::parse(s.begin(), s.end(), iso_parser); return iso_parser.get_duration(); } return std::numeric_limits::max(); } +} +} #endif // EXTRACTION_HELPER_FUNCTIONS_HPP diff --git a/include/extractor/extraction_node.hpp b/include/extractor/extraction_node.hpp new file mode 100644 index 000000000..e82f298ba --- /dev/null +++ b/include/extractor/extraction_node.hpp @@ -0,0 +1,19 @@ +#ifndef EXTRACTION_NODE_HPP +#define EXTRACTION_NODE_HPP + +namespace osrm +{ +namespace extractor +{ + +struct ExtractionNode +{ + ExtractionNode() : traffic_lights(false), barrier(false) {} + void clear() { traffic_lights = barrier = false; } + bool traffic_lights; + bool barrier; +}; +} +} + +#endif // EXTRACTION_NODE_HPP diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp new file mode 100644 index 000000000..f82787d96 --- /dev/null +++ b/include/extractor/extraction_way.hpp @@ -0,0 +1,58 @@ +#ifndef EXTRACTION_WAY_HPP +#define EXTRACTION_WAY_HPP + +#include "extractor/travel_mode.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +/** + * This struct is the direct result of the call to ```way_function``` + * in the lua based profile. + * + * It is split into multiple edge segments in the ExtractorCallback. + */ +struct ExtractionWay +{ + ExtractionWay() { clear(); } + + void clear() + { + forward_speed = -1; + backward_speed = -1; + duration = -1; + roundabout = false; + is_startpoint = true; + is_access_restricted = false; + name.clear(); + forward_travel_mode = TRAVEL_MODE_INACCESSIBLE; + backward_travel_mode = TRAVEL_MODE_INACCESSIBLE; + } + + // These accessors exists because it's not possible to take the address of a bitfield, + // and LUA therefore cannot read/write the mode attributes directly. + void set_forward_mode(const TravelMode m) { forward_travel_mode = m; } + TravelMode get_forward_mode() const { return forward_travel_mode; } + void set_backward_mode(const TravelMode m) { backward_travel_mode = m; } + TravelMode get_backward_mode() const { return backward_travel_mode; } + + double forward_speed; + double backward_speed; + double duration; + std::string name; + bool roundabout; + bool is_access_restricted; + bool is_startpoint; + TravelMode forward_travel_mode : 4; + TravelMode backward_travel_mode : 4; +}; +} +} + +#endif // EXTRACTION_WAY_HPP diff --git a/extractor/extractor.hpp b/include/extractor/extractor.hpp similarity index 65% rename from extractor/extractor.hpp rename to include/extractor/extractor.hpp index c147f1961..1b61513c1 100644 --- a/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -28,39 +28,56 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef EXTRACTOR_HPP #define EXTRACTOR_HPP -#include "extractor_options.hpp" -#include "edge_based_graph_factory.hpp" -#include "../algorithms/graph_compressor.hpp" +#include "extractor/edge_based_edge.hpp" +#include "extractor/extractor_config.hpp" +#include "extractor/edge_based_graph_factory.hpp" +#include "extractor/graph_compressor.hpp" -class extractor +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace extractor +{ + +struct ProfileProperties; + +class Extractor { public: - extractor(ExtractorConfig extractor_config) : config(std::move(extractor_config)) {} + Extractor(ExtractorConfig extractor_config) : config(std::move(extractor_config)) {} int run(); private: ExtractorConfig config; - void SetupScriptingEnvironment(lua_State *myLuaState, SpeedProfileProperties &speed_profile); + std::pair - BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, + BuildEdgeExpandedGraph(lua_State* lua_state, + const ProfileProperties& profile_properties, + std::vector &internal_to_external_node_map, std::vector &node_based_edge_list, std::vector &node_is_startpoint, - DeallocatingVector &edge_based_edge_list); + std::vector &edge_based_node_weights, + util::DeallocatingVector &edge_based_edge_list); + void WriteProfileProperties(const std::string& output_path, const ProfileProperties& properties) const; void WriteNodeMapping(const std::vector &internal_to_external_node_map); void FindComponents(unsigned max_edge_id, - const DeallocatingVector &edges, + const util::DeallocatingVector &edges, std::vector &nodes) const; void BuildRTree(std::vector node_based_edge_list, std::vector node_is_startpoint, const std::vector &internal_to_external_node_map); std::shared_ptr LoadRestrictionMap(); - std::shared_ptr + std::shared_ptr LoadNodeBasedGraph(std::unordered_set &barrier_nodes, std::unordered_set &traffic_lights, std::vector &internal_to_external_node_map); - void WriteEdgeBasedGraph(std::string const &output_file_filename, - size_t const max_edge_id, - DeallocatingVector const &edge_based_edge_list); + void WriteEdgeBasedGraph(const std::string &output_file_filename, + const size_t max_edge_id, + util::DeallocatingVector const &edge_based_edge_list); }; +} +} + #endif /* EXTRACTOR_HPP */ diff --git a/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp similarity index 51% rename from extractor/extractor_callbacks.hpp rename to include/extractor/extractor_callbacks.hpp index 0026b6fb0..db4e870f3 100644 --- a/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -1,50 +1,28 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef EXTRACTOR_CALLBACKS_HPP #define EXTRACTOR_CALLBACKS_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" #include #include #include -struct ExternalMemoryNode; -class ExtractionContainers; -struct InputRestrictionContainer; -struct ExtractionNode; -struct ExtractionWay; namespace osmium { class Node; class Way; } +namespace osrm +{ +namespace extractor +{ + +class ExtractionContainers; +struct InputRestrictionContainer; +struct ExtractionNode; +struct ExtractionWay; + /** * This class is uses by the extractor with the results of the * osmium based parsing and the customization through the lua profile. @@ -60,10 +38,11 @@ class ExtractorCallbacks ExtractionContainers &external_memory; public: - ExtractorCallbacks() = delete; - ExtractorCallbacks(const ExtractorCallbacks &) = delete; explicit ExtractorCallbacks(ExtractionContainers &extraction_containers); + ExtractorCallbacks(const ExtractorCallbacks &) = delete; + ExtractorCallbacks &operator=(const ExtractorCallbacks &) = delete; + // warning: caller needs to take care of synchronization! void ProcessNode(const osmium::Node ¤t_node, const ExtractionNode &result_node); @@ -73,5 +52,7 @@ class ExtractorCallbacks // warning: caller needs to take care of synchronization! void ProcessWay(const osmium::Way ¤t_way, const ExtractionWay &result_way); }; +} +} #endif /* EXTRACTOR_CALLBACKS_HPP */ diff --git a/extractor/extractor_options.hpp b/include/extractor/extractor_config.hpp similarity index 56% rename from extractor/extractor_options.hpp rename to include/extractor/extractor_config.hpp index 00d6f84b8..80196db87 100644 --- a/extractor/extractor_options.hpp +++ b/include/extractor/extractor_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,23 +25,55 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EXTRACTOR_OPTIONS_HPP -#define EXTRACTOR_OPTIONS_HPP +#ifndef EXTRACTOR_CONFIG_HPP +#define EXTRACTOR_CONFIG_HPP #include #include +#include -enum class return_code : unsigned +namespace osrm +{ +namespace extractor { - ok, - fail, - exit -}; struct ExtractorConfig { ExtractorConfig() noexcept : requested_num_threads(0) {} + void UseDefaultOutputNames() + { + std::string basepath = input_path.string(); + + auto pos = std::string::npos; + std::array known_extensions{ + {".osm.bz2", ".osm.pbf", ".osm.xml", ".pbf", ".osm"}}; + for (auto ext : known_extensions) + { + pos = basepath.find(ext); + if (pos != std::string::npos) + { + basepath.replace(pos, ext.size(), ""); + break; + } + } + + output_file_name = basepath + ".osrm"; + restriction_file_name = basepath + ".osrm.restrictions"; + names_file_name = basepath + ".osrm.names"; + timestamp_file_name = basepath + ".osrm.timestamp"; + geometry_output_path = basepath + ".osrm.geometry"; + node_output_path = basepath + ".osrm.nodes"; + edge_output_path = basepath + ".osrm.edges"; + edge_graph_output_path = basepath + ".osrm.ebg"; + rtree_nodes_output_path = basepath + ".osrm.ramIndex"; + rtree_leafs_output_path = basepath + ".osrm.fileIndex"; + edge_segment_lookup_path = basepath + ".osrm.edge_segment_lookup"; + edge_penalty_path = basepath + ".osrm.edge_penalties"; + edge_based_node_weights_output_path = basepath + ".osrm.enw"; + profile_properties_output_path = basepath + ".osrm.properties"; + } + boost::filesystem::path config_file_path; boost::filesystem::path input_path; boost::filesystem::path profile_path; @@ -53,9 +85,11 @@ struct ExtractorConfig std::string geometry_output_path; std::string edge_output_path; std::string edge_graph_output_path; + std::string edge_based_node_weights_output_path; std::string node_output_path; std::string rtree_nodes_output_path; std::string rtree_leafs_output_path; + std::string profile_properties_output_path; unsigned requested_num_threads; unsigned small_component_size; @@ -63,16 +97,8 @@ struct ExtractorConfig bool generate_edge_lookup; std::string edge_penalty_path; std::string edge_segment_lookup_path; -#ifdef DEBUG_GEOMETRY - std::string debug_turns_path; -#endif }; +} +} -struct ExtractorOptions -{ - static return_code ParseArguments(int argc, char *argv[], ExtractorConfig &extractor_config); - - static void GenerateOutputFilesNames(ExtractorConfig &extractor_config); -}; - -#endif // EXTRACTOR_OPTIONS_HPP +#endif // EXTRACTOR_CONFIG_HPP diff --git a/include/extractor/first_and_last_segment_of_way.hpp b/include/extractor/first_and_last_segment_of_way.hpp new file mode 100644 index 000000000..37d0e40c8 --- /dev/null +++ b/include/extractor/first_and_last_segment_of_way.hpp @@ -0,0 +1,60 @@ +#ifndef FIRST_AND_LAST_SEGMENT_OF_WAY_HPP +#define FIRST_AND_LAST_SEGMENT_OF_WAY_HPP + +#include "extractor/external_memory_node.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +struct FirstAndLastSegmentOfWay +{ + OSMWayID way_id; + OSMNodeID first_segment_source_id; + OSMNodeID first_segment_target_id; + OSMNodeID last_segment_source_id; + OSMNodeID last_segment_target_id; + + FirstAndLastSegmentOfWay() + : way_id(SPECIAL_OSM_WAYID), first_segment_source_id(SPECIAL_OSM_NODEID), + first_segment_target_id(SPECIAL_OSM_NODEID), last_segment_source_id(SPECIAL_OSM_NODEID), + last_segment_target_id(SPECIAL_OSM_NODEID) + { + } + + FirstAndLastSegmentOfWay(OSMWayID w, OSMNodeID fs, OSMNodeID ft, OSMNodeID ls, OSMNodeID lt) + : way_id(std::move(w)), first_segment_source_id(std::move(fs)), + first_segment_target_id(std::move(ft)), last_segment_source_id(std::move(ls)), + last_segment_target_id(std::move(lt)) + { + } + + static FirstAndLastSegmentOfWay min_value() + { + return {MIN_OSM_WAYID, MIN_OSM_NODEID, MIN_OSM_NODEID, MIN_OSM_NODEID, MIN_OSM_NODEID}; + } + static FirstAndLastSegmentOfWay max_value() + { + return {MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID}; + } +}; + +struct FirstAndLastSegmentOfWayStxxlCompare +{ + using value_type = FirstAndLastSegmentOfWay; + bool operator()(const FirstAndLastSegmentOfWay &a, const FirstAndLastSegmentOfWay &b) const + { + return a.way_id < b.way_id; + } + value_type max_value() { return FirstAndLastSegmentOfWay::max_value(); } + value_type min_value() { return FirstAndLastSegmentOfWay::min_value(); } +}; +} +} + +#endif /* FIRST_AND_LAST_SEGMENT_OF_WAY_HPP */ diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp new file mode 100644 index 000000000..78f3eaab3 --- /dev/null +++ b/include/extractor/graph_compressor.hpp @@ -0,0 +1,38 @@ +#ifndef GEOMETRY_COMPRESSOR_HPP +#define GEOMETRY_COMPRESSOR_HPP + +#include "util/typedefs.hpp" + +#include "util/node_based_graph.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +class CompressedEdgeContainer; +class RestrictionMap; + +class GraphCompressor +{ + using EdgeData = util::NodeBasedDynamicGraph::EdgeData; + + public: + void Compress(const std::unordered_set &barrier_nodes, + const std::unordered_set &traffic_lights, + RestrictionMap &restriction_map, + util::NodeBasedDynamicGraph &graph, + CompressedEdgeContainer &geometry_compressor); + + private: + void PrintStatistics(unsigned original_number_of_nodes, + unsigned original_number_of_edges, + const util::NodeBasedDynamicGraph &graph) const; +}; +} +} + +#endif diff --git a/include/extractor/guidance/classification_data.hpp b/include/extractor/guidance/classification_data.hpp new file mode 100644 index 000000000..53f54cf46 --- /dev/null +++ b/include/extractor/guidance/classification_data.hpp @@ -0,0 +1,67 @@ +#ifndef OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_ +#define OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_ + +#include + +#include + +// Forward Declaration to allow usage of external osmium::Way +namespace osmium +{ +class Way; +} + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +enum class FunctionalRoadClass : std::uint8_t +{ + UNKNOWN = 0, + MOTORWAY, + MOTORWAY_LINK, + TRUNK, + TRUNK_LINK, + PRIMARY, + PRIMARY_LINK, + SECONDARY, + SECONDARY_LINK, + TERTIARY, + TERTIARY_LINK, + UNCLASSIFIED, + RESIDENTIAL, + SERVICE, + LIVING_STREET, + LOW_PRIORITY_ROAD // a road simply included for connectivity. Should be avoided at all cost +}; + +FunctionalRoadClass functionalRoadClassFromTag(std::string const &tag); + +inline bool isRampClass(const FunctionalRoadClass road_class) +{ + // Primary Roads and down are usually too small to announce their links as ramps + return road_class == FunctionalRoadClass::MOTORWAY_LINK || + road_class == FunctionalRoadClass::TRUNK_LINK; +} + +// TODO augment this with all data required for guidance generation +struct RoadClassificationData +{ + FunctionalRoadClass road_class = FunctionalRoadClass::UNKNOWN; + + void augment(const osmium::Way &way); +}; + +inline bool operator==(const RoadClassificationData lhs, const RoadClassificationData rhs) +{ + return lhs.road_class == rhs.road_class; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_ diff --git a/include/extractor/guidance/constants.hpp b/include/extractor/guidance/constants.hpp new file mode 100644 index 000000000..041868221 --- /dev/null +++ b/include/extractor/guidance/constants.hpp @@ -0,0 +1,32 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_ + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +const bool constexpr INVERT = true; + +// what angle is interpreted as going straight +const double constexpr STRAIGHT_ANGLE = 180.; +// if a turn deviates this much from going straight, it will be kept straight +const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.; +// angle that lies between two nearly indistinguishable roads +const double constexpr NARROW_TURN_ANGLE = 40.; +const double constexpr GROUP_ANGLE = 90; +// angle difference that can be classified as straight, if its the only narrow turn +const double constexpr FUZZY_ANGLE_DIFFERENCE = 15.; +const double constexpr DISTINCTION_RATIO = 2; +const unsigned constexpr INVALID_NAME_ID = 0; + +const double constexpr MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction +const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_GUIDANCE_CONSTANTS_HPP_ diff --git a/include/extractor/guidance/discrete_angle.hpp b/include/extractor/guidance/discrete_angle.hpp new file mode 100644 index 000000000..08509cb51 --- /dev/null +++ b/include/extractor/guidance/discrete_angle.hpp @@ -0,0 +1,19 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE +#define OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +using DiscreteAngle = std::uint8_t; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /* OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE */ diff --git a/include/extractor/guidance/intersection.hpp b/include/extractor/guidance/intersection.hpp new file mode 100644 index 000000000..8b350e24d --- /dev/null +++ b/include/extractor/guidance/intersection.hpp @@ -0,0 +1,66 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_ + +#include +#include + +#include "extractor/guidance/turn_instruction.hpp" +#include "util/typedefs.hpp" // EdgeID + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +// Every Turn Operation describes a way of switching onto a segment, indicated by an EdgeID. The +// associated turn is described by an angle and an instruction that is used to announce it. +// The Turn Operation indicates what is exposed to the outside of the turn analysis. +struct TurnOperation final +{ + EdgeID eid; + double angle; + TurnInstruction instruction; +}; + +// A Connected Road is the internal representation of a potential turn. Internally, we require +// full list of all connected roads to determine the outcome. +// The reasoning behind is that even invalid turns can influence the perceived angles, or even +// instructions themselves. An pososible example can be described like this: +// +// aaa(2)aa +// a - bbbbb +// aaa(1)aa +// +// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2). +// In addition, they can influence whether a turn is obvious or not. b->(2) would also be no +// turn-operation, +// but rather a name change. +// +// If this were a normal intersection with +// +// cccccccc +// o bbbbb +// aaaaaaaa +// +// We would perceive a->c as a sharp turn, a->b as a slight turn, and b->c as a slight turn. +struct ConnectedRoad final +{ + ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false); + + // a turn may be relevant to good instructions, even if we cannot enter the road + bool entry_allowed; + TurnOperation turn; +}; + +// small helper function to print the content of a connected road +std::string toString(const ConnectedRoad &road); + +typedef std::vector Intersection; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_*/ diff --git a/include/extractor/guidance/intersection_generator.hpp b/include/extractor/guidance/intersection_generator.hpp new file mode 100644 index 000000000..be99a71c9 --- /dev/null +++ b/include/extractor/guidance/intersection_generator.hpp @@ -0,0 +1,68 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/guidance/intersection.hpp" +#include "extractor/query_node.hpp" +#include "extractor/restriction_map.hpp" +#include "util/node_based_graph.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ +// The Intersection Generator is given a turn location and generates an intersection representation +// from it. For this all turn possibilities are analysed. +// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile +// decisions. + +class IntersectionGenerator +{ + public: + IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const std::vector &node_info_list, + const CompressedEdgeContainer &compressed_edge_container); + + Intersection operator()(const NodeID nid, const EdgeID via_eid) const; + + private: + const util::NodeBasedDynamicGraph &node_based_graph; + const RestrictionMap &restriction_map; + const std::unordered_set &barrier_nodes; + const std::vector &node_info_list; + const CompressedEdgeContainer &compressed_edge_container; + + // Check for restrictions/barriers and generate a list of valid and invalid turns present at + // the + // node reached + // from `from_node` via `via_eid` + // The resulting candidates have to be analysed for their actual instructions later on. + Intersection getConnectedRoads(const NodeID from_node, const EdgeID via_eid) const; + + // Merge segregated roads to omit invalid turns in favor of treating segregated roads as + // one. + // This function combines roads the following way: + // + // * * + // * is converted to * + // v ^ + + // v ^ + + // + // The treatment results in a straight turn angle of 180º rather than a turn angle of approx + // 160 + Intersection mergeSegregatedRoads(Intersection intersection) const; +}; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */ diff --git a/include/extractor/guidance/intersection_handler.hpp b/include/extractor/guidance/intersection_handler.hpp new file mode 100644 index 000000000..25d2f4a53 --- /dev/null +++ b/include/extractor/guidance/intersection_handler.hpp @@ -0,0 +1,71 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_ + +#include "extractor/guidance/intersection.hpp" +#include "extractor/query_node.hpp" + +#include "util/name_table.hpp" +#include "util/node_based_graph.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +// Intersection handlers deal with all issues related to intersections. +// They assign appropriate turn operations to the TurnOperations. +// This base class provides both the interface and implementations for +// common functions. +class IntersectionHandler +{ + public: + IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table); + virtual ~IntersectionHandler(); + + // check whether the handler can actually handle the intersection + virtual bool + canProcess(const NodeID nid, const EdgeID via_eid, const Intersection &intersection) const = 0; + + // process the intersection + virtual Intersection + operator()(const NodeID nid, const EdgeID via_eid, Intersection intersection) const = 0; + + protected: + const util::NodeBasedDynamicGraph &node_based_graph; + const std::vector &node_info_list; + const util::NameTable &name_table; + + // counts the number on allowed entry roads + std::size_t countValid(const Intersection &intersection) const; + + // Decide on a basic turn types + TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const; + + // Get the Instruction for an obvious turn + TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates, + const EdgeID via_edge, + const bool through_street, + const ConnectedRoad &candidate) const; + + // Treating potential forks + void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const; + void assignFork(const EdgeID via_edge, + ConnectedRoad &left, + ConnectedRoad ¢er, + ConnectedRoad &right) const; + + bool isThroughStreet(const std::size_t index, const Intersection &intersection) const; +}; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_*/ diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp new file mode 100644 index 000000000..e3d9e34b6 --- /dev/null +++ b/include/extractor/guidance/motorway_handler.hpp @@ -0,0 +1,51 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_ + +#include "extractor/guidance/intersection_handler.hpp" +#include "extractor/guidance/intersection.hpp" +#include "extractor/query_node.hpp" + +#include "util/name_table.hpp" +#include "util/node_based_graph.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +// Intersection handlers deal with all issues related to intersections. +// They assign appropriate turn operations to the TurnOperations. +class MotorwayHandler : public IntersectionHandler +{ + public: + MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table); + ~MotorwayHandler() override final; + + // check whether the handler can actually handle the intersection + bool canProcess(const NodeID nid, + const EdgeID via_eid, + const Intersection &intersection) const override final; + + // process the intersection + Intersection operator()(const NodeID nid, + const EdgeID via_eid, + Intersection intersection) const override final; + + private: + Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const; + Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const; + + Intersection fallback(Intersection intersection) const; +}; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /*OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_*/ diff --git a/include/extractor/guidance/roundabout_handler.hpp b/include/extractor/guidance/roundabout_handler.hpp new file mode 100644 index 000000000..6a48387d5 --- /dev/null +++ b/include/extractor/guidance/roundabout_handler.hpp @@ -0,0 +1,71 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_ + +#include "extractor/guidance/intersection.hpp" +#include "extractor/guidance/intersection_handler.hpp" +#include "extractor/query_node.hpp" + +#include "util/name_table.hpp" +#include "util/node_based_graph.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +namespace detail +{ +struct RoundaboutFlags +{ + bool on_roundabout; + bool can_enter; + bool can_exit_separately; +}; +} // namespace detail + +// The roundabout handler processes all roundabout related instructions. +// It performs both the distinction between rotaries and roundabouts and +// assigns appropriate entry/exit instructions. +class RoundaboutHandler : public IntersectionHandler +{ + public: + RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table); + + ~RoundaboutHandler() override final; + + // check whether the handler can actually handle the intersection + bool canProcess(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const override final; + + // process the intersection + Intersection operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const override final; + + private: + detail::RoundaboutFlags getRoundaboutFlags(const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const; + + // decide whether we lookk at a roundabout or a rotary + bool isRotary(const NodeID nid) const; + + // TODO handle bike/walk cases that allow crossing a roundabout! + // Processing of roundabouts + // Produces instructions to enter/exit a roundabout or to stay on it. + // Performs the distinction between roundabout and rotaries. + Intersection handleRoundabouts(const bool is_rotary, + const EdgeID via_edge, + const bool on_roundabout, + const bool can_exit_roundabout, + Intersection intersection) const; +}; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /*OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_*/ diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp new file mode 100644 index 000000000..35fd5472c --- /dev/null +++ b/include/extractor/guidance/toolkit.hpp @@ -0,0 +1,404 @@ +#ifndef OSRM_GUIDANCE_TOOLKIT_HPP_ +#define OSRM_GUIDANCE_TOOLKIT_HPP_ + +#include "util/bearing.hpp" +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/query_node.hpp" + +#include "extractor/guidance/classification_data.hpp" +#include "extractor/guidance/discrete_angle.hpp" +#include "extractor/guidance/turn_instruction.hpp" + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +namespace detail +{ +const constexpr double DESIRED_SEGMENT_LENGTH = 10.0; +const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, true, false}; +const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true}; +const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = { + 0, 36, 93, 121, 136, 163, 220, 255}; +const constexpr double discrete_angle_step_size = 360. / 256.; + +template +util::Coordinate +getCoordinateFromCompressedRange(util::Coordinate current_coordinate, + const IteratorType compressed_geometry_begin, + const IteratorType compressed_geometry_end, + const util::Coordinate final_coordinate, + const std::vector &query_nodes) +{ + const auto extractCoordinateFromNode = + [](const extractor::QueryNode &node) -> util::Coordinate { + return {node.lon, node.lat}; + }; + double distance_to_current_coordinate = 0; + double distance_to_next_coordinate = 0; + + // get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH + const auto getFactor = [](const double first_distance, const double second_distance) { + BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH); + double segment_length = second_distance - first_distance; + BOOST_ASSERT(segment_length > 0); + BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH); + double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance; + return std::max(0., std::min(missing_distance / segment_length, 1.0)); + }; + + for (auto compressed_geometry_itr = compressed_geometry_begin; + compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr) + { + const auto next_coordinate = + extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]); + distance_to_next_coordinate = + distance_to_current_coordinate + + util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate); + + // reached point where coordinates switch between + if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH) + return util::coordinate_calculation::interpolateLinear( + getFactor(distance_to_current_coordinate, distance_to_next_coordinate), + current_coordinate, next_coordinate); + + // prepare for next iteration + current_coordinate = next_coordinate; + distance_to_current_coordinate = distance_to_next_coordinate; + } + + distance_to_next_coordinate = + distance_to_current_coordinate + + util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate); + + // reached point where coordinates switch between + if (distance_to_current_coordinate < detail::DESIRED_SEGMENT_LENGTH && + distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH) + return util::coordinate_calculation::interpolateLinear( + getFactor(distance_to_current_coordinate, distance_to_next_coordinate), + current_coordinate, final_coordinate); + else + return final_coordinate; +} +} // namespace detail + +// Finds a (potentially inteprolated) coordinate that is DESIRED_SEGMENT_LENGTH away +// from the start of an edge +inline util::Coordinate +getRepresentativeCoordinate(const NodeID from_node, + const NodeID to_node, + const EdgeID via_edge_id, + const bool traverse_in_reverse, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &query_nodes) +{ + const auto extractCoordinateFromNode = + [](const extractor::QueryNode &node) -> util::Coordinate { + return {node.lon, node.lat}; + }; + + // Uncompressed roads are simple, return the coordinate at the end + if (!compressed_geometries.HasEntryForID(via_edge_id)) + { + return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node] + : query_nodes[to_node]); + } + else + { + const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id); + + const auto base_node_id = (traverse_in_reverse) ? to_node : from_node; + const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]); + + const auto final_node = (traverse_in_reverse) ? from_node : to_node; + const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]); + + if (traverse_in_reverse) + return detail::getCoordinateFromCompressedRange( + base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes); + else + return detail::getCoordinateFromCompressedRange( + base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes); + } +} + +// shift an instruction around the degree circle in CCW order +inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier) +{ + return static_cast((static_cast(modifier) + 1) % + detail::num_direction_modifiers); +} + +inline DirectionModifier shiftCCW(const DirectionModifier modifier) +{ + if (detail::shiftable_ccw[static_cast(modifier)]) + return forcedShiftCCW(modifier); + else + return modifier; +} + +// shift an instruction around the degree circle in CW order +inline DirectionModifier forcedShiftCW(const DirectionModifier modifier) +{ + return static_cast( + (static_cast(modifier) + detail::num_direction_modifiers - 1) % + detail::num_direction_modifiers); +} + +inline DirectionModifier shiftCW(const DirectionModifier modifier) +{ + if (detail::shiftable_cw[static_cast(modifier)]) + return forcedShiftCW(modifier); + else + return modifier; +} + +inline bool isBasic(const TurnType type) +{ + return type == TurnType::Turn || type == TurnType::EndOfRoad; +} + +inline bool isUturn(const TurnInstruction instruction) +{ + return isBasic(instruction.type) && instruction.direction_modifier == DirectionModifier::UTurn; +} + +inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_cw) +{ + const auto shifted_turn = resolve_cw ? shiftCW(to_resolve.direction_modifier) + : shiftCCW(to_resolve.direction_modifier); + if (shifted_turn == neighbor.direction_modifier || + shifted_turn == to_resolve.direction_modifier) + return false; + + to_resolve.direction_modifier = shifted_turn; + return true; +} + +inline bool resolveTransitive(TurnInstruction &first, + TurnInstruction &second, + const TurnInstruction third, + bool resolve_cw) +{ + if (resolve(second, third, resolve_cw)) + { + first.direction_modifier = + resolve_cw ? shiftCW(first.direction_modifier) : shiftCCW(first.direction_modifier); + return true; + } + return false; +} + +inline bool isSlightTurn(const TurnInstruction turn) +{ + return (isBasic(turn.type) || turn.type == TurnType::NoTurn) && + (turn.direction_modifier == DirectionModifier::Straight || + turn.direction_modifier == DirectionModifier::SlightRight || + turn.direction_modifier == DirectionModifier::SlightLeft); +} + +inline bool isSlightModifier(const DirectionModifier direction_modifier) +{ + return (direction_modifier == DirectionModifier::Straight || + direction_modifier == DirectionModifier::SlightRight || + direction_modifier == DirectionModifier::SlightLeft); +} + +inline bool isSharpTurn(const TurnInstruction turn) +{ + return isBasic(turn.type) && (turn.direction_modifier == DirectionModifier::SharpLeft || + turn.direction_modifier == DirectionModifier::SharpRight); +} + +inline bool isStraight(const TurnInstruction turn) +{ + return (isBasic(turn.type) || turn.type == TurnType::NoTurn) && + turn.direction_modifier == DirectionModifier::Straight; +} + +inline bool isConflict(const TurnInstruction first, const TurnInstruction second) +{ + return (first.type == second.type && first.direction_modifier == second.direction_modifier) || + (isStraight(first) && isStraight(second)); +} + +inline DiscreteAngle discretizeAngle(const double angle) +{ + BOOST_ASSERT(angle >= 0. && angle <= 360.); + return DiscreteAngle(static_cast(angle / detail::discrete_angle_step_size)); +} + +inline double angleFromDiscreteAngle(const DiscreteAngle angle) +{ + return static_cast(angle) * detail::discrete_angle_step_size; +} + +inline double angularDeviation(const double angle, const double from) +{ + const double deviation = std::abs(angle - from); + return std::min(360 - deviation, deviation); +} + +inline double getAngularPenalty(const double angle, DirectionModifier modifier) +{ + // these are not aligned with getTurnDirection but represent an ideal center + const double center[] = {0, 45, 90, 135, 180, 225, 270, 315}; + return angularDeviation(center[static_cast(modifier)], angle); +} + +inline double getTurnConfidence(const double angle, TurnInstruction instruction) +{ + + // special handling of U-Turns and Roundabout + if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn) + return 1.0; + + const double deviations[] = {0, 45, 50, 30, 20, 30, 50, 45}; + const double difference = getAngularPenalty(angle, instruction.direction_modifier); + const double max_deviation = deviations[static_cast(instruction.direction_modifier)]; + return 1.0 - (difference / max_deviation) * (difference / max_deviation); +} + +// Translates between angles and their human-friendly directional representation +inline DirectionModifier getTurnDirection(const double angle) +{ + // An angle of zero is a u-turn + // 180 goes perfectly straight + // 0-180 are right turns + // 180-360 are left turns + if (angle > 0 && angle < 60) + return DirectionModifier::SharpRight; + if (angle >= 60 && angle < 140) + return DirectionModifier::Right; + if (angle >= 140 && angle < 170) + return DirectionModifier::SlightRight; + if (angle >= 165 && angle <= 195) + return DirectionModifier::Straight; + if (angle > 190 && angle <= 220) + return DirectionModifier::SlightLeft; + if (angle > 220 && angle <= 300) + return DirectionModifier::Left; + if (angle > 300 && angle < 360) + return DirectionModifier::SharpLeft; + return DirectionModifier::UTurn; +} + +// swaps left <-> right modifier types +inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier) +{ + const constexpr DirectionModifier results[] = { + DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left, + DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight, + DirectionModifier::Right, DirectionModifier::SharpRight}; + return results[modifier]; +} + +inline bool canBeSuppressed(const TurnType type) +{ + if (type == TurnType::Turn) + return true; + return false; +} + +inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class) +{ + return road_class == FunctionalRoadClass::LOW_PRIORITY_ROAD || + road_class == FunctionalRoadClass::SERVICE; +} + +inline bool isDistinct(const DirectionModifier first, const DirectionModifier second) +{ + if ((first + 1) % detail::num_direction_modifiers == second) + return false; + + if ((second + 1) % detail::num_direction_modifiers == first) + return false; + + return true; +} + +inline bool requiresNameAnnounced(const std::string &from, const std::string &to) +{ + // FIXME, handle in profile to begin with? + // this uses the encoding of references in the profile, which is very BAD + // Input for this function should be a struct separating streetname, suffix (e.g. road, + // boulevard, North, West ...), and a list of references + std::string from_name; + std::string from_ref; + std::string to_name; + std::string to_ref; + + // Split from the format "{name} ({ref})" -> name, ref + auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) { + const auto ref_begin = name.find_first_of('('); + if (ref_begin != std::string::npos) + { + if (ref_begin != 0) + out_name = name.substr(0, ref_begin - 1); + const auto ref_end = name.find_first_of(')'); + out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1); + } + else + { + out_name = name; + } + }; + + split(from, from_name, from_ref); + split(to, to_name, to_ref); + + // check similarity of names + const auto names_are_empty = from_name.empty() && to_name.empty(); + const auto names_are_equal = from_name == to_name; + const auto name_is_removed = !from_name.empty() && to_name.empty(); + // references are contained in one another + const auto refs_are_empty = from_ref.empty() && to_ref.empty(); + const auto ref_is_contained = + from_ref.empty() || to_ref.empty() || + (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos); + const auto ref_is_removed = !from_ref.empty() && to_ref.empty(); + + const auto obvious_change = + (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) || + (names_are_equal && refs_are_empty) || name_is_removed || ref_is_removed; + + return !obvious_change; +} + +inline int getPriority(const FunctionalRoadClass road_class) +{ + // The road priorities indicate which roads can bee seen as more or less equal. + // They are used in Fork-Discovery. Possibly should be moved to profiles post v5? + // A fork can happen between road types that are at most 1 priority apart from each other + const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6, + 10, 8, 10, 11, 10, 12, 10, 14}; + return road_priority[static_cast(road_class)]; +} + +inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second) +{ + // forks require similar road categories + // Based on the priorities assigned above, we can set forks only if the road priorities match + // closely. + // Potentially we could include features like number of lanes here and others? + // Should also be moved to profiles + return std::abs(getPriority(first) - getPriority(second)) <= 1; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_GUIDANCE_TOOLKIT_HPP_ diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp new file mode 100644 index 000000000..f52d1b259 --- /dev/null +++ b/include/extractor/guidance/turn_analysis.hpp @@ -0,0 +1,63 @@ +#ifndef OSRM_EXTRACTOR_TURN_ANALYSIS +#define OSRM_EXTRACTOR_TURN_ANALYSIS + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/guidance/intersection.hpp" +#include "extractor/guidance/intersection_generator.hpp" +#include "extractor/guidance/toolkit.hpp" +#include "extractor/guidance/turn_classification.hpp" +#include "extractor/guidance/roundabout_handler.hpp" +#include "extractor/guidance/motorway_handler.hpp" +#include "extractor/guidance/turn_handler.hpp" +#include "extractor/query_node.hpp" +#include "extractor/restriction_map.hpp" + +#include "util/name_table.hpp" +#include "util/node_based_graph.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +class TurnAnalysis +{ + + public: + TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const CompressedEdgeContainer &compressed_edge_container, + const util::NameTable &name_table); + + // the entry into the turn analysis + std::vector getTurns(const NodeID from_node, const EdgeID via_eid) const; + + private: + const util::NodeBasedDynamicGraph &node_based_graph; + const IntersectionGenerator intersection_generator; + const RoundaboutHandler roundabout_handler; + const MotorwayHandler motorway_handler; + const TurnHandler turn_handler; + + // Utility function, setting basic turn types. Prepares for normal turn handling. + Intersection + setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const; +}; // class TurnAnalysis + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_TURN_ANALYSIS diff --git a/include/extractor/guidance/turn_classification.hpp b/include/extractor/guidance/turn_classification.hpp new file mode 100644 index 000000000..58f76666e --- /dev/null +++ b/include/extractor/guidance/turn_classification.hpp @@ -0,0 +1,123 @@ +#ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ +#define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ + +#include "extractor/guidance/toolkit.hpp" + +#include "util/typedefs.hpp" +#include "util/coordinate.hpp" +#include "util/node_based_graph.hpp" + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/query_node.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +struct TurnPossibility +{ + TurnPossibility(DiscreteAngle angle, EdgeID edge_id) + : angle(std::move(angle)), edge_id(std::move(edge_id)) + { + } + + TurnPossibility() : angle(0), edge_id(SPECIAL_EDGEID) {} + + DiscreteAngle angle; + EdgeID edge_id; +}; + +struct CompareTurnPossibilities +{ + bool operator()(const std::vector &left, + const std::vector &right) const + { + if (left.size() < right.size()) + return true; + if (left.size() > right.size()) + return false; + for (std::size_t i = 0; i < left.size(); ++i) + { + if ((((int)left[i].angle + 16) % 256) / 32 < (((int)right[i].angle + 16) % 256) / 32) + return true; + if ((((int)left[i].angle + 16) % 256) / 32 > (((int)right[i].angle + 16) % 256) / 32) + return false; + } + return false; + } +}; + +inline std::vector +classifyIntersection(NodeID nid, + const util::NodeBasedDynamicGraph &graph, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &query_nodes) +{ + + std::vector turns; + + if (graph.BeginEdges(nid) == graph.EndEdges(nid)) + return std::vector(); + + const EdgeID base_id = graph.BeginEdges(nid); + const auto base_coordinate = getRepresentativeCoordinate(nid, graph.GetTarget(base_id), base_id, + graph.GetEdgeData(base_id).reversed, + compressed_geometries, query_nodes); + const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat); + + // generate a list of all turn angles between a base edge, the node and a current edge + for (const EdgeID eid : graph.GetAdjacentEdgeRange(nid)) + { + const auto edge_coordinate = getRepresentativeCoordinate( + nid, graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes); + + double angle = util::coordinate_calculation::computeAngle(base_coordinate, node_coordinate, + edge_coordinate); + turns.emplace_back(discretizeAngle(angle), eid); + } + + std::sort(turns.begin(), turns.end(), + [](const TurnPossibility left, const TurnPossibility right) + { + return left.angle < right.angle; + }); + + turns.push_back(turns.front()); // sentinel + for (std::size_t turn_nr = 0; turn_nr + 1 < turns.size(); ++turn_nr) + { + turns[turn_nr].angle = (256 + static_cast(turns[turn_nr + 1].angle) - + static_cast(turns[turn_nr].angle)) % + 256; // calculate the difference to the right + } + turns.pop_back(); // remove sentinel again + + // find largest: + std::size_t best_id = 0; + DiscreteAngle largest_turn_angle = turns.front().angle; + for (std::size_t current_turn_id = 1; current_turn_id < turns.size(); ++current_turn_id) + { + if (turns[current_turn_id].angle > largest_turn_angle) + { + largest_turn_angle = turns[current_turn_id].angle; + best_id = current_turn_id; + } + } + + // rotate all angles so the largest angle comes first + std::rotate(turns.begin(), turns.begin() + best_id, turns.end()); + + return turns; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ diff --git a/include/extractor/guidance/turn_handler.hpp b/include/extractor/guidance/turn_handler.hpp new file mode 100644 index 000000000..3420aa788 --- /dev/null +++ b/include/extractor/guidance/turn_handler.hpp @@ -0,0 +1,74 @@ +#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_ +#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_ + +#include "extractor/guidance/intersection_handler.hpp" +#include "extractor/guidance/intersection.hpp" +#include "extractor/query_node.hpp" + +#include "util/name_table.hpp" +#include "util/node_based_graph.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +// Intersection handlers deal with all issues related to intersections. +// They assign appropriate turn operations to the TurnOperations. +class TurnHandler : public IntersectionHandler +{ + public: + TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table); + ~TurnHandler() override final; + + // check whether the handler can actually handle the intersection + bool canProcess(const NodeID nid, + const EdgeID via_eid, + const Intersection &intersection) const override final; + + // process the intersection + Intersection operator()(const NodeID nid, + const EdgeID via_eid, + Intersection intersection) const override final; + + private: + // Dead end. + Intersection handleOneWayTurn(Intersection intersection) const; + + // Mode Changes, new names... + Intersection handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const; + + // Forks, T intersections and similar + Intersection handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const; + + // Handling of turns larger then degree three + Intersection handleComplexTurn(const EdgeID via_edge, Intersection intersection) const; + + void + handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const; + + // Classification + std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const; + std::pair findFork(const Intersection &intersection) const; + + Intersection assignLeftTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t starting_at) const; + Intersection assignRightTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t up_to) const; +}; + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_*/ diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp new file mode 100644 index 000000000..d96c007d7 --- /dev/null +++ b/include/extractor/guidance/turn_instruction.hpp @@ -0,0 +1,135 @@ +#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ +#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ + +#include + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +namespace detail +{ +// inclusive bounds for turn modifiers +const constexpr uint8_t num_direction_modifiers = 8; +} // detail + +// direction modifiers based on angle +// Would be nice to have +// enum class DirectionModifier : unsigned char +enum DirectionModifier +{ + UTurn, + SharpRight, + Right, + SlightRight, + Straight, + SlightLeft, + Left, + SharpLeft +}; + +// enum class TurnType : unsigned char +enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption +{ + Invalid, // no valid turn instruction + NoTurn, // end of segment without turn/middle of a segment + Suppressed, // location that suppresses a turn + NewName, // no turn, but name changes + Continue, // remain on a street + Turn, // basic turn + FirstTurn, // First of x turns + SecondTurn, // Second of x turns + ThirdTurn, // Third of x turns + FourthTurn, // Fourth of x turns + Merge, // merge onto a street + Ramp, // special turn (highway ramp exits) + FirstRamp, // first turn onto a ramp + SecondRamp, // second turn onto a ramp + ThirdRamp, // third turn onto a ramp + FourthRamp, // fourth turn onto a ramp + Fork, // fork road splitting up + EndOfRoad, // T intersection + EnterRoundabout, // Entering a small Roundabout + EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit + EnterAndExitRoundabout, // Touching a roundabout + ExitRoundabout, // Exiting a small Roundabout + EnterRotary, // Enter a rotary + EnterRotaryAtExit, // Enter A Rotary at a countable exit + EnterAndExitRotary, // Touching a rotary + ExitRotary, // Exit a rotary + StayOnRoundabout, // Continue on Either a small or a large Roundabout + Restriction, // Cross a Barrier, requires barrier penalties instead of full block + Notification // Travel Mode Changes` +}; + +// turn angle in 1.40625 degree -> 128 == 180 degree +struct TurnInstruction +{ + TurnInstruction(const TurnType type = TurnType::Invalid, + const DirectionModifier direction_modifier = DirectionModifier::Straight) + : type(type), direction_modifier(direction_modifier) + { + } + + TurnType type : 5; + DirectionModifier direction_modifier : 3; + + static TurnInstruction INVALID() + { + return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn); + } + + static TurnInstruction NO_TURN() + { + return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn); + } + + static TurnInstruction REMAIN_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier) + { + (void)is_rotary; // staying does not require a different instruction at the moment + return TurnInstruction(TurnType::StayOnRoundabout, modifier); + } + + static TurnInstruction ENTER_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier) + { + return {is_rotary ? TurnType::EnterRotary : TurnType::EnterRoundabout, modifier}; + } + + static TurnInstruction EXIT_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier) + { + return {is_rotary ? TurnType::ExitRotary : TurnType::ExitRoundabout, modifier}; + } + + static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(bool is_rotary, + const DirectionModifier modifier) + { + return {is_rotary ? TurnType::EnterAndExitRotary : TurnType::EnterAndExitRoundabout, + modifier}; + } + + static TurnInstruction SUPPRESSED(const DirectionModifier modifier) + { + return {TurnType::Suppressed, modifier}; + } +}; + +inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs) +{ + return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier; +} + +inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs) +{ + return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm + +#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ diff --git a/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp similarity index 60% rename from extractor/internal_extractor_edge.hpp rename to include/extractor/internal_extractor_edge.hpp index 0df6e8cb8..2ccdb2c2d 100644 --- a/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -1,46 +1,26 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef INTERNAL_EXTRACTOR_EDGE_HPP #define INTERNAL_EXTRACTOR_EDGE_HPP -#include "../typedefs.h" -#include "../data_structures/travel_mode.hpp" -#include "../data_structures/import_edge.hpp" +#include "util/typedefs.hpp" +#include "extractor/travel_mode.hpp" +#include "extractor/node_based_edge.hpp" #include -#include +#include "osrm/coordinate.hpp" #include +#include "extractor/guidance/classification_data.hpp" + +namespace osrm +{ +namespace extractor +{ struct InternalExtractorEdge { // specify the type of the weight data - enum class WeightType : char { + enum class WeightType : char + { INVALID, SPEED, EDGE_DURATION, @@ -50,9 +30,7 @@ struct InternalExtractorEdge struct WeightData { - WeightData() : duration(0.0), type(WeightType::INVALID) - { - } + WeightData() : duration(0.0), type(WeightType::INVALID) {} union { @@ -63,8 +41,18 @@ struct InternalExtractorEdge }; explicit InternalExtractorEdge() - : result(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, 0, false, false, false, false, true, - TRAVEL_MODE_INACCESSIBLE, false) + : result(MIN_OSM_NODEID, + MIN_OSM_NODEID, + 0, + 0, + false, + false, + false, + false, + true, + TRAVEL_MODE_INACCESSIBLE, + false, + guidance::RoadClassificationData()) { } @@ -78,7 +66,8 @@ struct InternalExtractorEdge bool access_restricted, bool startpoint, TravelMode travel_mode, - bool is_split) + bool is_split, + guidance::RoadClassificationData road_classification) : result(OSMNodeID(source), OSMNodeID(target), name_id, @@ -89,7 +78,8 @@ struct InternalExtractorEdge access_restricted, startpoint, travel_mode, - is_split), + is_split, + std::move(road_classification)), weight_data(std::move(weight_data)) { } @@ -99,19 +89,20 @@ struct InternalExtractorEdge // intermediate edge weight WeightData weight_data; // coordinate of the source node - FixedPointCoordinate source_coordinate; - + util::Coordinate source_coordinate; // necessary static util functions for stxxl's sorting static InternalExtractorEdge min_osm_value() { - return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, WeightData(), false, false, false, - false, true, TRAVEL_MODE_INACCESSIBLE, false); + return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, WeightData(), false, false, + false, false, true, TRAVEL_MODE_INACCESSIBLE, false, + guidance::RoadClassificationData()); } static InternalExtractorEdge max_osm_value() { - return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, - false, false, false, true, TRAVEL_MODE_INACCESSIBLE, false); + return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, false, + false, false, true, TRAVEL_MODE_INACCESSIBLE, false, + guidance::RoadClassificationData()); } static InternalExtractorEdge min_internal_value() @@ -128,7 +119,6 @@ struct InternalExtractorEdge v.result.target = std::numeric_limits::max(); return v; } - }; struct CmpEdgeByInternalStartThenInternalTargetID @@ -136,9 +126,9 @@ struct CmpEdgeByInternalStartThenInternalTargetID using value_type = InternalExtractorEdge; bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const { - return (lhs.result.source < rhs.result.source) || - ((lhs.result.source == rhs.result.source) && - (lhs.result.target < rhs.result.target)); + return (lhs.result.source < rhs.result.source) || + ((lhs.result.source == rhs.result.source) && + (lhs.result.target < rhs.result.target)); } value_type max_value() { return InternalExtractorEdge::max_internal_value(); } @@ -168,5 +158,7 @@ struct CmpEdgeByOSMTargetID value_type max_value() { return InternalExtractorEdge::max_osm_value(); } value_type min_value() { return InternalExtractorEdge::min_osm_value(); } }; +} +} #endif // INTERNAL_EXTRACTOR_EDGE_HPP diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp new file mode 100644 index 000000000..47090e6f7 --- /dev/null +++ b/include/extractor/node_based_edge.hpp @@ -0,0 +1,143 @@ +#ifndef NODE_BASED_EDGE_HPP +#define NODE_BASED_EDGE_HPP + +#include "extractor/travel_mode.hpp" +#include "util/typedefs.hpp" + +#include "extractor/guidance/classification_data.hpp" + +namespace osrm +{ +namespace extractor +{ + +struct NodeBasedEdge +{ + NodeBasedEdge(); + + NodeBasedEdge(NodeID source, + NodeID target, + NodeID name_id, + EdgeWeight weight, + bool forward, + bool backward, + bool roundabout, + bool access_restricted, + bool startpoint, + TravelMode travel_mode, + bool is_split, + guidance::RoadClassificationData road_classification); + + bool operator<(const NodeBasedEdge &other) const; + + NodeID source; + NodeID target; + NodeID name_id; + EdgeWeight weight; + bool forward : 1; + bool backward : 1; + bool roundabout : 1; + bool access_restricted : 1; + bool startpoint : 1; + bool is_split : 1; + TravelMode travel_mode : 4; + guidance::RoadClassificationData road_classification; +}; + +struct NodeBasedEdgeWithOSM : NodeBasedEdge +{ + NodeBasedEdgeWithOSM(OSMNodeID source, + OSMNodeID target, + NodeID name_id, + EdgeWeight weight, + bool forward, + bool backward, + bool roundabout, + bool access_restricted, + bool startpoint, + TravelMode travel_mode, + bool is_split, + guidance::RoadClassificationData road_classification); + + OSMNodeID osm_source_id; + OSMNodeID osm_target_id; +}; + +// Impl. + +inline NodeBasedEdge::NodeBasedEdge() + : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false), + backward(false), roundabout(false), access_restricted(false), startpoint(true), + is_split(false), travel_mode(false) +{ +} + +inline NodeBasedEdge::NodeBasedEdge(NodeID source, + NodeID target, + NodeID name_id, + EdgeWeight weight, + bool forward, + bool backward, + bool roundabout, + bool access_restricted, + bool startpoint, + TravelMode travel_mode, + bool is_split, + guidance::RoadClassificationData road_classification) + : source(source), target(target), name_id(name_id), weight(weight), forward(forward), + backward(backward), roundabout(roundabout), access_restricted(access_restricted), + startpoint(startpoint), is_split(is_split), travel_mode(travel_mode), + road_classification(std::move(road_classification)) +{ +} + +inline bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const +{ + if (source == other.source) + { + if (target == other.target) + { + if (weight == other.weight) + { + return forward && backward && ((!other.forward) || (!other.backward)); + } + return weight < other.weight; + } + return target < other.target; + } + return source < other.source; +} + +inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM( + OSMNodeID source, + OSMNodeID target, + NodeID name_id, + EdgeWeight weight, + bool forward, + bool backward, + bool roundabout, + bool access_restricted, + bool startpoint, + TravelMode travel_mode, + bool is_split, + guidance::RoadClassificationData road_classification) + : NodeBasedEdge(SPECIAL_NODEID, + SPECIAL_NODEID, + name_id, + weight, + forward, + backward, + roundabout, + access_restricted, + startpoint, + travel_mode, + is_split, + std::move(road_classification)), + osm_source_id(std::move(source)), osm_target_id(std::move(target)) +{ +} + +} // ns extractor +} // ns osrm + +#endif /* NODE_BASED_EDGE_HPP */ diff --git a/include/extractor/original_edge_data.hpp b/include/extractor/original_edge_data.hpp new file mode 100644 index 000000000..17d234af9 --- /dev/null +++ b/include/extractor/original_edge_data.hpp @@ -0,0 +1,42 @@ +#ifndef ORIGINAL_EDGE_DATA_HPP +#define ORIGINAL_EDGE_DATA_HPP + +#include "extractor/travel_mode.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "util/typedefs.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ + +struct OriginalEdgeData +{ + explicit OriginalEdgeData(NodeID via_node, + unsigned name_id, + guidance::TurnInstruction turn_instruction, + TravelMode travel_mode) + : via_node(via_node), name_id(name_id), turn_instruction(turn_instruction), + travel_mode(travel_mode) + { + } + + OriginalEdgeData() + : via_node(std::numeric_limits::max()), + name_id(std::numeric_limits::max()), + turn_instruction(guidance::TurnInstruction::INVALID()), + travel_mode(TRAVEL_MODE_INACCESSIBLE) + { + } + + NodeID via_node; + unsigned name_id; + guidance::TurnInstruction turn_instruction; + TravelMode travel_mode; +}; +} +} + +#endif // ORIGINAL_EDGE_DATA_HPP diff --git a/include/extractor/profile_properties.hpp b/include/extractor/profile_properties.hpp new file mode 100644 index 000000000..79768754f --- /dev/null +++ b/include/extractor/profile_properties.hpp @@ -0,0 +1,48 @@ +#ifndef PROFILE_PROPERTIES_HPP +#define PROFILE_PROPERTIES_HPP + +#include + +namespace osrm +{ +namespace extractor +{ + +struct ProfileProperties +{ + ProfileProperties() + : traffic_signal_penalty(0), u_turn_penalty(0), continue_straight_at_waypoint(true), use_turn_restrictions(false) + { + } + + double GetUturnPenalty() const + { + return u_turn_penalty / 10.; + } + + void SetUturnPenalty(const double u_turn_penalty_) + { + u_turn_penalty = boost::numeric_cast(u_turn_penalty_ * 10.); + } + + double GetTrafficSignalPenalty() const + { + return traffic_signal_penalty / 10.; + } + + void SetTrafficSignalPenalty(const double traffic_signal_penalty_) + { + traffic_signal_penalty = boost::numeric_cast(traffic_signal_penalty_ * 10.); + } + + //! penalty to cross a traffic light in deci-seconds + int traffic_signal_penalty; + //! penalty to do a uturn in deci-seconds + int u_turn_penalty; + bool continue_straight_at_waypoint; + bool use_turn_restrictions; +}; +} +} + +#endif diff --git a/include/extractor/query_node.hpp b/include/extractor/query_node.hpp new file mode 100644 index 000000000..93c2d61b2 --- /dev/null +++ b/include/extractor/query_node.hpp @@ -0,0 +1,51 @@ +#ifndef QUERY_NODE_HPP +#define QUERY_NODE_HPP + +#include "util/typedefs.hpp" + +#include "util/coordinate.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ + +struct QueryNode +{ + using key_type = OSMNodeID; // type of NodeID + using value_type = int; // type of lat,lons + + explicit QueryNode(const util::FixedLongitude lon_, + const util::FixedLatitude lat_, + key_type node_id) + : lon(lon_), lat(lat_), node_id(std::move(node_id)) + { + } + QueryNode() + : lon(std::numeric_limits::max()), lat(std::numeric_limits::max()), + node_id(SPECIAL_OSM_NODEID) + { + } + + util::FixedLongitude lon; + util::FixedLatitude lat; + key_type node_id; + + static QueryNode min_value() + { + return QueryNode(util::FixedLongitude(-180 * COORDINATE_PRECISION), + util::FixedLatitude(-90 * COORDINATE_PRECISION), MIN_OSM_NODEID); + } + + static QueryNode max_value() + { + return QueryNode(util::FixedLongitude(180 * COORDINATE_PRECISION), + util::FixedLatitude(90 * COORDINATE_PRECISION), MAX_OSM_NODEID); + } +}; +} +} + +#endif // QUERY_NODE_HPP diff --git a/data_structures/raster_source.hpp b/include/extractor/raster_source.hpp similarity index 69% rename from data_structures/raster_source.hpp rename to include/extractor/raster_source.hpp index 86c859639..4cc7e0202 100644 --- a/data_structures/raster_source.hpp +++ b/include/extractor/raster_source.hpp @@ -1,34 +1,8 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef RASTER_SOURCE_HPP #define RASTER_SOURCE_HPP -#include "../util/osrm_exception.hpp" +#include "util/exception.hpp" +#include "util/coordinate.hpp" #include #include @@ -40,6 +14,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace extractor +{ + /** \brief Small wrapper around raster source queries to optionally provide results gracefully, depending on source bounds @@ -64,10 +43,10 @@ class RasterGrid ydim = _ydim; _data.reserve(ydim * xdim); - boost::filesystem::ifstream stream(filepath); + boost::filesystem::ifstream stream(filepath, std::ios::binary); if (!stream) { - throw osrm::exception("Unable to open raster file."); + throw util::exception("Unable to open raster file."); } stream.seekg(0, std::ios_base::end); @@ -92,13 +71,13 @@ class RasterGrid } catch (std::exception const &ex) { - throw osrm::exception( + throw util::exception( std::string("Failed to read from raster source with exception: ") + ex.what()); } if (!r || itr != end) { - throw osrm::exception("Failed to parse raster source correctly."); + throw util::exception("Failed to parse raster source correctly."); } } @@ -163,13 +142,15 @@ class SourceContainer std::size_t nrows, std::size_t ncols); - RasterDatum getRasterDataFromSource(unsigned int source_id, int lon, int lat); + RasterDatum getRasterDataFromSource(unsigned int source_id, double lon, double lat); - RasterDatum getRasterInterpolateFromSource(unsigned int source_id, int lon, int lat); + RasterDatum getRasterInterpolateFromSource(unsigned int source_id, double lon, double lat); private: std::vector LoadedSources; std::unordered_map LoadedSourcePaths; }; +} +} #endif /* RASTER_SOURCE_HPP */ diff --git a/data_structures/restriction.hpp b/include/extractor/restriction.hpp similarity index 69% rename from data_structures/restriction.hpp rename to include/extractor/restriction.hpp index ecab4f921..5916c85ea 100644 --- a/data_structures/restriction.hpp +++ b/include/extractor/restriction.hpp @@ -1,37 +1,15 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef RESTRICTION_HPP #define RESTRICTION_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" #include +namespace osrm +{ +namespace extractor +{ + struct TurnRestriction { union WayOrNode @@ -79,7 +57,7 @@ struct TurnRestriction /** * This is just a wrapper around TurnRestriction used in the extractor. - * + * * Could be merged with TurnRestriction. For now the type-destiction makes sense * as the format in which the restriction is presented in the extractor and in the * preprocessing is different. (see restriction_parser.cpp) @@ -130,5 +108,7 @@ struct CmpRestrictionContainerByTo value_type max_value() const { return InputRestrictionContainer::max_value(); } value_type min_value() const { return InputRestrictionContainer::min_value(); } }; +} +} #endif // RESTRICTION_HPP diff --git a/data_structures/restriction_map.hpp b/include/extractor/restriction_map.hpp similarity index 72% rename from data_structures/restriction_map.hpp rename to include/extractor/restriction_map.hpp index dbca35651..3e4786797 100644 --- a/data_structures/restriction_map.hpp +++ b/include/extractor/restriction_map.hpp @@ -1,36 +1,10 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef RESTRICTION_MAP_HPP #define RESTRICTION_MAP_HPP -#include "restriction.hpp" -#include "../util/std_hash.hpp" -#include "../typedefs.h" +#include "extractor/edge_based_edge.hpp" +#include "extractor/restriction.hpp" +#include "util/std_hash.hpp" +#include "util/typedefs.hpp" #include @@ -39,6 +13,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace extractor +{ + struct RestrictionSource { NodeID start_node; @@ -64,26 +43,32 @@ struct RestrictionTarget return (lhs.target_node == rhs.target_node && lhs.is_only == rhs.is_only); } }; +} +} namespace std { -template <> struct hash +template <> struct hash { - size_t operator()(const RestrictionSource &r_source) const + size_t operator()(const osrm::extractor::RestrictionSource &r_source) const { return hash_val(r_source.start_node, r_source.via_node); } }; -template <> struct hash +template <> struct hash { - size_t operator()(const RestrictionTarget &r_target) const + size_t operator()(const osrm::extractor::RestrictionTarget &r_target) const { return hash_val(r_target.target_node, r_target.is_only); } }; } +namespace osrm +{ +namespace extractor +{ /** \brief Efficent look up if an edge is the start + via node of a TurnRestriction EdgeBasedEdgeFactory decides by it if edges are inserted or geometry is compressed @@ -91,7 +76,7 @@ template <> struct hash class RestrictionMap { public: - RestrictionMap() : m_count(0) {}; + RestrictionMap() : m_count(0){} RestrictionMap(const std::vector &restriction_list); // Replace end v with w in each turn restriction containing u as via node @@ -172,5 +157,7 @@ class RestrictionMap std::unordered_set m_restriction_start_nodes; std::unordered_set m_no_turn_via_node_set; }; +} +} #endif // RESTRICTION_MAP_HPP diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp new file mode 100644 index 000000000..8722bf12b --- /dev/null +++ b/include/extractor/restriction_parser.hpp @@ -0,0 +1,58 @@ +#ifndef RESTRICTION_PARSER_HPP +#define RESTRICTION_PARSER_HPP + +#include "extractor/restriction.hpp" + +#include + +#include +#include + +struct lua_State; +namespace osmium +{ +class Relation; +} + +namespace osrm +{ +namespace extractor +{ + +struct ProfileProperties; + +/** + * Parses the relations that represents turn restrictions. + * + * Currently only restrictions where the via objects is a node are supported. + * from via to + * ------->(x)--------> + * + * While this class does not directly invoke any lua code _per relation_ it does + * load configuration values from the profile, that are saved in variables. + * Namely ```use_turn_restrictions``` and ```get_exceptions```. + * + * The restriction is represented by the osm id of the from way, the osm id of the + * to way and the osm id of the via node. This representation must be post-processed + * in the extractor to work with the edge-based data-model of OSRM: + * Since the from and to way share the via-node a turn will have the following form: + * ...----(a)-----(via)------(b)----... + * So it can be represented by the tripe (a, via, b). + */ +class RestrictionParser +{ + public: + RestrictionParser(lua_State *lua_state, const ProfileProperties& properties); + boost::optional TryParse(const osmium::Relation &relation) const; + + private: + void ReadRestrictionExceptions(lua_State *lua_state); + bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; + + std::vector restriction_exceptions; + bool use_turn_restrictions; +}; +} +} + +#endif /* RESTRICTION_PARSER_HPP */ diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp new file mode 100644 index 000000000..5494c1268 --- /dev/null +++ b/include/extractor/scripting_environment.hpp @@ -0,0 +1,54 @@ +#ifndef SCRIPTING_ENVIRONMENT_HPP +#define SCRIPTING_ENVIRONMENT_HPP + +#include "extractor/profile_properties.hpp" +#include "extractor/raster_source.hpp" + +#include "util/lua_util.hpp" + +#include +#include +#include +#include + +struct lua_State; + +namespace osrm +{ +namespace extractor +{ + +/** + * Creates a lua context and binds osmium way, node and relation objects and + * ExtractionWay and ExtractionNode to lua objects. + * + * Each thread has its own lua state which is implemented with thread specific + * storage from TBB. + */ +class ScriptingEnvironment +{ + public: + struct Context + { + ProfileProperties properties; + SourceContainer sources; + util::LuaState state; + }; + + explicit ScriptingEnvironment(const std::string &file_name); + + ScriptingEnvironment(const ScriptingEnvironment &) = delete; + ScriptingEnvironment &operator=(const ScriptingEnvironment &) = delete; + + Context &GetContex(); + + private: + void InitContext(Context &context); + std::mutex init_mutex; + std::string file_name; + tbb::enumerable_thread_specific> script_contexts; +}; +} +} + +#endif /* SCRIPTING_ENVIRONMENT_HPP */ diff --git a/algorithms/tarjan_scc.hpp b/include/extractor/tarjan_scc.hpp similarity index 74% rename from algorithms/tarjan_scc.hpp rename to include/extractor/tarjan_scc.hpp index 59077322d..874d3f677 100644 --- a/algorithms/tarjan_scc.hpp +++ b/include/extractor/tarjan_scc.hpp @@ -1,45 +1,18 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TARJAN_SCC_HPP #define TARJAN_SCC_HPP -#include "../typedefs.h" -#include "../data_structures/deallocating_vector.hpp" -#include "../data_structures/import_edge.hpp" -#include "../data_structures/query_node.hpp" -#include "../data_structures/percent.hpp" +#include "util/typedefs.hpp" +#include "util/deallocating_vector.hpp" +#include "extractor/node_based_edge.hpp" +#include "extractor/query_node.hpp" +#include "util/percent.hpp" -#include "../util/integer_range.hpp" -#include "../util/simple_logger.hpp" -#include "../util/std_hash.hpp" -#include "../util/timing_util.hpp" +#include "util/integer_range.hpp" +#include "util/simple_logger.hpp" +#include "util/std_hash.hpp" +#include "util/timing_util.hpp" -#include +#include "osrm/coordinate.hpp" #include #include @@ -49,6 +22,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace extractor +{ + template class TarjanSCC { struct TarjanStackFrame @@ -93,7 +71,7 @@ template class TarjanSCC unsigned component_index = 0, size_of_current_component = 0; unsigned index = 0; std::vector processing_node_before_recursion(max_node_id, true); - for (const NodeID node : osrm::irange(0u, max_node_id)) + for (const NodeID node : util::irange(0u, max_node_id)) { if (SPECIAL_NODEID == components_index[node]) { @@ -168,8 +146,8 @@ template class TarjanSCC if (size_of_current_component > 1000) { - SimpleLogger().Write() << "large component [" << component_index - << "]=" << size_of_current_component; + util::SimpleLogger().Write() << "large component [" << component_index + << "]=" << size_of_current_component; } ++component_index; @@ -180,7 +158,7 @@ template class TarjanSCC } TIMER_STOP(SCC_RUN); - SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s"; + util::SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s"; size_one_counter = std::count_if(component_size_vector.begin(), component_size_vector.end(), [](unsigned value) @@ -200,5 +178,7 @@ template class TarjanSCC unsigned get_component_id(const NodeID node) const { return components_index[node]; } }; +} +} #endif /* TARJAN_SCC_HPP */ diff --git a/data_structures/travel_mode.hpp b/include/extractor/travel_mode.hpp similarity index 56% rename from data_structures/travel_mode.hpp rename to include/extractor/travel_mode.hpp index 2bbe46317..fa75c373b 100644 --- a/data_structures/travel_mode.hpp +++ b/include/extractor/travel_mode.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2014, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -28,10 +28,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef TRAVEL_MODE_HPP #define TRAVEL_MODE_HPP -namespace +namespace osrm { +namespace extractor +{ + +// This is a char instead of a typed enum, so that we can +// pack it into e.g. a "TravelMode mode : 4" packed bitfield using TravelMode = unsigned char; -static const TravelMode TRAVEL_MODE_INACCESSIBLE = 0; -static const TravelMode TRAVEL_MODE_DEFAULT = 1; } +} + +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_INACCESSIBLE = 0; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_DRIVING = 1; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_CYCLING = 2; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_WALKING = 3; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_FERRY = 4; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_TRAIN = 5; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_PUSHING_BIKE = 6; +// FIXME only for testbot.lua +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_STEPS_UP = 8; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_STEPS_DOWN = 9; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_UP = 10; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_DOWN = 11; +const constexpr osrm::extractor::TravelMode TRAVEL_MODE_ROUTE = 12; + #endif /* TRAVEL_MODE_HPP */ diff --git a/data_structures/tribool.hpp b/include/osrm/bearing.hpp similarity index 88% rename from data_structures/tribool.hpp rename to include/osrm/bearing.hpp index 2d4b6108d..75ec11036 100644 --- a/data_structures/tribool.hpp +++ b/include/osrm/bearing.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,16 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TRIBOOL_HPP -#define TRIBOOL_HPP +#ifndef OSRM_BEARING_HPP +#define OSRM_BEARING_HPP + +#include "engine/bearing.hpp" namespace osrm { -enum class tribool : char -{ - yes, - no, - indeterminate -}; + using engine::Bearing; } -#endif // TRIBOOL_HPP + +#endif diff --git a/include/osrm/coordinate.hpp b/include/osrm/coordinate.hpp index 6318465e1..39b684d01 100644 --- a/include/osrm/coordinate.hpp +++ b/include/osrm/coordinate.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,47 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef COORDINATE_HPP_ -#define COORDINATE_HPP_ +#ifndef GLOBAL_COORDINATE_HPP +#define GLOBAL_COORDINATE_HPP -#include //for std::ostream -#include -#include +#include "util/coordinate.hpp" -namespace +namespace osrm { -constexpr static const float COORDINATE_PRECISION = 1000000.f; +using util::Coordinate; } -struct FixedPointCoordinate -{ - int lat; - int lon; - - FixedPointCoordinate(); - FixedPointCoordinate(int lat, int lon); - - template - FixedPointCoordinate(const T &coordinate) - : lat(coordinate.lat), lon(coordinate.lon) - { - static_assert(std::is_same::value, - "coordinate types incompatible"); - static_assert(std::is_same::value, - "coordinate types incompatible"); - } - - bool is_valid() const; - bool operator==(const FixedPointCoordinate &other) const; - - float bearing(const FixedPointCoordinate &other) const; - void output(std::ostream &out) const; -}; - -inline std::ostream &operator<<(std::ostream &out_stream, FixedPointCoordinate const &coordinate) -{ - coordinate.output(out_stream); - return out_stream; -} - -#endif /* COORDINATE_HPP_ */ +#endif diff --git a/unit_tests/algorithm_tests.cpp b/include/osrm/engine_config.hpp similarity index 86% rename from unit_tests/algorithm_tests.cpp rename to include/osrm/engine_config.hpp index 0a9f3fdff..e48dceb86 100644 --- a/unit_tests/algorithm_tests.cpp +++ b/include/osrm/engine_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2014, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,10 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define BOOST_TEST_MODULE algorithm tests +#ifndef GLOBAL_ENGINE_CONFIG_HPP +#define GLOBAL_ENGINE_CONFIG_HPP -#include +#include "engine/engine_config.hpp" -/* - * This file will contain an automatically generated main function. - */ +namespace osrm +{ +using engine::EngineConfig; +} + +#endif diff --git a/include/osrm/json_container.hpp b/include/osrm/json_container.hpp index 63accb81c..7010cf48d 100644 --- a/include/osrm/json_container.hpp +++ b/include/osrm/json_container.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2013, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,72 +25,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// based on -// https://svn.apache.org/repos/asf/mesos/tags/release-0.9.0-incubating-RC0/src/common/json.hpp - -#ifndef JSON_CONTAINER_HPP -#define JSON_CONTAINER_HPP - -#include - -#include -#include -#include -#include - +#ifndef GLOBAL_JSON_CONTAINER_HPP +#define GLOBAL_JSON_CONTAINER_HPP +#include "util/json_container.hpp" namespace osrm { -namespace json -{ - -struct Object; -struct Array; - -struct String -{ - String() {} - String(const char *value) : value(value) {} - String(std::string value) : value(std::move(value)) {} - std::string value; -}; - -struct Number -{ - Number() {} - Number(double value) : value(static_cast(value)) {} - double value; -}; - -struct True -{ -}; - -struct False -{ -}; - -struct Null -{ -}; - -using Value = mapbox::util::variant, - mapbox::util::recursive_wrapper, - True, - False, - Null>; - -struct Object -{ - std::unordered_map values; -}; - -struct Array -{ - std::vector values; -}; - -} // namespace JSON -} // namespace osrm -#endif // JSON_CONTAINER_HPP +namespace json = osrm::util::json; +} +#endif diff --git a/include/osrm/match_parameters.hpp b/include/osrm/match_parameters.hpp new file mode 100644 index 000000000..36e7001ff --- /dev/null +++ b/include/osrm/match_parameters.hpp @@ -0,0 +1,38 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef GLOBAL_MATCH_PARAMETERS_HPP +#define GLOBAL_MATCH_PARAMETERS_HPP + +#include "engine/api/match_parameters.hpp" + +namespace osrm +{ +using engine::api::MatchParameters; +} + +#endif diff --git a/server/http/compression_type.hpp b/include/osrm/nearest_parameters.hpp similarity index 84% rename from server/http/compression_type.hpp rename to include/osrm/nearest_parameters.hpp index f0dc692fa..f19131c67 100644 --- a/server/http/compression_type.hpp +++ b/include/osrm/nearest_parameters.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,18 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef COMPRESSION_TYPE_HPP -#define COMPRESSION_TYPE_HPP +#ifndef GLOBAL_NEAREST_PARAMETERS_HPP +#define GLOBAL_NEAREST_PARAMETERS_HPP -namespace http -{ +#include "engine/api/nearest_parameters.hpp" -enum compression_type +namespace osrm { - no_compression, - gzip_rfc1952, - deflate_rfc1951 -}; +using engine::api::NearestParameters; } -#endif // COMPRESSION_TYPE_HPP +#endif diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index cbf14d3cf..1de8610a3 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -28,29 +28,111 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OSRM_HPP #define OSRM_HPP -#include +#include "osrm/osrm_fwd.hpp" +#include "osrm/status.hpp" -struct LibOSRMConfig; -struct RouteParameters; +#include +#include namespace osrm { -namespace json -{ -struct Object; -} -} +namespace json = util::json; +using engine::EngineConfig; +using engine::api::RouteParameters; +using engine::api::TableParameters; +using engine::api::NearestParameters; +using engine::api::TripParameters; +using engine::api::MatchParameters; +using engine::api::TileParameters; -class OSRM +/** + * Represents a Open Source Routing Machine with access to its services. + * + * This represents an Open Source Routing Machine (OSRM) instance, with the services: + * + * - Route: shortest path queries for coordinates + * - Table: distance tables for coordinates + * - Nearest: nearest street segment for coordinate + * - Trip: shortest round trip between coordinates + * - Match: snaps noisy coordinate traces to the road network + * - Tile: vector tiles with internal graph representation + * + * All services take service-specific parameters, fill a JSON object, and return a status code. + */ +class OSRM final { - private: - class OSRM_impl; - std::unique_ptr OSRM_pimpl_; - public: - OSRM(LibOSRMConfig &lib_config); - ~OSRM(); // needed because we need to define it with the implementation of OSRM_impl - int RunQuery(const RouteParameters &route_parameters, osrm::json::Object &json_result); + /** + * Constructs an OSRM instance with user-configurable settings. + * + * \param config The user-provided OSRM configuration. + * \see EngineConfig + */ + explicit OSRM(EngineConfig &config); + + ~OSRM(); + + // Moveable but not copyable + OSRM(OSRM &&) noexcept; + OSRM &operator=(OSRM &&) noexcept; + + /** + * Shortest path queries for coordinates. + * + * \param parameters route query specific parameters + * \return Status indicating success for the query or failure + * \see Status, RouteParameters and json::Object + */ + Status Route(const RouteParameters ¶meters, json::Object &result); + + /** + * Distance tables for coordinates. + * + * \param parameters table query specific parameters + * \return Status indicating success for the query or failure + * \see Status, TableParameters and json::Object + */ + Status Table(const TableParameters ¶meters, json::Object &result); + + /** + * Nearest street segment for coordinate. + * + * \param parameters nearest query specific parameters + * \return Status indicating success for the query or failure + * \see Status, NearestParameters and json::Object + */ + Status Nearest(const NearestParameters ¶meters, json::Object &result); + + /** + * Trip: shortest round trip between coordinates. + * + * \param parameters trip query specific parameters + * \return Status indicating success for the query or failure + * \see Status, TripParameters and json::Object + */ + Status Trip(const TripParameters ¶meters, json::Object &result); + + /** + * Match: snaps noisy coordinate traces to the road network + * + * \param parameters match query specific parameters + * \return Status indicating success for the query or failure + * \see Status, MatchParameters and json::Object + */ + Status Match(const MatchParameters ¶meters, json::Object &result); + + /** + * Tile: vector tiles with internal graph representation + * + * \param parameters tile query specific parameters + * \return Status indicating success for the query or failure + * \see Status, TileParameters and json::Object + */ + Status Tile(const TileParameters ¶meters, std::string &result); + + private: + std::unique_ptr engine_; }; +} #endif // OSRM_HPP diff --git a/util/floating_point.hpp b/include/osrm/osrm_fwd.hpp similarity index 68% rename from util/floating_point.hpp rename to include/osrm/osrm_fwd.hpp index 13f2d3bc4..f0f1f17af 100644 --- a/util/floating_point.hpp +++ b/include/osrm/osrm_fwd.hpp @@ -1,5 +1,6 @@ /* -Copyright (c) 2013, Project OSRM contributors + +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -24,21 +25,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FLOATING_POINT_HPP -#define FLOATING_POINT_HPP +#ifndef OSRM_FWD_HPP +#define OSRM_FWD_HPP -#include - -#include -#include +// OSRM API forward declarations for usage in interfaces. Exposes forward declarations for: +// osrm::util::json::Object, osrm::engine::api::XParameters namespace osrm { -template bool epsilon_compare(const FloatT number1, const FloatT number2) -{ - static_assert(std::is_floating_point::value, "type must be floating point"); - return (std::abs(number1 - number2) < std::numeric_limits::epsilon()); -} -} -#endif // FLOATING_POINT_HPP +namespace util +{ +namespace json +{ +struct Object; +} // ns json +} // ns util + +namespace engine +{ +namespace api +{ +struct RouteParameters; +struct TableParameters; +struct NearestParameters; +struct TripParameters; +struct MatchParameters; +struct TileParameters; +} // ns api + +class Engine; +struct EngineConfig; +} // ns engine +} // ns osrm + +#endif diff --git a/include/osrm/route_parameters.hpp b/include/osrm/route_parameters.hpp index 6d2dda719..6c0508c30 100644 --- a/include/osrm/route_parameters.hpp +++ b/include/osrm/route_parameters.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,92 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ROUTE_PARAMETERS_HPP -#define ROUTE_PARAMETERS_HPP +#ifndef GLOBAL_ROUTE_PARAMETERS_HPP +#define GLOBAL_ROUTE_PARAMETERS_HPP -#include +#include "engine/api/route_parameters.hpp" -#include -#include - -#include -#include - -struct RouteParameters +namespace osrm { - RouteParameters(); +using engine::api::RouteParameters; +} - void setZoomLevel(const short level); - - void setNumberOfResults(const short number); - - void setAlternateRouteFlag(const bool flag); - - void setUTurn(const bool flag); - - void setAllUTurns(const bool flag); - - void setClassify(const bool classify); - - void setMatchingBeta(const double beta); - - void setGPSPrecision(const double precision); - - void setDeprecatedAPIFlag(const std::string &); - - void setChecksum(const unsigned check_sum); - - void setInstructionFlag(const bool flag); - - void setService(const std::string &service); - - void setOutputFormat(const std::string &format); - - void setJSONpParameter(const std::string ¶meter); - - void addHint(const std::string &hint); - - void addTimestamp(const unsigned timestamp); - - void addBearing(const boost::fusion::vector> &received_bearing, boost::spirit::qi::unused_type unused, bool& pass); - - void setLanguage(const std::string &language); - - void setGeometryFlag(const bool flag); - - void setCompressionFlag(const bool flag); - - void addCoordinate(const boost::fusion::vector &received_coordinates); - - void addDestination(const boost::fusion::vector &received_coordinates); - - void addSource(const boost::fusion::vector &received_coordinates); - - void getCoordinatesFromGeometry(const std::string &geometry_string); - - short zoom_level; - bool print_instructions; - bool alternate_route; - bool geometry; - bool compression; - bool deprecatedAPI; - bool uturn_default; - bool classify; - double matching_beta; - double gps_precision; - unsigned check_sum; - short num_results; - std::string service; - std::string output_format; - std::string jsonp_parameter; - std::string language; - std::vector hints; - std::vector timestamps; - std::vector>> bearings; - std::vector uturns; - std::vector coordinates; - std::vector is_destination; - std::vector is_source; -}; - -#endif // ROUTE_PARAMETERS_HPP +#endif diff --git a/util/fingerprint.cpp b/include/osrm/status.hpp similarity index 87% rename from util/fingerprint.cpp rename to include/osrm/status.hpp index 8a705b0c8..8bb1f665a 100644 --- a/util/fingerprint.cpp +++ b/include/osrm/status.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM, Dennis Luxen, others +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,5 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/fingerprint.hpp" -#include "util/fingerprint_impl.hpp" +#ifndef OSRM_STATUS_HPP +#define OSRM_STATUS_HPP + +#include "engine/status.hpp" + +namespace osrm +{ +using engine::Status; +} + +#endif diff --git a/unit_tests/datastructure_tests.cpp b/include/osrm/storage_config.hpp similarity index 85% rename from unit_tests/datastructure_tests.cpp rename to include/osrm/storage_config.hpp index 850d61910..353c75eed 100644 --- a/unit_tests/datastructure_tests.cpp +++ b/include/osrm/storage_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2014, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,10 +25,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define BOOST_TEST_MODULE datastructure tests +#ifndef GLOBAL_STORAGE_CONFIG_HPP +#define GLOBAL_STORAGE_CONFIG_HPP -#include +#include "storage/storage_config.hpp" -/* - * This file will contain an automatically generated main function. - */ +namespace osrm +{ +using storage::StorageConfig; +} + +#endif diff --git a/include/osrm/strong_typedef.hpp b/include/osrm/strong_typedef.hpp deleted file mode 100644 index c2364b053..000000000 --- a/include/osrm/strong_typedef.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef OSRM_STRONG_TYPEDEF_HPP -#define OSRM_STRONG_TYPEDEF_HPP - -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include - -/* Creates strongly typed wrappers around scalar types. - * Useful for stopping accidental assignment of lats to lons, - * etc. Also clarifies what this random "int" value is - * being used for. - */ -#define OSRM_STRONG_TYPEDEF(From, To) \ - class To final { \ - static_assert(std::is_arithmetic(), ""); \ - From x; \ - \ - public: \ - To() = default; \ - explicit To(const From x_) : x(x_) {} \ - explicit operator From&() { return x; } \ - explicit operator const From&() const { return x; } \ - bool operator <(const To &z_) const { return x < static_cast(z_) ; } \ - bool operator >(const To &z_) const { return x > static_cast(z_) ; } \ - bool operator <=(const To &z_) const { return x <= static_cast(z_) ; } \ - bool operator >=(const To &z_) const { return x >= static_cast(z_) ; } \ - bool operator ==(const To &z_) const { return x == static_cast(z_) ; } \ - bool operator !=(const To &z_) const { return x != static_cast(z_) ; } \ - }; \ - inline From To##_to_##From(To to) { return static_cast(to); } \ - namespace std { \ - template <> \ - struct hash \ - { \ - std::size_t operator()(const To& k) const \ - { \ - return std::hash()(static_cast(k)); \ - } \ - }; \ - } - -#endif // OSRM_STRONG_TYPEDEF_HPP diff --git a/include/osrm/table_parameters.hpp b/include/osrm/table_parameters.hpp new file mode 100644 index 000000000..7bb16eb43 --- /dev/null +++ b/include/osrm/table_parameters.hpp @@ -0,0 +1,38 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef GLOBAL_TABLE_PARAMETERS_HPP +#define GLOBAL_TABLE_PARAMETERS_HPP + +#include "engine/api/table_parameters.hpp" + +namespace osrm +{ +using engine::api::TableParameters; +} + +#endif diff --git a/include/osrm/tile_parameters.hpp b/include/osrm/tile_parameters.hpp new file mode 100644 index 000000000..0672deaa6 --- /dev/null +++ b/include/osrm/tile_parameters.hpp @@ -0,0 +1,38 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef GLOBAL_TILE_PARAMETERS_HPP +#define GLOBAL_TILE_PARAMETERS_HPP + +#include "engine/api/tile_parameters.hpp" + +namespace osrm +{ +using engine::api::TileParameters; +} + +#endif diff --git a/include/osrm/trip_parameters.hpp b/include/osrm/trip_parameters.hpp new file mode 100644 index 000000000..15a70c1fa --- /dev/null +++ b/include/osrm/trip_parameters.hpp @@ -0,0 +1,38 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef GLOBAL_TRIP_PARAMETERS_HPP +#define GLOBAL_TRIP_PARAMETERS_HPP + +#include "engine/api/trip_parameters.hpp" + +namespace osrm +{ +using engine::api::TripParameters; +} + +#endif diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp new file mode 100644 index 000000000..4d441a573 --- /dev/null +++ b/include/server/api/base_parameters_grammar.hpp @@ -0,0 +1,142 @@ +#ifndef SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP +#define SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/base_parameters.hpp" + +#include "engine/bearing.hpp" +#include "engine/hint.hpp" +#include "engine/polyline_compressor.hpp" + +#include +//#define BOOST_SPIRIT_DEBUG +#include + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +template struct no_trailing_dot_policy : qi::real_policies +{ + template static bool parse_dot(Iterator &first, Iterator const &last) + { + if (first == last || *first != '.') + return false; + + static const constexpr char fmt[sizeof...(Fmt)] = {Fmt...}; + + if (first + sizeof(fmt) < last && std::equal(fmt, fmt + sizeof(fmt), first + 1u)) + return false; + + ++first; + return true; + } + + template static bool parse_exp(Iterator &, const Iterator &) + { + return false; + } + + template + static bool parse_exp_n(Iterator &, const Iterator &, Attribute &) + { + return false; + } + + template + static bool parse_nan(Iterator &, const Iterator &, Attribute &) + { + return false; + } + + template + static bool parse_inf(Iterator &, const Iterator &, Attribute &) + { + return false; + } +}; + +struct BaseParametersGrammar : boost::spirit::qi::grammar +{ + using Iterator = std::string::iterator; + using RadiusesT = std::vector>; + using json_policy = no_trailing_dot_policy; + + BaseParametersGrammar(qi::rule &root_rule_, engine::api::BaseParameters ¶meters_) + : BaseParametersGrammar::base_type(root_rule_), base_parameters(parameters_) + { + const auto add_bearing = + [this](boost::optional> bearing_range) { + boost::optional bearing; + if (bearing_range) + { + bearing = engine::Bearing{boost::fusion::at_c<0>(*bearing_range), + boost::fusion::at_c<1>(*bearing_range)}; + } + base_parameters.bearings.push_back(std::move(bearing)); + }; + const auto set_radiuses = [this](RadiusesT radiuses) { + base_parameters.radiuses = std::move(radiuses); + }; + const auto add_hint = [this](const std::string &hint_string) { + if (hint_string.size() > 0) + { + base_parameters.hints.push_back(engine::Hint::FromBase64(hint_string)); + } + }; + const auto add_coordinate = [this](const boost::fusion::vector &lonLat) { + base_parameters.coordinates.emplace_back(util::Coordinate( + util::FixedLongitude(boost::fusion::at_c<0>(lonLat) * COORDINATE_PRECISION), + util::FixedLatitude(boost::fusion::at_c<1>(lonLat) * COORDINATE_PRECISION))); + }; + const auto polyline_to_coordinates = [this](const std::string &polyline) { + base_parameters.coordinates = engine::decodePolyline(polyline); + }; + + alpha_numeral = +qi::char_("a-zA-Z0-9"); + polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^"); + base64_char = qi::char_("a-zA-Z0-9--_="); + + unlimited.add("unlimited", std::numeric_limits::infinity()); + + radiuses_rule = qi::lit("radiuses=") > -(unlimited | qi::double_) % ";"; + hints_rule = + qi::lit("hints=") > + qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";"; + bearings_rule = + qi::lit("bearings=") > (-(qi::short_ > ',' > qi::short_))[add_bearing] % ";"; + polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > qi::lit(")")] + [polyline_to_coordinates]; + location_rule = (double_ > qi::lit(',') > double_)[add_coordinate]; + query_rule = (location_rule % ';') | polyline_rule; + + base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule; + } + + protected: + qi::rule base_rule; + qi::rule query_rule; + + private: + engine::api::BaseParameters &base_parameters; + qi::rule bearings_rule; + qi::rule hints_rule; + qi::rule polyline_rule, location_rule; + qi::symbols unlimited; + qi::rule radiuses_rule; + qi::rule base64_char; + qi::rule alpha_numeral, polyline_chars; + qi::real_parser double_; +}; +} +} +} + +#endif diff --git a/include/server/api/match_parameter_grammar.hpp b/include/server/api/match_parameter_grammar.hpp new file mode 100644 index 000000000..6671a58b6 --- /dev/null +++ b/include/server/api/match_parameter_grammar.hpp @@ -0,0 +1,74 @@ +#ifndef MATCH_PARAMETERS_GRAMMAR_HPP +#define MATCH_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/match_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct MatchParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + using StepsT = bool; + using TimestampsT = std::vector; + using GeometriesT = engine::api::RouteParameters::GeometriesType; + using OverviewT = engine::api::RouteParameters::OverviewType; + + MatchParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_geojson_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON; + }; + const auto set_polyline_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline; + }; + + const auto set_simplified_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Simplified; + }; + const auto set_full_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Full; + }; + const auto set_false_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::False; + }; + const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; }; + const auto set_timestamps = [this](TimestampsT timestamps) { + parameters.timestamps = std::move(timestamps); + }; + + steps_rule = qi::lit("steps=") > qi::bool_; + geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | + qi::lit("geometries=polyline")[set_polyline_type]; + overview_rule = qi::lit("overview=simplified")[set_simplified_type] | + qi::lit("overview=full")[set_full_type] | + qi::lit("overview=false")[set_false_type]; + timestamps_rule = qi::lit("timestamps=") > qi::uint_ % ";"; + match_rule = steps_rule[set_steps] | geometries_rule | overview_rule | + timestamps_rule[set_timestamps]; + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (match_rule | base_rule) % '&'); + } + + engine::api::MatchParameters parameters; + + private: + qi::rule root_rule, match_rule, geometries_rule, overview_rule; + qi::rule timestamps_rule; + qi::rule steps_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/nearest_parameter_grammar.hpp b/include/server/api/nearest_parameter_grammar.hpp new file mode 100644 index 000000000..09b6c2ab5 --- /dev/null +++ b/include/server/api/nearest_parameter_grammar.hpp @@ -0,0 +1,43 @@ +#ifndef NEAREST_PARAMETERS_GRAMMAR_HPP +#define NEAREST_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/nearest_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct NearestParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + + NearestParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_number = [this](const unsigned number) { + parameters.number_of_results = number; + }; + nearest_rule = (qi::lit("number=") > qi::uint_)[set_number]; + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (nearest_rule | base_rule) % '&'); + } + + engine::api::NearestParameters parameters; + + private: + qi::rule root_rule; + qi::rule nearest_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/parameters_parser.hpp b/include/server/api/parameters_parser.hpp new file mode 100644 index 000000000..161910dbe --- /dev/null +++ b/include/server/api/parameters_parser.hpp @@ -0,0 +1,49 @@ +#ifndef SERVER_API_ROUTE_PARAMETERS_PARSER_HPP +#define SERVER_API_ROUTE_PARAMETERS_PARSER_HPP + +#include "engine/api/base_parameters.hpp" +#include "engine/api/tile_parameters.hpp" + +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +// Note: this file provides only the interface for the generic parseParameters function. +// The actual implementations for each concrete parameter type live in the cpp file. + +namespace detail +{ +template +using is_parameter_t = + std::integral_constant::value || + std::is_same::value>; +} // ns detail + +// Starts parsing and iter and modifies it until iter == end or parsing failed +template ::value, int>::type = 0> +boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end); + +// Copy on purpose because we need mutability +template ::value, int>::type = 0> +boost::optional parseParameters(std::string options_string) +{ + auto first = options_string.begin(); + const auto last = options_string.end(); + return parseParameters(first, last); +} + +} // ns api +} // ns server +} // ns osrm + +#endif diff --git a/include/server/api/parsed_url.hpp b/include/server/api/parsed_url.hpp new file mode 100644 index 000000000..c80519636 --- /dev/null +++ b/include/server/api/parsed_url.hpp @@ -0,0 +1,37 @@ +#ifndef SERVER_API_PARSED_URL_HPP +#define SERVER_API_PARSED_URL_HPP + +#include "util/coordinate.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +struct ParsedURL final +{ + std::string service; + unsigned version; + std::string profile; + std::string query; +}; + +} // api +} // server +} // osrm + +BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL, + (std::string, service) + (unsigned, version) + (std::string, profile) + (std::string, query) +) + +#endif diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp new file mode 100644 index 000000000..d4280df44 --- /dev/null +++ b/include/server/api/route_parameters_grammar.hpp @@ -0,0 +1,80 @@ +#ifndef ROUTE_PARAMETERS_GRAMMAR_HPP +#define ROUTE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/route_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct RouteParametersGrammar : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + using StepsT = bool; + using AlternativeT = bool; + using GeometriesT = engine::api::RouteParameters::GeometriesType; + using OverviewT = engine::api::RouteParameters::OverviewType; + using UturnsT = bool; + + RouteParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_geojson_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON; + }; + const auto set_polyline_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline; + }; + + const auto set_simplified_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Simplified; + }; + const auto set_full_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Full; + }; + const auto set_false_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::False; + }; + const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; }; + const auto set_alternatives = [this](const AlternativeT alternatives) { + parameters.alternatives = alternatives; + }; + const auto set_continue_straight = [this](UturnsT continue_straight) { parameters.continue_straight = std::move(continue_straight); }; + + alternatives_rule = qi::lit("alternatives=") > qi::bool_; + steps_rule = qi::lit("steps=") > qi::bool_; + geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | + qi::lit("geometries=polyline")[set_polyline_type]; + overview_rule = qi::lit("overview=simplified")[set_simplified_type] | + qi::lit("overview=full")[set_full_type] | + qi::lit("overview=false")[set_false_type]; + continue_straight_rule = qi::lit("continue_straight=default") | (qi::lit("continue_straight=") > qi::bool_)[set_continue_straight]; + route_rule = steps_rule[set_steps] | alternatives_rule[set_alternatives] | geometries_rule | + overview_rule | continue_straight_rule; + + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (route_rule | base_rule) % '&'); + } + + engine::api::RouteParameters parameters; + + private: + qi::rule root_rule; + qi::rule route_rule, geometries_rule, overview_rule; + qi::rule continue_straight_rule; + qi::rule steps_rule; + qi::rule alternatives_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/table_parameter_grammar.hpp b/include/server/api/table_parameter_grammar.hpp new file mode 100644 index 000000000..b95b1b8c5 --- /dev/null +++ b/include/server/api/table_parameter_grammar.hpp @@ -0,0 +1,59 @@ +#ifndef TABLE_PARAMETERS_GRAMMAR_HPP +#define TABLE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/table_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct TableParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + using SourcesT = std::vector; + using DestinationsT = std::vector; + + TableParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_destiantions = [this](DestinationsT dests) { + parameters.destinations = std::move(dests); + }; + const auto set_sources = [this](SourcesT sources) { + parameters.sources = std::move(sources); + }; +// TODO: ulonglong -> size_t not only on Windows but on all 32 bit platforms; unsupported anyway as of now +#ifdef WIN32 + destinations_rule = qi::lit("destinations=all") | (qi::lit("destinations=") > (qi::ulong_long % ";")[set_destiantions]); + sources_rule = qi::lit("sources=all") | (qi::lit("sources=") > (qi::ulong_long % ";")[set_sources]); +#else + destinations_rule = qi::lit("destinations=all") | (qi::lit("destinations=") > (qi::ulong_ % ";")[set_destiantions]); + sources_rule = qi::lit("sources=all") | (qi::lit("sources=") > (qi::ulong_ % ";")[set_sources]); +#endif + table_rule = destinations_rule | sources_rule; + + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (table_rule | base_rule) % '&'); + } + + engine::api::TableParameters parameters; + + private: + qi::rule root_rule; + qi::rule table_rule; + qi::rule sources_rule; + qi::rule destinations_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/tile_parameter_grammar.hpp b/include/server/api/tile_parameter_grammar.hpp new file mode 100644 index 000000000..ef73357b9 --- /dev/null +++ b/include/server/api/tile_parameter_grammar.hpp @@ -0,0 +1,48 @@ +#ifndef SERVER_API_TILE_PARAMETERS_GRAMMAR_HPP +#define SERVER_API_TILE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/tile_parameters.hpp" + +#include "engine/hint.hpp" +#include "engine/polyline_compressor.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; +struct TileParametersGrammar final : boost::spirit::qi::grammar +{ + using Iterator = std::string::iterator; + + TileParametersGrammar() : TileParametersGrammar::base_type(root_rule) + { + const auto set_x = [this](const unsigned x_) { parameters.x = x_; }; + const auto set_y = [this](const unsigned y_) { parameters.y = y_; }; + const auto set_z = [this](const unsigned z_) { parameters.z = z_; }; + + query_rule = qi::lit("tile(") > qi::uint_[set_x] // + > qi::lit(",") > qi::uint_[set_y] > // + qi::lit(",") > qi::uint_[set_z] > qi::lit(")"); // + + root_rule = query_rule > qi::lit(".mvt"); + } + engine::api::TileParameters parameters; + + private: + qi::rule root_rule; + qi::rule query_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/trip_parameter_grammar.hpp b/include/server/api/trip_parameter_grammar.hpp new file mode 100644 index 000000000..52c66223a --- /dev/null +++ b/include/server/api/trip_parameter_grammar.hpp @@ -0,0 +1,68 @@ +#ifndef TRIP_PARAMETERS_GRAMMAR_HPP +#define TRIP_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/trip_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct TripParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + using StepsT = bool; + using GeometriesT = engine::api::RouteParameters::GeometriesType; + using OverviewT = engine::api::RouteParameters::OverviewType; + + TripParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_geojson_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON; + }; + const auto set_polyline_type = [this] { + parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline; + }; + + const auto set_simplified_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Simplified; + }; + const auto set_full_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::Full; + }; + const auto set_false_type = [this] { + parameters.overview = engine::api::RouteParameters::OverviewType::False; + }; + const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; }; + + steps_rule = qi::lit("steps=") > qi::bool_; + geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | + qi::lit("geometries=polyline")[set_polyline_type]; + overview_rule = qi::lit("overview=simplified")[set_simplified_type] | + qi::lit("overview=full")[set_full_type] | + qi::lit("overview=false")[set_false_type]; + trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule; + + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (trip_rule | base_rule) % '&'); + } + + engine::api::TripParameters parameters; + + private: + qi::rule root_rule, trip_rule, geometries_rule, overview_rule; + qi::rule steps_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/url_parser.hpp b/include/server/api/url_parser.hpp new file mode 100644 index 000000000..5fcca36d1 --- /dev/null +++ b/include/server/api/url_parser.hpp @@ -0,0 +1,29 @@ +#ifndef SERVER_URL_PARSER_HPP +#define SERVER_URL_PARSER_HPP + +#include "server/api/parsed_url.hpp" + +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +// Starts parsing and iter and modifies it until iter == end or parsing failed +boost::optional parseURL(std::string::iterator &iter, const std::string::iterator end); + +inline boost::optional parseURL(std::string url_string) +{ + auto iter = url_string.begin(); + return parseURL(iter, url_string.end()); +} +} +} +} + +#endif diff --git a/include/server/connection.hpp b/include/server/connection.hpp new file mode 100644 index 000000000..08821597e --- /dev/null +++ b/include/server/connection.hpp @@ -0,0 +1,72 @@ +#ifndef CONNECTION_HPP +#define CONNECTION_HPP + +#include "server/http/compression_type.hpp" +#include "server/http/reply.hpp" +#include "server/http/request.hpp" +#include "server/request_parser.hpp" + +#include +#include +#include +#include + +#include +#include + +// workaround for incomplete std::shared_ptr compatibility in old boost versions +#if BOOST_VERSION < 105300 || defined BOOST_NO_CXX11_SMART_PTR + +namespace boost +{ +template const T *get_pointer(std::shared_ptr const &p) { return p.get(); } + +template T *get_pointer(std::shared_ptr &p) { return p.get(); } +} // namespace boost + +#endif + +namespace osrm +{ +namespace server +{ + +class RequestHandler; + +/// Represents a single connection from a client. +class Connection : public std::enable_shared_from_this +{ + public: + explicit Connection(boost::asio::io_service &io_service, RequestHandler &handler); + Connection(const Connection &) = delete; + Connection &operator=(const Connection &) = delete; + + boost::asio::ip::tcp::socket &socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + + private: + void handle_read(const boost::system::error_code &e, std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code &e); + + std::vector compress_buffers(const std::vector &uncompressed_data, + const http::compression_type compression_type); + + boost::asio::io_service::strand strand; + boost::asio::ip::tcp::socket TCP_socket; + RequestHandler &request_handler; + RequestParser request_parser; + boost::array incoming_data_buffer; + http::request current_request; + http::reply current_reply; + std::vector compressed_output; + // Header compression_header; + std::vector output_buffer; +}; +} +} + +#endif // CONNECTION_HPP diff --git a/include/server/http/compression_type.hpp b/include/server/http/compression_type.hpp new file mode 100644 index 000000000..7ff929c0e --- /dev/null +++ b/include/server/http/compression_type.hpp @@ -0,0 +1,21 @@ +#ifndef COMPRESSION_TYPE_HPP +#define COMPRESSION_TYPE_HPP + +namespace osrm +{ +namespace server +{ +namespace http +{ + +enum compression_type +{ + no_compression, + gzip_rfc1952, + deflate_rfc1951 +}; +} +} +} + +#endif // COMPRESSION_TYPE_HPP diff --git a/include/server/http/header.hpp b/include/server/http/header.hpp new file mode 100644 index 000000000..33cc4bb2f --- /dev/null +++ b/include/server/http/header.hpp @@ -0,0 +1,34 @@ +#ifndef HEADER_HPP +#define HEADER_HPP + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace http +{ + +struct header +{ + // explicitly use default copy c'tor as adding move c'tor + header &operator=(const header &other) = default; + header(std::string name, std::string value) : name(std::move(name)), value(std::move(value)) {} + header(header &&other) : name(std::move(other.name)), value(std::move(other.value)) {} + + void clear() + { + name.clear(); + value.clear(); + } + + std::string name; + std::string value; +}; +} +} +} + +#endif // HEADER_HPP diff --git a/include/server/http/reply.hpp b/include/server/http/reply.hpp new file mode 100644 index 000000000..8474a5c2d --- /dev/null +++ b/include/server/http/reply.hpp @@ -0,0 +1,45 @@ +#ifndef REPLY_HPP +#define REPLY_HPP + +#include "server/http/header.hpp" + +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace http +{ + +class reply +{ + public: + enum status_type + { + ok = 200, + bad_request = 400, + internal_server_error = 500 + } status; + + std::vector
headers; + std::vector to_buffers(); + std::vector headers_to_buffers(); + std::vector content; + static reply stock_reply(const status_type status); + void set_size(const std::size_t size); + void set_uncompressed_size(); + + reply(); + + private: + std::string status_to_string(reply::status_type status); + boost::asio::const_buffer status_to_buffer(reply::status_type status); +}; +} +} +} + +#endif // REPLY_HPP diff --git a/include/server/http/request.hpp b/include/server/http/request.hpp new file mode 100644 index 000000000..1b86253f7 --- /dev/null +++ b/include/server/http/request.hpp @@ -0,0 +1,26 @@ +#ifndef REQUEST_HPP +#define REQUEST_HPP + +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace http +{ + +struct request +{ + std::string uri; + std::string referrer; + std::string agent; + boost::asio::ip::address endpoint; +}; +} +} +} + +#endif // REQUEST_HPP diff --git a/include/server/request_handler.hpp b/include/server/request_handler.hpp new file mode 100644 index 000000000..d76ba6dac --- /dev/null +++ b/include/server/request_handler.hpp @@ -0,0 +1,37 @@ +#ifndef REQUEST_HANDLER_HPP +#define REQUEST_HANDLER_HPP + +#include "server/service_handler.hpp" + +#include + +namespace osrm +{ +namespace server +{ + +namespace http +{ +class reply; +struct request; +} + +class RequestHandler +{ + + public: + RequestHandler() = default; + RequestHandler(const RequestHandler &) = delete; + RequestHandler &operator=(const RequestHandler &) = delete; + + void RegisterServiceHandler(std::unique_ptr service_handler); + + void HandleRequest(const http::request ¤t_request, http::reply ¤t_reply); + + private: + std::unique_ptr service_handler; +}; +} +} + +#endif // REQUEST_HANDLER_HPP diff --git a/include/server/request_parser.hpp b/include/server/request_parser.hpp new file mode 100644 index 000000000..3ac4c17a5 --- /dev/null +++ b/include/server/request_parser.hpp @@ -0,0 +1,76 @@ +#ifndef REQUEST_PARSER_HPP +#define REQUEST_PARSER_HPP + +#include "server/http/compression_type.hpp" +#include "server/http/header.hpp" + +#include + +namespace osrm +{ +namespace server +{ + +namespace http +{ +struct request; +} + +class RequestParser +{ + public: + RequestParser(); + + enum class RequestStatus : char + { + valid, + invalid, + indeterminate + }; + + std::tuple + parse(http::request ¤t_request, char *begin, char *end); + + private: + RequestStatus consume(http::request ¤t_request, const char input); + + bool is_char(const int character) const; + + bool is_CTL(const int character) const; + + bool is_special(const int character) const; + + bool is_digit(const int character) const; + + enum class internal_state : unsigned char + { + method_start, + method, + uri_start, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state; + + http::header current_header; + http::compression_type selected_compression; +}; +} +} + +#endif // REQUEST_PARSER_HPP diff --git a/server/server.hpp b/include/server/server.hpp similarity index 57% rename from server/server.hpp rename to include/server/server.hpp index 80fae9704..881307420 100644 --- a/server/server.hpp +++ b/include/server/server.hpp @@ -1,38 +1,12 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef SERVER_HPP #define SERVER_HPP -#include "connection.hpp" -#include "request_handler.hpp" +#include "server/connection.hpp" +#include "server/request_handler.hpp" +#include "server/service_handler.hpp" -#include "../util/integer_range.hpp" -#include "../util/simple_logger.hpp" +#include "util/integer_range.hpp" +#include "util/simple_logger.hpp" #include #include @@ -45,6 +19,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace server +{ + class Server { public: @@ -52,7 +31,8 @@ class Server static std::shared_ptr CreateServer(std::string &ip_address, int ip_port, unsigned requested_num_threads) { - SimpleLogger().Write() << "http 1.1 compression handled by zlib version " << zlibVersion(); + util::SimpleLogger().Write() << "http 1.1 compression handled by zlib version " + << zlibVersion(); const unsigned hardware_threads = std::max(1u, std::thread::hardware_concurrency()); const unsigned real_num_threads = std::min(hardware_threads, requested_num_threads); return std::make_shared(ip_address, ip_port, real_num_threads); @@ -60,7 +40,7 @@ class Server explicit Server(const std::string &address, const int port, const unsigned thread_pool_size) : thread_pool_size(thread_pool_size), acceptor(io_service), - new_connection(std::make_shared(io_service, request_handler)) + new_connection(std::make_shared(io_service, request_handler)) { const auto port_string = std::to_string(port); @@ -72,6 +52,9 @@ class Server acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor.bind(endpoint); acceptor.listen(); + + util::SimpleLogger().Write() << "Listening on: " << acceptor.local_endpoint(); + acceptor.async_accept( new_connection->socket(), boost::bind(&Server::HandleAccept, this, boost::asio::placeholders::error)); @@ -94,7 +77,10 @@ class Server void Stop() { io_service.stop(); } - RequestHandler &GetRequestHandlerPtr() { return request_handler; } + void RegisterServiceHandler(std::unique_ptr service_handler_) + { + request_handler.RegisterServiceHandler(std::move(service_handler_)); + } private: void HandleAccept(const boost::system::error_code &e) @@ -102,7 +88,7 @@ class Server if (!e) { new_connection->start(); - new_connection = std::make_shared(io_service, request_handler); + new_connection = std::make_shared(io_service, request_handler); acceptor.async_accept( new_connection->socket(), boost::bind(&Server::HandleAccept, this, boost::asio::placeholders::error)); @@ -112,8 +98,10 @@ class Server unsigned thread_pool_size; boost::asio::io_service io_service; boost::asio::ip::tcp::acceptor acceptor; - std::shared_ptr new_connection; + std::shared_ptr new_connection; RequestHandler request_handler; }; +} +} #endif // SERVER_HPP diff --git a/include/server/service/base_service.hpp b/include/server/service/base_service.hpp new file mode 100644 index 000000000..36c82dbf9 --- /dev/null +++ b/include/server/service/base_service.hpp @@ -0,0 +1,39 @@ +#ifndef SERVER_SERVICE_BASE_SERVICE_HPP +#define SERVER_SERVICE_BASE_SERVICE_HPP + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class BaseService +{ + public: + using ResultT = mapbox::util::variant; + + BaseService(OSRM &routing_machine) : routing_machine(routing_machine) {} + virtual ~BaseService() = default; + + virtual engine::Status RunQuery(std::string &query, ResultT &result) = 0; + + virtual unsigned GetVersion() = 0; + + protected: + OSRM &routing_machine; +}; +} +} +} + +#endif diff --git a/include/server/service/match_service.hpp b/include/server/service/match_service.hpp new file mode 100644 index 000000000..23212c182 --- /dev/null +++ b/include/server/service/match_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_MATCH_SERVICE_HPP +#define SERVER_SERVICE_MATCH_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class MatchService final : public BaseService +{ + public: + MatchService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/nearest_service.hpp b/include/server/service/nearest_service.hpp new file mode 100644 index 000000000..222003c7f --- /dev/null +++ b/include/server/service/nearest_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_NEAREST_SERVICE_HPP +#define SERVER_SERVICE_NEAREST_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class NearestService final : public BaseService +{ + public: + NearestService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/route_service.hpp b/include/server/service/route_service.hpp new file mode 100644 index 000000000..11155856e --- /dev/null +++ b/include/server/service/route_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_ROUTE_SERVICE_HPP +#define SERVER_SERVICE_ROUTE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class RouteService final : public BaseService +{ + public: + RouteService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/table_service.hpp b/include/server/service/table_service.hpp new file mode 100644 index 000000000..61b9d84b1 --- /dev/null +++ b/include/server/service/table_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_TABLE_SERVICE_HPP +#define SERVER_SERVICE_TABLE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class TableService final : public BaseService +{ + public: + TableService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/tile_service.hpp b/include/server/service/tile_service.hpp new file mode 100644 index 000000000..05365d437 --- /dev/null +++ b/include/server/service/tile_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_TILE_SERVICE_HPP +#define SERVER_SERVICE_TILE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class TileService final : public BaseService +{ + public: + TileService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/trip_service.hpp b/include/server/service/trip_service.hpp new file mode 100644 index 000000000..7daeba6f0 --- /dev/null +++ b/include/server/service/trip_service.hpp @@ -0,0 +1,33 @@ +#ifndef SERVER_SERVICE_TRIP_SERVICE_HPP +#define SERVER_SERVICE_TRIP_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class TripService final : public BaseService +{ + public: + TripService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service/utils.hpp b/include/server/service/utils.hpp new file mode 100644 index 000000000..e4fd7df13 --- /dev/null +++ b/include/server/service/utils.hpp @@ -0,0 +1,29 @@ +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +const constexpr char PARAMETER_SIZE_MISMATCH_MSG[] = + "Number of elements in %1% size %2% does not match coordinate size %3%"; + +template +bool constrainParamSize(const char *msg_template, + const char *name, + const ParamT ¶m, + const std::size_t target_size, + std::string &help) +{ + if (param.size() > 0 && param.size() != target_size) + { + help = (boost::format(msg_template) % name % param.size() % target_size).str(); + return true; + } + return false; +} +} +} +} diff --git a/include/server/service_handler.hpp b/include/server/service_handler.hpp new file mode 100644 index 000000000..cb9b03762 --- /dev/null +++ b/include/server/service_handler.hpp @@ -0,0 +1,41 @@ +#ifndef SERVER_SERVICE_HANLDER_HPP +#define SERVER_SERVICE_HANLDER_HPP + +#include "server/service/base_service.hpp" + +#include "osrm/osrm.hpp" + +#include + +namespace osrm +{ +namespace util +{ +namespace json +{ +struct Object; +} +} +namespace server +{ +namespace api +{ +struct ParsedURL; +} + +class ServiceHandler +{ + public: + ServiceHandler(osrm::EngineConfig &config); + using ResultT = service::BaseService::ResultT; + + engine::Status RunQuery(api::ParsedURL parsed_url, ResultT &result); + + private: + std::unordered_map> service_map; + OSRM routing_machine; +}; +} +} + +#endif diff --git a/include/storage/shared_barriers.hpp b/include/storage/shared_barriers.hpp new file mode 100644 index 000000000..48d305c79 --- /dev/null +++ b/include/storage/shared_barriers.hpp @@ -0,0 +1,39 @@ +#ifndef SHARED_BARRIERS_HPP +#define SHARED_BARRIERS_HPP + +#include +#include + +namespace osrm +{ +namespace storage +{ +struct SharedBarriers +{ + + SharedBarriers() + : pending_update_mutex(boost::interprocess::open_or_create, "pending_update"), + update_mutex(boost::interprocess::open_or_create, "update"), + query_mutex(boost::interprocess::open_or_create, "query"), + no_running_queries_condition(boost::interprocess::open_or_create, "no_running_queries"), + update_ongoing(false), number_of_queries(0) + { + } + + // Mutex to protect access to the boolean variable + boost::interprocess::named_mutex pending_update_mutex; + boost::interprocess::named_mutex update_mutex; + boost::interprocess::named_mutex query_mutex; + + // Condition that no update is running + boost::interprocess::named_condition no_running_queries_condition; + + // Is there an ongoing update? + bool update_ongoing; + // Is there any query? + int number_of_queries; +}; +} +} + +#endif // SHARED_BARRIERS_HPP diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp new file mode 100644 index 000000000..ec1033a0d --- /dev/null +++ b/include/storage/shared_datatype.hpp @@ -0,0 +1,136 @@ +#ifndef SHARED_DATA_TYPE_HPP +#define SHARED_DATA_TYPE_HPP + +#include "util/exception.hpp" +#include "util/simple_logger.hpp" + +#include + +#include + +namespace osrm +{ +namespace storage +{ + +// Added at the start and end of each block as sanity check +const constexpr char CANARY[] = "OSRM"; + +struct SharedDataLayout +{ + enum BlockID + { + NAME_OFFSETS = 0, + NAME_BLOCKS, + NAME_CHAR_LIST, + NAME_ID_LIST, + VIA_NODE_LIST, + GRAPH_NODE_LIST, + GRAPH_EDGE_LIST, + COORDINATE_LIST, + TURN_INSTRUCTION, + TRAVEL_MODE, + R_SEARCH_TREE, + GEOMETRIES_INDEX, + GEOMETRIES_LIST, + HSGR_CHECKSUM, + TIMESTAMP, + FILE_INDEX_PATH, + CORE_MARKER, + DATASOURCES_LIST, + DATASOURCE_NAME_DATA, + DATASOURCE_NAME_OFFSETS, + DATASOURCE_NAME_LENGTHS, + PROPERTIES, + NUM_BLOCKS + }; + + std::array num_entries; + std::array entry_size; + + SharedDataLayout() : num_entries(), entry_size() {} + + template inline void SetBlockSize(BlockID bid, uint64_t entries) + { + num_entries[bid] = entries; + entry_size[bid] = sizeof(T); + } + + inline uint64_t GetBlockSize(BlockID bid) const + { + // special bit encoding + if (bid == CORE_MARKER) + { + return (num_entries[bid] / 32 + 1) * entry_size[bid]; + } + + return num_entries[bid] * entry_size[bid]; + } + + inline uint64_t GetSizeOfLayout() const + { + return GetBlockOffset(NUM_BLOCKS) + NUM_BLOCKS * 2 * sizeof(CANARY); + } + + inline uint64_t GetBlockOffset(BlockID bid) const + { + uint64_t result = sizeof(CANARY); + for (auto i = 0; i < bid; i++) + { + result += GetBlockSize((BlockID)i) + 2 * sizeof(CANARY); + } + return result; + } + + template + inline T *GetBlockPtr(char *shared_memory, BlockID bid) + { + T *ptr = (T *)(shared_memory + GetBlockOffset(bid)); + if (WRITE_CANARY) + { + char *start_canary_ptr = shared_memory + GetBlockOffset(bid) - sizeof(CANARY); + char *end_canary_ptr = shared_memory + GetBlockOffset(bid) + GetBlockSize(bid); + std::copy(CANARY, CANARY + sizeof(CANARY), start_canary_ptr); + std::copy(CANARY, CANARY + sizeof(CANARY), end_canary_ptr); + } + else + { + char *start_canary_ptr = shared_memory + GetBlockOffset(bid) - sizeof(CANARY); + char *end_canary_ptr = shared_memory + GetBlockOffset(bid) + GetBlockSize(bid); + bool start_canary_alive = std::equal(CANARY, CANARY + sizeof(CANARY), start_canary_ptr); + bool end_canary_alive = std::equal(CANARY, CANARY + sizeof(CANARY), end_canary_ptr); + if (!start_canary_alive) + { + throw util::exception("Start canary of block corrupted."); + } + if (!end_canary_alive) + { + throw util::exception("End canary of block corrupted."); + } + } + + return ptr; + } +}; + +enum SharedDataType +{ + CURRENT_REGIONS, + LAYOUT_1, + DATA_1, + LAYOUT_2, + DATA_2, + LAYOUT_NONE, + DATA_NONE +}; + +struct SharedDataTimestamp +{ + SharedDataType layout; + SharedDataType data; + unsigned timestamp; +}; +} +} + +#endif /* SHARED_DATA_TYPE_HPP */ diff --git a/data_structures/shared_memory_factory.hpp b/include/storage/shared_memory.hpp similarity index 68% rename from data_structures/shared_memory_factory.hpp rename to include/storage/shared_memory.hpp index 833b4aae4..47238cfdf 100644 --- a/data_structures/shared_memory_factory.hpp +++ b/include/storage/shared_memory.hpp @@ -1,35 +1,8 @@ -/* +#ifndef SHARED_MEMORY_HPP +#define SHARED_MEMORY_HPP -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SHARED_MEMORY_FACTORY_HPP -#define SHARED_MEMORY_FACTORY_HPP - -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" #include #include @@ -51,6 +24,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace storage +{ + struct OSRMLockFile { boost::filesystem::path operator()() @@ -80,15 +58,18 @@ class SharedMemory } shm_remove() : m_shmid(INT_MIN), m_initialized(false) {} + shm_remove(const shm_remove &) = delete; + shm_remove &operator=(const shm_remove &) = delete; + ~shm_remove() { if (m_initialized) { - SimpleLogger().Write(logDEBUG) << "automatic memory deallocation"; + util::SimpleLogger().Write(logDEBUG) << "automatic memory deallocation"; if (!boost::interprocess::xsi_shared_memory::remove(m_shmid)) { - SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid; + util::SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid; } } } @@ -97,7 +78,6 @@ class SharedMemory public: void *Ptr() const { return region.get_address(); } - SharedMemory() = delete; SharedMemory(const SharedMemory &) = delete; SharedMemory &operator=(const SharedMemory &) = delete; @@ -131,14 +111,15 @@ class SharedMemory { if (ENOMEM == errno) { - SimpleLogger().Write(logWARNING) << "could not lock shared memory to RAM"; + util::SimpleLogger().Write(logWARNING) << "could not lock shared memory to RAM"; } } #endif region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write); remover.SetID(shm.get_shmid()); - SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size << " bytes"; + util::SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size + << " bytes"; } } @@ -185,7 +166,7 @@ class SharedMemory bool ret = false; try { - SimpleLogger().Write(logDEBUG) << "deallocating prev memory"; + util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory"; boost::interprocess::xsi_shared_memory xsi(boost::interprocess::open_only, key); ret = boost::interprocess::xsi_shared_memory::remove(xsi.get_shmid()); } @@ -214,8 +195,6 @@ class SharedMemory class shm_remove { private: - shm_remove(const shm_remove &) = delete; - shm_remove &operator=(const shm_remove &) = delete; char *m_shmid; bool m_initialized; @@ -228,14 +207,17 @@ class SharedMemory shm_remove() : m_shmid("undefined"), m_initialized(false) {} + shm_remove(const shm_remove &) = delete; + shm_remove &operator=(const shm_remove &) = delete; + ~shm_remove() { if (m_initialized) { - SimpleLogger().Write(logDEBUG) << "automatic memory deallocation"; + util::SimpleLogger().Write(logDEBUG) << "automatic memory deallocation"; if (!boost::interprocess::shared_memory_object::remove(m_shmid)) { - SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid; + util::SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid; } } } @@ -272,7 +254,8 @@ class SharedMemory region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write); remover.SetID(key); - SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size << " bytes"; + util::SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size + << " bytes"; } } @@ -322,7 +305,7 @@ class SharedMemory bool ret = false; try { - SimpleLogger().Write(logDEBUG) << "deallocating prev memory"; + util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory"; ret = boost::interprocess::shared_memory_object::remove(key); } catch (const boost::interprocess::interprocess_exception &e) @@ -342,45 +325,36 @@ class SharedMemory }; #endif -template class SharedMemoryFactory_tmpl +template +SharedMemory *makeSharedMemory(const IdentifierT &id, + const uint64_t size = 0, + bool read_write = false, + bool remove_prev = true) { - public: - template - static SharedMemory *Get(const IdentifierT &id, - const uint64_t size = 0, - bool read_write = false, - bool remove_prev = true) + try { - try + LockFileT lock_file; + if (!boost::filesystem::exists(lock_file())) { - LockFileT lock_file; - if (!boost::filesystem::exists(lock_file())) + if (0 == size) { - if (0 == size) - { - throw osrm::exception("lock file does not exist, exiting"); - } - else - { - boost::filesystem::ofstream ofs(lock_file()); - ofs.close(); - } + throw util::exception("lock file does not exist, exiting"); + } + else + { + boost::filesystem::ofstream ofs(lock_file()); } - return new SharedMemory(lock_file(), id, size, read_write, remove_prev); - } - catch (const boost::interprocess::interprocess_exception &e) - { - SimpleLogger().Write(logWARNING) << "caught exception: " << e.what() << ", code " - << e.get_error_code(); - throw osrm::exception(e.what()); } + return new SharedMemory(lock_file(), id, size, read_write, remove_prev); } + catch (const boost::interprocess::interprocess_exception &e) + { + util::SimpleLogger().Write(logWARNING) << "caught exception: " << e.what() << ", code " + << e.get_error_code(); + throw util::exception(e.what()); + } +} +} +} - SharedMemoryFactory_tmpl() = delete; - SharedMemoryFactory_tmpl(const SharedMemoryFactory_tmpl &) = delete; - SharedMemoryFactory_tmpl &operator=(const SharedMemoryFactory_tmpl &) = delete; -}; - -using SharedMemoryFactory = SharedMemoryFactory_tmpl<>; - -#endif // SHARED_MEMORY_FACTORY_HPP +#endif // SHARED_MEMORY_HPP diff --git a/server/http/request.hpp b/include/storage/storage.hpp similarity index 80% rename from server/http/request.hpp rename to include/storage/storage.hpp index c487fba65..f5934ddf4 100644 --- a/server/http/request.hpp +++ b/include/storage/storage.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,24 +25,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef REQUEST_HPP -#define REQUEST_HPP +#ifndef STORAGE_HPP +#define STORAGE_HPP -#include +#include "storage/storage_config.hpp" + +#include #include -namespace http +namespace osrm { +namespace storage +{ +class Storage +{ + public: + Storage(StorageConfig config); + int Run(); -struct request -{ - std::string uri; - std::string referrer; - std::string agent; - boost::asio::ip::address endpoint; + private: + StorageConfig config; }; +} +} -} // namespace http - -#endif // REQUEST_HPP +#endif diff --git a/util/json_logger.hpp b/include/storage/storage_config.hpp similarity index 53% rename from util/json_logger.hpp rename to include/storage/storage_config.hpp index 8726f78d9..62f2fddd5 100644 --- a/util/json_logger.hpp +++ b/include/storage/storage_config.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2015, Project OSRM contributors +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,67 +25,47 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JSON_LOGGER_HPP -#define JSON_LOGGER_HPP +#ifndef STORAGE_CONFIG_HPP +#define STORAGE_CONFIG_HPP -#include - -#include - -#include -#include +#include namespace osrm { -namespace json +namespace storage { -// Used to append additional debugging information to the JSON response in a -// thread safe manner. -class Logger +/** + * Configures OSRM's file storage paths. + * + * \see OSRM, EngineConfig + */ +struct StorageConfig final { - using MapT = std::unordered_map; + StorageConfig() = default; - public: - static Logger* get() - { - static Logger logger; + /** + * Constructs a storage configuration setting paths based on a base path. + * + * \param base The base path (e.g. france.pbf.osrm) to derive auxiliary file suffixes from. + */ + StorageConfig(const boost::filesystem::path &base); + bool IsValid() const; - bool return_logger = true; -#ifdef NDEBUG - return_logger = false; -#endif -#ifdef ENABLE_JSON_LOGGING - return_logger = true; -#endif - - if (return_logger) - { - return &logger; - } - - return nullptr; - } - - void initialize(const std::string& name) - { - if (!map.get()) - { - map.reset(new MapT()); - } - (*map)[name] = Object(); - } - - void render(const std::string& name, Object& obj) const - { - obj.values["debug"] = map->at(name); - } - - boost::thread_specific_ptr map; + boost::filesystem::path ram_index_path; + boost::filesystem::path file_index_path; + boost::filesystem::path hsgr_data_path; + boost::filesystem::path nodes_data_path; + boost::filesystem::path edges_data_path; + boost::filesystem::path core_data_path; + boost::filesystem::path geometries_path; + boost::filesystem::path timestamp_path; + boost::filesystem::path datasource_names_path; + boost::filesystem::path datasource_indexes_path; + boost::filesystem::path names_data_path; + boost::filesystem::path properties_path; }; - - } } -#endif /* JSON_LOGGER_HPP */ +#endif diff --git a/include/util/assert.hpp b/include/util/assert.hpp new file mode 100644 index 000000000..d9bfb9542 --- /dev/null +++ b/include/util/assert.hpp @@ -0,0 +1,20 @@ +#ifndef OSRM_ASSERT_HPP +#define OSRM_ASSERT_HPP + +#include + +#include + +namespace osrm +{ +namespace util +{ +// Assertion type to be thrown for stack unwinding +struct assertionError final : std::logic_error +{ + assertionError(const char *msg) : std::logic_error{msg} {} +}; +} +} + +#endif diff --git a/util/bearing.hpp b/include/util/bearing.hpp similarity index 62% rename from util/bearing.hpp rename to include/util/bearing.hpp index 5f3036a85..f1ba6244f 100644 --- a/util/bearing.hpp +++ b/include/util/bearing.hpp @@ -1,36 +1,14 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef BEARING_HPP #define BEARING_HPP #include #include +namespace osrm +{ +namespace util +{ + namespace bearing { inline std::string get(const double heading) @@ -112,5 +90,7 @@ inline bool CheckInBounds(const int A, const int B, const int range) } } } +} +} #endif // BEARING_HPP diff --git a/data_structures/binary_heap.hpp b/include/util/binary_heap.hpp similarity index 83% rename from data_structures/binary_heap.hpp rename to include/util/binary_heap.hpp index 85703f4a6..f3c01f6b6 100644 --- a/data_structures/binary_heap.hpp +++ b/include/util/binary_heap.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef BINARY_HEAP_H #define BINARY_HEAP_H @@ -37,6 +10,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + template class ArrayStorage { public: @@ -189,9 +167,10 @@ class BinaryHeap return inserted_nodes[heap[1].index].node; } - Weight MinKey() const { - BOOST_ASSERT(heap.size() > 1); - return heap[1].weight; + Weight MinKey() const + { + BOOST_ASSERT(heap.size() > 1); + return heap[1].weight; } NodeID DeleteMin() @@ -311,5 +290,7 @@ class BinaryHeap #endif } }; +} +} #endif // BINARY_HEAP_H diff --git a/include/util/cast.hpp b/include/util/cast.hpp new file mode 100644 index 000000000..e78f630b6 --- /dev/null +++ b/include/util/cast.hpp @@ -0,0 +1,49 @@ +#ifndef CAST_HPP +#define CAST_HPP + +#include +#include +#include +#include + +#include +#include + +namespace osrm +{ +namespace util +{ + +namespace cast +{ +template +inline auto enum_to_underlying(Enumeration const value) -> + typename std::underlying_type::type +{ + return static_cast::type>(value); +} + +template inline std::string to_string_with_precision(const T x) +{ + static_assert(std::is_arithmetic::value, "integral or floating point type required"); + + std::ostringstream out; + out << std::fixed << std::setprecision(Precision) << x; + auto rv = out.str(); + + // Javascript has no separation of float / int, digits without a '.' are integral typed + // X.Y.0 -> X.Y + // X.0 -> X + boost::trim_right_if(rv, boost::is_any_of("0")); + boost::trim_right_if(rv, boost::is_any_of(".")); + // Note: + // - assumes the locale to use '.' as digit separator + // - this is not identical to: trim_right_if(rv, is_any_of('0 .')) + + return rv; +} +} +} +} + +#endif // CAST_HPP diff --git a/include/util/container.hpp b/include/util/container.hpp new file mode 100644 index 000000000..bd85e9c9c --- /dev/null +++ b/include/util/container.hpp @@ -0,0 +1,20 @@ +#ifndef CONTAINER_HPP +#define CONTAINER_HPP + +namespace osrm +{ +namespace util +{ + +template void append_to_container(Container &&) {} + +template +void append_to_container(Container &&container, T value, Args &&... args) +{ + container.emplace_back(value); + append_to_container(std::forward(container), std::forward(args)...); +} +} +} + +#endif diff --git a/include/util/coordinate.hpp b/include/util/coordinate.hpp new file mode 100644 index 000000000..e249bb227 --- /dev/null +++ b/include/util/coordinate.hpp @@ -0,0 +1,187 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef COORDINATE_HPP_ +#define COORDINATE_HPP_ + +#include "util/strong_typedef.hpp" + +#include + +#include //for std::ostream +#include +#include +#include + +namespace osrm +{ + +constexpr const double COORDINATE_PRECISION = 1e6; + +namespace util +{ + +OSRM_STRONG_TYPEDEF(int32_t, FixedLatitude) +OSRM_STRONG_TYPEDEF(int32_t, FixedLongitude) +OSRM_STRONG_TYPEDEF(double, FloatLatitude) +OSRM_STRONG_TYPEDEF(double, FloatLongitude) + +/** + * Converts a typed latitude from floating to fixed representation. + * + * \param floating typed latitude in floating representation. + * \return typed latitude in fixed representation + * \see Coordinate, toFloating + */ +inline FixedLatitude toFixed(const FloatLatitude floating) +{ + const auto latitude = static_cast(floating); + const auto fixed = boost::numeric_cast(latitude * COORDINATE_PRECISION); + return FixedLatitude(fixed); +} + +/** + * Converts a typed longitude from floating to fixed representation. + * + * \param floating typed longitude in floating representation. + * \return typed latitude in fixed representation + * \see Coordinate, toFloating + */ +inline FixedLongitude toFixed(const FloatLongitude floating) +{ + const auto longitude = static_cast(floating); + const auto fixed = boost::numeric_cast(longitude * COORDINATE_PRECISION); + return FixedLongitude(fixed); +} + +/** + * Converts a typed latitude from fixed to floating representation. + * + * \param fixed typed latitude in fixed representation. + * \return typed latitude in floating representation + * \see Coordinate, toFixed + */ +inline FloatLatitude toFloating(const FixedLatitude fixed) +{ + const auto latitude = static_cast(fixed); + const auto floating = boost::numeric_cast(latitude / COORDINATE_PRECISION); + return FloatLatitude(floating); +} + +/** + * Converts a typed longitude from fixed to floating representation. + * + * \param fixed typed longitude in fixed representation. + * \return typed longitude in floating representation + * \see Coordinate, toFixed + */ +inline FloatLongitude toFloating(const FixedLongitude fixed) +{ + const auto longitude = static_cast(fixed); + const auto floating = boost::numeric_cast(longitude / COORDINATE_PRECISION); + return FloatLongitude(floating); +} + +// fwd. decl. +struct FloatCoordinate; + +/** + * Represents a coordinate based on longitude and latitude in fixed representation. + * + * To prevent accidental longitude and latitude flips, we provide typed longitude and latitude + * wrappers. You can cast these wrappers back to their underlying representation or convert them + * from one representation to the other. + * + * The two representation we provide are: + * - Fixed point + * - Floating point + * + * \see FloatCoordinate, toFixed, toFloating + */ +struct Coordinate +{ + FixedLongitude lon; + FixedLatitude lat; + + Coordinate(); + Coordinate(const FloatCoordinate &other); + Coordinate(const FixedLongitude lon_, const FixedLatitude lat_); + Coordinate(const FloatLongitude lon_, const FloatLatitude lat_); + + template Coordinate(const T &coordinate) : lon(coordinate.lon), lat(coordinate.lat) + { + static_assert(!std::is_same::value, + "This constructor should not be used for Coordinates"); + static_assert(std::is_same::value, + "coordinate types incompatible"); + static_assert(std::is_same::value, + "coordinate types incompatible"); + } + + bool IsValid() const; + friend bool operator==(const Coordinate lhs, const Coordinate rhs); + friend bool operator!=(const Coordinate lhs, const Coordinate rhs); + friend std::ostream &operator<<(std::ostream &out, const Coordinate coordinate); +}; + +/** + * Represents a coordinate based on longitude and latitude in floating representation. + * + * To prevent accidental longitude and latitude flips, we provide typed longitude and latitude + * wrappers. You can cast these wrappers back to their underlying representation or convert them + * from one representation to the other. + * + * The two representation we provide are: + * - Fixed point + * - Floating point + * + * \see Coordinate, toFixed, toFloating + */ +struct FloatCoordinate +{ + FloatLongitude lon; + FloatLatitude lat; + + FloatCoordinate(); + FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_); + FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_); + FloatCoordinate(const Coordinate other); + + bool IsValid() const; + friend bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs); + friend bool operator!=(const FloatCoordinate lhs, const FloatCoordinate rhs); + friend std::ostream &operator<<(std::ostream &out, const FloatCoordinate coordinate); +}; + +bool operator==(const Coordinate lhs, const Coordinate rhs); +bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs); +std::ostream &operator<<(std::ostream &out, const Coordinate coordinate); +std::ostream &operator<<(std::ostream &out, const FloatCoordinate coordinate); +} +} + +#endif /* COORDINATE_HPP_ */ diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp new file mode 100644 index 000000000..afa6c6532 --- /dev/null +++ b/include/util/coordinate_calculation.hpp @@ -0,0 +1,74 @@ +#ifndef COORDINATE_CALCULATION +#define COORDINATE_CALCULATION + +#include "util/coordinate.hpp" + +#include + +#include + +namespace osrm +{ +namespace util +{ +namespace coordinate_calculation +{ + +namespace detail +{ +const constexpr long double DEGREE_TO_RAD = 0.017453292519943295769236907684886; +const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD; +// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) +// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) +const constexpr long double EARTH_RADIUS = 6372797.560856; +} + + +//! Takes the squared euclidean distance of the input coordinates. Does not return meters! +std::uint64_t squaredEuclideanDistance(const Coordinate &lhs, const Coordinate &rhs); + +double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate); + +double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate); + +std::pair +projectPointOnSegment(const FloatCoordinate &projected_xy_source, + const FloatCoordinate &projected_xy_target, + const FloatCoordinate &projected_xy_coordinate); + +double perpendicularDistance(const Coordinate segment_source, + const Coordinate segment_target, + const Coordinate query_location); + +double perpendicularDistance(const Coordinate segment_source, + const Coordinate segment_target, + const Coordinate query_location, + Coordinate &nearest_location, + double &ratio); + +Coordinate centroid(const Coordinate lhs, const Coordinate rhs); + +double bearing(const Coordinate first_coordinate, const Coordinate second_coordinate); + +// Get angle of line segment (A,C)->(C,B) +double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third); + +// find the center of a circle through three coordinates +boost::optional circleCenter(const Coordinate first_coordinate, + const Coordinate second_coordinate, + const Coordinate third_coordinate); + +// find the radius of a circle through three coordinates +double circleRadius(const Coordinate first_coordinate, + const Coordinate second_coordinate, + const Coordinate third_coordinate); + +// factor in [0,1]. Returns point along the straight line between from and to. 0 returns from, 1 +// returns to +Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to); + +} // ns coordinate_calculation +} // ns util +} // ns osrm + +#endif // COORDINATE_CALCULATION diff --git a/data_structures/deallocating_vector.hpp b/include/util/deallocating_vector.hpp similarity index 85% rename from data_structures/deallocating_vector.hpp rename to include/util/deallocating_vector.hpp index 72cb0816c..c8883b2c6 100644 --- a/data_structures/deallocating_vector.hpp +++ b/include/util/deallocating_vector.hpp @@ -1,34 +1,7 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef DEALLOCATING_VECTOR_HPP #define DEALLOCATING_VECTOR_HPP -#include "../util/integer_range.hpp" +#include "util/integer_range.hpp" #include @@ -36,6 +9,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + template struct ConstDeallocatingVectorIteratorState { ConstDeallocatingVectorIteratorState() @@ -47,14 +25,15 @@ template struct ConstDeallocatingVectorIteratorState { } explicit ConstDeallocatingVectorIteratorState(const std::size_t idx, - const std::vector *input_list) + const std::vector *input_list) : index(idx), bucket_list(input_list) { } std::size_t index; const std::vector *bucket_list; - ConstDeallocatingVectorIteratorState &operator=(const ConstDeallocatingVectorIteratorState &other) + ConstDeallocatingVectorIteratorState & + operator=(const ConstDeallocatingVectorIteratorState &other) { index = other.index; bucket_list = other.bucket_list; @@ -237,11 +216,10 @@ class DeallocatingVectorRemoveIterator } }; -template -class DeallocatingVector; +template class DeallocatingVector; -template -void swap(DeallocatingVector& lhs, DeallocatingVector& rhs); +template +void swap(DeallocatingVector &lhs, DeallocatingVector &rhs); template class DeallocatingVector @@ -263,7 +241,8 @@ class DeallocatingVector ~DeallocatingVector() { clear(); } - friend void swap<>(DeallocatingVector& lhs, DeallocatingVector& rhs); + friend void swap<>(DeallocatingVector &lhs, + DeallocatingVector &rhs); void swap(DeallocatingVector &other) { @@ -327,8 +306,7 @@ class DeallocatingVector else { // down-size const std::size_t number_of_necessary_buckets = 1 + (new_size / ELEMENTS_PER_BLOCK); - for (const auto bucket_index : - osrm::irange(number_of_necessary_buckets, bucket_list.size())) + for (const auto bucket_index : irange(number_of_necessary_buckets, bucket_list.size())) { if (nullptr != bucket_list[bucket_index]) { @@ -394,10 +372,12 @@ class DeallocatingVector } }; -template -void swap(DeallocatingVector& lhs, DeallocatingVector& rhs) +template +void swap(DeallocatingVector &lhs, DeallocatingVector &rhs) { lhs.swap(rhs); } +} +} #endif /* DEALLOCATING_VECTOR_HPP */ diff --git a/util/dist_table_wrapper.hpp b/include/util/dist_table_wrapper.hpp similarity index 58% rename from util/dist_table_wrapper.hpp rename to include/util/dist_table_wrapper.hpp index 15550fafe..714bceffe 100644 --- a/util/dist_table_wrapper.hpp +++ b/include/util/dist_table_wrapper.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef DIST_TABLE_WRAPPER_H #define DIST_TABLE_WRAPPER_H @@ -33,6 +6,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + // This Wrapper provides an easier access to a distance table that is given as an linear vector template class DistTableWrapper @@ -47,7 +25,7 @@ template class DistTableWrapper BOOST_ASSERT_MSG(table.size() == 0, "table is empty"); BOOST_ASSERT_MSG(number_of_nodes_ * number_of_nodes_ <= table_.size(), "number_of_nodes_ is invalid"); - }; + } std::size_t GetNumberOfNodes() const { return number_of_nodes_; } @@ -84,5 +62,7 @@ template class DistTableWrapper std::vector table_; const std::size_t number_of_nodes_; }; +} +} #endif // DIST_TABLE_WRAPPER_H diff --git a/data_structures/dynamic_graph.hpp b/include/util/dynamic_graph.hpp similarity index 80% rename from data_structures/dynamic_graph.hpp rename to include/util/dynamic_graph.hpp index 4f63c02d4..7df32a9fc 100644 --- a/data_structures/dynamic_graph.hpp +++ b/include/util/dynamic_graph.hpp @@ -1,36 +1,9 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef DYNAMICGRAPH_HPP #define DYNAMICGRAPH_HPP -#include "deallocating_vector.hpp" -#include "../util/integer_range.hpp" -#include "../typedefs.h" +#include "util/deallocating_vector.hpp" +#include "util/integer_range.hpp" +#include "util/typedefs.hpp" #include @@ -42,13 +15,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + template class DynamicGraph { public: using EdgeData = EdgeDataT; using NodeIterator = unsigned; using EdgeIterator = unsigned; - using EdgeRange = osrm::range; + using EdgeRange = range; class InputEdge { @@ -91,14 +69,15 @@ template class DynamicGraph template DynamicGraph(const NodeIterator nodes, const ContainerT &graph) { // we need to cast here because DeallocatingVector does not have a valid const iterator - BOOST_ASSERT(std::is_sorted(const_cast(graph).begin(), const_cast(graph).end())); + BOOST_ASSERT(std::is_sorted(const_cast(graph).begin(), + const_cast(graph).end())); number_of_nodes = nodes; number_of_edges = static_cast(graph.size()); node_array.resize(number_of_nodes + 1); EdgeIterator edge = 0; EdgeIterator position = 0; - for (const auto node : osrm::irange(0u, number_of_nodes)) + for (const auto node : irange(0u, number_of_nodes)) { EdgeIterator last_edge = edge; while (edge < number_of_edges && graph[edge].source == node) @@ -113,10 +92,10 @@ template class DynamicGraph edge_list.reserve(static_cast(edge_list.size() * 1.1)); edge_list.resize(position); edge = 0; - for (const auto node : osrm::irange(0u, number_of_nodes)) + for (const auto node : irange(0u, number_of_nodes)) { - for (const auto i : osrm::irange(node_array[node].first_edge, - node_array[node].first_edge + node_array[node].edges)) + for (const auto i : irange(node_array[node].first_edge, + node_array[node].first_edge + node_array[node].edges)) { edge_list[i].target = graph[edge].target; BOOST_ASSERT(edge_list[i].target < number_of_nodes); @@ -137,7 +116,7 @@ template class DynamicGraph unsigned GetDirectedOutDegree(const NodeIterator n) const { unsigned degree = 0; - for (const auto edge : osrm::irange(BeginEdges(n), EndEdges(n))) + for (const auto edge : irange(BeginEdges(n), EndEdges(n))) { if (!GetEdgeData(edge).reversed) { @@ -167,7 +146,7 @@ template class DynamicGraph EdgeRange GetAdjacentEdgeRange(const NodeIterator node) const { - return osrm::irange(BeginEdges(node), EndEdges(node)); + return irange(BeginEdges(node), EndEdges(node)); } NodeIterator InsertNode() @@ -201,12 +180,12 @@ template class DynamicGraph edge_list.reserve(requiredCapacity * 1.1); } edge_list.resize(edge_list.size() + newSize); - for (const auto i : osrm::irange(0u, node.edges)) + for (const auto i : irange(0u, node.edges)) { edge_list[newFirstEdge + i] = edge_list[node.first_edge + i]; makeDummy(node.first_edge + i); } - for (const auto i : osrm::irange(node.edges + 1, newSize)) + for (const auto i : irange(node.edges + 1, newSize)) { makeDummy(newFirstEdge + i); } @@ -261,7 +240,7 @@ template class DynamicGraph // searches for a specific edge EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const { - for (const auto i : osrm::irange(BeginEdges(from), EndEdges(from))) + for (const auto i : irange(BeginEdges(from), EndEdges(from))) { if (to == edge_list[i].target) { @@ -341,5 +320,7 @@ template class DynamicGraph std::vector node_array; DeallocatingVector edge_list; }; +} +} #endif // DYNAMICGRAPH_HPP diff --git a/include/util/exception.hpp b/include/util/exception.hpp new file mode 100644 index 000000000..41efb0b91 --- /dev/null +++ b/include/util/exception.hpp @@ -0,0 +1,30 @@ +#ifndef OSRM_EXCEPTION_HPP +#define OSRM_EXCEPTION_HPP + +#include +#include +#include + +namespace osrm +{ +namespace util +{ + +class exception final : public std::exception +{ + public: + explicit exception(const char *message) : message(message) {} + explicit exception(std::string message) : message(std::move(message)) {} + const char *what() const noexcept override { return message.c_str(); } + + private: + // This function exists to 'anchor' the class, and stop the compiler from + // copying vtable and RTTI info into every object file that includes + // this header. (Caught by -Wweak-vtables under Clang.) + virtual void anchor() const; + const std::string message; +}; +} +} + +#endif /* OSRM_EXCEPTION_HPP */ diff --git a/include/util/fingerprint.hpp b/include/util/fingerprint.hpp new file mode 100644 index 000000000..e8238c0ac --- /dev/null +++ b/include/util/fingerprint.hpp @@ -0,0 +1,39 @@ +#ifndef FINGERPRINT_H +#define FINGERPRINT_H + +#include +#include + +namespace osrm +{ +namespace util +{ + +// implements a singleton, i.e. there is one and only one conviguration object +class FingerPrint +{ + public: + static FingerPrint GetValid(); + const boost::uuids::uuid &GetFingerPrint() const; + bool IsMagicNumberOK(const FingerPrint &other) const; + bool TestGraphUtil(const FingerPrint &other) const; + bool TestContractor(const FingerPrint &other) const; + bool TestRTree(const FingerPrint &other) const; + bool TestQueryObjects(const FingerPrint &other) const; + + private: + unsigned magic_number; + char md5_prepare[33]; + char md5_tree[33]; + char md5_graph[33]; + char md5_objects[33]; + + // initialize to {6ba7b810-9dad-11d1-80b4-00c04fd430c8} + boost::uuids::uuid named_uuid; +}; + +static_assert(std::is_trivial::value, "FingerPrint needs to be trivial."); +} +} + +#endif /* FingerPrint_H */ diff --git a/util/fingerprint_impl.hpp.in b/include/util/fingerprint_impl.hpp.in similarity index 58% rename from util/fingerprint_impl.hpp.in rename to include/util/fingerprint_impl.hpp.in index 88e8436cb..7b31d6dd7 100644 --- a/util/fingerprint_impl.hpp.in +++ b/include/util/fingerprint_impl.hpp.in @@ -1,31 +1,4 @@ -/* - -Copyright (c) 2015, Project OSRM, Dennis Luxen, others -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "util/osrm_exception.hpp" +#include "util/exception.hpp" #include #include @@ -40,6 +13,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #cmakedefine MD5GRAPH "${MD5GRAPH}" #cmakedefine MD5OBJECTS "${MD5OBJECTS}" +namespace osrm +{ +namespace util +{ + FingerPrint FingerPrint::GetValid() { FingerPrint fingerprint; @@ -79,16 +57,16 @@ bool FingerPrint::TestGraphUtil(const FingerPrint &other) const { if (!IsMagicNumberOK(other)) { - throw osrm::exception("hsgr input file misses magic number. Check or reprocess the file"); + throw exception("hsgr input file misses magic number. Check or reprocess the file"); } return std::equal(md5_graph, md5_graph + 32, other.md5_graph); } -bool FingerPrint::TestPrepare(const FingerPrint &other) const +bool FingerPrint::TestContractor(const FingerPrint &other) const { if (!IsMagicNumberOK(other)) { - throw osrm::exception("osrm input file misses magic number. Check or reprocess the file"); + throw exception("osrm input file misses magic number. Check or reprocess the file"); } return std::equal(md5_prepare, md5_prepare + 32, other.md5_prepare); } @@ -97,7 +75,7 @@ bool FingerPrint::TestRTree(const FingerPrint &other) const { if (!IsMagicNumberOK(other)) { - throw osrm::exception("r-tree input file misses magic number. Check or reprocess the file"); + throw exception("r-tree input file misses magic number. Check or reprocess the file"); } return std::equal(md5_tree, md5_tree + 32, other.md5_tree); } @@ -106,7 +84,10 @@ bool FingerPrint::TestQueryObjects(const FingerPrint &other) const { if (!IsMagicNumberOK(other)) { - throw osrm::exception("missing magic number. Check or reprocess the file"); + throw exception("missing magic number. Check or reprocess the file"); } return std::equal(md5_objects, md5_objects + 32, other.md5_objects); } + +} +} diff --git a/include/util/for_each_pair.hpp b/include/util/for_each_pair.hpp new file mode 100644 index 000000000..86fc75e28 --- /dev/null +++ b/include/util/for_each_pair.hpp @@ -0,0 +1,45 @@ +#ifndef FOR_EACH_PAIR_HPP +#define FOR_EACH_PAIR_HPP + +#include +#include + +namespace osrm +{ +namespace util +{ + +// TODO: check why this is not an option here: +// std::adjacent_find(begin, end, [=](const auto& l, const auto& r){ return function(), false; }); +template +Function for_each_pair(ForwardIterator begin, ForwardIterator end, Function function) +{ + if (begin == end) + { + return function; + } + + auto next = begin; + next = std::next(next); + + while (next != end) + { + function(*begin, *next); + begin = std::next(begin); + next = std::next(next); + } + return function; +} + +template +Function for_each_pair(ContainerT &container, Function function) +{ + using std::begin; + using std::end; + return for_each_pair(begin(container), end(container), function); +} + +} // namespace util +} // namespace osrm + +#endif /* FOR_EACH_PAIR_HPP */ diff --git a/util/graph_loader.hpp b/include/util/graph_loader.hpp similarity index 71% rename from util/graph_loader.hpp rename to include/util/graph_loader.hpp index 6ddd0ebcb..b611e04bb 100644 --- a/util/graph_loader.hpp +++ b/include/util/graph_loader.hpp @@ -1,41 +1,14 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef GRAPH_LOADER_HPP #define GRAPH_LOADER_HPP -#include "fingerprint.hpp" -#include "osrm_exception.hpp" -#include "simple_logger.hpp" -#include "../data_structures/external_memory_node.hpp" -#include "../data_structures/import_edge.hpp" -#include "../data_structures/query_node.hpp" -#include "../data_structures/restriction.hpp" -#include "../typedefs.h" +#include "util/fingerprint.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "extractor/external_memory_node.hpp" +#include "extractor/node_based_edge.hpp" +#include "extractor/query_node.hpp" +#include "extractor/restriction.hpp" +#include "util/typedefs.hpp" #include #include @@ -49,19 +22,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + /** * Reads the .restrictions file and loads it to a vector. * The since the restrictions reference nodes using their external node id, * we need to renumber it to the new internal id. */ unsigned loadRestrictionsFromFile(std::istream &input_stream, - std::vector &restriction_list) + std::vector &restriction_list) { const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; unsigned number_of_usable_restrictions = 0; input_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); - if (!fingerprint_loaded.TestPrepare(fingerprint_valid)) + if (!fingerprint_loaded.TestContractor(fingerprint_valid)) { SimpleLogger().Write(logWARNING) << ".restrictions was prepared with different build.\n" "Reprocess to get rid of this warning."; @@ -72,7 +50,7 @@ unsigned loadRestrictionsFromFile(std::istream &input_stream, if (number_of_usable_restrictions > 0) { input_stream.read((char *)restriction_list.data(), - number_of_usable_restrictions * sizeof(TurnRestriction)); + number_of_usable_restrictions * sizeof(extractor::TurnRestriction)); } return number_of_usable_restrictions; @@ -87,13 +65,13 @@ unsigned loadRestrictionsFromFile(std::istream &input_stream, NodeID loadNodesFromFile(std::istream &input_stream, std::vector &barrier_node_list, std::vector &traffic_light_node_list, - std::vector &node_array) + std::vector &node_array) { const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; input_stream.read(reinterpret_cast(&fingerprint_loaded), sizeof(FingerPrint)); - if (!fingerprint_loaded.TestPrepare(fingerprint_valid)) + if (!fingerprint_loaded.TestContractor(fingerprint_valid)) { SimpleLogger().Write(logWARNING) << ".osrm was prepared with different build.\n" "Reprocess to get rid of this warning."; @@ -103,11 +81,12 @@ NodeID loadNodesFromFile(std::istream &input_stream, input_stream.read(reinterpret_cast(&n), sizeof(NodeID)); SimpleLogger().Write() << "Importing n = " << n << " nodes "; - ExternalMemoryNode current_node; + extractor::ExternalMemoryNode current_node; for (NodeID i = 0; i < n; ++i) { - input_stream.read(reinterpret_cast(¤t_node), sizeof(ExternalMemoryNode)); - node_array.emplace_back(current_node.lat, current_node.lon, current_node.node_id); + input_stream.read(reinterpret_cast(¤t_node), + sizeof(extractor::ExternalMemoryNode)); + node_array.emplace_back(current_node.lon, current_node.lat, current_node.node_id); if (current_node.barrier) { barrier_node_list.emplace_back(i); @@ -128,21 +107,22 @@ NodeID loadNodesFromFile(std::istream &input_stream, /** * Reads a .osrm file and produces the edges. */ -NodeID loadEdgesFromFile(std::istream &input_stream, std::vector &edge_list) +NodeID loadEdgesFromFile(std::istream &input_stream, + std::vector &edge_list) { EdgeID m; input_stream.read(reinterpret_cast(&m), sizeof(unsigned)); edge_list.resize(m); SimpleLogger().Write() << " and " << m << " edges "; - input_stream.read((char *)edge_list.data(), m * sizeof(NodeBasedEdge)); + input_stream.read((char *)edge_list.data(), m * sizeof(extractor::NodeBasedEdge)); BOOST_ASSERT(edge_list.size() > 0); #ifndef NDEBUG SimpleLogger().Write() << "Validating loaded edges..."; tbb::parallel_sort(edge_list.begin(), edge_list.end(), - [](const NodeBasedEdge &lhs, const NodeBasedEdge &rhs) + [](const extractor::NodeBasedEdge &lhs, const extractor::NodeBasedEdge &rhs) { return (lhs.source < rhs.source) || (lhs.source == rhs.source && lhs.target < rhs.target); @@ -175,11 +155,11 @@ unsigned readHSGRFromStream(const boost::filesystem::path &hsgr_file, { if (!boost::filesystem::exists(hsgr_file)) { - throw osrm::exception("hsgr file does not exist"); + throw exception("hsgr file does not exist"); } if (0 == boost::filesystem::file_size(hsgr_file)) { - throw osrm::exception("hsgr file is empty"); + throw exception("hsgr file is empty"); } boost::filesystem::ifstream hsgr_input_stream(hsgr_file, std::ios::binary); @@ -214,9 +194,10 @@ unsigned readHSGRFromStream(const boost::filesystem::path &hsgr_file, hsgr_input_stream.read(reinterpret_cast(&edge_list[0]), number_of_edges * sizeof(EdgeT)); } - hsgr_input_stream.close(); return number_of_nodes; } +} +} #endif // GRAPH_LOADER_HPP diff --git a/util/graph_utils.hpp b/include/util/graph_utils.hpp similarity index 84% rename from util/graph_utils.hpp rename to include/util/graph_utils.hpp index 9af8dbc07..c2165c5b6 100644 --- a/util/graph_utils.hpp +++ b/include/util/graph_utils.hpp @@ -1,26 +1,31 @@ #ifndef GRAPH_UTILS_HPP #define GRAPH_UTILS_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" #include #include +namespace osrm +{ +namespace util +{ + /// This function checks if the graph (consisting of directed edges) is undirected -template -bool isUndirectedGraph(const GraphT& graph) +template bool isUndirectedGraph(const GraphT &graph) { for (auto source = 0u; source < graph.GetNumberOfNodes(); ++source) { for (auto edge = graph.BeginEdges(source); edge < graph.EndEdges(source); ++edge) { - const auto& data = graph.GetEdgeData(edge); + const auto &data = graph.GetEdgeData(edge); auto target = graph.GetTarget(edge); BOOST_ASSERT(target != SPECIAL_NODEID); bool found_reverse = false; - for (auto rev_edge = graph.BeginEdges(target); rev_edge < graph.EndEdges(target); ++rev_edge) + for (auto rev_edge = graph.BeginEdges(target); rev_edge < graph.EndEdges(target); + ++rev_edge) { auto rev_target = graph.GetTarget(rev_edge); BOOST_ASSERT(rev_target != SPECIAL_NODEID); @@ -44,7 +49,6 @@ bool isUndirectedGraph(const GraphT& graph) return true; } - /// Since DynamicGraph assumes directed edges we have to make sure we transformed /// the compressed edge format into single directed edges. We do this to make sure /// every node also knows its incoming edges, not only its outgoing edges and use the reversed=true @@ -60,13 +64,14 @@ bool isUndirectedGraph(const GraphT& graph) /// (a <-- b gets reducted to b --> a) /// 2. a --> b will be transformed to a --> b and b <-- a /// 3. a <-> b will be transformed to a --> b and b --> a -template -std::vector directedEdgesFromCompressed(const std::vector& input_edge_list, FunctorT copy_data) +template +std::vector directedEdgesFromCompressed(const std::vector &input_edge_list, + FunctorT copy_data) { std::vector output_edge_list; OutputEdgeT edge; - for (const auto& input_edge : input_edge_list) + for (const auto &input_edge : input_edge_list) { // edges that are not forward get converted by flipping the end points BOOST_ASSERT(input_edge.forward); @@ -91,4 +96,7 @@ std::vector directedEdgesFromCompressed(const std::vector + +namespace osrm +{ +namespace util +{ + +// Computes a 64 bit value that corresponds to the hilbert space filling curve +std::uint64_t hilbertCode(const Coordinate coordinate); +} +} + +#endif /* HILBERT_VALUE_HPP */ diff --git a/include/util/integer_range.hpp b/include/util/integer_range.hpp new file mode 100644 index 000000000..29d428f80 --- /dev/null +++ b/include/util/integer_range.hpp @@ -0,0 +1,58 @@ +#ifndef INTEGER_RANGE_HPP +#define INTEGER_RANGE_HPP + +#include + +#include + +#include + +namespace osrm +{ +namespace util +{ + +// Warning: do not try to replace this with Boost's irange, as it is broken on Boost 1.55: +// auto r = boost::irange(0, 15); +// std::cout << r.size() << std::endl; +// results in -4294967281. Latest Boost versions fix this, but we still support older ones. + +template class range +{ + private: + const Integer last; + Integer iter; + + public: + range(Integer start, Integer end) noexcept : last(end), iter(start) + { + BOOST_ASSERT_MSG(start <= end, "backwards counting ranges not suppoted"); + static_assert(std::is_integral::value, "range type must be integral"); + } + + // Iterable functions + const range &begin() const noexcept { return *this; } + const range &end() const noexcept { return *this; } + Integer front() const noexcept { return iter; } + Integer back() const noexcept { return last - 1; } + std::size_t size() const noexcept { return static_cast(last - iter); } + + // Iterator functions + bool operator!=(const range &) const noexcept { return iter < last; } + void operator++() noexcept { ++iter; } + Integer operator*() const noexcept { return iter; } +}; + +// convenience function to construct an integer range with type deduction +template +range +irange(const Integer first, + const Integer last, + typename std::enable_if::value>::type * = 0) noexcept +{ + return range(first, last); +} +} +} + +#endif // INTEGER_RANGE_HPP diff --git a/include/util/io.hpp b/include/util/io.hpp new file mode 100644 index 000000000..70ab64a1b --- /dev/null +++ b/include/util/io.hpp @@ -0,0 +1,127 @@ +#ifndef OSRM_INCLUDE_UTIL_IO_HPP_ +#define OSRM_INCLUDE_UTIL_IO_HPP_ + +#include "util/simple_logger.hpp" + +#include + +#include +#include + +#include +#include +#include + +#include "util/fingerprint.hpp" + +namespace osrm +{ +namespace util +{ + +inline bool writeFingerprint(std::ostream &stream) +{ + const auto fingerprint = FingerPrint::GetValid(); + stream.write(reinterpret_cast(&fingerprint), sizeof(fingerprint)); + return static_cast(stream); +} + +inline bool readAndCheckFingerprint(std::istream &stream) +{ + FingerPrint fingerprint; + const auto valid = FingerPrint::GetValid(); + stream.read(reinterpret_cast(&fingerprint), sizeof(fingerprint)); + // compare the compilation state stored in the fingerprint + return static_cast(stream) && valid.IsMagicNumberOK(fingerprint) && + valid.TestContractor(fingerprint) && valid.TestGraphUtil(fingerprint) && + valid.TestRTree(fingerprint) && valid.TestQueryObjects(fingerprint); +} + +template +bool serializeVector(const std::string &filename, const std::vector &data) +{ + std::ofstream stream(filename, std::ios::binary); + + writeFingerprint(stream); + + std::uint64_t count = data.size(); + stream.write(reinterpret_cast(&count), sizeof(count)); + if (!data.empty()) + stream.write(reinterpret_cast(&data[0]), sizeof(simple_type) * count); + return static_cast(stream); +} + +template +bool deserializeVector(const std::string &filename, std::vector &data) +{ + std::ifstream stream(filename, std::ios::binary); + + if (!readAndCheckFingerprint(stream)) + return false; + + std::uint64_t count = 0; + stream.read(reinterpret_cast(&count), sizeof(count)); + data.resize(count); + if (count) + stream.read(reinterpret_cast(&data[0]), sizeof(simple_type) * count); + return static_cast(stream); +} + +inline bool serializeFlags(const boost::filesystem::path &path, const std::vector &flags) +{ + // TODO this should be replaced with a FILE-based write using error checking + std::ofstream flag_stream(path.string(), std::ios::binary); + + writeFingerprint(flag_stream); + + std::uint32_t number_of_bits = flags.size(); + flag_stream.write(reinterpret_cast(&number_of_bits), sizeof(number_of_bits)); + // putting bits in ints + std::uint32_t chunk = 0; + std::size_t chunk_count = 0; + for (std::size_t bit_nr = 0; bit_nr < number_of_bits;) + { + std::bitset<32> chunk_bitset; + for (std::size_t chunk_bit = 0; chunk_bit < 32 && bit_nr < number_of_bits; + ++chunk_bit, ++bit_nr) + chunk_bitset[chunk_bit] = flags[bit_nr]; + + chunk = chunk_bitset.to_ulong(); + ++chunk_count; + flag_stream.write(reinterpret_cast(&chunk), sizeof(chunk)); + } + SimpleLogger().Write() << "Wrote " << number_of_bits << " bits in " << chunk_count + << " chunks (Flags)."; + return static_cast(flag_stream); +} + +inline bool deserializeFlags(const boost::filesystem::path &path, std::vector &flags) +{ + SimpleLogger().Write() << "Reading flags from " << path; + std::ifstream flag_stream(path.string(), std::ios::binary); + + if (!readAndCheckFingerprint(flag_stream)) + return false; + + std::uint32_t number_of_bits; + flag_stream.read(reinterpret_cast(&number_of_bits), sizeof(number_of_bits)); + flags.resize(number_of_bits); + // putting bits in ints + std::uint32_t chunks = (number_of_bits + 31) / 32; + std::size_t bit_position = 0; + std::uint32_t chunk; + for (std::size_t chunk_id = 0; chunk_id < chunks; ++chunk_id) + { + flag_stream.read(reinterpret_cast(&chunk), sizeof(chunk)); + std::bitset<32> chunk_bits(chunk); + for (std::size_t bit = 0; bit < 32 && bit_position < number_of_bits; ++bit, ++bit_position) + flags[bit_position] = chunk_bits[bit]; + } + SimpleLogger().Write() << "Read " << number_of_bits << " bits in " << chunks + << " Chunks from disk."; + return static_cast(flag_stream); +} +} // namespace util +} // namespace osrm + +#endif // OSRM_INCLUDE_UTIL_IO_HPP_ diff --git a/util/iso_8601_duration_parser.hpp b/include/util/iso_8601_duration_parser.hpp similarity index 61% rename from util/iso_8601_duration_parser.hpp rename to include/util/iso_8601_duration_parser.hpp index 3e558cee2..91f9d2dc4 100644 --- a/util/iso_8601_duration_parser.hpp +++ b/include/util/iso_8601_duration_parser.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef ISO_8601_DURATION_PARSER_HPP #define ISO_8601_DURATION_PARSER_HPP @@ -32,6 +5,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + namespace qi = boost::spirit::qi; template struct iso_8601_grammar : qi::grammar @@ -98,5 +76,7 @@ template struct iso_8601_grammar : qi::grammar return temp; } }; +} +} #endif // ISO_8601_DURATION_PARSER_HPP diff --git a/include/util/json_container.hpp b/include/util/json_container.hpp new file mode 100644 index 000000000..9a383bb86 --- /dev/null +++ b/include/util/json_container.hpp @@ -0,0 +1,154 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// based on +// https://svn.apache.org/repos/asf/mesos/tags/release-0.9.0-incubating-RC0/src/common/json.hpp + +#ifndef JSON_CONTAINER_HPP +#define JSON_CONTAINER_HPP + +#include + +#include +#include +#include +#include + +namespace osrm +{ + +namespace util +{ + +/** + * JSON types representing OSRM responses. + * + * The json::Value type represents the basic sum-type, implemented as a variant. + * + * There are two ways for destructuring such types: + * - Either provide a visitor and use the apply_visitor function or + * - use the get function and explicitely specify the type + * + * See the following documentations on variants: + * - https://github.com/mapbox/variant + * - http://www.boost.org/doc/libs/1_55_0/doc/html/variant.html + * + * And take a look at the example we provide. + * + * \see OSRM + */ +namespace json +{ + +// fwd. decls. +struct Object; +struct Array; + +/** + * Typed string wrapper. + * + * Unwrap the type via its value member attribute. + */ +struct String +{ + String() = default; + String(const char *value_) : value{value_} {} + String(std::string value_) : value{std::move(value_)} {} + std::string value; +}; + +/** + * Typed floating point number. + * + * Unwrap the type via its value member attribute. + */ +struct Number +{ + Number() = default; + Number(double value_) : value{value_} {} + double value; +}; + +/** + * Typed True. + */ +struct True +{ +}; + +/** + * Typed False. + */ +struct False +{ +}; + +/** + * Typed Null. + */ +struct Null +{ +}; + +/** + * Typed Value sum-type implemented as a variant able to represent tree-like JSON structures. + * + * Dispatch on its type by either by using apply_visitor or its get function. + */ +using Value = mapbox::util::variant, + mapbox::util::recursive_wrapper, + True, + False, + Null>; + +/** + * Typed Object. + * + * Unwrap the key-value pairs holding type via its values member attribute. + */ +struct Object +{ + std::unordered_map values; +}; + +/** + * Typed Array. + * + * Unwrap the Value holding type via its values member attribute. + */ +struct Array +{ + std::vector values; +}; + +} // namespace json +} // namespace util +} // namespace osrm + +#endif // JSON_CONTAINER_HPP diff --git a/include/util/json_deep_compare.hpp b/include/util/json_deep_compare.hpp new file mode 100644 index 000000000..32b0bde4b --- /dev/null +++ b/include/util/json_deep_compare.hpp @@ -0,0 +1,158 @@ +#ifndef UTIL_JSON_DEEP_COMPARE_HPP +#define UTIL_JSON_DEEP_COMPARE_HPP + +#include "util/json_container.hpp" +#include "util/integer_range.hpp" + +#include + +#include +#include +#include + +namespace osrm +{ +namespace util +{ +namespace json +{ + +struct Comparator : mapbox::util::static_visitor +{ + Comparator(std::string &reason_, const std::string &lhs_path_, const std::string &rhs_path_) + : reason(reason_), lhs_path(lhs_path_), rhs_path(rhs_path_) + { + } + + bool operator()(const String &lhs, const String &rhs) const { + bool is_same = lhs.value == rhs.value; + if (!is_same) + { + reason = lhs_path + " (= \"" + lhs.value + "\") != " + rhs_path + " (= \"" + rhs.value + "\")"; + } + return is_same; + } + + bool operator()(const Number &lhs, const Number &rhs) const { + bool is_same = lhs.value == rhs.value; + if (!is_same) + { + reason = lhs_path + " (= " + std::to_string(lhs.value) + ") != " + rhs_path + " (= " + std::to_string(rhs.value) + ")"; + } + return is_same; + } + + bool operator()(const Object &lhs, const Object &rhs) const + { + std::set lhs_keys; + for (const auto &key_value : lhs.values) + { + lhs_keys.insert(key_value.first); + } + + std::set rhs_keys; + for (const auto &key_value : rhs.values) + { + rhs_keys.insert(key_value.first); + } + + for (const auto &key : lhs_keys) + { + if (rhs_keys.find(key) == rhs_keys.end()) + { + reason = rhs_path + " doesn't have key \"" + key + "\""; + return false; + } + } + + for (const auto &key : rhs_keys) + { + if (lhs_keys.find(key) == lhs_keys.end()) + { + reason = lhs_path + " doesn't have key \"" + key + "\""; + return false; + } + } + + for (const auto &key : lhs_keys) + { + BOOST_ASSERT(rhs.values.find(key) != rhs.values.end()); + BOOST_ASSERT(lhs.values.find(key) != lhs.values.end()); + + const auto &rhs_child = rhs.values.find(key)->second; + const auto &lhs_child = lhs.values.find(key)->second; + auto is_same = mapbox::util::apply_visitor( + Comparator(reason, lhs_path + "." + key, rhs_path + "." + key), lhs_child, + rhs_child); + if (!is_same) + { + return false; + } + } + return true; + } + + bool operator()(const Array &lhs, const Array &rhs) const + { + if (lhs.values.size() != rhs.values.size()) + { + reason = lhs_path + ".length " + std::to_string(lhs.values.size()) + " != " + rhs_path + + ".length " + std::to_string(rhs.values.size()); + return false; + } + + for (auto i = 0UL; i < lhs.values.size(); ++i) + { + auto is_same = mapbox::util::apply_visitor( + Comparator(reason, lhs_path + "[" + std::to_string(i) + "]", + rhs_path + "[" + std::to_string(i) + "]"), + lhs.values[i], rhs.values[i]); + if (!is_same) + { + return false; + } + } + + return true; + } + + bool operator()(const True &, const True &) const { return true; } + bool operator()(const False &, const False &) const { return true; } + bool operator()(const Null &, const Null &) const { return true; } + + bool operator()(const False &, const True &) const + { + reason = lhs_path + " is false but " + rhs_path + " is true"; + return false; + } + bool operator()(const True &, const False &) const + { + reason = lhs_path + " is true but " + rhs_path + " is false"; + return false; + } + + template ::value>::type> + bool operator()(const T1 &, const T2 &) + { + reason = lhs_path + " and " + rhs_path + " have different types"; + return false; + } + + private: + std::string &reason; + const std::string &lhs_path; + const std::string &rhs_path; +}; + +inline bool compare(const Value &reference, const Value &result, std::string &reason) +{ + return mapbox::util::apply_visitor(Comparator(reason, "reference", "result"), reference, + result); +} +} +} +} + +#endif diff --git a/include/util/json_logger.hpp b/include/util/json_logger.hpp new file mode 100644 index 000000000..aa72b54cf --- /dev/null +++ b/include/util/json_logger.hpp @@ -0,0 +1,62 @@ +#ifndef JSON_LOGGER_HPP +#define JSON_LOGGER_HPP + +#include "osrm/json_container.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace util +{ +namespace json +{ + +// Used to append additional debugging information to the JSON response in a +// thread safe manner. +class Logger +{ + using MapT = std::unordered_map; + + public: + static Logger *get() + { + static Logger logger; + + bool return_logger = true; +#ifdef NDEBUG + return_logger = false; +#endif +#ifdef ENABLE_JSON_LOGGING + return_logger = true; +#endif + + if (return_logger) + { + return &logger; + } + + return nullptr; + } + + void initialize(const std::string &name) + { + if (!map.get()) + { + map.reset(new MapT()); + } + (*map)[name] = Object(); + } + + void render(const std::string &name, Object &obj) const { obj.values["debug"] = map->at(name); } + + boost::thread_specific_ptr map; +}; +} +} +} + +#endif /* JSON_LOGGER_HPP */ diff --git a/util/json_renderer.hpp b/include/util/json_renderer.hpp similarity index 72% rename from util/json_renderer.hpp rename to include/util/json_renderer.hpp index 09d7bb251..a1ac7a04a 100644 --- a/util/json_renderer.hpp +++ b/include/util/json_renderer.hpp @@ -1,47 +1,27 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - // based on // https://svn.apache.org/repos/asf/mesos/tags/release-0.9.0-incubating-RC0/src/common/json.hpp #ifndef JSON_RENDERER_HPP #define JSON_RENDERER_HPP -#include "cast.hpp" -#include "string_util.hpp" +#include "util/cast.hpp" +#include "util/string_util.hpp" -#include +#include "osrm/json_container.hpp" + +#include +#include +#include +#include namespace osrm { +namespace util +{ namespace json { -struct Renderer : mapbox::util::static_visitor<> +struct Renderer { explicit Renderer(std::ostream &_out) : out(_out) {} @@ -97,7 +77,7 @@ struct Renderer : mapbox::util::static_visitor<> std::ostream &out; }; -struct ArrayRenderer : mapbox::util::static_visitor<> +struct ArrayRenderer { explicit ArrayRenderer(std::vector &_out) : out(_out) {} @@ -183,5 +163,7 @@ inline void render(std::vector &out, const Object &object) } } // namespace json +} // namespace util } // namespace osrm + #endif // JSON_RENDERER_HPP diff --git a/include/util/json_util.hpp b/include/util/json_util.hpp new file mode 100644 index 000000000..32a1bd7e4 --- /dev/null +++ b/include/util/json_util.hpp @@ -0,0 +1,58 @@ +#ifndef JSON_UTIL_HPP +#define JSON_UTIL_HPP + +#include "osrm/json_container.hpp" +#include "util/container.hpp" + +#include +#include + +namespace osrm +{ +namespace util +{ +namespace json +{ +// Make sure we don't have inf and NaN values +template T clamp_float(T d) +{ + if (std::isnan(d) || std::numeric_limits::infinity() == d) + { + return std::numeric_limits::max(); + } + if (-std::numeric_limits::infinity() == d) + { + return std::numeric_limits::lowest(); + } + + return d; +} + +template Array make_array(Args... args) +{ + Array a; + // TODO: check why a.values.emplace_back(args...); is not an option here + append_to_container(a.values, args...); + return a; +} + +// Easy acces to object hierachies +inline Value &get(Value &value) { return value; } + +template Value &get(Value &value, const char *key, Keys... keys) +{ + using recursive_object_t = mapbox::util::recursive_wrapper; + return get(value.get().get().values[key], keys...); +} + +template Value &get(Value &value, unsigned key, Keys... keys) +{ + using recursive_array_t = mapbox::util::recursive_wrapper; + return get(value.get().get().values[key], keys...); +} + +} // namespace json +} // namespace util +} // namespace osrm + +#endif // JSON_UTIL_HPP diff --git a/include/util/lua_util.hpp b/include/util/lua_util.hpp new file mode 100644 index 000000000..4d4fe17be --- /dev/null +++ b/include/util/lua_util.hpp @@ -0,0 +1,54 @@ +#ifndef LUA_UTIL_HPP +#define LUA_UTIL_HPP + +extern "C" { +#include +#include +#include +} + +#include +#include + +#include +#include + +namespace osrm +{ +namespace util +{ + +struct LuaState +{ + LuaState() : handle{::luaL_newstate(), &::lua_close} { luaL_openlibs(*this); } + + operator lua_State *() { return handle.get(); } + operator lua_State const *() const { return handle.get(); } + + using handle_type = std::unique_ptr; + handle_type handle; +}; + +// Check if the lua function is defined +inline bool luaFunctionExists(lua_State *lua_state, const char *name) +{ + luabind::object globals_table = luabind::globals(lua_state); + luabind::object lua_function = globals_table[name]; + return lua_function && (luabind::type(lua_function) == LUA_TFUNCTION); +} + +// Add the folder contain the script to the lua load path, so script can easily require() other lua +// scripts inside that folder, or subfolders. +// See http://lua-users.org/wiki/PackagePath for details on the package.path syntax. +inline void luaAddScriptFolderToLoadPath(lua_State *lua_state, const char *file_name) +{ + boost::filesystem::path profile_path = boost::filesystem::canonical(file_name); + std::string folder = profile_path.parent_path().string(); + // TODO: This code is most probably not Windows safe since it uses UNIX'ish path delimiters + const std::string lua_code = "package.path = \"" + folder + "/?.lua;\" .. package.path"; + luaL_dostring(lua_state, lua_code.c_str()); +} +} +} + +#endif // LUA_UTIL_HPP diff --git a/util/make_unique.hpp b/include/util/make_unique.hpp similarity index 51% rename from util/make_unique.hpp rename to include/util/make_unique.hpp index 83e230194..74d4e3d0e 100644 --- a/util/make_unique.hpp +++ b/include/util/make_unique.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef MAKE_UNIQUE_H_ #define MAKE_UNIQUE_H_ @@ -34,6 +7,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { +namespace util +{ + // Implement make_unique according to N3656. Taken from libcxx's implementation /// \brief Constructs a `new T()` with the given args and returns a @@ -70,4 +46,6 @@ make_unique(size_t n) template typename std::enable_if::value != 0>::type make_unique(Args &&...) = delete; } +} + #endif // MAKE_UNIQUE_H_ diff --git a/include/util/matrix_graph_wrapper.hpp b/include/util/matrix_graph_wrapper.hpp new file mode 100644 index 000000000..3b4ee5c5d --- /dev/null +++ b/include/util/matrix_graph_wrapper.hpp @@ -0,0 +1,51 @@ +#ifndef MATRIX_GRAPH_WRAPPER_H +#define MATRIX_GRAPH_WRAPPER_H + +#include +#include +#include + +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace util +{ + +// This Wrapper provides all methods that are needed for extractor::TarjanSCC, when the graph is +// given in a +// matrix representation (e.g. as output from a distance table call) + +template class MatrixGraphWrapper +{ + public: + MatrixGraphWrapper(std::vector table, const std::size_t number_of_nodes) + : table_(std::move(table)), number_of_nodes_(number_of_nodes){} + + std::size_t GetNumberOfNodes() const { return number_of_nodes_; } + + std::vector GetAdjacentEdgeRange(const NodeID node) const + { + + std::vector edges; + // find all valid adjacent edges and move to vector `edges` + for (std::size_t i = 0; i < number_of_nodes_; ++i) + { + if (*(std::begin(table_) + node * number_of_nodes_ + i) != INVALID_EDGE_WEIGHT) + { + edges.push_back(i); + } + } + return edges; + } + + EdgeWeight GetTarget(const EdgeWeight edge) const { return edge; } + + private: + const std::vector table_; + const std::size_t number_of_nodes_; +}; +} +} + +#endif // MATRIX_GRAPH_WRAPPER_H diff --git a/include/util/name_table.hpp b/include/util/name_table.hpp new file mode 100644 index 000000000..318cffca1 --- /dev/null +++ b/include/util/name_table.hpp @@ -0,0 +1,31 @@ +#ifndef OSRM_UTIL_NAME_TABLE_HPP +#define OSRM_UTIL_NAME_TABLE_HPP + +#include "util/range_table.hpp" +#include "util/shared_memory_vector_wrapper.hpp" + +#include + +namespace osrm +{ +namespace util +{ + +// While this could, theoretically, hold any names in the fitting format, +// the NameTable allows access to a part of the Datafacade to allow +// processing based on name indices. +class NameTable +{ + private: + // FIXME should this use shared memory + RangeTable<16, false> m_name_table; + ShM::vector m_names_char_list; + + public: + NameTable(const std::string &filename); + std::string GetNameForID(const unsigned name_id) const; +}; +} // namespace util +} // namespace osrm + +#endif // OSRM_UTIL_NAME_TABLE_HPP diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp new file mode 100644 index 000000000..463fbb1b3 --- /dev/null +++ b/include/util/node_based_graph.hpp @@ -0,0 +1,95 @@ +#ifndef NODE_BASED_GRAPH_HPP +#define NODE_BASED_GRAPH_HPP + +#include "util/dynamic_graph.hpp" +#include "extractor/node_based_edge.hpp" +#include "util/graph_utils.hpp" +#include "extractor/guidance/classification_data.hpp" + +#include + +#include + +namespace osrm +{ +namespace util +{ + +struct NodeBasedEdgeData +{ + NodeBasedEdgeData() + : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), + name_id(std::numeric_limits::max()), access_restricted(false), reversed(false), + roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE) + { + } + + NodeBasedEdgeData(int distance, + unsigned edge_id, + unsigned name_id, + bool access_restricted, + bool reversed, + bool roundabout, + bool startpoint, + extractor::TravelMode travel_mode) + : distance(distance), edge_id(edge_id), name_id(name_id), + access_restricted(access_restricted), reversed(reversed), roundabout(roundabout), + startpoint(startpoint), travel_mode(travel_mode) + { + } + + int distance; + unsigned edge_id; + unsigned name_id; + bool access_restricted : 1; + bool reversed : 1; + bool roundabout : 1; + bool startpoint : 1; + extractor::TravelMode travel_mode : 4; + extractor::guidance::RoadClassificationData road_classification; + + bool IsCompatibleTo(const NodeBasedEdgeData &other) const + { + return (name_id == other.name_id) && (reversed == other.reversed) && + (roundabout == other.roundabout) && (startpoint == other.startpoint) && + (access_restricted == other.access_restricted) && + (travel_mode == other.travel_mode) && + (road_classification == other.road_classification); + } +}; + +using NodeBasedDynamicGraph = DynamicGraph; + +/// Factory method to create NodeBasedDynamicGraph from NodeBasedEdges +/// Since DynamicGraph expects directed edges, we need to insert +/// two edges for undirected edges. +inline std::shared_ptr +NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes, + const std::vector &input_edge_list) +{ + auto edges_list = directedEdgesFromCompressed( + input_edge_list, [](NodeBasedDynamicGraph::InputEdge &output_edge, + const extractor::NodeBasedEdge &input_edge) + { + output_edge.data.distance = static_cast(input_edge.weight); + BOOST_ASSERT(output_edge.data.distance > 0); + + output_edge.data.roundabout = input_edge.roundabout; + output_edge.data.name_id = input_edge.name_id; + output_edge.data.access_restricted = input_edge.access_restricted; + output_edge.data.travel_mode = input_edge.travel_mode; + output_edge.data.startpoint = input_edge.startpoint; + output_edge.data.road_classification = input_edge.road_classification; + }); + + tbb::parallel_sort(edges_list.begin(), edges_list.end()); + + auto graph = std::make_shared( + static_cast(number_of_nodes), edges_list); + + return graph; +} +} +} + +#endif // NODE_BASED_GRAPH_HPP diff --git a/data_structures/percent.hpp b/include/util/percent.hpp similarity index 57% rename from data_structures/percent.hpp rename to include/util/percent.hpp index 392417e76..d34a77d07 100644 --- a/data_structures/percent.hpp +++ b/include/util/percent.hpp @@ -1,36 +1,14 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef PERCENT_HPP #define PERCENT_HPP #include #include +namespace osrm +{ +namespace util +{ + class Percent { public: @@ -97,5 +75,7 @@ class Percent } } }; +} +} #endif // PERCENT_HPP diff --git a/data_structures/range_table.hpp b/include/util/range_table.hpp similarity index 80% rename from data_structures/range_table.hpp rename to include/util/range_table.hpp index e750c90a7..c1a12c2ab 100644 --- a/data_structures/range_table.hpp +++ b/include/util/range_table.hpp @@ -1,39 +1,17 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef RANGE_TABLE_HPP #define RANGE_TABLE_HPP -#include "../util/integer_range.hpp" -#include "shared_memory_factory.hpp" -#include "shared_memory_vector_wrapper.hpp" +#include "util/integer_range.hpp" +#include "util/shared_memory_vector_wrapper.hpp" -#include #include +#include +#include + +namespace osrm +{ +namespace util +{ /* * These pre-declarations are needed because parsing C++ is hard * and otherwise the compiler gets confused. @@ -62,7 +40,7 @@ template class RangeTable using BlockT = std::array; using BlockContainerT = typename ShM::vector; using OffsetContainerT = typename ShM::vector; - using RangeT = osrm::range; + using RangeT = range; friend std::ostream &operator<<<>(std::ostream &out, const RangeTable &table); friend std::istream &operator>><>(std::istream &in, RangeTable &table); @@ -75,16 +53,15 @@ template class RangeTable const unsigned sum_lengths) : sum_lengths(sum_lengths) { - block_offsets.swap(external_offsets); - diff_blocks.swap(external_blocks); + using std::swap; + swap(block_offsets, external_offsets); + swap(diff_blocks, external_blocks); } // construct table from length vector - template - explicit RangeTable(const VectorT &lengths) + template explicit RangeTable(const VectorT &lengths) { - const unsigned number_of_blocks = [&lengths]() - { + const unsigned number_of_blocks = [&lengths]() { unsigned num = (lengths.size() + 1) / (BLOCK_SIZE + 1); if ((lengths.size() + 1) % (BLOCK_SIZE + 1) != 0) { @@ -191,10 +168,10 @@ template class RangeTable end_idx = block_offsets[block_idx + 1]; } - BOOST_ASSERT(begin_idx < sum_lengths && end_idx <= sum_lengths); + BOOST_ASSERT(end_idx <= sum_lengths); BOOST_ASSERT(begin_idx <= end_idx); - return osrm::irange(begin_idx, end_idx); + return irange(begin_idx, end_idx); } private: @@ -256,5 +233,7 @@ std::istream &operator>>(std::istream &in, RangeTable + +#include "osrm/coordinate.hpp" + +#include +#include +#include +#include + +#include + +namespace osrm +{ +namespace util +{ + +// TODO: Make template type, add tests +struct RectangleInt2D +{ + RectangleInt2D() + : min_lon(std::numeric_limits::max()), + max_lon(std::numeric_limits::min()), + min_lat(std::numeric_limits::max()), + max_lat(std::numeric_limits::min()) + { + } + + RectangleInt2D(FixedLongitude min_lon_, + FixedLongitude max_lon_, + FixedLatitude min_lat_, + FixedLatitude max_lat_) + : min_lon(min_lon_), max_lon(max_lon_), min_lat(min_lat_), max_lat(max_lat_) + { + } + + RectangleInt2D(FloatLongitude min_lon_, + FloatLongitude max_lon_, + FloatLatitude min_lat_, + FloatLatitude max_lat_) + : min_lon(toFixed(min_lon_)), max_lon(toFixed(max_lon_)), min_lat(toFixed(min_lat_)), + max_lat(toFixed(max_lat_)) + { + } + + FixedLongitude min_lon, max_lon; + FixedLatitude min_lat, max_lat; + + void MergeBoundingBoxes(const RectangleInt2D &other) + { + min_lon = std::min(min_lon, other.min_lon); + max_lon = std::max(max_lon, other.max_lon); + min_lat = std::min(min_lat, other.min_lat); + max_lat = std::max(max_lat, other.max_lat); + BOOST_ASSERT(min_lon != FixedLongitude(std::numeric_limits::min())); + BOOST_ASSERT(min_lat != FixedLatitude(std::numeric_limits::min())); + BOOST_ASSERT(max_lon != FixedLongitude(std::numeric_limits::min())); + BOOST_ASSERT(max_lat != FixedLatitude(std::numeric_limits::min())); + } + + Coordinate Centroid() const + { + Coordinate centroid; + // The coordinates of the midpoints are given by: + // x = (x1 + x2) /2 and y = (y1 + y2) /2. + centroid.lon = (min_lon + max_lon) / FixedLongitude(2); + centroid.lat = (min_lat + max_lat) / FixedLatitude(2); + return centroid; + } + + bool Intersects(const RectangleInt2D &other) const + { + // Standard box intersection test - check if boxes *don't* overlap, + // and return the negative of that + return !(max_lon < other.min_lon || min_lon > other.max_lon || max_lat < other.min_lat || + min_lat > other.max_lat); + } + + // This code assumes that we are operating in euclidean space! + // That means if you just put unprojected lat/lon in here you will + // get invalid results. + std::uint64_t GetMinSquaredDist(const Coordinate location) const + { + const bool is_contained = Contains(location); + if (is_contained) + { + return 0.0f; + } + + enum Direction + { + INVALID = 0, + NORTH = 1, + SOUTH = 2, + EAST = 4, + NORTH_EAST = 5, + SOUTH_EAST = 6, + WEST = 8, + NORTH_WEST = 9, + SOUTH_WEST = 10 + }; + + Direction d = INVALID; + if (location.lat > max_lat) + d = (Direction)(d | NORTH); + else if (location.lat < min_lat) + d = (Direction)(d | SOUTH); + if (location.lon > max_lon) + d = (Direction)(d | EAST); + else if (location.lon < min_lon) + d = (Direction)(d | WEST); + + BOOST_ASSERT(d != INVALID); + + std::uint64_t min_dist = std::numeric_limits::max(); + switch (d) + { + case NORTH: + min_dist = coordinate_calculation::squaredEuclideanDistance( + location, Coordinate(location.lon, max_lat)); + break; + case SOUTH: + min_dist = coordinate_calculation::squaredEuclideanDistance( + location, Coordinate(location.lon, min_lat)); + break; + case WEST: + min_dist = coordinate_calculation::squaredEuclideanDistance( + location, Coordinate(min_lon, location.lat)); + break; + case EAST: + min_dist = coordinate_calculation::squaredEuclideanDistance( + location, Coordinate(max_lon, location.lat)); + break; + case NORTH_EAST: + min_dist = + coordinate_calculation::squaredEuclideanDistance(location, Coordinate(max_lon, max_lat)); + break; + case NORTH_WEST: + min_dist = + coordinate_calculation::squaredEuclideanDistance(location, Coordinate(min_lon, max_lat)); + break; + case SOUTH_EAST: + min_dist = + coordinate_calculation::squaredEuclideanDistance(location, Coordinate(max_lon, min_lat)); + break; + case SOUTH_WEST: + min_dist = + coordinate_calculation::squaredEuclideanDistance(location, Coordinate(min_lon, min_lat)); + break; + default: + break; + } + + BOOST_ASSERT(min_dist < std::numeric_limits::max()); + + return min_dist; + } + + bool Contains(const Coordinate location) const + { + const bool lons_contained = (location.lon >= min_lon) && (location.lon <= max_lon); + const bool lats_contained = (location.lat >= min_lat) && (location.lat <= max_lat); + return lons_contained && lats_contained; + } + + friend std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect); +}; +inline std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect) +{ + out << std::setprecision(12) << "(" << toFloating(rect.min_lon) << "," + << toFloating(rect.max_lon) << "," << toFloating(rect.min_lat) << "," + << toFloating(rect.max_lat) << ")"; + return out; +} +} +} + +#endif diff --git a/data_structures/shared_memory_vector_wrapper.hpp b/include/util/shared_memory_vector_wrapper.hpp similarity index 61% rename from data_structures/shared_memory_vector_wrapper.hpp rename to include/util/shared_memory_vector_wrapper.hpp index dc51cff0d..74f4a8e81 100644 --- a/data_structures/shared_memory_vector_wrapper.hpp +++ b/include/util/shared_memory_vector_wrapper.hpp @@ -1,39 +1,20 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef SHARED_MEMORY_VECTOR_WRAPPER_HPP #define SHARED_MEMORY_VECTOR_WRAPPER_HPP #include +#include + #include #include #include #include +#include + +namespace osrm +{ +namespace util +{ template class ShMemIterator : public std::iterator { @@ -74,13 +55,6 @@ template class SharedMemoryWrapper SharedMemoryWrapper(DataT *ptr, std::size_t size) : m_ptr(ptr), m_size(size) {} - void swap(SharedMemoryWrapper &other) - { - // BOOST_ASSERT_MSG(m_size != 0 || other.size() != 0, "size invalid"); - std::swap(m_size, other.m_size); - std::swap(m_ptr, other.m_ptr); - } - DataT &at(const std::size_t index) { return m_ptr[index]; } const DataT &at(const std::size_t index) const { return m_ptr[index]; } @@ -104,6 +78,9 @@ template class SharedMemoryWrapper BOOST_ASSERT_MSG(index < m_size, "invalid size"); return m_ptr[index]; } + + template + friend void swap(SharedMemoryWrapper &, SharedMemoryWrapper &) noexcept; }; template <> class SharedMemoryWrapper @@ -117,18 +94,11 @@ template <> class SharedMemoryWrapper SharedMemoryWrapper(unsigned *ptr, std::size_t size) : m_ptr(ptr), m_size(size) {} - void swap(SharedMemoryWrapper &other) - { - // BOOST_ASSERT_MSG(m_size != 0 || other.size() != 0, "size invalid"); - std::swap(m_size, other.m_size); - std::swap(m_ptr, other.m_ptr); - } - bool at(const std::size_t index) const { const std::size_t bucket = index / 32; const unsigned offset = static_cast(index % 32); - return m_ptr[bucket] & (1 << offset); + return m_ptr[bucket] & (1u << offset); } std::size_t size() const { return m_size; } @@ -140,15 +110,28 @@ template <> class SharedMemoryWrapper BOOST_ASSERT_MSG(index < m_size, "invalid size"); const unsigned bucket = index / 32; const unsigned offset = index % 32; - return m_ptr[bucket] & (1 << offset); + return m_ptr[bucket] & (1u << offset); } + + template + friend void swap(SharedMemoryWrapper &, SharedMemoryWrapper &) noexcept; }; +// Both SharedMemoryWrapper and the SharedMemoryWrapper specializations share this impl. +template +void swap(SharedMemoryWrapper &lhs, SharedMemoryWrapper &rhs) noexcept +{ + std::swap(lhs.m_ptr, rhs.m_ptr); + std::swap(lhs.m_size, rhs.m_size); +} + template struct ShM { using vector = typename std::conditional, std::vector>::type; }; +} +} #endif // SHARED_MEMORY_VECTOR_WRAPPER_HPP diff --git a/include/util/simple_logger.hpp b/include/util/simple_logger.hpp new file mode 100644 index 000000000..f71f0ff7d --- /dev/null +++ b/include/util/simple_logger.hpp @@ -0,0 +1,55 @@ +#ifndef SIMPLE_LOGGER_HPP +#define SIMPLE_LOGGER_HPP + +#include +#include +#include + +enum LogLevel +{ + logINFO, + logWARNING, + logDEBUG +}; + +namespace osrm +{ +namespace util +{ + +class LogPolicy +{ + public: + void Unmute(); + + void Mute(); + + bool IsMute() const; + + static LogPolicy &GetInstance(); + + LogPolicy(const LogPolicy &) = delete; + LogPolicy &operator=(const LogPolicy &) = delete; + + private: + LogPolicy() : m_is_mute(true) {} + std::atomic m_is_mute; +}; + +class SimpleLogger +{ + public: + SimpleLogger(); + + virtual ~SimpleLogger(); + std::mutex &get_mutex(); + std::ostringstream &Write(LogLevel l = logINFO) noexcept; + + private: + std::ostringstream os; + LogLevel level; +}; +} +} + +#endif /* SIMPLE_LOGGER_HPP */ diff --git a/data_structures/static_graph.hpp b/include/util/static_graph.hpp similarity index 70% rename from data_structures/static_graph.hpp rename to include/util/static_graph.hpp index 3b8f92738..a4fa2d771 100644 --- a/data_structures/static_graph.hpp +++ b/include/util/static_graph.hpp @@ -1,37 +1,10 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef STATIC_GRAPH_HPP #define STATIC_GRAPH_HPP -#include "percent.hpp" -#include "shared_memory_vector_wrapper.hpp" -#include "../util/integer_range.hpp" -#include "../typedefs.h" +#include "util/percent.hpp" +#include "util/shared_memory_vector_wrapper.hpp" +#include "util/integer_range.hpp" +#include "util/typedefs.hpp" #include @@ -40,13 +13,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + template class StaticGraph { public: using NodeIterator = NodeID; using EdgeIterator = NodeID; using EdgeData = EdgeDataT; - using EdgeRange = osrm::range; + using EdgeRange = range; class InputEdge { @@ -84,20 +62,20 @@ template class StaticGraph EdgeRange GetAdjacentEdgeRange(const NodeID node) const { - return osrm::irange(BeginEdges(node), EndEdges(node)); + return irange(BeginEdges(node), EndEdges(node)); } - template - StaticGraph(const int nodes, const ContainerT &graph) + template StaticGraph(const int nodes, const ContainerT &graph) { - BOOST_ASSERT(std::is_sorted(const_cast(graph).begin(), const_cast(graph).end())); + BOOST_ASSERT(std::is_sorted(const_cast(graph).begin(), + const_cast(graph).end())); number_of_nodes = nodes; number_of_edges = static_cast(graph.size()); node_array.resize(number_of_nodes + 1); EdgeIterator edge = 0; EdgeIterator position = 0; - for (const auto node : osrm::irange(0u, number_of_nodes + 1)) + for (const auto node : irange(0u, number_of_nodes + 1)) { EdgeIterator last_edge = edge; while (edge < number_of_edges && graph[edge].source == node) @@ -109,10 +87,10 @@ template class StaticGraph } edge_array.resize(position); //(edge) edge = 0; - for (const auto node : osrm::irange(0u, number_of_nodes)) + for (const auto node : irange(0u, number_of_nodes)) { EdgeIterator e = node_array[node + 1].first_edge; - for (const auto i : osrm::irange(node_array[node].first_edge, e)) + for (const auto i : irange(node_array[node].first_edge, e)) { edge_array[i].target = graph[edge].target; edge_array[i].data = graph[edge].data; @@ -127,8 +105,9 @@ template class StaticGraph number_of_nodes = static_cast(nodes.size() - 1); number_of_edges = static_cast(edges.size()); - node_array.swap(nodes); - edge_array.swap(edges); + using std::swap; + swap(node_array, nodes); + swap(edge_array, edges); } unsigned GetNumberOfNodes() const { return number_of_nodes; } @@ -159,7 +138,7 @@ template class StaticGraph // searches for a specific edge EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const { - for (const auto i : osrm::irange(BeginEdges(from), EndEdges(from))) + for (const auto i : irange(BeginEdges(from), EndEdges(from))) { if (to == edge_array[i].target) { @@ -215,5 +194,7 @@ template class StaticGraph typename ShM::vector node_array; typename ShM::vector edge_array; }; +} +} #endif // STATIC_GRAPH_HPP diff --git a/data_structures/static_rtree.hpp b/include/util/static_rtree.hpp similarity index 53% rename from data_structures/static_rtree.hpp rename to include/util/static_rtree.hpp index 6bc367824..2e5a98857 100644 --- a/data_structures/static_rtree.hpp +++ b/include/util/static_rtree.hpp @@ -1,46 +1,18 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef STATIC_RTREE_HPP #define STATIC_RTREE_HPP -#include "deallocating_vector.hpp" -#include "hilbert_value.hpp" -#include "rectangle.hpp" -#include "shared_memory_factory.hpp" -#include "shared_memory_vector_wrapper.hpp" +#include "util/deallocating_vector.hpp" +#include "util/hilbert_value.hpp" +#include "util/rectangle.hpp" +#include "util/shared_memory_vector_wrapper.hpp" +#include "util/bearing.hpp" +#include "util/exception.hpp" +#include "util/integer_range.hpp" +#include "util/typedefs.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/web_mercator.hpp" -#include "../util/bearing.hpp" -#include "../util/integer_range.hpp" -#include "../util/mercator.hpp" -#include "../util/osrm_exception.hpp" -#include "../typedefs.h" - -#include +#include "osrm/coordinate.hpp" #include #include @@ -59,12 +31,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + // Static RTree for serving nearest neighbour queries +// All coordinates are pojected first to Web Mercator before the bounding boxes +// are computed, this means the internal distance metric doesn not represent meters! template , + class CoordinateListT = std::vector, bool UseSharedMemory = false, - uint32_t BRANCHING_FACTOR = 64, - uint32_t LEAF_NODE_SIZE = 1024> + std::uint32_t BRANCHING_FACTOR = 64, + std::uint32_t LEAF_NODE_SIZE = 256> class StaticRTree { public: @@ -72,34 +51,19 @@ class StaticRTree using EdgeData = EdgeDataT; using CoordinateList = CoordinateListT; - static constexpr std::size_t MAX_CHECKED_ELEMENTS = 4 * LEAF_NODE_SIZE; + struct CandidateSegment + { + Coordinate fixed_projected_coordinate; + EdgeDataT data; + }; struct TreeNode { TreeNode() : child_count(0), child_is_on_disk(false) {} Rectangle minimum_bounding_rectangle; - uint32_t child_count : 31; + std::uint32_t child_count : 31; bool child_is_on_disk : 1; - uint32_t children[BRANCHING_FACTOR]; - }; - - private: - struct WrappedInputElement - { - explicit WrappedInputElement(const uint64_t _hilbert_value, const uint32_t _array_index) - : m_hilbert_value(_hilbert_value), m_array_index(_array_index) - { - } - - WrappedInputElement() : m_hilbert_value(0), m_array_index(UINT_MAX) {} - - uint64_t m_hilbert_value; - uint32_t m_array_index; - - inline bool operator<(const WrappedInputElement &other) const - { - return m_hilbert_value < other.m_hilbert_value; - } + std::uint32_t children[BRANCHING_FACTOR]; }; struct LeafNode @@ -109,16 +73,36 @@ class StaticRTree std::array objects; }; - using QueryNodeType = mapbox::util::variant; + private: + struct WrappedInputElement + { + explicit WrappedInputElement(const uint64_t _hilbert_value, + const std::uint32_t _array_index) + : m_hilbert_value(_hilbert_value), m_array_index(_array_index) + { + } + + WrappedInputElement() : m_hilbert_value(0), m_array_index(UINT_MAX) {} + + uint64_t m_hilbert_value; + std::uint32_t m_array_index; + + inline bool operator<(const WrappedInputElement &other) const + { + return m_hilbert_value < other.m_hilbert_value; + } + }; + + using QueryNodeType = mapbox::util::variant; struct QueryCandidate { inline bool operator<(const QueryCandidate &other) const { // Attn: this is reversed order. std::pq is a max pq! - return other.min_dist < min_dist; + return other.squared_min_dist < squared_min_dist; } - float min_dist; + std::uint64_t squared_min_dist; QueryNodeType node; }; @@ -129,8 +113,8 @@ class StaticRTree boost::filesystem::ifstream leaves_stream; public: - StaticRTree() = delete; StaticRTree(const StaticRTree &) = delete; + StaticRTree &operator=(const StaticRTree &) = delete; template // Construct a packed Hilbert-R-Tree with Kamel-Faloutsos algorithm [1] @@ -142,12 +126,10 @@ class StaticRTree { std::vector input_wrapper_vector(m_element_count); - HilbertCode get_hilbert_number; - // generate auxiliary vector of hilbert-values tbb::parallel_for( tbb::blocked_range(0, m_element_count), - [&input_data_vector, &input_wrapper_vector, &get_hilbert_number, + [&input_data_vector, &input_wrapper_vector, &coordinate_list](const tbb::blocked_range &range) { for (uint64_t element_counter = range.begin(), end = range.end(); @@ -159,16 +141,16 @@ class StaticRTree EdgeDataT const ¤t_element = input_data_vector[element_counter]; // Get Hilbert-Value for centroid in mercartor projection - FixedPointCoordinate current_centroid = EdgeDataT::Centroid( - FixedPointCoordinate(coordinate_list.at(current_element.u).lat, - coordinate_list.at(current_element.u).lon), - FixedPointCoordinate(coordinate_list.at(current_element.v).lat, - coordinate_list.at(current_element.v).lon)); - current_centroid.lat = - COORDINATE_PRECISION * - mercator::lat2y(current_centroid.lat / COORDINATE_PRECISION); + BOOST_ASSERT(current_element.u < coordinate_list.size()); + BOOST_ASSERT(current_element.v < coordinate_list.size()); - current_wrapper.m_hilbert_value = get_hilbert_number(current_centroid); + Coordinate current_centroid = coordinate_calculation::centroid( + coordinate_list[current_element.u], coordinate_list[current_element.v]); + current_centroid.lat = + FixedLatitude(COORDINATE_PRECISION * + web_mercator::latToY(toFloating(current_centroid.lat))); + + current_wrapper.m_hilbert_value = hilbertCode(current_centroid); } }); @@ -187,12 +169,12 @@ class StaticRTree LeafNode current_leaf; TreeNode current_node; - for (uint32_t current_element_index = 0; LEAF_NODE_SIZE > current_element_index; + for (std::uint32_t current_element_index = 0; LEAF_NODE_SIZE > current_element_index; ++current_element_index) { if (m_element_count > (processed_objects_count + current_element_index)) { - uint32_t index_of_next_object = + std::uint32_t index_of_next_object = input_wrapper_vector[processed_objects_count + current_element_index] .m_array_index; current_leaf.objects[current_element_index] = @@ -213,19 +195,16 @@ class StaticRTree processed_objects_count += current_leaf.object_count; } - // close leaf file - leaf_node_file.close(); - - uint32_t processing_level = 0; + std::uint32_t processing_level = 0; while (1 < tree_nodes_in_level.size()) { std::vector tree_nodes_in_next_level; - uint32_t processed_tree_nodes_in_level = 0; + std::uint32_t processed_tree_nodes_in_level = 0; while (processed_tree_nodes_in_level < tree_nodes_in_level.size()) { TreeNode parent_node; // pack BRANCHING_FACTOR elements into tree_nodes each - for (uint32_t current_child_node_index = 0; + for (std::uint32_t current_child_node_index = 0; BRANCHING_FACTOR > current_child_node_index; ++current_child_node_index) { if (processed_tree_nodes_in_level < tree_nodes_in_level.size()) @@ -255,17 +234,18 @@ class StaticRTree // reverse and renumber tree to have root at index 0 std::reverse(m_search_tree.begin(), m_search_tree.end()); - uint32_t search_tree_size = m_search_tree.size(); - tbb::parallel_for(tbb::blocked_range(0, search_tree_size), - [this, &search_tree_size](const tbb::blocked_range &range) + std::uint32_t search_tree_size = m_search_tree.size(); + tbb::parallel_for(tbb::blocked_range(0, search_tree_size), + [this, &search_tree_size](const tbb::blocked_range &range) { - for (uint32_t i = range.begin(), end = range.end(); i != end; ++i) + for (std::uint32_t i = range.begin(), end = range.end(); i != end; + ++i) { TreeNode ¤t_tree_node = this->m_search_tree[i]; - for (uint32_t j = 0; j < current_tree_node.child_count; ++j) + for (std::uint32_t j = 0; j < current_tree_node.child_count; ++j) { - const uint32_t old_id = current_tree_node.children[j]; - const uint32_t new_id = search_tree_size - old_id - 1; + const std::uint32_t old_id = current_tree_node.children[j]; + const std::uint32_t new_id = search_tree_size - old_id - 1; current_tree_node.children[j] = new_id; } } @@ -274,12 +254,10 @@ class StaticRTree // open tree file boost::filesystem::ofstream tree_node_file(tree_node_filename, std::ios::binary); - uint32_t size_of_tree = m_search_tree.size(); + std::uint32_t size_of_tree = m_search_tree.size(); BOOST_ASSERT_MSG(0 < size_of_tree, "tree empty"); - tree_node_file.write((char *)&size_of_tree, sizeof(uint32_t)); + tree_node_file.write((char *)&size_of_tree, sizeof(std::uint32_t)); tree_node_file.write((char *)&m_search_tree[0], sizeof(TreeNode) * size_of_tree); - // close tree node file. - tree_node_file.close(); } explicit StaticRTree(const boost::filesystem::path &node_file, @@ -292,31 +270,31 @@ class StaticRTree if (!boost::filesystem::exists(node_file)) { - throw osrm::exception("ram index file does not exist"); + throw exception("ram index file does not exist"); } if (0 == boost::filesystem::file_size(node_file)) { - throw osrm::exception("ram index file is empty"); + throw exception("ram index file is empty"); } boost::filesystem::ifstream tree_node_file(node_file, std::ios::binary); - uint32_t tree_size = 0; - tree_node_file.read((char *)&tree_size, sizeof(uint32_t)); + std::uint32_t tree_size = 0; + tree_node_file.read((char *)&tree_size, sizeof(std::uint32_t)); m_search_tree.resize(tree_size); if (tree_size > 0) { tree_node_file.read((char *)&m_search_tree[0], sizeof(TreeNode) * tree_size); } - tree_node_file.close(); + // open leaf node file and store thread specific pointer if (!boost::filesystem::exists(leaf_file)) { - throw osrm::exception("mem index file does not exist"); + throw exception("mem index file does not exist"); } if (0 == boost::filesystem::file_size(leaf_file)) { - throw osrm::exception("mem index file is empty"); + throw exception("mem index file is empty"); } leaves_stream.open(leaf_file, std::ios::binary); @@ -333,27 +311,94 @@ class StaticRTree // open leaf node file and store thread specific pointer if (!boost::filesystem::exists(leaf_file)) { - throw osrm::exception("mem index file does not exist"); + throw exception("mem index file does not exist"); } if (0 == boost::filesystem::file_size(leaf_file)) { - throw osrm::exception("mem index file is empty"); + throw exception("mem index file is empty"); } leaves_stream.open(leaf_file, std::ios::binary); leaves_stream.read((char *)&m_element_count, sizeof(uint64_t)); } + /* Returns all features inside the bounding box. + Rectangle needs to be projected!*/ + std::vector SearchInBox(const Rectangle &search_rectangle) + { + const Rectangle projected_rectangle{ + search_rectangle.min_lon, search_rectangle.max_lon, + toFixed(FloatLatitude{ + web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.min_lat)))}), + toFixed(FloatLatitude{ + web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.max_lat)))})}; + std::vector results; + + std::queue traversal_queue; + + traversal_queue.push(m_search_tree[0]); + + while (!traversal_queue.empty()) + { + auto const current_tree_node = traversal_queue.front(); + traversal_queue.pop(); + + if (current_tree_node.child_is_on_disk) + { + LeafNode current_leaf_node; + LoadLeafFromDisk(current_tree_node.children[0], current_leaf_node); + + for (const auto i : irange(0u, current_leaf_node.object_count)) + { + const auto ¤t_edge = current_leaf_node.objects[i]; + + // we don't need to project the coordinates here, + // because we use the unprojected rectangle to test against + const Rectangle bbox{std::min((*m_coordinate_list)[current_edge.u].lon, + (*m_coordinate_list)[current_edge.v].lon), + std::max((*m_coordinate_list)[current_edge.u].lon, + (*m_coordinate_list)[current_edge.v].lon), + std::min((*m_coordinate_list)[current_edge.u].lat, + (*m_coordinate_list)[current_edge.v].lat), + std::max((*m_coordinate_list)[current_edge.u].lat, + (*m_coordinate_list)[current_edge.v].lat)}; + + // use the _unprojected_ input rectangle here + if (bbox.Intersects(search_rectangle)) + { + results.push_back(current_edge); + } + } + } + else + { + // If it's a tree node, look at all children and add them + // to the search queue if their bounding boxes intersect + for (std::uint32_t i = 0; i < current_tree_node.child_count; ++i) + { + const std::int32_t child_id = current_tree_node.children[i]; + const auto &child_tree_node = m_search_tree[child_id]; + const auto &child_rectangle = child_tree_node.minimum_bounding_rectangle; + + if (child_rectangle.Intersects(projected_rectangle)) + { + traversal_queue.push(m_search_tree[child_id]); + } + } + } + } + return results; + } + // Override filter and terminator for the desired behaviour. - std::vector Nearest(const FixedPointCoordinate &input_coordinate, - const std::size_t max_results) + std::vector Nearest(const Coordinate input_coordinate, const std::size_t max_results) { return Nearest(input_coordinate, - [](const EdgeDataT &) + [](const CandidateSegment &) { return std::make_pair(true, true); }, - [max_results](const std::size_t num_results, const float) + [max_results](const std::size_t num_results, const CandidateSegment &) { return num_results >= max_results; }); @@ -361,28 +406,20 @@ class StaticRTree // Override filter and terminator for the desired behaviour. template - std::vector Nearest(const FixedPointCoordinate &input_coordinate, - const FilterT filter, - const TerminationT terminate) + std::vector + Nearest(const Coordinate input_coordinate, const FilterT filter, const TerminationT terminate) { std::vector results; - std::pair projected_coordinate = { - mercator::lat2y(input_coordinate.lat / COORDINATE_PRECISION), - input_coordinate.lon / COORDINATE_PRECISION}; + auto projected_coordinate = web_mercator::fromWGS84(input_coordinate); + Coordinate fixed_projected_coordinate{projected_coordinate}; // initialize queue with root element std::priority_queue traversal_queue; - traversal_queue.push(QueryCandidate {0.f, m_search_tree[0]}); + traversal_queue.push(QueryCandidate{0, m_search_tree[0]}); while (!traversal_queue.empty()) { - const QueryCandidate current_query_node = traversal_queue.top(); - if (terminate(results.size(), current_query_node.min_dist)) - { - traversal_queue = std::priority_queue{}; - break; - } - + QueryCandidate current_query_node = traversal_queue.top(); traversal_queue.pop(); if (current_query_node.node.template is()) @@ -391,37 +428,37 @@ class StaticRTree current_query_node.node.template get(); if (current_tree_node.child_is_on_disk) { - ExploreLeafNode(current_tree_node.children[0], input_coordinate, + ExploreLeafNode(current_tree_node.children[0], fixed_projected_coordinate, projected_coordinate, traversal_queue); } else { - ExploreTreeNode(current_tree_node, input_coordinate, traversal_queue); + ExploreTreeNode(current_tree_node, fixed_projected_coordinate, traversal_queue); } } else { // inspecting an actual road segment - const auto ¤t_segment = current_query_node.node.template get(); + auto ¤t_candidate = current_query_node.node.template get(); + // to allow returns of no-results if too restrictive filtering, this needs to be done here + // even though performance would indicate that we want to stop after adding the first candidate + if (terminate(results.size(), current_candidate)) + { + traversal_queue = std::priority_queue{}; + break; + } - auto use_segment = filter(current_segment); + auto use_segment = filter(current_candidate); if (!use_segment.first && !use_segment.second) { continue; } + current_candidate.data.forward_segment_id.enabled &= use_segment.first; + current_candidate.data.reverse_segment_id.enabled &= use_segment.second; // store phantom node in result vector - results.push_back(std::move(current_segment)); - - if (!use_segment.first) - { - results.back().forward_edge_based_node_id = SPECIAL_NODEID; - } - else if (!use_segment.second) - { - results.back().reverse_edge_based_node_id = SPECIAL_NODEID; - } + results.push_back(std::move(current_candidate.data)); } } @@ -431,44 +468,53 @@ class StaticRTree private: template void ExploreLeafNode(const std::uint32_t leaf_id, - const FixedPointCoordinate &input_coordinate, - const std::pair &projected_coordinate, + const Coordinate projected_input_coordinate_fixed, + const FloatCoordinate &projected_input_coordinate, QueueT &traversal_queue) { LeafNode current_leaf_node; LoadLeafFromDisk(leaf_id, current_leaf_node); // current object represents a block on disk - for (const auto i : osrm::irange(0u, current_leaf_node.object_count)) + for (const auto i : irange(0u, current_leaf_node.object_count)) { - auto ¤t_edge = current_leaf_node.objects[i]; - const float current_perpendicular_distance = - coordinate_calculation::perpendicular_distance_from_projected_coordinate( - m_coordinate_list->at(current_edge.u), m_coordinate_list->at(current_edge.v), - input_coordinate, projected_coordinate); - // distance must be non-negative - BOOST_ASSERT(0.f <= current_perpendicular_distance); + const auto ¤t_edge = current_leaf_node.objects[i]; + const auto projected_u = web_mercator::fromWGS84((*m_coordinate_list)[current_edge.u]); + const auto projected_v = web_mercator::fromWGS84((*m_coordinate_list)[current_edge.v]); - traversal_queue.push(QueryCandidate {current_perpendicular_distance, std::move(current_edge)}); + FloatCoordinate projected_nearest; + std::tie(std::ignore, projected_nearest) = + coordinate_calculation::projectPointOnSegment(projected_u, projected_v, + projected_input_coordinate); + + const auto squared_distance = coordinate_calculation::squaredEuclideanDistance( + projected_input_coordinate_fixed, projected_nearest); + // distance must be non-negative + BOOST_ASSERT(0. <= squared_distance); + traversal_queue.push( + QueryCandidate{squared_distance, CandidateSegment{Coordinate{projected_nearest}, + std::move(current_edge)}}); } } template void ExploreTreeNode(const TreeNode &parent, - const FixedPointCoordinate &input_coordinate, + const Coordinate fixed_projected_input_coordinate, QueueT &traversal_queue) { - for (uint32_t i = 0; i < parent.child_count; ++i) + for (std::uint32_t i = 0; i < parent.child_count; ++i) { - const int32_t child_id = parent.children[i]; + const std::int32_t child_id = parent.children[i]; const auto &child_tree_node = m_search_tree[child_id]; const auto &child_rectangle = child_tree_node.minimum_bounding_rectangle; - const float lower_bound_to_element = child_rectangle.GetMinDist(input_coordinate); - traversal_queue.push(QueryCandidate {lower_bound_to_element, m_search_tree[child_id]}); + const auto squared_lower_bound_to_element = + child_rectangle.GetMinSquaredDist(fixed_projected_input_coordinate); + traversal_queue.push( + QueryCandidate{squared_lower_bound_to_element, m_search_tree[child_id]}); } } - inline void LoadLeafFromDisk(const uint32_t leaf_id, LeafNode &result_node) + inline void LoadLeafFromDisk(const std::uint32_t leaf_id, LeafNode &result_node) { if (!leaves_stream.is_open()) { @@ -476,7 +522,7 @@ class StaticRTree } if (!leaves_stream.good()) { - throw osrm::exception("Could not read from leaf file."); + throw exception("Could not read from leaf file."); } const uint64_t seek_pos = sizeof(uint64_t) + leaf_id * sizeof(LeafNode); leaves_stream.seekg(seek_pos); @@ -488,29 +534,42 @@ class StaticRTree template void InitializeMBRectangle(Rectangle &rectangle, const std::array &objects, - const uint32_t element_count, + const std::uint32_t element_count, const std::vector &coordinate_list) { - for (uint32_t i = 0; i < element_count; ++i) + for (std::uint32_t i = 0; i < element_count; ++i) { + BOOST_ASSERT(objects[i].u < coordinate_list.size()); + BOOST_ASSERT(objects[i].v < coordinate_list.size()); + + Coordinate projected_u{ + web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].u]})}; + Coordinate projected_v{ + web_mercator::fromWGS84(Coordinate{coordinate_list[objects[i].v]})}; + + BOOST_ASSERT(toFloating(projected_u.lon) <= FloatLongitude(180.)); + BOOST_ASSERT(toFloating(projected_u.lon) >= FloatLongitude(-180.)); + BOOST_ASSERT(toFloating(projected_u.lat) <= FloatLatitude(180.)); + BOOST_ASSERT(toFloating(projected_u.lat) >= FloatLatitude(-180.)); + BOOST_ASSERT(toFloating(projected_v.lon) <= FloatLongitude(180.)); + BOOST_ASSERT(toFloating(projected_v.lon) >= FloatLongitude(-180.)); + BOOST_ASSERT(toFloating(projected_v.lat) <= FloatLatitude(180.)); + BOOST_ASSERT(toFloating(projected_v.lat) >= FloatLatitude(-180.)); + rectangle.min_lon = - std::min(rectangle.min_lon, std::min(coordinate_list.at(objects[i].u).lon, - coordinate_list.at(objects[i].v).lon)); + std::min(rectangle.min_lon, std::min(projected_u.lon, projected_v.lon)); rectangle.max_lon = - std::max(rectangle.max_lon, std::max(coordinate_list.at(objects[i].u).lon, - coordinate_list.at(objects[i].v).lon)); + std::max(rectangle.max_lon, std::max(projected_u.lon, projected_v.lon)); rectangle.min_lat = - std::min(rectangle.min_lat, std::min(coordinate_list.at(objects[i].u).lat, - coordinate_list.at(objects[i].v).lat)); + std::min(rectangle.min_lat, std::min(projected_u.lat, projected_v.lat)); rectangle.max_lat = - std::max(rectangle.max_lat, std::max(coordinate_list.at(objects[i].u).lat, - coordinate_list.at(objects[i].v).lat)); + std::max(rectangle.max_lat, std::max(projected_u.lat, projected_v.lat)); } - BOOST_ASSERT(rectangle.min_lat != std::numeric_limits::min()); - BOOST_ASSERT(rectangle.min_lon != std::numeric_limits::min()); - BOOST_ASSERT(rectangle.max_lat != std::numeric_limits::min()); - BOOST_ASSERT(rectangle.max_lon != std::numeric_limits::min()); + BOOST_ASSERT(rectangle.min_lon != FixedLongitude(std::numeric_limits::min())); + BOOST_ASSERT(rectangle.min_lat != FixedLatitude(std::numeric_limits::min())); + BOOST_ASSERT(rectangle.max_lon != FixedLongitude(std::numeric_limits::min())); + BOOST_ASSERT(rectangle.max_lat != FixedLatitude(std::numeric_limits::min())); } }; @@ -518,4 +577,7 @@ class StaticRTree //[2] "Nearest Neighbor Queries", N. Roussopulos et al; 1995; DOI: 10.1145/223784.223794 //[3] "Distance Browsing in Spatial Databases"; G. Hjaltason, H. Samet; 1999; ACM Trans. DB Sys // Vol.24 No.2, pp.265-318 +} +} + #endif // STATIC_RTREE_HPP diff --git a/include/util/std_hash.hpp b/include/util/std_hash.hpp new file mode 100644 index 000000000..b456ab1ca --- /dev/null +++ b/include/util/std_hash.hpp @@ -0,0 +1,41 @@ +#ifndef STD_HASH_HPP +#define STD_HASH_HPP + +#include + +// this is largely inspired by boost's hash combine as can be found in +// "The C++ Standard Library" 2nd Edition. Nicolai M. Josuttis. 2012. + +template void hash_combine(std::size_t &seed, const T &val) +{ + seed ^= std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +template void hash_val(std::size_t &seed, const T &val) { hash_combine(seed, val); } + +template +void hash_val(std::size_t &seed, const T &val, const Types &... args) +{ + hash_combine(seed, val); + hash_val(seed, args...); +} + +template std::size_t hash_val(const Types &... args) +{ + std::size_t seed = 0; + hash_val(seed, args...); + return seed; +} + +namespace std +{ +template struct hash> +{ + size_t operator()(const std::pair &pair) const + { + return hash_val(pair.first, pair.second); + } +}; +} + +#endif // STD_HASH_HPP diff --git a/util/string_util.hpp b/include/util/string_util.hpp similarity index 68% rename from util/string_util.hpp rename to include/util/string_util.hpp index 8075e659c..41c5f091f 100644 --- a/util/string_util.hpp +++ b/include/util/string_util.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef STRING_UTIL_HPP #define STRING_UTIL_HPP @@ -34,10 +7,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + // precision: position after decimal point // length: maximum number of digits including comma and decimals // work with negative values to prevent overflowing when taking -value -template static inline char *printInt(char *buffer, int value) +template char *printInt(char *buffer, int value) { static_assert(length > 0, "length must be positive"); static_assert(precision > 0, "precision must be positive"); @@ -148,5 +126,7 @@ inline std::size_t URIDecode(const std::string &input, std::string &output) } inline std::size_t URIDecodeInPlace(std::string &URI) { return URIDecode(URI, URI); } +} +} #endif // STRING_UTIL_HPP diff --git a/include/util/strong_typedef.hpp b/include/util/strong_typedef.hpp new file mode 100644 index 000000000..eabcaa3c3 --- /dev/null +++ b/include/util/strong_typedef.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2016, Project OSRM contributors +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef STRONG_TYPEDEF_HPP +#define STRONG_TYPEDEF_HPP + +#include +#include +#include + +namespace osrm +{ + +/* Creates strongly typed wrappers around scalar types. + * Useful for stopping accidental assignment of lats to lons, + * etc. Also clarifies what this random "int" value is + * being used for. + */ +#define OSRM_STRONG_TYPEDEF(From, To) \ + class To final \ + { \ + static_assert(std::is_arithmetic(), ""); \ + From x; \ + friend std::ostream &operator<<(std::ostream &stream, const To &inst); \ + \ + public: \ + To() = default; \ + explicit To(const From x_) : x(x_) {} \ + explicit operator From &() { return x; } \ + explicit operator From() const { return x; } \ + To operator+(const To rhs_) const { return To(x + static_cast(rhs_)); } \ + To operator-(const To rhs_) const { return To(x - static_cast(rhs_)); } \ + To operator*(const To rhs_) const { return To(x * static_cast(rhs_)); } \ + To operator/(const To rhs_) const { return To(x / static_cast(rhs_)); } \ + bool operator<(const To z_) const { return x < static_cast(z_); } \ + bool operator>(const To z_) const { return x > static_cast(z_); } \ + bool operator<=(const To z_) const { return x <= static_cast(z_); } \ + bool operator>=(const To z_) const { return x >= static_cast(z_); } \ + bool operator==(const To z_) const { return x == static_cast(z_); } \ + bool operator!=(const To z_) const { return x != static_cast(z_); } \ + }; \ + inline std::ostream &operator<<(std::ostream &stream, const To &inst) \ + { \ + return stream << inst.x; \ + } + +#define OSRM_STRONG_TYPEDEF_HASHABLE(From, To) \ + namespace std \ + { \ + template <> struct hash \ + { \ + std::size_t operator()(const To &k) const \ + { \ + return std::hash()(static_cast(k)); \ + } \ + }; \ + } +} + +#endif // OSRM_STRONG_TYPEDEF_HPP diff --git a/util/timing_util.hpp b/include/util/timing_util.hpp similarity index 68% rename from util/timing_util.hpp rename to include/util/timing_util.hpp index c0c59c8a6..537f0dc5e 100644 --- a/util/timing_util.hpp +++ b/include/util/timing_util.hpp @@ -1,30 +1,3 @@ -/* - -Copyright (c) 2014, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TIMING_UTIL_HPP #define TIMING_UTIL_HPP @@ -34,6 +7,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + struct GlobalTimer { GlobalTimer() : time(0) {} @@ -86,5 +64,7 @@ class GlobalTimerFactory std::chrono::duration_cast(_X##_stop - _X##_start).count()) #define TIMER_MIN(_X) \ std::chrono::duration_cast(_X##_stop - _X##_start).count() +} +} #endif // TIMING_UTIL_HPP diff --git a/util/trigonometry_table.hpp b/include/util/trigonometry_table.hpp similarity index 95% rename from util/trigonometry_table.hpp rename to include/util/trigonometry_table.hpp index 234a94e56..fe1d316ba 100644 --- a/util/trigonometry_table.hpp +++ b/include/util/trigonometry_table.hpp @@ -1,38 +1,18 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - #ifndef TRIGONOMETRY_TABLE_HPP #define TRIGONOMETRY_TABLE_HPP -#include "../typedefs.h" +#include "util/typedefs.hpp" #include #include +#include + +namespace osrm +{ +namespace util +{ + constexpr unsigned short atan_table[4096] = { 0x0000, 0x0014, 0x0028, 0x003d, 0x0051, 0x0065, 0x007a, 0x008e, 0x00a3, 0x00b7, 0x00cb, 0x00e0, 0x00f4, 0x0108, 0x011d, 0x0131, 0x0146, 0x015a, 0x016e, 0x0183, 0x0197, 0x01ab, 0x01c0, 0x01d4, @@ -378,19 +358,27 @@ constexpr unsigned short atan_table[4096] = { 0xffe0, 0xffea, 0xfff4, 0xffff}; // max value is pi/4 -constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF; +#ifdef _MSC_VER //TODO: remove as soon as boost allows C++14 features with Visual Studio +const constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF; +#else +const constexpr double SCALING_FACTOR = 4. / boost::math::constants::pi() * 0xFFFF; +#endif + inline double atan2_lookup(double y, double x) { + + using namespace boost::math::constants; + if (std::abs(x) < std::numeric_limits::epsilon()) { if (y >= 0.) { - return M_PI / 2.; + return half_pi(); } else { - return -M_PI / 2.; + return -half_pi(); } } @@ -421,28 +409,30 @@ inline double atan2_lookup(double y, double x) case 0: break; case 1: - angle = M_PI - angle; + angle = pi() - angle; break; case 2: angle = -angle; break; case 3: - angle = -M_PI + angle; + angle = -pi() + angle; break; case 4: - angle = M_PI / 2.0 - angle; + angle = half_pi() - angle; break; case 5: - angle = M_PI / 2.0 + angle; + angle = half_pi() + angle; break; case 6: - angle = -M_PI / 2.0 + angle; + angle = -half_pi() + angle; break; case 7: - angle = -M_PI / 2.0 - angle; + angle = -half_pi() - angle; break; } return angle; } +} +} #endif // TRIGONOMETRY_TABLE_HPP diff --git a/typedefs.h b/include/util/typedefs.hpp similarity index 80% rename from typedefs.h rename to include/util/typedefs.hpp index dc77f39c7..b3404bc2c 100644 --- a/typedefs.h +++ b/include/util/typedefs.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2013, Project OSRM, Dennis Luxen, others +Copyright (c) 2016, Project OSRM contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -28,21 +28,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef TYPEDEFS_H #define TYPEDEFS_H -#include -#include -#include +#include "util/strong_typedef.hpp" -// Necessary workaround for Windows as VS doesn't implement C99 -#ifdef _MSC_VER -#define WIN32_LEAN_AND_MEAN -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#endif +#include + +#include +#include // OpenStreetMap node ids are higher than 2^32 OSRM_STRONG_TYPEDEF(uint64_t, OSMNodeID) +OSRM_STRONG_TYPEDEF_HASHABLE(uint64_t, OSMNodeID) + OSRM_STRONG_TYPEDEF(uint32_t, OSMWayID) +OSRM_STRONG_TYPEDEF_HASHABLE(uint32_t, OSMWayID) static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID(std::numeric_limits::max()); static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID(std::numeric_limits::max()); @@ -60,9 +58,26 @@ using EdgeID = unsigned int; using EdgeWeight = int; static const NodeID SPECIAL_NODEID = std::numeric_limits::max(); +static const NodeID SPECIAL_SEGMENTID = std::numeric_limits::max(); static const EdgeID SPECIAL_EDGEID = std::numeric_limits::max(); static const unsigned INVALID_NAMEID = std::numeric_limits::max(); static const unsigned INVALID_COMPONENTID = 0; static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits::max(); +struct SegmentID +{ + SegmentID(const NodeID id_, const bool enabled_) : id{id_}, enabled{enabled_} + { + BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID); + } + + NodeID id : 31; + bool enabled : 1; +}; + +// bit-fields are broken on Windows +#ifndef _MSC_VER +static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big"); +#endif + #endif /* TYPEDEFS_H */ diff --git a/include/util/vector_tile.hpp b/include/util/vector_tile.hpp new file mode 100644 index 000000000..8d816da6d --- /dev/null +++ b/include/util/vector_tile.hpp @@ -0,0 +1,37 @@ +#ifndef OSRM_UTIL_VECTOR_TILE_HPP +#define OSRM_UTIL_VECTOR_TILE_HPP + +#include + +namespace osrm +{ +namespace util +{ +namespace vector_tile +{ + +const constexpr std::uint32_t LAYER_TAG = 3; +const constexpr std::uint32_t NAME_TAG = 1; +const constexpr std::uint32_t VERSION_TAG = 15; +const constexpr std::uint32_t EXTEND_TAG = 5; +const constexpr std::uint32_t FEATURE_TAG = 2; +const constexpr std::uint32_t GEOMETRY_TAG = 3; +const constexpr std::uint32_t VARIANT_TAG = 4; +const constexpr std::uint32_t KEY_TAG = 3; +const constexpr std::uint32_t ID_TAG = 1; +const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2; +const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2; +const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4; +const constexpr std::uint32_t VARIANT_TYPE_UINT32 = 5; +const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7; +const constexpr std::uint32_t VARIANT_TYPE_STRING = 1; +const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3; + +// Vector tiles are 4096 virtual pixels on each side +const constexpr double EXTENT = 4096.0; +const constexpr double BUFFER = 128.0; + +} +} +} +#endif diff --git a/include/util/version.hpp.in b/include/util/version.hpp.in new file mode 100644 index 000000000..cb97dca12 --- /dev/null +++ b/include/util/version.hpp.in @@ -0,0 +1,10 @@ +#ifndef VERSION_HPP +#define VERSION_HPP + +#define OSRM_VERSION_MAJOR "@OSRM_VERSION_MAJOR@" +#define OSRM_VERSION_MINOR "@OSRM_VERSION_MINOR@" +#define OSRM_VERSION_PATCH "@OSRM_VERSION_PATCH@" + +#define OSRM_VERSION "v" OSRM_VERSION_MAJOR "." OSRM_VERSION_MINOR "." OSRM_VERSION_PATCH + +#endif // VERSION_HPP diff --git a/include/util/viewport.hpp b/include/util/viewport.hpp new file mode 100644 index 000000000..211aa5745 --- /dev/null +++ b/include/util/viewport.hpp @@ -0,0 +1,51 @@ +#ifndef UTIL_VIEWPORT_HPP +#define UTIL_VIEWPORT_HPP + +#include "util/coordinate.hpp" +#include "util/web_mercator.hpp" + +#include + +#include +#include + +// Port of https://github.com/mapbox/geo-viewport + +namespace osrm +{ +namespace util +{ +namespace viewport +{ + +namespace detail +{ +static constexpr unsigned MAX_ZOOM = 18; +static constexpr unsigned MIN_ZOOM = 1; +// this is an upper bound to current display sizes +static constexpr double VIEWPORT_WIDTH = 8 * web_mercator::TILE_SIZE; +static constexpr double VIEWPORT_HEIGHT = 5 * web_mercator::TILE_SIZE; +static double INV_LOG_2 = 1. / std::log(2); +} + +inline unsigned getFittedZoom(util::Coordinate south_west, util::Coordinate north_east) +{ + const auto min_x = web_mercator::degreeToPixel(toFloating(south_west.lon), detail::MAX_ZOOM); + const auto max_y = web_mercator::degreeToPixel(toFloating(south_west.lat), detail::MAX_ZOOM); + const auto max_x = web_mercator::degreeToPixel(toFloating(north_east.lon), detail::MAX_ZOOM); + const auto min_y = web_mercator::degreeToPixel(toFloating(north_east.lat), detail::MAX_ZOOM); + const double width_ratio = (max_x - min_x) / detail::VIEWPORT_WIDTH; + const double height_ratio = (max_y - min_y) / detail::VIEWPORT_HEIGHT; + const auto zoom = detail::MAX_ZOOM - + std::max(std::log(width_ratio), std::log(height_ratio)) * detail::INV_LOG_2; + + if (std::isfinite(zoom)) + return std::max(detail::MIN_ZOOM, zoom); + else + return detail::MIN_ZOOM; +} +} +} +} + +#endif diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp new file mode 100644 index 000000000..8c23de143 --- /dev/null +++ b/include/util/web_mercator.hpp @@ -0,0 +1,127 @@ +#ifndef OSRM_WEB_MERCATOR_HPP +#define OSRM_WEB_MERCATOR_HPP + +#include "util/coordinate.hpp" + +#include + +namespace osrm +{ +namespace util +{ +namespace web_mercator +{ +namespace detail +{ +const constexpr long double DEGREE_TO_RAD = 0.017453292519943295769236907684886; +const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD; +// radius used by WGS84 +const constexpr double EARTH_RADIUS_WGS84 = 6378137.0; +// earth circumference devided by 2 +const constexpr double MAXEXTENT = EARTH_RADIUS_WGS84 * boost::math::constants::pi(); +// ^ math functions are not constexpr since they have side-effects (setting errno) :( +const constexpr double MAX_LATITUDE = 85.; +const constexpr double MAX_LONGITUDE = 180.0; +} + +// Converts projected mercator degrees to PX +const constexpr double DEGREE_TO_PX = detail::MAXEXTENT / 180.0; +// This is the global default tile size for all Mapbox Vector Tiles +const constexpr double TILE_SIZE = 256.0; + +inline FloatLatitude yToLat(const double y) +{ + const auto clamped_y = std::max(-180., std::min(180., y)); + const double normalized_lat = + detail::RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * detail::DEGREE_TO_RAD)); + + return FloatLatitude(normalized_lat - 90.); +} + +inline double latToY(const FloatLatitude latitude) +{ + // apparently this is the (faster) version of the canonical log(tan()) version + const double f = std::sin(detail::DEGREE_TO_RAD * static_cast(latitude)); + const double y = detail::RAD_TO_DEGREE * 0.5 * std::log((1 + f) / (1 - f)); + const auto clamped_y = std::max(-180., std::min(180., y)); + return clamped_y; +} + +inline FloatLatitude clamp(const FloatLatitude lat) +{ + return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)), + FloatLatitude(-detail::MAX_LATITUDE)); +} + +inline FloatLongitude clamp(const FloatLongitude lon) +{ + return std::max(std::min(lon, FloatLongitude(detail::MAX_LONGITUDE)), + FloatLongitude(-detail::MAX_LONGITUDE)); +} + +inline void pixelToDegree(const double shift, double &x, double &y) +{ + const double b = shift / 2.0; + x = (x - b) / shift * 360.0; + // FIXME needs to be simplified + const double g = (y - b) / -(shift / (2 * M_PI)) / detail::DEGREE_TO_RAD; + static_assert(detail::DEGREE_TO_RAD / (2 * M_PI) - 1 / 360. < 0.0001, ""); + y = static_cast(yToLat(g)); +} + +inline double degreeToPixel(FloatLongitude lon, unsigned zoom) +{ + const double shift = (1u << zoom) * TILE_SIZE; + const double b = shift / 2.0; + const double x = b * (1 + static_cast(lon) / 180.0); + return x; +} + +inline double degreeToPixel(FloatLatitude lat, unsigned zoom) +{ + const double shift = (1u << zoom) * TILE_SIZE; + const double b = shift / 2.0; + const double y = b * (1. - latToY(lat) / 180.); + return y; +} + +inline FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate) +{ + return {wgs84_coordinate.lon, FloatLatitude{latToY(wgs84_coordinate.lat)}}; +} + +inline FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate) +{ + return {mercator_coordinate.lon, yToLat(static_cast(mercator_coordinate.lat))}; +} + +// Converts a WMS tile coordinate (z,x,y) into a wgs bounding box +inline void xyzToWGS84( + const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy) +{ + minx = x * TILE_SIZE; + miny = (y + 1.0) * TILE_SIZE; + maxx = (x + 1.0) * TILE_SIZE; + maxy = y * TILE_SIZE; + // 2^z * TILE_SIZE + const double shift = (1u << static_cast(z)) * TILE_SIZE; + pixelToDegree(shift, minx, miny); + pixelToDegree(shift, maxx, maxy); +} + +// Converts a WMS tile coordinate (z,x,y) into a mercator bounding box +inline void xyzToMercator( + const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy) +{ + xyzToWGS84(x, y, z, minx, miny, maxx, maxy); + + minx = static_cast(clamp(util::FloatLongitude(minx))) * DEGREE_TO_PX; + miny = latToY(clamp(util::FloatLatitude(miny))) * DEGREE_TO_PX; + maxx = static_cast(clamp(util::FloatLongitude(maxx))) * DEGREE_TO_PX; + maxy = latToY(clamp(util::FloatLatitude(maxy))) * DEGREE_TO_PX; +} +} +} +} + +#endif diff --git a/include/util/xor_fast_hash.hpp b/include/util/xor_fast_hash.hpp new file mode 100644 index 000000000..6ad79d66a --- /dev/null +++ b/include/util/xor_fast_hash.hpp @@ -0,0 +1,71 @@ +#ifndef XOR_FAST_HASH_HPP +#define XOR_FAST_HASH_HPP + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace osrm +{ +namespace util +{ + +/* + This is an implementation of Tabulation hashing, which has suprising properties like + universality. + The space requirement is 2*2^16 = 256 kb of memory, which fits into L2 cache. + Evaluation boils down to 10 or less assembly instruction on any recent X86 CPU: + + 1: movq table2(%rip), %rdx + 2: movl %edi, %eax + 3: movzwl %di, %edi + 4: shrl $16, %eax + 5: movzwl %ax, %eax + 6: movzbl (%rdx,%rax), %eax + 7: movq table1(%rip), %rdx + 8: xorb (%rdx,%rdi), %al + 9: movzbl %al, %eax + 10: ret + +*/ +template class XORFastHash +{ + static_assert(MaxNumElements <= (1u << 16u), "only 65536 elements indexable with uint16_t"); + + std::array table1; + std::array table2; + + public: + XORFastHash() + { + std::mt19937 generator; // impl. defined but deterministic default seed + + std::iota(begin(table1), end(table1), 0u); + std::shuffle(begin(table1), end(table1), generator); + + std::iota(begin(table2), end(table2), 0u); + std::shuffle(begin(table2), end(table2), generator); + } + + inline std::uint16_t operator()(const std::uint32_t originalValue) const + { + std::uint16_t lsb = originalValue & 0xffffu; + std::uint16_t msb = originalValue >> 16u; + + BOOST_ASSERT(lsb < table1.size()); + BOOST_ASSERT(msb < table2.size()); + + return table1[lsb] ^ table2[msb]; + } +}; +} +} + +#endif // XOR_FAST_HASH_HPP diff --git a/include/util/xor_fast_hash_storage.hpp b/include/util/xor_fast_hash_storage.hpp new file mode 100644 index 000000000..6d2d76baa --- /dev/null +++ b/include/util/xor_fast_hash_storage.hpp @@ -0,0 +1,84 @@ +#ifndef XOR_FAST_HASH_STORAGE_HPP +#define XOR_FAST_HASH_STORAGE_HPP + +#include "util/xor_fast_hash.hpp" + +#include +#include + +namespace osrm +{ +namespace util +{ + +template +class XORFastHashStorage +{ + public: + struct HashCell + { + unsigned time; + NodeID id; + Key key; + HashCell() + : time(std::numeric_limits::max()), id(std::numeric_limits::max()), + key(std::numeric_limits::max()) + { + } + + HashCell(const HashCell &other) : time(other.key), id(other.id), key(other.time) {} + operator Key() const { return key; } + void operator=(const Key key_to_insert) { key = key_to_insert; } + }; + + explicit XORFastHashStorage(size_t) : positions(MaxNumElements), current_timestamp{0u} {} + + HashCell &operator[](const NodeID node) + { + std::uint16_t position = fast_hasher(node); + while ((positions[position].time == current_timestamp) && (positions[position].id != node)) + { + ++position %= MaxNumElements; + } + + positions[position].time = current_timestamp; + positions[position].id = node; + + BOOST_ASSERT(position < positions.size()); + + return positions[position]; + } + + // peek into table, get key for node, think of it as a read-only operator[] + Key peek_index(const NodeID node) const + { + std::uint16_t position = fast_hasher(node); + while ((positions[position].time == current_timestamp) && (positions[position].id != node)) + { + ++position %= MaxNumElements; + } + + BOOST_ASSERT(position < positions.size()); + + return positions[position].key; + } + + void Clear() + { + ++current_timestamp; + if (std::numeric_limits::max() == current_timestamp) + { + positions.clear(); + positions.resize(MaxNumElements); + } + } + + private: + std::vector positions; + XORFastHash fast_hasher; + unsigned current_timestamp; +}; +} +} + +#endif // XOR_FAST_HASH_STORAGE_HPP diff --git a/library/osrm_impl.cpp b/library/osrm_impl.cpp deleted file mode 100644 index 069ef95a4..000000000 --- a/library/osrm_impl.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -namespace boost -{ -namespace interprocess -{ -class named_mutex; -} -} - -#include "osrm_impl.hpp" - -#include "../plugins/distance_table.hpp" -#include "../plugins/hello_world.hpp" -#include "../plugins/nearest.hpp" -#include "../plugins/timestamp.hpp" -#include "../plugins/trip.hpp" -#include "../plugins/viaroute.hpp" -#include "../plugins/match.hpp" -#include "../server/data_structures/datafacade_base.hpp" -#include "../server/data_structures/internal_datafacade.hpp" -#include "../server/data_structures/shared_barriers.hpp" -#include "../server/data_structures/shared_datafacade.hpp" -#include "../util/make_unique.hpp" -#include "../util/routed_options.hpp" -#include "../util/simple_logger.hpp" - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -OSRM::OSRM_impl::OSRM_impl(LibOSRMConfig& lib_config) -{ - if (lib_config.use_shared_memory) - { - barrier = osrm::make_unique(); - query_data_facade = new SharedDataFacade(); - } - else - { - // populate base path - populate_base_path(lib_config.server_paths); - query_data_facade = new InternalDataFacade(lib_config.server_paths); - } - - // The following plugins handle all requests. - RegisterPlugin(new DistanceTablePlugin>( - query_data_facade, lib_config.max_locations_distance_table)); - RegisterPlugin(new HelloWorldPlugin()); - RegisterPlugin(new NearestPlugin>(query_data_facade)); - RegisterPlugin(new MapMatchingPlugin>( - query_data_facade, lib_config.max_locations_map_matching)); - RegisterPlugin(new TimestampPlugin>(query_data_facade)); - RegisterPlugin(new ViaRoutePlugin>(query_data_facade, - lib_config.max_locations_viaroute)); - RegisterPlugin(new RoundTripPlugin>(query_data_facade, - lib_config.max_locations_trip)); -} - -void OSRM::OSRM_impl::RegisterPlugin(BasePlugin *raw_plugin_ptr) -{ - std::unique_ptr plugin_ptr(raw_plugin_ptr); - SimpleLogger().Write() << "loaded plugin: " << plugin_ptr->GetDescriptor(); - plugin_map[plugin_ptr->GetDescriptor()] = std::move(plugin_ptr); -} - -int OSRM::OSRM_impl::RunQuery(const RouteParameters &route_parameters, osrm::json::Object &json_result) -{ - const auto &plugin_iterator = plugin_map.find(route_parameters.service); - - if (plugin_map.end() == plugin_iterator) - { - json_result.values["status_message"] = "Service not found"; - return 400; - } - - increase_concurrent_query_count(); - BasePlugin::Status return_code; - if (barrier) { - // Get a shared data lock so that other threads won't update - // things while the query is running - boost::shared_lock data_lock{ - (static_cast *>(query_data_facade))->data_mutex}; - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); - } else { - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); - } - decrease_concurrent_query_count(); - return static_cast(return_code); -} - -// decrease number of concurrent queries -void OSRM::OSRM_impl::decrease_concurrent_query_count() -{ - if (!barrier) - { - return; - } - // lock query - boost::interprocess::scoped_lock query_lock( - barrier->query_mutex); - - // decrement query count - --(barrier->number_of_queries); - BOOST_ASSERT_MSG(0 <= barrier->number_of_queries, "invalid number of queries"); - - // notify all processes that were waiting for this condition - if (0 == barrier->number_of_queries) - { - barrier->no_running_queries_condition.notify_all(); - } -} - -// increase number of concurrent queries -void OSRM::OSRM_impl::increase_concurrent_query_count() -{ - if (!barrier) - { - return; - } - - // lock update pending - boost::interprocess::scoped_lock pending_lock( - barrier->pending_update_mutex); - - // lock query - boost::interprocess::scoped_lock query_lock( - barrier->query_mutex); - - // unlock update pending - pending_lock.unlock(); - - // increment query count - ++(barrier->number_of_queries); - - (static_cast *>(query_data_facade)) - ->CheckAndReloadFacade(); -} - -// proxy code for compilation firewall -OSRM::OSRM(LibOSRMConfig &lib_config) : OSRM_pimpl_(osrm::make_unique(lib_config)) {} - -// needed because unique_ptr needs the size of OSRM_impl for delete -OSRM::~OSRM() {} - -int OSRM::RunQuery(const RouteParameters &route_parameters, osrm::json::Object &json_result) -{ - return OSRM_pimpl_->RunQuery(route_parameters, json_result); -} diff --git a/library/osrm_impl.hpp b/library/osrm_impl.hpp deleted file mode 100644 index c223449e4..000000000 --- a/library/osrm_impl.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef OSRM_IMPL_HPP -#define OSRM_IMPL_HPP - -class BasePlugin; -struct RouteParameters; - -#include "../data_structures/query_edge.hpp" - -#include -#include -#include - -#include -#include -#include - -struct SharedBarriers; -template class BaseDataFacade; - -class OSRM::OSRM_impl final -{ - private: - using PluginMap = std::unordered_map>; - - public: - OSRM_impl(LibOSRMConfig &lib_config); - OSRM_impl(const OSRM_impl &) = delete; - int RunQuery(const RouteParameters &route_parameters, osrm::json::Object &json_result); - - private: - void RegisterPlugin(BasePlugin *plugin); - PluginMap plugin_map; - // will only be initialized if shared memory is used - std::unique_ptr barrier; - // base class pointer to the objects - BaseDataFacade *query_data_facade; - - // decrease number of concurrent queries - void decrease_concurrent_query_count(); - // increase number of concurrent queries - void increase_concurrent_query_count(); -}; - -#endif // OSRM_IMPL_HPP diff --git a/package.json b/package.json new file mode 100644 index 000000000..46c0bb70c --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "osrm-backend-test-suite", + "version": "0.0.0", + "private": true, + "description": "The Open Source Routing Machine is a high performance routing engine written in C++11 designed to run on OpenStreetMap data.", + "dependencies": { + "cucumber": "^0.9.4", + "d3-queue": "^2.0.3", + "node-timeout": "0.0.4", + "request": "^2.69.0", + "xmlbuilder": "^4.2.1", + "chalk": "^1.1.3" + }, + "bin": { + "cucumber": "./node_modules/cucumber/bin/cucumber.js" + }, + "scripts": { + "lint": "eslint -c ./.eslintrc features/step_definitions/ features/support/", + "test": "npm run lint && ./node_modules/cucumber/bin/cucumber.js features/ -p verify", + "clean-test": "rm -rf test/cache", + "cucumber": "./node_modules/cucumber/bin/cucumber.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/Project-OSRM/osrm-backend.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Project-OSRM/osrm-backend/issues" + }, + "homepage": "https://github.com/Project-OSRM/osrm-backend", + "engines": { + "node": ">=4.0.0" + }, + "devDependencies": { + "eslint": "^2.4.0" + } +} diff --git a/plugins/distance_table.hpp b/plugins/distance_table.hpp deleted file mode 100644 index c4ed250c8..000000000 --- a/plugins/distance_table.hpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef DISTANCE_TABLE_HPP -#define DISTANCE_TABLE_HPP - -#include "plugin_base.hpp" - -#include "../algorithms/object_encoder.hpp" -#include "../data_structures/query_edge.hpp" -#include "../data_structures/search_engine.hpp" -#include "../descriptors/descriptor_base.hpp" -#include "../util/json_renderer.hpp" -#include "../util/make_unique.hpp" -#include "../util/string_util.hpp" -#include "../util/timing_util.hpp" - -#include - -#include - -#include -#include -#include -#include -#include - -template class DistanceTablePlugin final : public BasePlugin -{ - private: - std::unique_ptr> search_engine_ptr; - int max_locations_distance_table; - - public: - explicit DistanceTablePlugin(DataFacadeT *facade, const int max_locations_distance_table) - : max_locations_distance_table(max_locations_distance_table), descriptor_string("table"), - facade(facade) - { - search_engine_ptr = osrm::make_unique>(facade); - } - - virtual ~DistanceTablePlugin() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) override final - { - if (!check_all_coordinates(route_parameters.coordinates)) - { - json_result.values["status_message"] = "Coordinates are invalid"; - return Status::Error; - } - - const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && - route_parameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinates"; - return Status::Error; - } - - auto number_of_sources = - std::count_if(route_parameters.is_source.begin(), route_parameters.is_source.end(), - [](const bool is_source) - { - return is_source; - }); - auto number_of_destination = - std::count_if(route_parameters.is_destination.begin(), - route_parameters.is_destination.end(), [](const bool is_destination) - { - return is_destination; - }); - - if (max_locations_distance_table > 0 && - (number_of_sources * number_of_destination > - max_locations_distance_table * max_locations_distance_table)) - { - json_result.values["status_message"] = - "Number of entries " + std::to_string(number_of_sources * number_of_destination) + - " is higher than current maximum (" + - std::to_string(max_locations_distance_table * max_locations_distance_table) + ")"; - return Status::Error; - } - - const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum()); - - std::vector phantom_node_source_vector(number_of_sources); - std::vector phantom_node_target_vector(number_of_destination); - auto phantom_node_source_out_iter = phantom_node_source_vector.begin(); - auto phantom_node_target_out_iter = phantom_node_target_vector.begin(); - for (const auto i : osrm::irange(0u, route_parameters.coordinates.size())) - { - if (checksum_OK && i < route_parameters.hints.size() && - !route_parameters.hints[i].empty()) - { - PhantomNode current_phantom_node; - ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node); - if (current_phantom_node.is_valid(facade->GetNumberOfNodes())) - { - if (route_parameters.is_source[i]) - { - *phantom_node_source_out_iter = - std::make_pair(current_phantom_node, current_phantom_node); - if (route_parameters.is_destination[i]) - { - *phantom_node_target_out_iter = *phantom_node_source_out_iter; - phantom_node_target_out_iter++; - } - phantom_node_source_out_iter++; - } - else - { - BOOST_ASSERT(route_parameters.is_destination[i] && - !route_parameters.is_source[i]); - *phantom_node_target_out_iter = - std::make_pair(current_phantom_node, current_phantom_node); - phantom_node_target_out_iter++; - } - continue; - } - } - const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0; - const int range = input_bearings.size() > 0 - ? (input_bearings[i].second ? *input_bearings[i].second : 10) - : 180; - if (route_parameters.is_source[i]) - { - *phantom_node_source_out_iter = - facade->NearestPhantomNodeWithAlternativeFromBigComponent( - route_parameters.coordinates[i], bearing, range); - // we didn't found a fitting node, return error - if (!phantom_node_source_out_iter->first.is_valid(facade->GetNumberOfNodes())) - { - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + std::to_string(i); - return Status::NoSegment; - } - - if (route_parameters.is_destination[i]) - { - *phantom_node_target_out_iter = *phantom_node_source_out_iter; - phantom_node_target_out_iter++; - } - phantom_node_source_out_iter++; - } - else - { - BOOST_ASSERT(route_parameters.is_destination[i] && !route_parameters.is_source[i]); - - *phantom_node_target_out_iter = - facade->NearestPhantomNodeWithAlternativeFromBigComponent( - route_parameters.coordinates[i], bearing, range); - // we didn't found a fitting node, return error - if (!phantom_node_target_out_iter->first.is_valid(facade->GetNumberOfNodes())) - { - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + std::to_string(i); - return Status::NoSegment; - } - phantom_node_target_out_iter++; - } - } - - BOOST_ASSERT((phantom_node_source_out_iter - phantom_node_source_vector.begin()) == - number_of_sources); - BOOST_ASSERT((phantom_node_target_out_iter - phantom_node_target_vector.begin()) == - number_of_destination); - - // FIXME we should clear phantom_node_source_vector and phantom_node_target_vector after - // this - auto snapped_source_phantoms = snapPhantomNodes(phantom_node_source_vector); - auto snapped_target_phantoms = snapPhantomNodes(phantom_node_target_vector); - - auto result_table = - search_engine_ptr->distance_table(snapped_source_phantoms, snapped_target_phantoms); - - if (!result_table) - { - json_result.values["status_message"] = "No distance table found"; - return Status::EmptyResult; - } - - osrm::json::Array matrix_json_array; - for (const auto row : osrm::irange(0, number_of_sources)) - { - osrm::json::Array json_row; - auto row_begin_iterator = result_table->begin() + (row * number_of_destination); - auto row_end_iterator = result_table->begin() + ((row + 1) * number_of_destination); - json_row.values.insert(json_row.values.end(), row_begin_iterator, row_end_iterator); - matrix_json_array.values.push_back(json_row); - } - json_result.values["distance_table"] = matrix_json_array; - - osrm::json::Array target_coord_json_array; - for (const auto &phantom : snapped_target_phantoms) - { - osrm::json::Array json_coord; - json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION); - json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION); - target_coord_json_array.values.push_back(json_coord); - } - json_result.values["destination_coordinates"] = target_coord_json_array; - osrm::json::Array source_coord_json_array; - for (const auto &phantom : snapped_source_phantoms) - { - osrm::json::Array json_coord; - json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION); - json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION); - source_coord_json_array.values.push_back(json_coord); - } - json_result.values["source_coordinates"] = source_coord_json_array; - return Status::Ok; - } - - private: - std::string descriptor_string; - DataFacadeT *facade; -}; - -#endif // DISTANCE_TABLE_HPP diff --git a/plugins/hello_world.hpp b/plugins/hello_world.hpp deleted file mode 100644 index 512fb07e0..000000000 --- a/plugins/hello_world.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef HELLO_WORLD_HPP -#define HELLO_WORLD_HPP - -#include "plugin_base.hpp" - -#include "../util/json_renderer.hpp" - -#include - -#include - -class HelloWorldPlugin final : public BasePlugin -{ - private: - std::string temp_string; - - public: - HelloWorldPlugin() : descriptor_string("hello") {} - virtual ~HelloWorldPlugin() {} - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - osrm::json::Object &json_result) override final - { - std::string temp_string; - json_result.values["title"] = "Hello World"; - - temp_string = std::to_string(routeParameters.zoom_level); - json_result.values["zoom_level"] = temp_string; - - temp_string = std::to_string(routeParameters.check_sum); - json_result.values["check_sum"] = temp_string; - json_result.values["instructions"] = (routeParameters.print_instructions ? "yes" : "no"); - json_result.values["geometry"] = (routeParameters.geometry ? "yes" : "no"); - json_result.values["compression"] = (routeParameters.compression ? "yes" : "no"); - json_result.values["output_format"] = - (!routeParameters.output_format.empty() ? "yes" : "no"); - - json_result.values["jsonp_parameter"] = - (!routeParameters.jsonp_parameter.empty() ? "yes" : "no"); - json_result.values["language"] = (!routeParameters.language.empty() ? "yes" : "no"); - - temp_string = std::to_string(routeParameters.coordinates.size()); - json_result.values["location_count"] = temp_string; - - osrm::json::Array json_locations; - unsigned counter = 0; - for (const FixedPointCoordinate &coordinate : routeParameters.coordinates) - { - osrm::json::Object json_location; - osrm::json::Array json_coordinates; - - json_coordinates.values.push_back( - static_cast(coordinate.lat / COORDINATE_PRECISION)); - json_coordinates.values.push_back( - static_cast(coordinate.lon / COORDINATE_PRECISION)); - json_location.values[std::to_string(counter)] = json_coordinates; - json_locations.values.push_back(json_location); - ++counter; - } - json_result.values["locations"] = json_locations; - json_result.values["hint_count"] = routeParameters.hints.size(); - - osrm::json::Array json_hints; - counter = 0; - for (const std::string ¤t_hint : routeParameters.hints) - { - json_hints.values.push_back(current_hint); - ++counter; - } - json_result.values["hints"] = json_hints; - return Status::Ok; - } - - private: - std::string descriptor_string; -}; - -#endif // HELLO_WORLD_HPP diff --git a/plugins/match.hpp b/plugins/match.hpp deleted file mode 100644 index e213d19ab..000000000 --- a/plugins/match.hpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef MATCH_HPP -#define MATCH_HPP - -#include "plugin_base.hpp" - -#include "../algorithms/bayes_classifier.hpp" -#include "../algorithms/object_encoder.hpp" -#include "../data_structures/search_engine.hpp" -#include "../descriptors/descriptor_base.hpp" -#include "../descriptors/json_descriptor.hpp" -#include "../routing_algorithms/map_matching.hpp" -#include "../util/compute_angle.hpp" -#include "../util/integer_range.hpp" -#include "../util/json_logger.hpp" -#include "../util/json_util.hpp" -#include "../util/string_util.hpp" - -#include - -#include -#include -#include -#include - -template class MapMatchingPlugin : public BasePlugin -{ - std::shared_ptr> search_engine_ptr; - - using ClassifierT = BayesClassifier; - using TraceClassification = ClassifierT::ClassificationT; - - public: - MapMatchingPlugin(DataFacadeT *facade, const int max_locations_map_matching) - : descriptor_string("match"), facade(facade), - max_locations_map_matching(max_locations_map_matching), - // the values where derived from fitting a laplace distribution - // to the values of manually classified traces - classifier(LaplaceDistribution(0.005986, 0.016646), - LaplaceDistribution(0.054385, 0.458432), - 0.696774) // valid apriori probability - { - search_engine_ptr = std::make_shared>(facade); - } - - virtual ~MapMatchingPlugin() {} - - const std::string GetDescriptor() const final override { return descriptor_string; } - - TraceClassification - classify(const float trace_length, const float matched_length, const int removed_points) const - { - (void)removed_points; // unused - - const double distance_feature = -std::log(trace_length) + std::log(matched_length); - - // matched to the same point - if (!std::isfinite(distance_feature)) - { - return std::make_pair(ClassifierT::ClassLabel::NEGATIVE, 1.0); - } - - const auto label_with_confidence = classifier.classify(distance_feature); - - return label_with_confidence; - } - - osrm::matching::CandidateLists getCandidates( - const std::vector &input_coords, - const std::vector>> &input_bearings, - const double gps_precision, - std::vector &sub_trace_lengths) - { - osrm::matching::CandidateLists candidates_lists; - - // assuming the gps_precision is the standart-diviation of normal distribution that models - // GPS noise (in this model) this should give us the correct candidate with >0.95 - double query_radius = 3 * gps_precision; - double last_distance = - coordinate_calculation::haversine_distance(input_coords[0], input_coords[1]); - - sub_trace_lengths.resize(input_coords.size()); - sub_trace_lengths[0] = 0; - for (const auto current_coordinate : osrm::irange(0, input_coords.size())) - { - bool allow_uturn = false; - if (0 < current_coordinate) - { - last_distance = coordinate_calculation::haversine_distance( - input_coords[current_coordinate - 1], input_coords[current_coordinate]); - - sub_trace_lengths[current_coordinate] += - sub_trace_lengths[current_coordinate - 1] + last_distance; - } - - if (input_coords.size() - 1 > current_coordinate && 0 < current_coordinate) - { - double turn_angle = ComputeAngle::OfThreeFixedPointCoordinates( - input_coords[current_coordinate - 1], input_coords[current_coordinate], - input_coords[current_coordinate + 1]); - - // sharp turns indicate a possible uturn - if (turn_angle <= 90.0 || turn_angle >= 270.0) - { - allow_uturn = true; - } - } - - // Use bearing values if supplied, otherwise fallback to 0,180 defaults - auto bearing = input_bearings.size() > 0 ? input_bearings[current_coordinate].first : 0; - auto range = input_bearings.size() > 0 - ? (input_bearings[current_coordinate].second - ? *input_bearings[current_coordinate].second - : 10) - : 180; - auto candidates = facade->NearestPhantomNodesInRange(input_coords[current_coordinate], - query_radius, bearing, range); - - if (candidates.size() == 0) - { - break; - } - - // sort by foward id, then by reverse id and then by distance - std::sort( - candidates.begin(), candidates.end(), - [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) - { - return lhs.phantom_node.forward_node_id < rhs.phantom_node.forward_node_id || - (lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id && - (lhs.phantom_node.reverse_node_id < rhs.phantom_node.reverse_node_id || - (lhs.phantom_node.reverse_node_id == - rhs.phantom_node.reverse_node_id && - lhs.distance < rhs.distance))); - }); - - auto new_end = std::unique( - candidates.begin(), candidates.end(), - [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) - { - return lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id && - lhs.phantom_node.reverse_node_id == rhs.phantom_node.reverse_node_id; - }); - candidates.resize(new_end - candidates.begin()); - - if (!allow_uturn) - { - const auto compact_size = candidates.size(); - for (const auto i : osrm::irange(0, compact_size)) - { - // Split edge if it is bidirectional and append reverse direction to end of list - if (candidates[i].phantom_node.forward_node_id != SPECIAL_NODEID && - candidates[i].phantom_node.reverse_node_id != SPECIAL_NODEID) - { - PhantomNode reverse_node(candidates[i].phantom_node); - reverse_node.forward_node_id = SPECIAL_NODEID; - candidates.push_back( - PhantomNodeWithDistance{reverse_node, candidates[i].distance}); - - candidates[i].phantom_node.reverse_node_id = SPECIAL_NODEID; - } - } - } - - // sort by distance to make pruning effective - std::sort(candidates.begin(), candidates.end(), - [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) - { - return lhs.distance < rhs.distance; - }); - - candidates_lists.push_back(std::move(candidates)); - } - - return candidates_lists; - } - - osrm::json::Object submatchingToJSON(const osrm::matching::SubMatching &sub, - const RouteParameters &route_parameters, - const InternalRouteResult &raw_route) - { - osrm::json::Object subtrace; - - if (route_parameters.classify) - { - subtrace.values["confidence"] = sub.confidence; - } - - JSONDescriptor json_descriptor(facade); - json_descriptor.SetConfig(route_parameters); - - subtrace.values["hint_data"] = json_descriptor.BuildHintData(raw_route); - - if (route_parameters.geometry || route_parameters.print_instructions) - { - DescriptionFactory factory; - FixedPointCoordinate current_coordinate; - factory.SetStartSegment(raw_route.segment_end_coordinates.front().source_phantom, - raw_route.source_traversed_in_reverse.front()); - for (const auto i : - osrm::irange(0, raw_route.unpacked_path_segments.size())) - { - for (const PathData &path_data : raw_route.unpacked_path_segments[i]) - { - current_coordinate = facade->GetCoordinateOfNode(path_data.node); - factory.AppendSegment(current_coordinate, path_data); - } - factory.SetEndSegment(raw_route.segment_end_coordinates[i].target_phantom, - raw_route.target_traversed_in_reverse[i], - raw_route.is_via_leg(i)); - } - - factory.Run(route_parameters.zoom_level); - - // we need because we don't run path simplification - for (auto &segment : factory.path_description) - { - segment.necessary = true; - } - - if (route_parameters.geometry) - { - subtrace.values["geometry"] = - factory.AppendGeometryString(route_parameters.compression); - } - - if (route_parameters.print_instructions) - { - std::vector::Segment> temp_segments; - subtrace.values["instructions"] = - json_descriptor.BuildTextualDescription(factory, temp_segments); - } - - factory.BuildRouteSummary(factory.get_entire_length(), raw_route.shortest_path_length); - osrm::json::Object json_route_summary; - json_route_summary.values["total_distance"] = factory.summary.distance; - json_route_summary.values["total_time"] = factory.summary.duration; - subtrace.values["route_summary"] = json_route_summary; - } - - subtrace.values["indices"] = osrm::json::make_array(sub.indices); - - osrm::json::Array points; - for (const auto &node : sub.nodes) - { - points.values.emplace_back( - osrm::json::make_array(node.location.lat / COORDINATE_PRECISION, - node.location.lon / COORDINATE_PRECISION)); - } - subtrace.values["matched_points"] = points; - - osrm::json::Array names; - for (const auto &node : sub.nodes) - { - names.values.emplace_back(facade->get_name_for_id(node.name_id)); - } - subtrace.values["matched_names"] = names; - - return subtrace; - } - - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) final override - { - // enforce maximum number of locations for performance reasons - if (max_locations_map_matching > 0 && - static_cast(route_parameters.coordinates.size()) > max_locations_map_matching) - { - json_result.values["status_message"] = "Too many coodindates"; - return Status::Error; - } - - // check number of parameters - if (!check_all_coordinates(route_parameters.coordinates)) - { - json_result.values["status_message"] = "Invalid coordinates"; - return Status::Error; - } - - std::vector sub_trace_lengths; - const auto &input_coords = route_parameters.coordinates; - const auto &input_timestamps = route_parameters.timestamps; - const auto &input_bearings = route_parameters.bearings; - if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size()) - { - json_result.values["status_message"] = - "Number of timestamps does not match number of coordinates"; - return Status::Error; - } - - if (input_bearings.size() > 0 && input_coords.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinates"; - return Status::Error; - } - - // enforce maximum number of locations for performance reasons - if (static_cast(input_coords.size()) < 2) - { - json_result.values["status_message"] = "At least two coordinates needed"; - return Status::Error; - } - - const auto candidates_lists = getCandidates( - input_coords, input_bearings, route_parameters.gps_precision, sub_trace_lengths); - if (candidates_lists.size() != input_coords.size()) - { - BOOST_ASSERT(candidates_lists.size() < input_coords.size()); - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + - std::to_string(candidates_lists.size()); - return Status::NoSegment; - } - - // setup logging if enabled - if (osrm::json::Logger::get()) - osrm::json::Logger::get()->initialize("matching"); - - // call the actual map matching - osrm::matching::SubMatchingList sub_matchings; - search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps, - route_parameters.matching_beta, - route_parameters.gps_precision, sub_matchings); - - osrm::json::Array matchings; - for (auto &sub : sub_matchings) - { - // classify result - if (route_parameters.classify) - { - double trace_length = - sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()]; - TraceClassification classification = - classify(trace_length, sub.length, - (sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size()); - if (classification.first == ClassifierT::ClassLabel::POSITIVE) - { - sub.confidence = classification.second; - } - else - { - sub.confidence = 1 - classification.second; - } - } - - BOOST_ASSERT(sub.nodes.size() > 1); - - // FIXME we only run this to obtain the geometry - // The clean way would be to get this directly from the map matching plugin - InternalRouteResult raw_route; - PhantomNodes current_phantom_node_pair; - for (unsigned i = 0; i < sub.nodes.size() - 1; ++i) - { - current_phantom_node_pair.source_phantom = sub.nodes[i]; - current_phantom_node_pair.target_phantom = sub.nodes[i + 1]; - BOOST_ASSERT(current_phantom_node_pair.source_phantom.is_valid()); - BOOST_ASSERT(current_phantom_node_pair.target_phantom.is_valid()); - raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair); - } - search_engine_ptr->shortest_path( - raw_route.segment_end_coordinates, - std::vector(raw_route.segment_end_coordinates.size() + 1, true), raw_route); - - BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT); - - matchings.values.emplace_back(submatchingToJSON(sub, route_parameters, raw_route)); - } - - if (osrm::json::Logger::get()) - osrm::json::Logger::get()->render("matching", json_result); - json_result.values["matchings"] = matchings; - - if (sub_matchings.empty()) - { - json_result.values["status_message"] = "Cannot find matchings"; - return Status::EmptyResult; - } - - json_result.values["status_message"] = "Found matchings"; - return Status::Ok; - } - - private: - std::string descriptor_string; - DataFacadeT *facade; - int max_locations_map_matching; - ClassifierT classifier; -}; - -#endif // MATCH_HPP diff --git a/plugins/nearest.hpp b/plugins/nearest.hpp deleted file mode 100644 index 245981981..000000000 --- a/plugins/nearest.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef NEAREST_HPP -#define NEAREST_HPP - -#include "plugin_base.hpp" - -#include "../data_structures/phantom_node.hpp" -#include "../util/integer_range.hpp" -#include "../util/json_renderer.hpp" - -#include - -#include - -/* - * This Plugin locates the nearest point on a street in the road network for a given coordinate. - */ - -template class NearestPlugin final : public BasePlugin -{ - public: - explicit NearestPlugin(DataFacadeT *facade) : facade(facade), descriptor_string("nearest") {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) override final - { - // check number of parameters - if (route_parameters.coordinates.empty() || - !route_parameters.coordinates.front().is_valid()) - { - return Status::Error; - } - - const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && - route_parameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinates"; - return Status::Error; - } - - auto number_of_results = static_cast(route_parameters.num_results); - const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; - const int range = - input_bearings.size() > 0 - ? (input_bearings.front().second ? *input_bearings.front().second : 10) - : 180; - auto phantom_node_vector = facade->NearestPhantomNodes(route_parameters.coordinates.front(), - number_of_results, bearing, range); - - if (phantom_node_vector.empty()) - { - json_result.values["status_message"] = - std::string("Could not find a matching segments for coordinate"); - return Status::NoSegment; - } - else - { - json_result.values["status_message"] = "Found nearest edge"; - if (number_of_results > 1) - { - osrm::json::Array results; - - auto vector_length = phantom_node_vector.size(); - for (const auto i : - osrm::irange(0, std::min(number_of_results, vector_length))) - { - const auto &node = phantom_node_vector[i].phantom_node; - osrm::json::Array json_coordinate; - osrm::json::Object result; - json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); - json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); - result.values["mapped coordinate"] = json_coordinate; - result.values["name"] = facade->get_name_for_id(node.name_id); - results.values.push_back(result); - } - json_result.values["results"] = results; - } - else - { - osrm::json::Array json_coordinate; - json_coordinate.values.push_back( - phantom_node_vector.front().phantom_node.location.lat / COORDINATE_PRECISION); - json_coordinate.values.push_back( - phantom_node_vector.front().phantom_node.location.lon / COORDINATE_PRECISION); - json_result.values["mapped_coordinate"] = json_coordinate; - json_result.values["name"] = - facade->get_name_for_id(phantom_node_vector.front().phantom_node.name_id); - } - } - return Status::Ok; - } - - private: - DataFacadeT *facade; - std::string descriptor_string; -}; - -#endif /* NEAREST_HPP */ diff --git a/plugins/plugin_base.hpp b/plugins/plugin_base.hpp deleted file mode 100644 index a05785a37..000000000 --- a/plugins/plugin_base.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef BASE_PLUGIN_HPP -#define BASE_PLUGIN_HPP - -#include "../data_structures/phantom_node.hpp" - -#include -#include -#include - -#include -#include -#include - -class BasePlugin -{ - public: - enum class Status : int - { - Ok = 200, - EmptyResult = 207, - NoSegment = 208, - Error = 400 - }; - - BasePlugin() {} - // Maybe someone can explain the pure virtual destructor thing to me (dennis) - virtual ~BasePlugin() {} - virtual const std::string GetDescriptor() const = 0; - virtual Status HandleRequest(const RouteParameters &, osrm::json::Object &) = 0; - virtual bool check_all_coordinates(const std::vector &coordinates, - const unsigned min = 2) const final - { - if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates), - [](const FixedPointCoordinate &coordinate) - { - return !coordinate.is_valid(); - })) - { - return false; - } - return true; - } - - // Decides whether to use the phantom node from a big or small component if both are found. - // Returns true if all phantom nodes are in the same component after snapping. - std::vector snapPhantomNodes( - const std::vector> &phantom_node_pair_list) const - { - const auto check_component_id_is_tiny = - [](const std::pair &phantom_pair) - { - return phantom_pair.first.component.is_tiny; - }; - - // are all phantoms from a tiny cc? - const auto check_all_in_same_component = - [](const std::vector> &nodes) - { - const auto component_id = nodes.front().first.component.id; - - return std::all_of(std::begin(nodes), std::end(nodes), - [component_id](const PhantomNodePair &phantom_pair) - { - return component_id == phantom_pair.first.component.id; - }); - }; - - const auto fallback_to_big_component = - [](const std::pair &phantom_pair) - { - if (phantom_pair.first.component.is_tiny && phantom_pair.second.is_valid() && - !phantom_pair.second.component.is_tiny) - { - return phantom_pair.second; - } - return phantom_pair.first; - }; - - const auto use_closed_phantom = [](const std::pair &phantom_pair) - { - return phantom_pair.first; - }; - - const bool every_phantom_is_in_tiny_cc = - std::all_of(std::begin(phantom_node_pair_list), std::end(phantom_node_pair_list), - check_component_id_is_tiny); - auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list); - - std::vector snapped_phantoms; - snapped_phantoms.reserve(phantom_node_pair_list.size()); - - // The only case we don't snap to the big component if all phantoms are in the same small - // component - if (every_phantom_is_in_tiny_cc && all_in_same_component) - { - std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(), - std::back_inserter(snapped_phantoms), use_closed_phantom); - } - else - { - std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(), - std::back_inserter(snapped_phantoms), fallback_to_big_component); - } - - return snapped_phantoms; - } -}; - -#endif /* BASE_PLUGIN_HPP */ diff --git a/plugins/timestamp.hpp b/plugins/timestamp.hpp deleted file mode 100644 index 899ef4670..000000000 --- a/plugins/timestamp.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef TIMESTAMP_PLUGIN_H -#define TIMESTAMP_PLUGIN_H - -#include "plugin_base.hpp" - -#include "../util/json_renderer.hpp" - -#include - -#include - -template class TimestampPlugin final : public BasePlugin -{ - public: - explicit TimestampPlugin(const DataFacadeT *facade) - : facade(facade), descriptor_string("timestamp") - { - } - const std::string GetDescriptor() const override final { return descriptor_string; } - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) override final - { - (void)route_parameters; // unused - - const std::string timestamp = facade->GetTimestamp(); - json_result.values["timestamp"] = timestamp; - return Status::Ok; - } - - private: - const DataFacadeT *facade; - std::string descriptor_string; -}; - -#endif /* TIMESTAMP_PLUGIN_H */ diff --git a/plugins/trip.hpp b/plugins/trip.hpp deleted file mode 100644 index b1ba55b88..000000000 --- a/plugins/trip.hpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef TRIP_HPP -#define TRIP_HPP - -#include "plugin_base.hpp" - -#include "../algorithms/object_encoder.hpp" -#include "../algorithms/tarjan_scc.hpp" -#include "../algorithms/trip_nearest_neighbour.hpp" -#include "../algorithms/trip_farthest_insertion.hpp" -#include "../algorithms/trip_brute_force.hpp" -#include "../data_structures/search_engine.hpp" -#include "../data_structures/matrix_graph_wrapper.hpp" // wrapper to use tarjan - // scc on dist table -#include "../descriptors/descriptor_base.hpp" // to make json output -#include "../descriptors/json_descriptor.hpp" // to make json output -#include "../util/make_unique.hpp" -#include "../util/timing_util.hpp" // to time runtime -//#include "../util/simple_logger.hpp" // for logging output -#include "../util/dist_table_wrapper.hpp" // to access the dist - // table more easily - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -template class RoundTripPlugin final : public BasePlugin -{ - private: - std::string descriptor_string; - DataFacadeT *facade; - std::unique_ptr> search_engine_ptr; - int max_locations_trip; - - public: - explicit RoundTripPlugin(DataFacadeT *facade, int max_locations_trip) - : descriptor_string("trip"), facade(facade), max_locations_trip(max_locations_trip) - { - search_engine_ptr = osrm::make_unique>(facade); - } - - const std::string GetDescriptor() const override final { return descriptor_string; } - - std::vector GetPhantomNodes(const RouteParameters &route_parameters) - { - const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum()); - const auto &input_bearings = route_parameters.bearings; - - std::vector phantom_node_list; - phantom_node_list.reserve(route_parameters.coordinates.size()); - - // find phantom nodes for all input coords - for (const auto i : osrm::irange(0, route_parameters.coordinates.size())) - { - // if client hints are helpful, encode hints - if (checksum_OK && i < route_parameters.hints.size() && - !route_parameters.hints[i].empty()) - { - PhantomNode current_phantom_node; - ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node); - if (current_phantom_node.is_valid(facade->GetNumberOfNodes())) - { - phantom_node_list.push_back(std::move(current_phantom_node)); - continue; - } - } - const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0; - const int range = input_bearings.size() > 0 - ? (input_bearings[i].second ? *input_bearings[i].second : 10) - : 180; - auto results = facade->NearestPhantomNodes(route_parameters.coordinates[i], 1, bearing, range); - if (results.empty()) - { - break; - } - phantom_node_list.push_back(std::move(results.front().phantom_node)); - BOOST_ASSERT(phantom_node_list.back().is_valid(facade->GetNumberOfNodes())); - } - - return phantom_node_list; - } - - // Object to hold all strongly connected components (scc) of a graph - // to access all graphs with component ID i, get the iterators by: - // auto start = std::begin(scc_component.component) + scc_component.range[i]; - // auto end = std::begin(scc_component.component) + scc_component.range[i+1]; - struct SCC_Component - { - // in_component: all NodeIDs sorted by component ID - // in_range: index where a new component starts - // - // example: NodeID 0, 1, 2, 4, 5 are in component 0 - // NodeID 3, 6, 7, 8 are in component 1 - // => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8] - // => in_range = [0, 5] - SCC_Component(std::vector in_component_nodes, std::vector in_range) - : component(std::move(in_component_nodes)), range(std::move(in_range)) - { - BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component"); - BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) == component.size(), - "scc component ranges are out of bound"); - BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) == 0, - "invalid scc component range"); - BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)), - "invalid component ranges"); - }; - - std::size_t GetNumberOfComponents() const - { - BOOST_ASSERT_MSG(range.size() > 0, "there's no range"); - return range.size() - 1; - } - - const std::vector component; - std::vector range; - }; - - // takes the number of locations and its distance matrix, - // identifies and splits the graph in its strongly connected components (scc) - // and returns an SCC_Component - SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations, - const DistTableWrapper &result_table) - { - - if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) == - std::end(result_table)) - { - // whole graph is one scc - std::vector location_ids(number_of_locations); - std::iota(std::begin(location_ids), std::end(location_ids), 0); - std::vector range = {0, location_ids.size()}; - return SCC_Component(std::move(location_ids), std::move(range)); - } - - // Run TarjanSCC - auto wrapper = std::make_shared>(result_table.GetTable(), - number_of_locations); - auto scc = TarjanSCC>(wrapper); - scc.run(); - - const auto number_of_components = scc.get_number_of_components(); - - std::vector range_insertion; - std::vector range; - range_insertion.reserve(number_of_components); - range.reserve(number_of_components); - - std::vector components(number_of_locations, 0); - - std::size_t prefix = 0; - for (std::size_t j = 0; j < number_of_components; ++j) - { - range_insertion.push_back(prefix); - range.push_back(prefix); - prefix += scc.get_component_size(j); - } - // senitel - range.push_back(components.size()); - - for (std::size_t i = 0; i < number_of_locations; ++i) - { - components[range_insertion[scc.get_component_id(i)]] = i; - ++range_insertion[scc.get_component_id(i)]; - } - - return SCC_Component(std::move(components), std::move(range)); - } - - void SetLocPermutationOutput(const std::vector &permutation, - osrm::json::Object &json_result) - { - osrm::json::Array json_permutation; - json_permutation.values.insert(std::end(json_permutation.values), std::begin(permutation), - std::end(permutation)); - json_result.values["permutation"] = json_permutation; - } - - InternalRouteResult ComputeRoute(const std::vector &phantom_node_list, - const RouteParameters &route_parameters, - const std::vector &trip) - { - InternalRouteResult min_route; - // given he final trip, compute total distance and return the route and location permutation - PhantomNodes viapoint; - const auto start = std::begin(trip); - const auto end = std::end(trip); - // computes a roundtrip from the nodes in trip - for (auto it = start; it != end; ++it) - { - const auto from_node = *it; - // if from_node is the last node, compute the route from the last to the first location - const auto to_node = std::next(it) != end ? *std::next(it) : *start; - - viapoint = PhantomNodes{phantom_node_list[from_node], phantom_node_list[to_node]}; - min_route.segment_end_coordinates.emplace_back(viapoint); - } - BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); - - std::vector uturns(trip.size() + 1); - BOOST_ASSERT(route_parameters.uturns.size() > 0); - std::transform(trip.begin(), trip.end(), uturns.begin(), - [&route_parameters](const NodeID idx) - { - return route_parameters.uturns[idx]; - }); - BOOST_ASSERT(uturns.size() > 0); - uturns.back() = route_parameters.uturns[trip.front()]; - - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, uturns, min_route); - - BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route"); - return min_route; - } - - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) override final - { - if (max_locations_trip > 0 && - (static_cast(route_parameters.coordinates.size()) > max_locations_trip)) - { - json_result.values["status_message"] = - "Number of entries " + std::to_string(route_parameters.coordinates.size()) + - " is higher than current maximum (" + std::to_string(max_locations_trip) + ")"; - return Status::Error; - } - - // check if all inputs are coordinates - if (!check_all_coordinates(route_parameters.coordinates)) - { - json_result.values["status_message"] = "Invalid coordinates"; - return Status::Error; - } - - const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && - route_parameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinates"; - return Status::Error; - } - - // get phantom nodes - auto phantom_node_list = GetPhantomNodes(route_parameters); - if (phantom_node_list.size() != route_parameters.coordinates.size()) - { - BOOST_ASSERT(phantom_node_list.size() < route_parameters.coordinates.size()); - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + - std::to_string(phantom_node_list.size()); - return Status::NoSegment; - } - - const auto number_of_locations = phantom_node_list.size(); - - // compute the distance table of all phantom nodes - const auto result_table = DistTableWrapper( - *search_engine_ptr->distance_table(phantom_node_list, phantom_node_list), - number_of_locations); - - if (result_table.size() == 0) - { - return Status::Error; - } - - const constexpr std::size_t BF_MAX_FEASABLE = 10; - BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations, - "Distance Table has wrong size"); - - // get scc components - SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table); - - using NodeIDIterator = typename std::vector::const_iterator; - - std::vector> route_result; - route_result.reserve(scc.GetNumberOfComponents()); - TIMER_START(TRIP_TIMER); - // run Trip computation for every SCC - for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k) - { - const auto component_size = scc.range[k + 1] - scc.range[k]; - - BOOST_ASSERT_MSG(component_size > 0, "invalid component size"); - - std::vector scc_route; - NodeIDIterator start = std::begin(scc.component) + scc.range[k]; - NodeIDIterator end = std::begin(scc.component) + scc.range[k + 1]; - - if (component_size > 1) - { - - if (component_size < BF_MAX_FEASABLE) - { - scc_route = - osrm::trip::BruteForceTrip(start, end, number_of_locations, result_table); - } - else - { - scc_route = osrm::trip::FarthestInsertionTrip(start, end, number_of_locations, - result_table); - } - - // use this output if debugging of route is needed: - // SimpleLogger().Write() << "Route #" << k << ": " << [&scc_route]() - // { - // std::string s = ""; - // for (auto x : scc_route) - // { - // s += std::to_string(x) + " "; - // } - // return s; - // }(); - - } - else - { - scc_route = std::vector(start, end); - } - - route_result.push_back(std::move(scc_route)); - } - - // compute all round trip routes - std::vector comp_route; - comp_route.reserve(route_result.size()); - for (auto &elem : route_result) - { - comp_route.push_back(ComputeRoute(phantom_node_list, route_parameters, elem)); - } - - TIMER_STOP(TRIP_TIMER); - - // prepare JSON output - // create a json object for every trip - osrm::json::Array trip; - for (std::size_t i = 0; i < route_result.size(); ++i) - { - std::unique_ptr> descriptor = - osrm::make_unique>(facade); - descriptor->SetConfig(route_parameters); - - osrm::json::Object scc_trip; - - // set permutation output - SetLocPermutationOutput(route_result[i], scc_trip); - // set viaroute output - descriptor->Run(comp_route[i], scc_trip); - - trip.values.push_back(std::move(scc_trip)); - } - - - if (trip.values.empty()) - { - json_result.values["status_message"] = "Cannot find trips"; - return Status::EmptyResult; - } - - json_result.values["trips"] = std::move(trip); - json_result.values["status_message"] = "Found trips"; - return Status::Ok; - } -}; - -#endif // TRIP_HPP diff --git a/plugins/viaroute.hpp b/plugins/viaroute.hpp deleted file mode 100644 index 21e871444..000000000 --- a/plugins/viaroute.hpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef VIA_ROUTE_HPP -#define VIA_ROUTE_HPP - -#include "plugin_base.hpp" - -#include "../algorithms/object_encoder.hpp" -#include "../data_structures/search_engine.hpp" -#include "../descriptors/descriptor_base.hpp" -#include "../descriptors/gpx_descriptor.hpp" -#include "../descriptors/json_descriptor.hpp" -#include "../util/integer_range.hpp" -#include "../util/json_renderer.hpp" -#include "../util/make_unique.hpp" -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" - -#include - -#include - -#include -#include -#include -#include - -template class ViaRoutePlugin final : public BasePlugin -{ - private: - DescriptorTable descriptor_table; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - DataFacadeT *facade; - int max_locations_viaroute; - - public: - explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute) - : descriptor_string("viaroute"), facade(facade), - max_locations_viaroute(max_locations_viaroute) - { - search_engine_ptr = osrm::make_unique>(facade); - - descriptor_table.emplace("json", 0); - descriptor_table.emplace("gpx", 1); - // descriptor_table.emplace("geojson", 2); - } - - virtual ~ViaRoutePlugin() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &route_parameters, - osrm::json::Object &json_result) override final - { - if (max_locations_viaroute > 0 && - (static_cast(route_parameters.coordinates.size()) > max_locations_viaroute)) - { - json_result.values["status_message"] = - "Number of entries " + std::to_string(route_parameters.coordinates.size()) + - " is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")"; - return Status::Error; - } - - if (!check_all_coordinates(route_parameters.coordinates)) - { - json_result.values["status_message"] = "Invalid coordinates"; - return Status::Error; - } - - const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && - route_parameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinate"; - return Status::Error; - } - - std::vector phantom_node_pair_list(route_parameters.coordinates.size()); - const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum()); - - for (const auto i : osrm::irange(0, route_parameters.coordinates.size())) - { - if (checksum_OK && i < route_parameters.hints.size() && - !route_parameters.hints[i].empty()) - { - ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], - phantom_node_pair_list[i].first); - if (phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes())) - { - continue; - } - } - const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0; - const int range = input_bearings.size() > 0 - ? (input_bearings[i].second ? *input_bearings[i].second : 10) - : 180; - phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent( - route_parameters.coordinates[i], bearing, range); - // we didn't found a fitting node, return error - if (!phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes())) - { - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + - std::to_string(i); - return Status::NoSegment; - } - BOOST_ASSERT(phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes())); - BOOST_ASSERT(phantom_node_pair_list[i].second.is_valid(facade->GetNumberOfNodes())); - } - - auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list); - - InternalRouteResult raw_route; - auto build_phantom_pairs = [&raw_route](const PhantomNode &first_node, - const PhantomNode &second_node) - { - raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node}); - }; - osrm::for_each_pair(snapped_phantoms, build_phantom_pairs); - - if (1 == raw_route.segment_end_coordinates.size()) - { - if (route_parameters.alternate_route) - { - search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), - raw_route); - } - else - { - search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates, - route_parameters.uturns, raw_route); - } - } - else - { - search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, - route_parameters.uturns, raw_route); - } - - // we can only know this after the fact, different SCC ids still - // allow for connection in one direction. - if (raw_route.shortest_path_length == INVALID_EDGE_WEIGHT) - { - auto first_component_id = snapped_phantoms.front().component.id; - auto not_in_same_component = - std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(), - [first_component_id](const PhantomNode &node) - { - return node.component.id != first_component_id; - }); - - if (not_in_same_component) - { - json_result.values["status_message"] = "Impossible route between points"; - return Status::EmptyResult; - } - else - { - json_result.values["status_message"] = "No route found between points"; - return Status::Error; - } - } - else - { - std::unique_ptr> descriptor; - switch (descriptor_table.get_id(route_parameters.output_format)) - { - case 1: - descriptor = osrm::make_unique>(facade); - break; - // case 2: - // descriptor = osrm::make_unique>(); - // break; - default: - descriptor = osrm::make_unique>(facade); - break; - } - - descriptor->SetConfig(route_parameters); - descriptor->Run(raw_route, json_result); - json_result.values["status_message"] = "Found route between points"; - } - - return Status::Ok; - } -}; - -#endif // VIA_ROUTE_HPP diff --git a/prepare.cpp b/prepare.cpp deleted file mode 100644 index aaf937690..000000000 --- a/prepare.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "contractor/processing_chain.hpp" -#include "contractor/contractor_options.hpp" -#include "util/simple_logger.hpp" - -#include - -#include - -#include -#include -#include -#include - -int main(int argc, char *argv[]) try -{ - LogPolicy::GetInstance().Unmute(); - ContractorConfig contractor_config; - - const return_code result = ContractorOptions::ParseArguments(argc, argv, contractor_config); - - if (return_code::fail == result) - { - return EXIT_FAILURE; - } - - if (return_code::exit == result) - { - return EXIT_SUCCESS; - } - - ContractorOptions::GenerateOutputFilesNames(contractor_config); - - if (1 > contractor_config.requested_num_threads) - { - SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; - return EXIT_FAILURE; - } - - const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); - - if (recommended_num_threads != contractor_config.requested_num_threads) - { - SimpleLogger().Write(logWARNING) << "The recommended number of threads is " - << recommended_num_threads - << "! This setting may have performance side-effects."; - } - - if (!boost::filesystem::is_regular_file(contractor_config.osrm_input_path)) - { - SimpleLogger().Write(logWARNING) - << "Input file " << contractor_config.osrm_input_path.string() << " not found!"; - return EXIT_FAILURE; - } - - if (!boost::filesystem::is_regular_file(contractor_config.profile_path)) - { - SimpleLogger().Write(logWARNING) << "Profile " << contractor_config.profile_path.string() - << " not found!"; - return EXIT_FAILURE; - } - - SimpleLogger().Write() << "Input file: " - << contractor_config.osrm_input_path.filename().string(); - SimpleLogger().Write() << "Profile: " << contractor_config.profile_path.filename().string(); - SimpleLogger().Write() << "Threads: " << contractor_config.requested_num_threads; - - tbb::task_scheduler_init init(contractor_config.requested_num_threads); - - return Prepare(contractor_config).Run(); -} -catch (const std::bad_alloc &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - SimpleLogger().Write(logWARNING) - << "Please provide more memory or consider using a larger swapfile"; - return EXIT_FAILURE; -} -catch (const std::exception &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - return EXIT_FAILURE; -} diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index 1ebbffddf..644a254d1 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -6,9 +6,9 @@ local limit = require("lib/maxspeed").limit -- Begin of globals barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true, ["block"] = true } access_tag_whitelist = { ["yes"] = true, ["permissive"] = true, ["designated"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true } +access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["delivery"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } -access_tags_hierachy = { "bicycle", "vehicle", "access" } +access_tags_hierarchy = { "bicycle", "vehicle", "access" } cycleway_tags = {["track"]=true,["lane"]=true,["opposite"]=true,["opposite_lane"]=true,["opposite_track"]=true,["share_busway"]=true,["sharrow"]=true,["shared"]=true } service_tag_restricted = { ["parking_aisle"] = true } restriction_exception_tags = { "bicycle", "vehicle", "access" } @@ -91,9 +91,10 @@ surface_speeds = { } -- these need to be global because they are accesed externaly -traffic_signal_penalty = 2 -use_turn_restrictions = false -u_turn_penalty = 20 +properties.traffic_signal_penalty = 2 +properties.use_turn_restrictions = false +properties.u_turn_penalty = 20 +properties.continue_straight_at_waypoint = false local obey_oneway = true local ignore_areas = true @@ -105,13 +106,6 @@ local safety_penalty = 1.0 local use_public_transport = true local fallback_names = true ---modes -local mode_normal = 1 -local mode_pushing = 2 -local mode_ferry = 3 -local mode_train = 4 -local mode_movable_bridge = 5 - local function parse_maxspeed(source) if not source then return 0 @@ -137,7 +131,7 @@ function node_function (node, result) local highway = node:get_value_by_key("highway") local is_crossing = highway and highway == "crossing" - local access = find_access_tag(node, access_tags_hierachy) + local access = find_access_tag(node, access_tags_hierarchy) if access and access ~= "" then -- access restrictions on crossing nodes are not relevant for -- the traffic on the road @@ -186,11 +180,14 @@ function way_function (way, result) end -- access - local access = find_access_tag(way, access_tags_hierachy) + local access = find_access_tag(way, access_tags_hierarchy) if access and access_tag_blacklist[access] then return end + result.forward_mode = mode.cycling + result.backward_mode = mode.cycling + -- other tags local name = way:get_value_by_key("name") local ref = way:get_value_by_key("ref") @@ -237,14 +234,12 @@ function way_function (way, result) if duration and durationIsValid(duration) then result.duration = math.max( parseDuration(duration), 1 ) end - result.forward_mode = mode_movable_bridge - result.backward_mode = mode_movable_bridge result.forward_speed = bridge_speed result.backward_speed = bridge_speed elseif route_speeds[route] then -- ferries (doesn't cover routes tagged using relations) - result.forward_mode = mode_ferry - result.backward_mode = mode_ferry + result.forward_mode = mode.ferry + result.backward_mode = mode.ferry result.ignore_in_grid = true if duration and durationIsValid(duration) then result.duration = math.max( 1, parseDuration(duration) ) @@ -262,8 +257,8 @@ function way_function (way, result) result.forward_speed = platform_speeds[public_transport] result.backward_speed = platform_speeds[public_transport] elseif use_public_transport and railway and railway_speeds[railway] then - result.forward_mode = mode_train - result.backward_mode = mode_train + result.forward_mode = mode.train + result.backward_mode = mode.train -- railways if access and access_tag_whitelist[access] then result.forward_speed = railway_speeds[railway] @@ -293,27 +288,27 @@ function way_function (way, result) -- pedestrian-only ways and areas result.forward_speed = pedestrian_speeds[highway] result.backward_speed = pedestrian_speeds[highway] - result.forward_mode = mode_pushing - result.backward_mode = mode_pushing + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike elseif man_made and man_made_speeds[man_made] then -- man made structures result.forward_speed = man_made_speeds[man_made] result.backward_speed = man_made_speeds[man_made] - result.forward_mode = mode_pushing - result.backward_mode = mode_pushing + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike elseif foot == 'yes' then result.forward_speed = walking_speed result.backward_speed = walking_speed - result.forward_mode = mode_pushing - result.backward_mode = mode_pushing + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike elseif foot_forward == 'yes' then result.forward_speed = walking_speed - result.forward_mode = mode_pushing - result.backward_mode = 0 + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.inaccessible elseif foot_backward == 'yes' then result.forward_speed = walking_speed - result.forward_mode = 0 - result.backward_mode = mode_pushing + result.forward_mode = mode.inaccessible + result.backward_mode = mode.pushing_bike end end end @@ -325,48 +320,48 @@ function way_function (way, result) end if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then - result.backward_mode = 0 + result.backward_mode = mode.inaccessible elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then -- prevent implied oneway elseif onewayClass == "-1" then - result.forward_mode = 0 + result.forward_mode = mode.inaccessible elseif oneway == "no" or oneway == "0" or oneway == "false" then -- prevent implied oneway elseif cycleway and string.find(cycleway, "opposite") == 1 then if impliedOneway then - result.forward_mode = 0 - result.backward_mode = mode_normal + result.forward_mode = mode.inaccessible + result.backward_mode = mode.cycling result.backward_speed = bicycle_speeds["cycleway"] end elseif cycleway_left and cycleway_tags[cycleway_left] and cycleway_right and cycleway_tags[cycleway_right] then -- prevent implied elseif cycleway_left and cycleway_tags[cycleway_left] then if impliedOneway then - result.forward_mode = 0 - result.backward_mode = mode_normal + result.forward_mode = mode.inaccessible + result.backward_mode = mode.cycling result.backward_speed = bicycle_speeds["cycleway"] end elseif cycleway_right and cycleway_tags[cycleway_right] then if impliedOneway then - result.forward_mode = mode_normal + result.forward_mode = mode.cycling result.backward_speed = bicycle_speeds["cycleway"] - result.backward_mode = 0 + result.backward_mode = mode.inaccessible end elseif oneway == "-1" then - result.forward_mode = 0 + result.forward_mode = mode.inaccessible elseif oneway == "yes" or oneway == "1" or oneway == "true" or impliedOneway then - result.backward_mode = 0 + result.backward_mode = mode.inaccessible end -- pushing bikes if bicycle_speeds[highway] or pedestrian_speeds[highway] then if foot ~= "no" and junction ~= "roundabout" then - if result.backward_mode == 0 then + if result.backward_mode == mode.inaccessible then result.backward_speed = walking_speed - result.backward_mode = mode_pushing - elseif result.forward_mode == 0 then + result.backward_mode = mode.pushing_bike + elseif result.forward_mode == mode.inaccessible then result.forward_speed = walking_speed - result.forward_mode = mode_pushing + result.forward_mode = mode.pushing_bike end end end @@ -382,8 +377,8 @@ function way_function (way, result) -- dismount if bicycle == "dismount" then - result.forward_mode = mode_pushing - result.backward_mode = mode_pushing + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike result.forward_speed = walking_speed result.backward_speed = walking_speed end diff --git a/profiles/car.lua b/profiles/car.lua index 959558ff3..ffe62ca57 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -5,10 +5,9 @@ local find_access_tag = require("lib/access").find_access_tag -- Begin of globals barrier_whitelist = { ["cattle_grid"] = true, ["border_control"] = true, ["checkpoint"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["lift_gate"] = true, ["no"] = true, ["entrance"] = true } access_tag_whitelist = { ["yes"] = true, ["motorcar"] = true, ["motor_vehicle"] = true, ["vehicle"] = true, ["permissive"] = true, ["designated"] = true, ["destination"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true } +access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true, ["delivery"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } -access_tags = { "motorcar", "motor_vehicle", "vehicle" } -access_tags_hierachy = { "motorcar", "motor_vehicle", "vehicle", "access" } +access_tags_hierarchy = { "motorcar", "motor_vehicle", "vehicle", "access" } service_tag_restricted = { ["parking_aisle"] = true } restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" } @@ -125,21 +124,25 @@ maxspeed_table = { ["gb:motorway"] = (70*1609)/1000, ["uk:nsl_single"] = (60*1609)/1000, ["uk:nsl_dual"] = (70*1609)/1000, - ["uk:motorway"] = (70*1609)/1000 + ["uk:motorway"] = (70*1609)/1000, + ["none"] = 140 } --- these need to be global because they are accesed externaly -u_turn_penalty = 20 -traffic_signal_penalty = 2 -use_turn_restrictions = true +-- set profile properties +properties.u_turn_penalty = 20 +properties.traffic_signal_penalty = 2 +properties.use_turn_restrictions = true +properties.continue_straight_at_waypoint = true -local turn_penalty = 10 +local side_road_speed_multiplier = 0.8 + +local turn_penalty = 10 -- Note: this biases right-side driving. Should be -- inverted for left-driving countries. -local turn_bias = 1.2 +local turn_bias = 1.2 -local obey_oneway = true -local ignore_areas = true +local obey_oneway = true +local ignore_areas = true local abs = math.abs local min = math.min @@ -147,11 +150,6 @@ local max = math.max local speed_reduction = 0.8 ---modes -local mode_normal = 1 -local mode_ferry = 2 -local mode_movable_bridge = 3 - function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do vector:Add(v) @@ -184,7 +182,7 @@ end function node_function (node, result) -- parse access and barrier tags - local access = find_access_tag(node, access_tags_hierachy) + local access = find_access_tag(node, access_tags_hierarchy) if access and access ~= "" then if access_tag_blacklist[access] then result.barrier = true @@ -241,11 +239,14 @@ function way_function (way, result) end -- Check if we are allowed to access the way - local access = find_access_tag(way, access_tags_hierachy) + local access = find_access_tag(way, access_tags_hierarchy) if access_tag_blacklist[access] then return end + result.forward_mode = mode.driving + result.backward_mode = mode.driving + -- handling ferries and piers local route_speed = speed_profile[route] if (route_speed and route_speed > 0) then @@ -254,8 +255,8 @@ function way_function (way, result) if duration and durationIsValid(duration) then result.duration = max( parseDuration(duration), 1 ) end - result.forward_mode = mode_ferry - result.backward_mode = mode_ferry + result.forward_mode = mode.ferry + result.backward_mode = mode.ferry result.forward_speed = route_speed result.backward_speed = route_speed end @@ -269,8 +270,6 @@ function way_function (way, result) if duration and durationIsValid(duration) then result.duration = max( parseDuration(duration), 1 ) end - result.forward_mode = mode_movable_bridge - result.backward_mode = mode_movable_bridge result.forward_speed = bridge_speed result.backward_speed = bridge_speed end @@ -311,6 +310,14 @@ function way_function (way, result) return end + -- reduce speed on special side roads + local sideway = way:get_value_by_key("side_road") + if "yes" == sideway or + "rotary" == sideway then + result.forward_speed = result.forward_speed * side_road_speed_multiplier + result.backward_speed = result.backward_speed * side_road_speed_multiplier + end + -- reduce speed on bad surfaces local surface = way:get_value_by_key("surface") local tracktype = way:get_value_by_key("tracktype") @@ -368,14 +375,14 @@ function way_function (way, result) -- Set direction according to tags on way if obey_oneway then if oneway == "-1" then - result.forward_mode = 0 + result.forward_mode = mode.inaccessible elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" or (highway == "motorway_link" and oneway ~="no") or (highway == "motorway" and oneway ~= "no") then - result.backward_mode = 0 + result.backward_mode = mode.inaccessible end end @@ -383,7 +390,7 @@ function way_function (way, result) local maxspeed_forward = parse_maxspeed(way:get_value_by_key("maxspeed:forward")) local maxspeed_backward = parse_maxspeed(way:get_value_by_key("maxspeed:backward")) if maxspeed_forward and maxspeed_forward > 0 then - if 0 ~= result.forward_mode and 0 ~= result.backward_mode then + if mode.inaccessible ~= result.forward_mode and mode.inaccessible ~= result.backward_mode then result.backward_speed = result.forward_speed end result.forward_speed = maxspeed_forward @@ -398,15 +405,15 @@ function way_function (way, result) local advisory_backward = parse_maxspeed(way:get_value_by_key("maxspeed:advisory:backward")) -- apply bi-directional advisory speed first if advisory_speed and advisory_speed > 0 then - if 0 ~= result.forward_mode then + if mode.inaccessible ~= result.forward_mode then result.forward_speed = advisory_speed end - if 0 ~= result.backward_mode then + if mode.inaccessible ~= result.backward_mode then result.backward_speed = advisory_speed end end if advisory_forward and advisory_forward > 0 then - if 0 ~= result.forward_mode and 0 ~= result.backward_mode then + if mode.inaccessible ~= result.forward_mode and mode.inaccessible ~= result.backward_mode then result.backward_speed = result.forward_speed end result.forward_speed = advisory_forward @@ -429,7 +436,7 @@ function way_function (way, result) end end - local is_bidirectional = result.forward_mode ~= 0 and result.backward_mode ~= 0 + local is_bidirectional = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible -- scale speeds to get better avg driving times if result.forward_speed > 0 then @@ -451,7 +458,7 @@ function way_function (way, result) end -- only allow this road as start point if it not a ferry - result.is_startpoint = result.forward_mode == mode_normal or result.backward_mode == mode_normal + result.is_startpoint = result.forward_mode == mode.driving or result.backward_mode == mode.driving end function turn_function (angle) diff --git a/profiles/foot.lua b/profiles/foot.lua index 61751e356..04f16c2a2 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -5,9 +5,9 @@ local find_access_tag = require("lib/access").find_access_tag -- Begin of globals barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true, ["block"] = true} access_tag_whitelist = { ["yes"] = true, ["foot"] = true, ["permissive"] = true, ["designated"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true } +access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["delivery"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } -access_tags_hierachy = { "foot", "access" } +access_tags_hierarchy = { "foot", "access" } service_tag_restricted = { ["parking_aisle"] = true } ignore_in_grid = { ["ferry"] = true } restriction_exception_tags = { "foot" } @@ -64,14 +64,12 @@ leisure_speeds = { ["track"] = walking_speed } -traffic_signal_penalty = 2 -u_turn_penalty = 2 -use_turn_restrictions = false -local fallback_names = true +properties.traffic_signal_penalty = 2 +properties.u_turn_penalty = 2 +properties.use_turn_restrictions = false +properties.continue_straight_at_waypoint = false ---modes -local mode_normal = 1 -local mode_ferry = 2 +local fallback_names = true function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do @@ -81,7 +79,7 @@ end function node_function (node, result) local barrier = node:get_value_by_key("barrier") - local access = find_access_tag(node, access_tags_hierachy) + local access = find_access_tag(node, access_tags_hierarchy) local traffic_signal = node:get_value_by_key("highway") -- flag node if it carries a traffic light @@ -133,11 +131,14 @@ function way_function (way, result) end -- access - local access = find_access_tag(way, access_tags_hierachy) + local access = find_access_tag(way, access_tags_hierarchy) if access_tag_blacklist[access] then return end + result.forward_mode = mode.walking + result.backward_mode = mode.walking + local name = way:get_value_by_key("name") local ref = way:get_value_by_key("ref") local junction = way:get_value_by_key("junction") @@ -175,8 +176,8 @@ function way_function (way, result) result.forward_speed = route_speeds[route] result.backward_speed = route_speeds[route] end - result.forward_mode = mode_ferry - result.backward_mode = mode_ferry + result.forward_mode = mode.ferry + result.backward_mode = mode.ferry elseif railway and platform_speeds[railway] then -- railway platforms (old tagging scheme) result.forward_speed = platform_speeds[railway] @@ -205,11 +206,11 @@ function way_function (way, result) -- oneway if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then - result.backward_mode = 0 + result.backward_mode = mode.inaccessible elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then -- nothing to do elseif onewayClass == "-1" then - result.forward_mode = 0 + result.forward_mode = mode.inaccessible end -- surfaces diff --git a/profiles/lib/access.lua b/profiles/lib/access.lua index 3fd4ff605..3433c6568 100644 --- a/profiles/lib/access.lua +++ b/profiles/lib/access.lua @@ -2,8 +2,8 @@ local ipairs = ipairs local Access = {} -function Access.find_access_tag(source,access_tags_hierachy) - for i,v in ipairs(access_tags_hierachy) do +function Access.find_access_tag(source,access_tags_hierarchy) + for i,v in ipairs(access_tags_hierarchy) do local tag = source:get_value_by_key(v) if tag and tag ~= '' then return tag diff --git a/profiles/rasterbot.lua b/profiles/rasterbot.lua index 847384ddf..03ff8f269 100644 --- a/profiles/rasterbot.lua +++ b/profiles/rasterbot.lua @@ -13,6 +13,9 @@ function way_function (way, result) result.name = name end + result.forward_mode = mode.cycling + result.backward_mode = mode.cycling + result.forward_speed = 15 result.backward_speed = 15 end @@ -32,15 +35,15 @@ end function segment_function (source, target, distance, weight) local sourceData = sources:query(raster_source, source.lon, source.lat) local targetData = sources:query(raster_source, target.lon, target.lat) - print ("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum) + io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n") local invalid = sourceData.invalid_data() if sourceData.datum ~= invalid and targetData.datum ~= invalid then local slope = math.abs(sourceData.datum - targetData.datum) / distance - print (" slope: " .. slope) - print (" was speed: " .. weight.speed) + io.write(" slope: " .. slope .. "\n") + io.write(" was speed: " .. weight.speed .. "\n") weight.speed = weight.speed * (1 - (slope * 5)) - print (" new speed: " .. weight.speed) + io.write(" new speed: " .. weight.speed .. "\n") end end diff --git a/profiles/rasterbot-interp.lua b/profiles/rasterbotinterp.lua similarity index 77% rename from profiles/rasterbot-interp.lua rename to profiles/rasterbotinterp.lua index 42c98b6f4..8266b07c4 100644 --- a/profiles/rasterbot-interp.lua +++ b/profiles/rasterbotinterp.lua @@ -13,6 +13,9 @@ function way_function (way, result) result.name = name end + result.forward_mode = mode.cycling + result.backward_mode = mode.cycling + result.forward_speed = 15 result.backward_speed = 15 end @@ -32,15 +35,15 @@ end function segment_function (source, target, distance, weight) local sourceData = sources:interpolate(raster_source, source.lon, source.lat) local targetData = sources:interpolate(raster_source, target.lon, target.lat) - print ("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum) + io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n") local invalid = sourceData.invalid_data() if sourceData.datum ~= invalid and targetData.datum ~= invalid then local slope = math.abs(sourceData.datum - targetData.datum) / distance - print (" slope: " .. slope) - print (" was speed: " .. weight.speed) + io.write(" slope: " .. slope .. "\n") + io.write(" was speed: " .. weight.speed .. "\n") weight.speed = weight.speed * (1 - (slope * 5)) - print (" new speed: " .. weight.speed) + io.write(" new speed: " .. weight.speed .. "\n") end end diff --git a/profiles/testbot.lua b/profiles/testbot.lua index 6b10d04e2..8bad8100c 100644 --- a/profiles/testbot.lua +++ b/profiles/testbot.lua @@ -6,14 +6,6 @@ -- Secondary road: 18km/h = 18000m/3600s = 100m/20s -- Tertiary road: 12km/h = 12000m/3600s = 100m/30s --- modes: --- 1: normal --- 2: route --- 3: river downstream --- 4: river upstream --- 5: steps down --- 6: steps up - speed_profile = { ["primary"] = 36, ["secondary"] = 18, @@ -24,13 +16,10 @@ speed_profile = { -- these settings are read directly by osrm -take_minimum_of_speeds = true -obey_oneway = true -obey_barriers = true -use_turn_restrictions = true -ignore_areas = true -- future feature -traffic_signal_penalty = 7 -- seconds -u_turn_penalty = 20 +properties.continue_straight_at_waypoint = true +properties.use_turn_restrictions = true +properties.traffic_signal_penalty = 7 -- seconds +properties.u_turn_penalty = 20 function limit_speed(speed, limits) -- don't use ipairs(), since it stops at the first nil value @@ -68,24 +57,26 @@ function way_function (way, result) if name then result.name = name end + result.forward_mode = mode.driving + result.backward_mode = mode.driving if duration and durationIsValid(duration) then result.duration = math.max( 1, parseDuration(duration) ) - result.forward_mode = 2 - result.backward_mode = 2 + result.forward_mode = mode.route + result.backward_mode = mode.route else local speed_forw = speed_profile[highway] or speed_profile['default'] local speed_back = speed_forw if highway == "river" then local temp_speed = speed_forw - result.forward_mode = 3 - result.backward_mode = 4 + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up speed_forw = temp_speed*1.5 speed_back = temp_speed/1.5 elseif highway == "steps" then - result.forward_mode = 5 - result.backward_mode = 6 + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up end if maxspeed_forward ~= nil and maxspeed_forward > 0 then @@ -111,9 +102,9 @@ function way_function (way, result) if oneway == "no" or oneway == "0" or oneway == "false" then -- nothing to do elseif oneway == "-1" then - result.forward_mode = 0 + result.forward_mode = mode.inaccessible elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then - result.backward_mode = 0 + result.backward_mode = mode.inaccessible end if junction == 'roundabout' then diff --git a/routed.cpp b/routed.cpp deleted file mode 100644 index b4404c12e..000000000 --- a/routed.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "server/server.hpp" -#include "util/version.hpp" -#include "util/routed_options.hpp" -#include "util/simple_logger.hpp" - -#include -#include - -#ifdef __linux__ -#include -#endif - -#include - -#include - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -boost::function0 console_ctrl_function; - -BOOL WINAPI console_ctrl_handler(DWORD ctrl_type) -{ - switch (ctrl_type) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - case CTRL_CLOSE_EVENT: - case CTRL_SHUTDOWN_EVENT: - console_ctrl_function(); - return TRUE; - default: - return FALSE; - } -} -#endif - -int main(int argc, const char *argv[]) try -{ - LogPolicy::GetInstance().Unmute(); - - bool trial_run = false; - std::string ip_address; - int ip_port, requested_thread_num; - - LibOSRMConfig lib_config; - const unsigned init_result = GenerateServerProgramOptions( - argc, argv, lib_config.server_paths, ip_address, ip_port, requested_thread_num, - lib_config.use_shared_memory, trial_run, lib_config.max_locations_trip, lib_config.max_locations_viaroute, - lib_config.max_locations_distance_table, - lib_config.max_locations_map_matching); - if (init_result == INIT_OK_DO_NOT_START_ENGINE) - { - return EXIT_SUCCESS; - } - if (init_result == INIT_FAILED) - { - return EXIT_FAILURE; - } - -#ifdef __linux__ - struct MemoryLocker final - { - explicit MemoryLocker(bool shouldLock_) : shouldLock(shouldLock_) - { - if (shouldLock && -1 == mlockall(MCL_CURRENT | MCL_FUTURE)) - { - couldLock = false; - SimpleLogger().Write(logWARNING) << "memory could not be locked to RAM"; - } - } - ~MemoryLocker() - { - if (shouldLock && couldLock) - (void)munlockall(); - } - bool shouldLock = false, couldLock = true; - } memoryLocker(lib_config.use_shared_memory); -#endif - SimpleLogger().Write() << "starting up engines, " << OSRM_VERSION; - - if (lib_config.use_shared_memory) - { - SimpleLogger().Write(logDEBUG) << "Loading from shared memory"; - } - - SimpleLogger().Write(logDEBUG) << "Threads:\t" << requested_thread_num; - SimpleLogger().Write(logDEBUG) << "IP address:\t" << ip_address; - SimpleLogger().Write(logDEBUG) << "IP port:\t" << ip_port; - -#ifndef _WIN32 - int sig = 0; - sigset_t new_mask; - sigset_t old_mask; - sigfillset(&new_mask); - pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask); -#endif - - OSRM osrm_lib(lib_config); - auto routing_server = Server::CreateServer(ip_address, ip_port, requested_thread_num); - - routing_server->GetRequestHandlerPtr().RegisterRoutingMachine(&osrm_lib); - - if (trial_run) - { - SimpleLogger().Write() << "trial run, quitting after successful initialization"; - } - else - { - std::packaged_task server_task([&]() -> int - { - routing_server->Run(); - return 0; - }); - auto future = server_task.get_future(); - std::thread server_thread(std::move(server_task)); - -#ifndef _WIN32 - sigset_t wait_mask; - pthread_sigmask(SIG_SETMASK, &old_mask, nullptr); - sigemptyset(&wait_mask); - sigaddset(&wait_mask, SIGINT); - sigaddset(&wait_mask, SIGQUIT); - sigaddset(&wait_mask, SIGTERM); - pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); - SimpleLogger().Write() << "running and waiting for requests"; - sigwait(&wait_mask, &sig); -#else - // Set console control handler to allow server to be stopped. - console_ctrl_function = std::bind(&Server::Stop, routing_server); - SetConsoleCtrlHandler(console_ctrl_handler, TRUE); - SimpleLogger().Write() << "running and waiting for requests"; - routing_server->Run(); -#endif - SimpleLogger().Write() << "initiating shutdown"; - routing_server->Stop(); - SimpleLogger().Write() << "stopping threads"; - - auto status = future.wait_for(std::chrono::seconds(2)); - - if (status == std::future_status::ready) - { - server_thread.join(); - } - else - { - SimpleLogger().Write(logWARNING) << "Didn't exit within 2 seconds. Hard abort!"; - server_task.reset(); // just kill it - } - } - - SimpleLogger().Write() << "freeing objects"; - routing_server.reset(); - SimpleLogger().Write() << "shutdown completed"; -} -catch (const std::bad_alloc &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - SimpleLogger().Write(logWARNING) - << "Please provide more memory or consider using a larger swapfile"; - return EXIT_FAILURE; -} -catch (const std::exception &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - return EXIT_FAILURE; -} diff --git a/routing_algorithms/routing_base.hpp b/routing_algorithms/routing_base.hpp deleted file mode 100644 index ecbc07e35..000000000 --- a/routing_algorithms/routing_base.hpp +++ /dev/null @@ -1,687 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef ROUTING_BASE_HPP -#define ROUTING_BASE_HPP - -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/internal_route_result.hpp" -#include "../data_structures/search_engine_data.hpp" -#include "../data_structures/turn_instructions.hpp" - -#include - -#include - -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3; - -template class BasicRoutingInterface -{ - private: - using EdgeData = typename DataFacadeT::EdgeData; - - protected: - DataFacadeT *facade; - - public: - BasicRoutingInterface() = delete; - BasicRoutingInterface(const BasicRoutingInterface &) = delete; - explicit BasicRoutingInterface(DataFacadeT *facade) : facade(facade) {} - ~BasicRoutingInterface() {} - - // min_edge_offset is needed in case we use multiple - // nodes as start/target nodes with different (even negative) offsets. - // In that case the termination criterion is not correct - // anymore. - // - // Example: - // forward heap: a(-100), b(0), - // reverse heap: c(0), d(100) - // - // a --- d - // \ / - // / \ - // b --- c - // - // This is equivalent to running a bi-directional Dijkstra on the following graph: - // - // a --- d - // / \ / \ - // y x z - // \ / \ / - // b --- c - // - // The graph is constructed by inserting nodes y and z that are connected to the initial nodes - // using edges (y, a) with weight -100, (y, b) with weight 0 and, - // (d, z) with weight 100, (c, z) with weight 0 corresponding. - // Since we are dealing with a graph that contains _negative_ edges, - // we need to add an offset to the termination criterion. - void RoutingStep(SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - NodeID &middle_node_id, - int &upper_bound, - int min_edge_offset, - const bool forward_direction, - const bool stalling = true) const - { - const NodeID node = forward_heap.DeleteMin(); - const int distance = forward_heap.GetKey(node); - - if (reverse_heap.WasInserted(node)) - { - const int new_distance = reverse_heap.GetKey(node) + distance; - if (new_distance < upper_bound) - { - if (new_distance >= 0) - { - middle_node_id = node; - upper_bound = new_distance; - } - } - } - - // make sure we don't terminate too early if we initialize the distance - // for the nodes in the forward heap with the forward/reverse offset - BOOST_ASSERT(min_edge_offset <= 0); - if (distance + min_edge_offset > upper_bound) - { - forward_heap.DeleteAll(); - return; - } - - // Stalling - if (stalling) - { - for (const auto edge : facade->GetAdjacentEdgeRange(node)) - { - const EdgeData &data = facade->GetEdgeData(edge); - const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward); - if (reverse_flag) - { - const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.distance; - - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - - if (forward_heap.WasInserted(to)) - { - if (forward_heap.GetKey(to) + edge_weight < distance) - { - return; - } - } - } - } - } - - for (const auto edge : facade->GetAdjacentEdgeRange(node)) - { - const EdgeData &data = facade->GetEdgeData(edge); - bool forward_directionFlag = (forward_direction ? data.forward : data.backward); - if (forward_directionFlag) - { - - const NodeID to = facade->GetTarget(edge); - const int edge_weight = data.distance; - - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - const int to_distance = distance + edge_weight; - - // New Node discovered -> Add to Heap + Node Info Storage - if (!forward_heap.WasInserted(to)) - { - forward_heap.Insert(to, to_distance, node); - } - // Found a shorter Path -> Update distance - else if (to_distance < forward_heap.GetKey(to)) - { - // new parent - forward_heap.GetData(to).parent = node; - forward_heap.DecreaseKey(to, to_distance); - } - } - } - } - - template - void UnpackPath(RandomIter packed_path_begin, - RandomIter packed_path_end, - const PhantomNodes &phantom_node_pair, - std::vector &unpacked_path) const - { - const bool start_traversed_in_reverse = - (*packed_path_begin != phantom_node_pair.source_phantom.forward_node_id); - const bool target_traversed_in_reverse = - (*std::prev(packed_path_end) != phantom_node_pair.target_phantom.forward_node_id); - - BOOST_ASSERT(std::distance(packed_path_begin, packed_path_end) > 0); - std::stack> recursion_stack; - - // We have to push the path in reverse order onto the stack because it's LIFO. - for (auto current = std::prev(packed_path_end); current != packed_path_begin; - current = std::prev(current)) - { - recursion_stack.emplace(*std::prev(current), *current); - } - - std::pair edge; - while (!recursion_stack.empty()) - { - // edge.first edge.second - // *------------------>* - // edge_id - edge = recursion_stack.top(); - recursion_stack.pop(); - - // facade->FindEdge does not suffice here in case of shortcuts. - // The above explanation unclear? Think! - EdgeID smaller_edge_id = SPECIAL_EDGEID; - int edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const int weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - // edge.first edge.second - // *<------------------* - // edge_id - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const int weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != INVALID_EDGE_WEIGHT, "edge id invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut"); - unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id); - const TurnInstruction turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id); - const TravelMode travel_mode = facade->GetTravelModeForEdgeID(ed.id); - - if (!facade->EdgeIsCompressed(ed.id)) - { - BOOST_ASSERT(!facade->EdgeIsCompressed(ed.id)); - unpacked_path.emplace_back(facade->GetGeometryIndexForEdgeID(ed.id), name_index, - turn_instruction, ed.distance, travel_mode); - } - else - { - std::vector id_vector; - facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id), - id_vector); - - const std::size_t start_index = - (unpacked_path.empty() - ? ((start_traversed_in_reverse) - ? id_vector.size() - - phantom_node_pair.source_phantom.fwd_segment_position - 1 - : phantom_node_pair.source_phantom.fwd_segment_position) - : 0); - const std::size_t end_index = id_vector.size(); - - BOOST_ASSERT(start_index >= 0); - BOOST_ASSERT(start_index <= end_index); - for (std::size_t i = start_index; i < end_index; ++i) - { - unpacked_path.emplace_back(id_vector[i], name_index, - TurnInstruction::NoTurn, 0, travel_mode); - } - unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().segment_duration = ed.distance; - } - } - } - if (SPECIAL_EDGEID != phantom_node_pair.target_phantom.packed_geometry_id) - { - std::vector id_vector; - facade->GetUncompressedGeometry(phantom_node_pair.target_phantom.packed_geometry_id, - id_vector); - const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id == - phantom_node_pair.target_phantom.packed_geometry_id) && - unpacked_path.empty(); - - std::size_t start_index = 0; - if (is_local_path) - { - start_index = phantom_node_pair.source_phantom.fwd_segment_position; - if (target_traversed_in_reverse) - { - start_index = - id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position; - } - } - - std::size_t end_index = phantom_node_pair.target_phantom.fwd_segment_position; - if (target_traversed_in_reverse) - { - std::reverse(id_vector.begin(), id_vector.end()); - end_index = - id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position; - } - - if (start_index > end_index) - { - start_index = std::min(start_index, id_vector.size() - 1); - } - - for (std::size_t i = start_index; i != end_index; (start_index < end_index ? ++i : --i)) - { - BOOST_ASSERT(i < id_vector.size()); - BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0); - unpacked_path.emplace_back(PathData{ - id_vector[i], phantom_node_pair.target_phantom.name_id, TurnInstruction::NoTurn, - 0, phantom_node_pair.target_phantom.forward_travel_mode}); - } - } - - // there is no equivalent to a node-based node in an edge-expanded graph. - // two equivalent routes may start (or end) at different node-based edges - // as they are added with the offset how much "distance" on the edge - // has already been traversed. Depending on offset one needs to remove - // the last node. - if (unpacked_path.size() > 1) - { - const std::size_t last_index = unpacked_path.size() - 1; - const std::size_t second_to_last_index = last_index - 1; - - // looks like a trivially true check but tests for underflow - BOOST_ASSERT(last_index > second_to_last_index); - - if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node) - { - unpacked_path.pop_back(); - } - BOOST_ASSERT(!unpacked_path.empty()); - } - } - - void UnpackEdge(const NodeID s, const NodeID t, std::vector &unpacked_path) const - { - std::stack> recursion_stack; - recursion_stack.emplace(s, t); - - std::pair edge; - while (!recursion_stack.empty()) - { - edge = recursion_stack.top(); - recursion_stack.pop(); - - EdgeID smaller_edge_id = SPECIAL_EDGEID; - int edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const int weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const int weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != std::numeric_limits::max(), - "edge weight invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut"); - unpacked_path.emplace_back(edge.first); - } - } - unpacked_path.emplace_back(t); - } - - void RetrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, - const SearchEngineData::QueryHeap &reverse_heap, - const NodeID middle_node_id, - std::vector &packed_path) const - { - RetrievePackedPathFromSingleHeap(forward_heap, middle_node_id, packed_path); - std::reverse(packed_path.begin(), packed_path.end()); - packed_path.emplace_back(middle_node_id); - RetrievePackedPathFromSingleHeap(reverse_heap, middle_node_id, packed_path); - } - - void RetrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &search_heap, - const NodeID middle_node_id, - std::vector &packed_path) const - { - NodeID current_node_id = middle_node_id; - while (current_node_id != search_heap.GetData(current_node_id).parent) - { - current_node_id = search_heap.GetData(current_node_id).parent; - packed_path.emplace_back(current_node_id); - } - } - - // assumes that heaps are already setup correctly. - void Search(SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - int &distance, - std::vector &packed_leg) const - { - NodeID middle = SPECIAL_NODEID; - - // get offset to account for offsets on phantom nodes on compressed edges - const auto min_edge_offset = std::min(0, forward_heap.MinKey()); - BOOST_ASSERT(min_edge_offset <= 0); - // we only every insert negative offsets for nodes in the forward heap - BOOST_ASSERT(reverse_heap.MinKey() >= 0); - - // run two-Target Dijkstra routing step. - while (0 < (forward_heap.Size() + reverse_heap.Size())) - { - if (!forward_heap.Empty()) - { - RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, true); - } - if (!reverse_heap.Empty()) - { - RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, false); - } - } - - // No path found for both target nodes? - if (INVALID_EDGE_WEIGHT == distance || SPECIAL_NODEID == middle) - { - return; - } - - // Was a paths over one of the forward/reverse nodes not found? - BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), - "no path found"); - - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); - } - - // assumes that heaps are already setup correctly. - void SearchWithCore(SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - SearchEngineData::QueryHeap &forward_core_heap, - SearchEngineData::QueryHeap &reverse_core_heap, - int &distance, - std::vector &packed_leg) const - { - NodeID middle = SPECIAL_NODEID; - - std::vector> forward_entry_points; - std::vector> reverse_entry_points; - - // get offset to account for offsets on phantom nodes on compressed edges - const auto min_edge_offset = std::min(0, forward_heap.MinKey()); - // we only every insert negative offsets for nodes in the forward heap - BOOST_ASSERT(reverse_heap.MinKey() >= 0); - - // run two-Target Dijkstra routing step. - while (0 < (forward_heap.Size() + reverse_heap.Size())) - { - if (!forward_heap.Empty()) - { - if (facade->IsCoreNode(forward_heap.Min())) - { - const NodeID node = forward_heap.DeleteMin(); - const int key = forward_heap.GetKey(node); - forward_entry_points.emplace_back(node, key); - } - else - { - RoutingStep(forward_heap, reverse_heap, middle, distance, min_edge_offset, - true); - } - } - if (!reverse_heap.Empty()) - { - if (facade->IsCoreNode(reverse_heap.Min())) - { - const NodeID node = reverse_heap.DeleteMin(); - const int key = reverse_heap.GetKey(node); - reverse_entry_points.emplace_back(node, key); - } - else - { - RoutingStep(reverse_heap, forward_heap, middle, distance, min_edge_offset, - false); - } - } - } - - // TODO check if unordered_set might be faster - // sort by id and increasing by distance - auto entry_point_comparator = [](const std::pair &lhs, - const std::pair &rhs) - { - return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second < rhs.second); - }; - std::sort(forward_entry_points.begin(), forward_entry_points.end(), entry_point_comparator); - std::sort(reverse_entry_points.begin(), reverse_entry_points.end(), entry_point_comparator); - - NodeID last_id = SPECIAL_NODEID; - for (const auto p : forward_entry_points) - { - if (p.first == last_id) - { - continue; - } - forward_core_heap.Insert(p.first, p.second, p.first); - last_id = p.first; - } - last_id = SPECIAL_NODEID; - for (const auto p : reverse_entry_points) - { - if (p.first == last_id) - { - continue; - } - reverse_core_heap.Insert(p.first, p.second, p.first); - last_id = p.first; - } - - // get offset to account for offsets on phantom nodes on compressed edges - int min_core_edge_offset = 0; - if (forward_core_heap.Size() > 0) - { - min_core_edge_offset = std::min(min_core_edge_offset, forward_core_heap.MinKey()); - } - if (reverse_core_heap.Size() > 0 && reverse_core_heap.MinKey() < 0) - { - min_core_edge_offset = std::min(min_core_edge_offset, reverse_core_heap.MinKey()); - } - BOOST_ASSERT(min_core_edge_offset <= 0); - - // run two-target Dijkstra routing step on core with termination criterion - while (0 < (forward_core_heap.Size() + reverse_core_heap.Size()) && - distance > (forward_core_heap.MinKey() + reverse_core_heap.MinKey())) - { - if (!forward_core_heap.Empty()) - { - RoutingStep(forward_core_heap, reverse_core_heap, middle, distance, - min_core_edge_offset, true, false); - } - if (!reverse_core_heap.Empty()) - { - RoutingStep(reverse_core_heap, forward_core_heap, middle, distance, - min_core_edge_offset, false, false); - } - } - - // No path found for both target nodes? - if (INVALID_EDGE_WEIGHT == distance || SPECIAL_NODEID == middle) - { - return; - } - - // Was a paths over one of the forward/reverse nodes not found? - BOOST_ASSERT_MSG((SPECIAL_NODEID != middle && INVALID_EDGE_WEIGHT != distance), - "no path found"); - - // we need to unpack sub path from core heaps - if (facade->IsCoreNode(middle)) - { - std::vector packed_core_leg; - RetrievePackedPathFromHeap(forward_core_heap, reverse_core_heap, middle, - packed_core_leg); - BOOST_ASSERT(packed_core_leg.size() > 0); - RetrievePackedPathFromSingleHeap(forward_heap, packed_core_leg.front(), packed_leg); - std::reverse(packed_leg.begin(), packed_leg.end()); - packed_leg.insert(packed_leg.end(), packed_core_leg.begin(), packed_core_leg.end()); - RetrievePackedPathFromSingleHeap(reverse_heap, packed_core_leg.back(), packed_leg); - } - else - { - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); - } - } - - double get_network_distance(SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom) const - { - EdgeWeight upper_bound = INVALID_EDGE_WEIGHT; - NodeID middle_node = SPECIAL_NODEID; - EdgeWeight edge_offset = std::min(0, -source_phantom.GetForwardWeightPlusOffset()); - edge_offset = std::min(edge_offset, -source_phantom.GetReverseWeightPlusOffset()); - - if (source_phantom.forward_node_id != SPECIAL_NODEID) - { - forward_heap.Insert(source_phantom.forward_node_id, - -source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_node_id); - } - if (source_phantom.reverse_node_id != SPECIAL_NODEID) - { - forward_heap.Insert(source_phantom.reverse_node_id, - -source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_node_id); - } - - if (target_phantom.forward_node_id != SPECIAL_NODEID) - { - reverse_heap.Insert(target_phantom.forward_node_id, - target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_node_id); - } - if (target_phantom.reverse_node_id != SPECIAL_NODEID) - { - reverse_heap.Insert(target_phantom.reverse_node_id, - target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_node_id); - } - - // search from s and t till new_min/(1+epsilon) > length_of_shortest_path - while (0 < (forward_heap.Size() + reverse_heap.Size())) - { - if (0 < forward_heap.Size()) - { - RoutingStep(forward_heap, reverse_heap, middle_node, upper_bound, edge_offset, - true); - } - if (0 < reverse_heap.Size()) - { - RoutingStep(reverse_heap, forward_heap, middle_node, upper_bound, edge_offset, - false); - } - } - - double distance = std::numeric_limits::max(); - if (upper_bound != INVALID_EDGE_WEIGHT) - { - std::vector packed_leg; - RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle_node, packed_leg); - std::vector unpacked_path; - PhantomNodes nodes; - nodes.source_phantom = source_phantom; - nodes.target_phantom = target_phantom; - UnpackPath(packed_leg.begin(), packed_leg.end(), nodes, unpacked_path); - - FixedPointCoordinate previous_coordinate = source_phantom.location; - FixedPointCoordinate current_coordinate; - distance = 0; - for (const auto &p : unpacked_path) - { - current_coordinate = facade->GetCoordinateOfNode(p.node); - distance += coordinate_calculation::haversine_distance(previous_coordinate, - current_coordinate); - previous_coordinate = current_coordinate; - } - distance += coordinate_calculation::haversine_distance(previous_coordinate, - target_phantom.location); - } - return distance; - } -}; - -#endif // ROUTING_BASE_HPP diff --git a/scripts/check_taginfo.py b/scripts/check_taginfo.py new file mode 100755 index 000000000..c2c4c7276 --- /dev/null +++ b/scripts/check_taginfo.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2 + +import json +import sys +import re + +WHITELIST = set(["mph"]) + +if len(sys.argv) < 3: + print "Not enough arguments.\nUsage: " + sys.argv[0] + " taginfo.json profile.lua" + sys.exit(1) + +taginfo_path = sys.argv[1] +profile_path = sys.argv[2] + +taginfo = None +with open(taginfo_path) as f: + taginfo = json.load(f) + +valid_strings = [t["key"] for t in taginfo["tags"]] +valid_strings += [t["value"] for t in taginfo["tags"] if "value" in t] + +string_regxp = re.compile("\"([\d\w\_:]+)\"") + +profile = None +with open(profile_path) as f: + profile = f.readlines() + +n_errors = 0 +for n, line in enumerate(profile): + # ignore comments + if line.strip().startswith("--"): + continue + tokens = set(string_regxp.findall(line)) + errors = [] + for token in tokens: + if token not in WHITELIST and token not in valid_strings: + idx = line.find("\""+token+"\"") + errors.append((idx, token)) + errors = sorted(errors) + n_errors += len(errors) + if len(errors) > 0: + prefix = "%i: " % n + offset = len(prefix) + for idx, token in errors: + sys.stdout.write(prefix + line) + marker = " "*(idx+offset) + "~"*(len(token)+2) + print(marker) + +if n_errors > 0: + sys.exit(1) diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 000000000..561db8756 --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Runs the Clang Formatter in parallel on the code base. +# Return codes: +# - 1 there are files to be formatted +# - 0 everything looks fine + +set -eu -o pipefail + +find src include unit_tests example -type f -name '*.hpp' -o -name '*.cpp' \ + | xargs -I{} -P $(nproc) clang-format -i -style=file {} + + +dirty=$(git ls-files --modified) + +if [[ $dirty ]]; then + echo "The following files do not adhere to the .clang-format style file:" + echo $dirty + exit 1 +else + exit 0 +fi diff --git a/scripts/install_node.sh b/scripts/install_node.sh new file mode 100644 index 000000000..a22bfd2ef --- /dev/null +++ b/scripts/install_node.sh @@ -0,0 +1,9 @@ +# here we set up the node version on the fly. currently only node 4, but can be used for more values if need be +# This is done manually so that the build works the same on OS X +rm -rf ~/.nvm/ && git clone --depth 1 --branch v0.30.1 https://github.com/creationix/nvm.git ~/.nvm +source ~/.nvm/nvm.sh +nvm install $1 +nvm use $1 +node --version +npm --version +which node diff --git a/scripts/modernize.sh b/scripts/modernize.sh index ee0f2ed46..48d90ba36 100755 --- a/scripts/modernize.sh +++ b/scripts/modernize.sh @@ -3,4 +3,17 @@ # Runs the Clang Modernizer in parallel on the code base. # Requires a compilation database in the build directory. -git ls-files '*.cpp' | xargs -I{} -P $(nproc) clang-modernize -p build -final-syntax-check -format -style=file -summary -for-compilers=clang-3.4,gcc-4.8 -include . -exclude third_party {} +find src include unit_tests -type f -name '*.hpp' -o -name '*.cpp' \ + | xargs \ + -I{} \ + -P $(nproc) \ + clang-modernize \ + -p build \ + -final-syntax-check \ + -format \ + -style=file \ + -summary \ + -for-compilers=clang-3.4,gcc-4.8 \ + -include . \ + -exclude third_party \ + {} diff --git a/scripts/poly2req.js b/scripts/poly2req.js new file mode 100755 index 000000000..5b446f399 --- /dev/null +++ b/scripts/poly2req.js @@ -0,0 +1,132 @@ +#!/usr/bin/env node + +'use strict'; + +let fs = require('fs'); + +let VERSION = "route_5.0"; +let SAMPLE_SIZE = 20; +let NUM_REQUEST = 1000; +let NUM_COORDS = 2; +let PORT = 5000; +let url_templates = { + "route_5.0": "http://127.0.0.1:{port}/route/v1/driving/{coords}?steps=false&alternatives=false", + "route_4.9": "http://127.0.0.1:{port}/viaroute?{coords}&instructions=false&alt=false", + "table_5.0": "http://127.0.0.1:{port}/table/v1/driving/{coords}", + "table_4.9": "http://127.0.0.1:{port}/table?{coords}" +}; + +let coord_templates = { + "route_5.0": "{lon},{lat}", + "route_4.9": "loc={lat},{lon}", + "table_5.0": "{lon},{lat}", + "table_4.9": "loc={lat},{lon}" +}; + +let coords_separators = { + "route_5.0": ";", + "route_4.9": "&", + "table_5.0": ";", + "table_4.9": "&" +}; +var axis = "distance"; + +var sw = [Number.MAX_VALUE, Number.MAX_VALUE]; +var ne = [Number.MIN_VALUE, Number.MIN_VALUE]; + +if (process.argv.length > 2 && process.argv[2] == "planet") +{ + sw = [-180., -85.]; + ne = [180., 85.]; +} +if (process.argv.length > 2 && process.argv[2] == "us") +{ + sw = [-127., 24.]; + ne = [-67., 48.]; +} +else if (process.argv.length > 2 && process.argv[2] == "dc") +{ + sw = [-77.138, 38.808]; + ne = [-76.909, 38.974]; +} +else if (process.argv.length > 2) +{ + let monaco_poly_path = process.argv[2]; + let poly_data = fs.readFileSync(monaco_poly_path, 'utf-8'); + + // lets assume there is only one ring + // cut of name and ring number and the two END statements + let coordinates = poly_data.split('\n') + .filter((l) => l != '') + .slice(2, -2).map((coord_line) => coord_line.split(' ') + .filter((elem) => elem != '')) + .map((coord) => [parseFloat(coord[0]), parseFloat(coord[1])]); + + coordinates.forEach((c) => { + sw[0] = Math.min(sw[0], c[0]); + sw[1] = Math.min(sw[1], c[1]); + ne[0] = Math.max(ne[0], c[0]); + ne[1] = Math.max(ne[1], c[1]); + }); +} + +if (process.argv.length > 3) +{ + axis = process.argv[3]; +} + +console.error(sw); +console.error(ne); + +// Yes this an own seeded random number generator because its only a few lines +var seed = 0x1337; +function seededRandom(min, max) { + seed = (seed * 9301 + 49297) % 233280; + var rnd = seed / 233280; + return min + rnd * (max - min); +} + +function getRandomCoordinate() { + let lon = seededRandom(sw[0], ne[0]); + let lat = seededRandom(sw[1], ne[1]); + return [lon, lat]; +} + +function makeQuery(coords) { + let coords_string = coords.map((c) => coord_templates[VERSION].replace("{lon}", c[0]).replace("{lat}", c[1])).join(coords_separators[VERSION]); + return url_templates[VERSION].replace("{coords}", coords_string).replace("{port}", PORT); +} + +if (axis == "distance") +{ + for (var i = 0; i < NUM_REQUEST; ++i) + { + var coords = []; + for (var j = 0; j < NUM_COORDS; ++j) + { + coords.push(getRandomCoordinate()); + } + console.log(makeQuery(coords)); + } +} +else if (axis == "waypoints") +{ + for (var power = 0; power <= 1; ++power) + { + for (var factor = 1; factor <= 10; ++factor) + { + let num_coords = factor*Math.pow(10, power); + console.error(num_coords); + for (var i = 0; i < SAMPLE_SIZE; ++i) + { + var coords = []; + for (var j = 0; j < num_coords; ++j) + { + coords.push(getRandomCoordinate()); + } + console.log(makeQuery(coords)); + } + } + } +} + diff --git a/scripts/tidy.sh b/scripts/tidy.sh index 7ecabfa45..7f4ba1486 100755 --- a/scripts/tidy.sh +++ b/scripts/tidy.sh @@ -3,4 +3,12 @@ # Runs the Clang Tidy Tool in parallel on the code base. # Requires a compilation database in the build directory. -git ls-files '*.cpp' | grep -v third_party | xargs -I{} -P $(nproc) clang-tidy -p build -header-filter='.*' {} + +find src include unit_tests -type f -name '*.hpp' -o -name '*.cpp' \ + | xargs \ + -I{} \ + -P $(nproc) \ + clang-tidy \ + -p build \ + -header-filter='.*' \ + {} diff --git a/scripts/timer.sh b/scripts/timer.sh new file mode 100755 index 000000000..39d8e5b34 --- /dev/null +++ b/scripts/timer.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +TIMINGS_FILE=/tmp/osrm.timings +NAME=$1 +CMD=${@:2} +START=$(date "+%s.%N") +/bin/bash -c "$CMD" +END=$(date "+%s.%N") +TIME="$(echo "$END - $START" | bc)s" +NEW_ENTRY="$NAME\t$TIME\t$(date -Iseconds)" + +echo -e "$NEW_ENTRY" >> $TIMINGS_FILE diff --git a/scripts/update_depdendencies.sh b/scripts/update_depdendencies.sh index e443cfa5f..ec0210c6d 100755 --- a/scripts/update_depdendencies.sh +++ b/scripts/update_depdendencies.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash OSMIUM_REPO=https://github.com/osmcode/libosmium.git -OSMIUM_TAG=v2.3.0 +OSMIUM_TAG=v2.6.1 VARIANT_REPO=https://github.com/mapbox/variant.git -VARIANT_TAG=v1.0 +VARIANT_TAG=v1.1.0 VARIANT_LATEST=$(curl https://api.github.com/repos/mapbox/variant/releases/latest | jq ".tag_name") OSMIUM_LATEST=$(curl https://api.github.com/repos/osmcode/libosmium/releases/latest | jq ".tag_name") diff --git a/server/api_grammar.hpp b/server/api_grammar.hpp deleted file mode 100644 index 28bd1df68..000000000 --- a/server/api_grammar.hpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - -Copyright (c) 2013, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef API_GRAMMAR_HPP -#define API_GRAMMAR_HPP - -#include -#include -#include - -namespace qi = boost::spirit::qi; - -template struct APIGrammar : qi::grammar -{ - explicit APIGrammar(HandlerT *h) : APIGrammar::base_type(api_call), handler(h) - { - api_call = qi::lit('/') >> string[boost::bind(&HandlerT::setService, handler, ::_1)] >> - -query; - query = ('?') >> +(zoom | output | jsonp | checksum | uturns | location_with_options | destination_with_options | source_with_options | cmp | - language | instruction | geometry | alt_route | old_API | num_results | - matching_beta | gps_precision | classify | locs); - // all combinations of timestamp, uturn, hint and bearing without duplicates - t_u = (u >> -timestamp) | (timestamp >> -u); - t_h = (hint >> -timestamp) | (timestamp >> -hint); - u_h = (u >> -hint) | (hint >> -u); - t_u_h = (hint >> -t_u) | (u >> -t_h) | (timestamp >> -u_h); - location_options = (bearing >> -t_u_h) | (t_u_h >> -bearing) | // - (u >> bearing >> -t_h) | (timestamp >> bearing >> -u_h) | (hint >> bearing >> t_u) | // - (t_h >> bearing >> -u) | (u_h >> bearing >> -timestamp) | (t_u >> bearing >> -hint); - location_with_options = location >> -location_options; - source_with_options = source >> -location_options; - destination_with_options = destination >> -location_options; - zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> - qi::short_[boost::bind(&HandlerT::setZoomLevel, handler, ::_1)]; - output = (-qi::lit('&')) >> qi::lit("output") >> '=' >> - string[boost::bind(&HandlerT::setOutputFormat, handler, ::_1)]; - jsonp = (-qi::lit('&')) >> qi::lit("jsonp") >> '=' >> - stringwithPercent[boost::bind(&HandlerT::setJSONpParameter, handler, ::_1)]; - checksum = (-qi::lit('&')) >> qi::lit("checksum") >> '=' >> - qi::uint_[boost::bind(&HandlerT::setChecksum, handler, ::_1)]; - instruction = (-qi::lit('&')) >> qi::lit("instructions") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setInstructionFlag, handler, ::_1)]; - geometry = (-qi::lit('&')) >> qi::lit("geometry") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setGeometryFlag, handler, ::_1)]; - cmp = (-qi::lit('&')) >> qi::lit("compression") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setCompressionFlag, handler, ::_1)]; - location = (-qi::lit('&')) >> qi::lit("loc") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(&HandlerT::addCoordinate, handler, ::_1)]; - destination = (-qi::lit('&')) >> qi::lit("dst") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(&HandlerT::addDestination, handler, ::_1)]; - source = (-qi::lit('&')) >> qi::lit("src") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(&HandlerT::addSource, handler, ::_1)]; - hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >> - stringwithDot[boost::bind(&HandlerT::addHint, handler, ::_1)]; - timestamp = (-qi::lit('&')) >> qi::lit("t") >> '=' >> - qi::uint_[boost::bind(&HandlerT::addTimestamp, handler, ::_1)]; - bearing = (-qi::lit('&')) >> qi::lit("b") >> '=' >> - (qi::int_ >> -(qi::lit(',') >> qi::int_ | qi::attr(10)))[boost::bind(&HandlerT::addBearing, handler, ::_1, ::_2, ::_3)]; - u = (-qi::lit('&')) >> qi::lit("u") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setUTurn, handler, ::_1)]; - uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setAllUTurns, handler, ::_1)]; - language = (-qi::lit('&')) >> qi::lit("hl") >> '=' >> - string[boost::bind(&HandlerT::setLanguage, handler, ::_1)]; - alt_route = (-qi::lit('&')) >> qi::lit("alt") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setAlternateRouteFlag, handler, ::_1)]; - old_API = (-qi::lit('&')) >> qi::lit("geomformat") >> '=' >> - string[boost::bind(&HandlerT::setDeprecatedAPIFlag, handler, ::_1)]; - num_results = (-qi::lit('&')) >> qi::lit("num_results") >> '=' >> - qi::short_[boost::bind(&HandlerT::setNumberOfResults, handler, ::_1)]; - matching_beta = (-qi::lit('&')) >> qi::lit("matching_beta") >> '=' >> - qi::float_[boost::bind(&HandlerT::setMatchingBeta, handler, ::_1)]; - gps_precision = (-qi::lit('&')) >> qi::lit("gps_precision") >> '=' >> - qi::float_[boost::bind(&HandlerT::setGPSPrecision, handler, ::_1)]; - classify = (-qi::lit('&')) >> qi::lit("classify") >> '=' >> - qi::bool_[boost::bind(&HandlerT::setClassify, handler, ::_1)]; - locs = (-qi::lit('&')) >> qi::lit("locs") >> '=' >> - stringforPolyline[boost::bind(&HandlerT::getCoordinatesFromGeometry, handler, ::_1)]; - - string = +(qi::char_("a-zA-Z")); - stringwithDot = +(qi::char_("a-zA-Z0-9_.-")); - stringwithPercent = +(qi::char_("a-zA-Z0-9_.-") | qi::char_('[') | qi::char_(']') | - (qi::char_('%') >> qi::char_("0-9A-Z") >> qi::char_("0-9A-Z"))); - stringforPolyline = +(qi::char_("a-zA-Z0-9_.-[]{}@?|\\%~`^")); - } - - qi::rule api_call, query, location_options, location_with_options, destination_with_options, source_with_options, t_u, t_h, u_h, t_u_h; - qi::rule service, zoom, output, string, jsonp, checksum, location, destination, source, - hint, timestamp, bearing, stringwithDot, stringwithPercent, language, geometry, cmp, alt_route, u, - uturns, old_API, num_results, matching_beta, gps_precision, classify, locs, instruction, stringforPolyline; - - HandlerT *handler; -}; - -#endif /* API_GRAMMAR_HPP */ diff --git a/server/connection.hpp b/server/connection.hpp deleted file mode 100644 index aa6fd0544..000000000 --- a/server/connection.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef CONNECTION_HPP -#define CONNECTION_HPP - -#include "http/compression_type.hpp" -#include "http/reply.hpp" -#include "http/request.hpp" -#include "request_parser.hpp" - -#include -#include -#include -#include - -#include -#include - -// workaround for incomplete std::shared_ptr compatibility in old boost versions -#if BOOST_VERSION < 105300 || defined BOOST_NO_CXX11_SMART_PTR - -namespace boost -{ -template const T *get_pointer(std::shared_ptr const &p) { return p.get(); } - -template T *get_pointer(std::shared_ptr &p) { return p.get(); } -} // namespace boost - -#endif - -class RequestHandler; - -namespace http -{ - -/// Represents a single connection from a client. -class Connection : public std::enable_shared_from_this -{ - public: - explicit Connection(boost::asio::io_service &io_service, RequestHandler &handler); - Connection(const Connection &) = delete; - Connection() = delete; - - boost::asio::ip::tcp::socket &socket(); - - /// Start the first asynchronous operation for the connection. - void start(); - - private: - void handle_read(const boost::system::error_code &e, std::size_t bytes_transferred); - - /// Handle completion of a write operation. - void handle_write(const boost::system::error_code &e); - - std::vector compress_buffers(const std::vector &uncompressed_data, - const compression_type compression_type); - - boost::asio::io_service::strand strand; - boost::asio::ip::tcp::socket TCP_socket; - RequestHandler &request_handler; - RequestParser request_parser; - boost::array incoming_data_buffer; - request current_request; - reply current_reply; - std::vector compressed_output; -}; - -} // namespace http - -#endif // CONNECTION_HPP diff --git a/server/data_structures/datafacade_base.hpp b/server/data_structures/datafacade_base.hpp deleted file mode 100644 index 5c5c52296..000000000 --- a/server/data_structures/datafacade_base.hpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef DATAFACADE_BASE_HPP -#define DATAFACADE_BASE_HPP - -// Exposes all data access interfaces to the algorithms via base class ptr - -#include "../../data_structures/edge_based_node.hpp" -#include "../../data_structures/external_memory_node.hpp" -#include "../../data_structures/phantom_node.hpp" -#include "../../data_structures/turn_instructions.hpp" -#include "../../util/integer_range.hpp" -#include "../../util/osrm_exception.hpp" -#include "../../util/string_util.hpp" -#include "../../typedefs.h" - -#include - -#include -#include - -using EdgeRange = osrm::range; - -template class BaseDataFacade -{ - public: - using RTreeLeaf = EdgeBasedNode; - using EdgeData = EdgeDataT; - BaseDataFacade() {} - virtual ~BaseDataFacade() {} - - // search graph access - virtual unsigned GetNumberOfNodes() const = 0; - - virtual unsigned GetNumberOfEdges() const = 0; - - virtual unsigned GetOutDegree(const NodeID n) const = 0; - - virtual NodeID GetTarget(const EdgeID e) const = 0; - - virtual const EdgeDataT &GetEdgeData(const EdgeID e) const = 0; - - virtual EdgeID BeginEdges(const NodeID n) const = 0; - - virtual EdgeID EndEdges(const NodeID n) const = 0; - - virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; - - // searches for a specific edge - virtual EdgeID FindEdge(const NodeID from, const NodeID to) const = 0; - - virtual EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const = 0; - - virtual EdgeID - FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0; - - // node and edge information access - virtual FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const = 0; - - virtual bool EdgeIsCompressed(const unsigned id) const = 0; - - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0; - - virtual void GetUncompressedGeometry(const unsigned id, - std::vector &result_nodes) const = 0; - - virtual TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const = 0; - - virtual TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0; - - virtual std::vector - NearestPhantomNodesInRange(const FixedPointCoordinate &input_coordinate, - const float max_distance, - const int bearing = 0, - const int bearing_range = 180) = 0; - - virtual std::vector - NearestPhantomNodes(const FixedPointCoordinate &input_coordinate, - const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) = 0; - - virtual std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const FixedPointCoordinate &input_coordinate, - const int bearing = 0, - const int bearing_range = 180) = 0; - - virtual unsigned GetCheckSum() const = 0; - - virtual bool IsCoreNode(const NodeID id) const = 0; - - virtual unsigned GetNameIndexFromEdgeID(const unsigned id) const = 0; - - virtual std::string get_name_for_id(const unsigned name_id) const = 0; - - virtual std::size_t GetCoreSize() const = 0; - - virtual std::string GetTimestamp() const = 0; -}; - -#endif // DATAFACADE_BASE_HPP diff --git a/server/data_structures/internal_datafacade.hpp b/server/data_structures/internal_datafacade.hpp deleted file mode 100644 index a2f74e587..000000000 --- a/server/data_structures/internal_datafacade.hpp +++ /dev/null @@ -1,469 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef INTERNAL_DATAFACADE_HPP -#define INTERNAL_DATAFACADE_HPP - -// implements all data storage when shared memory is _NOT_ used - -#include "datafacade_base.hpp" - -#include "../../algorithms/geospatial_query.hpp" -#include "../../data_structures/original_edge_data.hpp" -#include "../../data_structures/query_node.hpp" -#include "../../data_structures/query_edge.hpp" -#include "../../data_structures/shared_memory_vector_wrapper.hpp" -#include "../../data_structures/static_graph.hpp" -#include "../../data_structures/static_rtree.hpp" -#include "../../data_structures/range_table.hpp" -#include "../../util/graph_loader.hpp" -#include "../../util/simple_logger.hpp" - -#include - -#include - -#include - -template class InternalDataFacade final : public BaseDataFacade -{ - - private: - using super = BaseDataFacade; - using QueryGraph = StaticGraph; - using InputEdge = typename QueryGraph::InputEdge; - using RTreeLeaf = typename super::RTreeLeaf; - using InternalRTree = StaticRTree::vector, false>; - using InternalGeospatialQuery = GeospatialQuery; - - InternalDataFacade() {} - - unsigned m_check_sum; - unsigned m_number_of_nodes; - std::unique_ptr m_query_graph; - std::string m_timestamp; - - std::shared_ptr::vector> m_coordinate_list; - ShM::vector m_via_node_list; - ShM::vector m_name_ID_list; - ShM::vector m_turn_instruction_list; - ShM::vector m_travel_mode_list; - ShM::vector m_names_char_list; - ShM::vector m_edge_is_compressed; - ShM::vector m_geometry_indices; - ShM::vector m_geometry_list; - ShM::vector m_is_core_node; - - boost::thread_specific_ptr m_static_rtree; - boost::thread_specific_ptr m_geospatial_query; - boost::filesystem::path ram_index_path; - boost::filesystem::path file_index_path; - RangeTable<16, false> m_name_table; - - void LoadTimestamp(const boost::filesystem::path ×tamp_path) - { - if (boost::filesystem::exists(timestamp_path)) - { - SimpleLogger().Write() << "Loading Timestamp"; - boost::filesystem::ifstream timestamp_stream(timestamp_path); - if (!timestamp_stream) - { - SimpleLogger().Write(logWARNING) << timestamp_path << " not found"; - } - getline(timestamp_stream, m_timestamp); - timestamp_stream.close(); - } - if (m_timestamp.empty()) - { - m_timestamp = "n/a"; - } - if (25 < m_timestamp.length()) - { - m_timestamp.resize(25); - } - } - - void LoadGraph(const boost::filesystem::path &hsgr_path) - { - typename ShM::vector node_list; - typename ShM::vector edge_list; - - SimpleLogger().Write() << "loading graph from " << hsgr_path.string(); - - m_number_of_nodes = readHSGRFromStream(hsgr_path, node_list, edge_list, &m_check_sum); - - BOOST_ASSERT_MSG(0 != node_list.size(), "node list empty"); - // BOOST_ASSERT_MSG(0 != edge_list.size(), "edge list empty"); - SimpleLogger().Write() << "loaded " << node_list.size() << " nodes and " << edge_list.size() - << " edges"; - m_query_graph = std::unique_ptr(new QueryGraph(node_list, edge_list)); - - BOOST_ASSERT_MSG(0 == node_list.size(), "node list not flushed"); - BOOST_ASSERT_MSG(0 == edge_list.size(), "edge list not flushed"); - SimpleLogger().Write() << "Data checksum is " << m_check_sum; - } - - void LoadNodeAndEdgeInformation(const boost::filesystem::path &nodes_file, - const boost::filesystem::path &edges_file) - { - boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary); - - QueryNode current_node; - unsigned number_of_coordinates = 0; - nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned)); - m_coordinate_list = - std::make_shared>(number_of_coordinates); - for (unsigned i = 0; i < number_of_coordinates; ++i) - { - nodes_input_stream.read((char *)¤t_node, sizeof(QueryNode)); - m_coordinate_list->at(i) = FixedPointCoordinate(current_node.lat, current_node.lon); - BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lat) >> 30) == 0); - BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lon) >> 30) == 0); - } - nodes_input_stream.close(); - - boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary); - unsigned number_of_edges = 0; - edges_input_stream.read((char *)&number_of_edges, sizeof(unsigned)); - m_via_node_list.resize(number_of_edges); - m_name_ID_list.resize(number_of_edges); - m_turn_instruction_list.resize(number_of_edges); - m_travel_mode_list.resize(number_of_edges); - m_edge_is_compressed.resize(number_of_edges); - - unsigned compressed = 0; - - OriginalEdgeData current_edge_data; - for (unsigned i = 0; i < number_of_edges; ++i) - { - edges_input_stream.read((char *)&(current_edge_data), sizeof(OriginalEdgeData)); - m_via_node_list[i] = current_edge_data.via_node; - m_name_ID_list[i] = current_edge_data.name_id; - m_turn_instruction_list[i] = current_edge_data.turn_instruction; - m_travel_mode_list[i] = current_edge_data.travel_mode; - m_edge_is_compressed[i] = current_edge_data.compressed_geometry; - if (m_edge_is_compressed[i]) - { - ++compressed; - } - } - - edges_input_stream.close(); - } - - void LoadCoreInformation(const boost::filesystem::path &core_data_file) - { - std::ifstream core_stream(core_data_file.string().c_str(), std::ios::binary); - unsigned number_of_markers; - core_stream.read((char *)&number_of_markers, sizeof(unsigned)); - - std::vector unpacked_core_markers(number_of_markers); - core_stream.read((char *)unpacked_core_markers.data(), sizeof(char) * number_of_markers); - - // in this case we have nothing to do - if (number_of_markers <= 0) - { - return; - } - - m_is_core_node.resize(number_of_markers); - for (auto i = 0u; i < number_of_markers; ++i) - { - BOOST_ASSERT(unpacked_core_markers[i] == 0 || unpacked_core_markers[i] == 1); - m_is_core_node[i] = unpacked_core_markers[i] == 1; - } - } - - void LoadGeometries(const boost::filesystem::path &geometry_file) - { - std::ifstream geometry_stream(geometry_file.string().c_str(), std::ios::binary); - unsigned number_of_indices = 0; - unsigned number_of_compressed_geometries = 0; - - geometry_stream.read((char *)&number_of_indices, sizeof(unsigned)); - - m_geometry_indices.resize(number_of_indices); - if (number_of_indices > 0) - { - geometry_stream.read((char *)&(m_geometry_indices[0]), - number_of_indices * sizeof(unsigned)); - } - - geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned)); - - BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries); - m_geometry_list.resize(number_of_compressed_geometries); - - if (number_of_compressed_geometries > 0) - { - geometry_stream.read((char *)&(m_geometry_list[0]), - number_of_compressed_geometries * sizeof(unsigned)); - } - geometry_stream.close(); - } - - void LoadRTree() - { - BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree"); - - m_static_rtree.reset(new InternalRTree(ram_index_path, file_index_path, m_coordinate_list)); - m_geospatial_query.reset(new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list)); - } - - void LoadStreetNames(const boost::filesystem::path &names_file) - { - boost::filesystem::ifstream name_stream(names_file, std::ios::binary); - - name_stream >> m_name_table; - - unsigned number_of_chars = 0; - name_stream.read((char *)&number_of_chars, sizeof(unsigned)); - BOOST_ASSERT_MSG(0 != number_of_chars, "name file broken"); - m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element - name_stream.read((char *)&m_names_char_list[0], number_of_chars * sizeof(char)); - if (0 == m_names_char_list.size()) - { - SimpleLogger().Write(logWARNING) << "list of street names is empty"; - } - name_stream.close(); - } - - public: - virtual ~InternalDataFacade() - { - m_static_rtree.reset(); - m_geospatial_query.reset(); - } - - explicit InternalDataFacade(const std::unordered_map &server_paths) - { - // cache end iterator to quickly check .find against - const auto end_it = end(server_paths); - - const auto file_for = [&server_paths, &end_it](const std::string &path) - { - const auto it = server_paths.find(path); - if (it == end_it || !boost::filesystem::is_regular_file(it->second)) - throw osrm::exception("no valid " + path + " file given in ini file"); - return it->second; - }; - - ram_index_path = file_for("ramindex"); - file_index_path = file_for("fileindex"); - - SimpleLogger().Write() << "loading graph data"; - LoadGraph(file_for("hsgrdata")); - - SimpleLogger().Write() << "loading edge information"; - LoadNodeAndEdgeInformation(file_for("nodesdata"), file_for("edgesdata")); - - SimpleLogger().Write() << "loading core information"; - LoadCoreInformation(file_for("coredata")); - - SimpleLogger().Write() << "loading geometries"; - LoadGeometries(file_for("geometries")); - - SimpleLogger().Write() << "loading timestamp"; - LoadTimestamp(file_for("timestamp")); - - SimpleLogger().Write() << "loading street names"; - LoadStreetNames(file_for("namesdata")); - } - - // search graph access - unsigned GetNumberOfNodes() const override final { return m_query_graph->GetNumberOfNodes(); } - - unsigned GetNumberOfEdges() const override final { return m_query_graph->GetNumberOfEdges(); } - - unsigned GetOutDegree(const NodeID n) const override final - { - return m_query_graph->GetOutDegree(n); - } - - NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } - - EdgeDataT &GetEdgeData(const EdgeID e) const override final - { - return m_query_graph->GetEdgeData(e); - } - - EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph->BeginEdges(n); } - - EdgeID EndEdges(const NodeID n) const override final { return m_query_graph->EndEdges(n); } - - EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final - { - return m_query_graph->GetAdjacentEdgeRange(node); - }; - - // searches for a specific edge - EdgeID FindEdge(const NodeID from, const NodeID to) const override final - { - return m_query_graph->FindEdge(from, to); - } - - EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const override final - { - return m_query_graph->FindEdgeInEitherDirection(from, to); - } - - EdgeID - FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const override final - { - return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); - } - - // node and edge information access - FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const override final - { - return m_coordinate_list->at(id); - }; - - bool EdgeIsCompressed(const unsigned id) const override final - { - return m_edge_is_compressed.at(id); - } - - TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final - { - return m_turn_instruction_list.at(id); - } - - TravelMode GetTravelModeForEdgeID(const unsigned id) const override final - { - return m_travel_mode_list.at(id); - } - - - std::vector - NearestPhantomNodesInRange(const FixedPointCoordinate &input_coordinate, - const float max_distance, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get()) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance, bearing, bearing_range); - } - - std::vector - NearestPhantomNodes(const FixedPointCoordinate &input_coordinate, - const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get()) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing, bearing_range); - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const FixedPointCoordinate &input_coordinate, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get()) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(input_coordinate, bearing, bearing_range); - } - - - unsigned GetCheckSum() const override final { return m_check_sum; } - - unsigned GetNameIndexFromEdgeID(const unsigned id) const override final - { - return m_name_ID_list.at(id); - } - - std::string get_name_for_id(const unsigned name_id) const override final - { - if (std::numeric_limits::max() == name_id) - { - return ""; - } - auto range = m_name_table.GetRange(name_id); - - std::string result; - result.reserve(range.size()); - if (range.begin() != range.end()) - { - result.resize(range.back() - range.front() + 1); - std::copy(m_names_char_list.begin() + range.front(), - m_names_char_list.begin() + range.back() + 1, result.begin()); - } - return result; - } - - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final - { - return m_via_node_list.at(id); - } - - virtual std::size_t GetCoreSize() const override final - { - return m_is_core_node.size(); - } - - virtual bool IsCoreNode(const NodeID id) const override final - { - if (m_is_core_node.size() > 0) - { - return m_is_core_node[id]; - } - else - { - return false; - } - } - - virtual void GetUncompressedGeometry(const unsigned id, - std::vector &result_nodes) const override final - { - const unsigned begin = m_geometry_indices.at(id); - const unsigned end = m_geometry_indices.at(id + 1); - - result_nodes.clear(); - result_nodes.insert(result_nodes.begin(), m_geometry_list.begin() + begin, - m_geometry_list.begin() + end); - } - - std::string GetTimestamp() const override final { return m_timestamp; } -}; - -#endif // INTERNAL_DATAFACADE_HPP diff --git a/server/data_structures/shared_barriers.hpp b/server/data_structures/shared_barriers.hpp deleted file mode 100644 index e6f1234ca..000000000 --- a/server/data_structures/shared_barriers.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SHARED_BARRIERS_HPP -#define SHARED_BARRIERS_HPP - -#include -#include - -struct SharedBarriers -{ - - SharedBarriers() - : pending_update_mutex(boost::interprocess::open_or_create, "pending_update"), - update_mutex(boost::interprocess::open_or_create, "update"), - query_mutex(boost::interprocess::open_or_create, "query"), - no_running_queries_condition(boost::interprocess::open_or_create, "no_running_queries"), - update_ongoing(false), number_of_queries(0) - { - } - - // Mutex to protect access to the boolean variable - boost::interprocess::named_mutex pending_update_mutex; - boost::interprocess::named_mutex update_mutex; - boost::interprocess::named_mutex query_mutex; - - // Condition that no update is running - boost::interprocess::named_condition no_running_queries_condition; - - // Is there an ongoing update? - bool update_ongoing; - // Is there any query? - int number_of_queries; -}; - -#endif // SHARED_BARRIERS_HPP diff --git a/server/data_structures/shared_datafacade.hpp b/server/data_structures/shared_datafacade.hpp deleted file mode 100644 index 4bbf076d3..000000000 --- a/server/data_structures/shared_datafacade.hpp +++ /dev/null @@ -1,503 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SHARED_DATAFACADE_HPP -#define SHARED_DATAFACADE_HPP - -// implements all data storage when shared memory _IS_ used - -#include "datafacade_base.hpp" -#include "shared_datatype.hpp" - -#include "../../algorithms/geospatial_query.hpp" -#include "../../data_structures/range_table.hpp" -#include "../../data_structures/static_graph.hpp" -#include "../../data_structures/static_rtree.hpp" -#include "../../util/make_unique.hpp" -#include "../../util/simple_logger.hpp" - -#include - -#include -#include -#include - -template class SharedDataFacade final : public BaseDataFacade -{ - - private: - using EdgeData = EdgeDataT; - using super = BaseDataFacade; - using QueryGraph = StaticGraph; - using GraphNode = typename StaticGraph::NodeArrayEntry; - using GraphEdge = typename StaticGraph::EdgeArrayEntry; - using NameIndexBlock = typename RangeTable<16, true>::BlockT; - using InputEdge = typename QueryGraph::InputEdge; - using RTreeLeaf = typename super::RTreeLeaf; - using SharedRTree = StaticRTree::vector, true>; - using SharedGeospatialQuery = GeospatialQuery; - using TimeStampedRTreePair = std::pair>; - using RTreeNode = typename SharedRTree::TreeNode; - - SharedDataLayout *data_layout; - char *shared_memory; - SharedDataTimestamp *data_timestamp_ptr; - - SharedDataType CURRENT_LAYOUT; - SharedDataType CURRENT_DATA; - unsigned CURRENT_TIMESTAMP; - - unsigned m_check_sum; - std::unique_ptr m_query_graph; - std::unique_ptr m_layout_memory; - std::unique_ptr m_large_memory; - std::string m_timestamp; - - std::shared_ptr::vector> m_coordinate_list; - ShM::vector m_via_node_list; - ShM::vector m_name_ID_list; - ShM::vector m_turn_instruction_list; - ShM::vector m_travel_mode_list; - ShM::vector m_names_char_list; - ShM::vector m_name_begin_indices; - ShM::vector m_edge_is_compressed; - ShM::vector m_geometry_indices; - ShM::vector m_geometry_list; - ShM::vector m_is_core_node; - - boost::thread_specific_ptr>> m_static_rtree; - boost::thread_specific_ptr m_geospatial_query; - boost::filesystem::path file_index_path; - - std::shared_ptr> m_name_table; - - void LoadChecksum() - { - m_check_sum = - *data_layout->GetBlockPtr(shared_memory, SharedDataLayout::HSGR_CHECKSUM); - SimpleLogger().Write() << "set checksum: " << m_check_sum; - } - - void LoadTimestamp() - { - char *timestamp_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::TIMESTAMP); - m_timestamp.resize(data_layout->GetBlockSize(SharedDataLayout::TIMESTAMP)); - std::copy(timestamp_ptr, - timestamp_ptr + data_layout->GetBlockSize(SharedDataLayout::TIMESTAMP), - m_timestamp.begin()); - } - - void LoadRTree() - { - BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree"); - - RTreeNode *tree_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::R_SEARCH_TREE); - m_static_rtree.reset(new TimeStampedRTreePair( - CURRENT_TIMESTAMP, - osrm::make_unique( - tree_ptr, data_layout->num_entries[SharedDataLayout::R_SEARCH_TREE], - file_index_path, m_coordinate_list))); - m_geospatial_query.reset(new SharedGeospatialQuery(*m_static_rtree->second, m_coordinate_list)); - } - - void LoadGraph() - { - GraphNode *graph_nodes_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::GRAPH_NODE_LIST); - - GraphEdge *graph_edges_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::GRAPH_EDGE_LIST); - - typename ShM::vector node_list( - graph_nodes_ptr, data_layout->num_entries[SharedDataLayout::GRAPH_NODE_LIST]); - typename ShM::vector edge_list( - graph_edges_ptr, data_layout->num_entries[SharedDataLayout::GRAPH_EDGE_LIST]); - m_query_graph.reset(new QueryGraph(node_list, edge_list)); - } - - void LoadNodeAndEdgeInformation() - { - - FixedPointCoordinate *coordinate_list_ptr = data_layout->GetBlockPtr( - shared_memory, SharedDataLayout::COORDINATE_LIST); - m_coordinate_list = osrm::make_unique::vector>( - coordinate_list_ptr, data_layout->num_entries[SharedDataLayout::COORDINATE_LIST]); - - TravelMode *travel_mode_list_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::TRAVEL_MODE); - typename ShM::vector travel_mode_list( - travel_mode_list_ptr, data_layout->num_entries[SharedDataLayout::TRAVEL_MODE]); - m_travel_mode_list.swap(travel_mode_list); - - TurnInstruction *turn_instruction_list_ptr = data_layout->GetBlockPtr( - shared_memory, SharedDataLayout::TURN_INSTRUCTION); - typename ShM::vector turn_instruction_list( - turn_instruction_list_ptr, - data_layout->num_entries[SharedDataLayout::TURN_INSTRUCTION]); - m_turn_instruction_list.swap(turn_instruction_list); - - unsigned *name_id_list_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::NAME_ID_LIST); - typename ShM::vector name_id_list( - name_id_list_ptr, data_layout->num_entries[SharedDataLayout::NAME_ID_LIST]); - m_name_ID_list.swap(name_id_list); - } - - void LoadViaNodeList() - { - NodeID *via_node_list_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::VIA_NODE_LIST); - typename ShM::vector via_node_list( - via_node_list_ptr, data_layout->num_entries[SharedDataLayout::VIA_NODE_LIST]); - m_via_node_list.swap(via_node_list); - } - - void LoadNames() - { - unsigned *offsets_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::NAME_OFFSETS); - NameIndexBlock *blocks_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::NAME_BLOCKS); - typename ShM::vector name_offsets( - offsets_ptr, data_layout->num_entries[SharedDataLayout::NAME_OFFSETS]); - typename ShM::vector name_blocks( - blocks_ptr, data_layout->num_entries[SharedDataLayout::NAME_BLOCKS]); - - char *names_list_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::NAME_CHAR_LIST); - typename ShM::vector names_char_list( - names_list_ptr, data_layout->num_entries[SharedDataLayout::NAME_CHAR_LIST]); - m_name_table = osrm::make_unique>( - name_offsets, name_blocks, static_cast(names_char_list.size())); - - m_names_char_list.swap(names_char_list); - } - - void LoadCoreInformation() - { - if (data_layout->num_entries[SharedDataLayout::CORE_MARKER] <= 0) - { - return; - } - - unsigned *core_marker_ptr = data_layout->GetBlockPtr( - shared_memory, SharedDataLayout::CORE_MARKER); - typename ShM::vector is_core_node( - core_marker_ptr, - data_layout->num_entries[SharedDataLayout::CORE_MARKER]); - m_is_core_node.swap(is_core_node); - } - - void LoadGeometries() - { - unsigned *geometries_compressed_ptr = data_layout->GetBlockPtr( - shared_memory, SharedDataLayout::GEOMETRIES_INDICATORS); - typename ShM::vector edge_is_compressed( - geometries_compressed_ptr, - data_layout->num_entries[SharedDataLayout::GEOMETRIES_INDICATORS]); - m_edge_is_compressed.swap(edge_is_compressed); - - unsigned *geometries_index_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::GEOMETRIES_INDEX); - typename ShM::vector geometry_begin_indices( - geometries_index_ptr, data_layout->num_entries[SharedDataLayout::GEOMETRIES_INDEX]); - m_geometry_indices.swap(geometry_begin_indices); - - unsigned *geometries_list_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::GEOMETRIES_LIST); - typename ShM::vector geometry_list( - geometries_list_ptr, data_layout->num_entries[SharedDataLayout::GEOMETRIES_LIST]); - m_geometry_list.swap(geometry_list); - } - - public: - virtual ~SharedDataFacade() {} - - boost::shared_mutex data_mutex; - - SharedDataFacade() - { - if (!SharedMemory::RegionExists(CURRENT_REGIONS)) - { - throw osrm::exception("No shared memory blocks found, have you forgotten to run osrm-datastore?"); - } - data_timestamp_ptr = (SharedDataTimestamp *)SharedMemoryFactory::Get( - CURRENT_REGIONS, sizeof(SharedDataTimestamp), false, false) - ->Ptr(); - CURRENT_LAYOUT = LAYOUT_NONE; - CURRENT_DATA = DATA_NONE; - CURRENT_TIMESTAMP = 0; - - // load data - CheckAndReloadFacade(); - } - - void CheckAndReloadFacade() - { - if (CURRENT_LAYOUT != data_timestamp_ptr->layout || - CURRENT_DATA != data_timestamp_ptr->data || - CURRENT_TIMESTAMP != data_timestamp_ptr->timestamp) - { - // Get exclusive lock - SimpleLogger().Write(logDEBUG) << "Updates available, getting exclusive lock"; - boost::unique_lock lock(data_mutex); - - if (CURRENT_LAYOUT != data_timestamp_ptr->layout || - CURRENT_DATA != data_timestamp_ptr->data) - { - // release the previous shared memory segments - SharedMemory::Remove(CURRENT_LAYOUT); - SharedMemory::Remove(CURRENT_DATA); - - CURRENT_LAYOUT = data_timestamp_ptr->layout; - CURRENT_DATA = data_timestamp_ptr->data; - CURRENT_TIMESTAMP = 0; // Force trigger a reload - - SimpleLogger().Write(logDEBUG) << "Current layout was different to new layout, swapping"; - } - else - { - SimpleLogger().Write(logDEBUG) << "Current layout was same to new layout, not swapping"; - } - - if (CURRENT_TIMESTAMP != data_timestamp_ptr->timestamp) - { - CURRENT_TIMESTAMP = data_timestamp_ptr->timestamp; - - SimpleLogger().Write(logDEBUG) << "Performing data reload"; - m_layout_memory.reset(SharedMemoryFactory::Get(CURRENT_LAYOUT)); - - data_layout = (SharedDataLayout *) (m_layout_memory->Ptr()); - - m_large_memory.reset(SharedMemoryFactory::Get(CURRENT_DATA)); - shared_memory = (char *) (m_large_memory->Ptr()); - - const char *file_index_ptr = - data_layout->GetBlockPtr(shared_memory, SharedDataLayout::FILE_INDEX_PATH); - file_index_path = boost::filesystem::path(file_index_ptr); - if (!boost::filesystem::exists(file_index_path)) { - SimpleLogger().Write(logDEBUG) << "Leaf file name " - << file_index_path.string(); - throw osrm::exception("Could not load leaf index file. " - "Is any data loaded into shared memory?"); - } - - LoadGraph(); - LoadChecksum(); - LoadNodeAndEdgeInformation(); - LoadGeometries(); - LoadTimestamp(); - LoadViaNodeList(); - LoadNames(); - LoadCoreInformation(); - - data_layout->PrintInformation(); - - SimpleLogger().Write() << "number of geometries: " << m_coordinate_list->size(); - for (unsigned i = 0; i < m_coordinate_list->size(); ++i) - { - if (!GetCoordinateOfNode(i).is_valid()) - { - SimpleLogger().Write() << "coordinate " << i << " not valid"; - } - } - } - SimpleLogger().Write(logDEBUG) << "Releasing exclusive lock"; - } - } - - // search graph access - unsigned GetNumberOfNodes() const override final { return m_query_graph->GetNumberOfNodes(); } - - unsigned GetNumberOfEdges() const override final { return m_query_graph->GetNumberOfEdges(); } - - unsigned GetOutDegree(const NodeID n) const override final - { - return m_query_graph->GetOutDegree(n); - } - - NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } - - EdgeDataT &GetEdgeData(const EdgeID e) const override final - { - return m_query_graph->GetEdgeData(e); - } - - EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph->BeginEdges(n); } - - EdgeID EndEdges(const NodeID n) const override final { return m_query_graph->EndEdges(n); } - - EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final - { - return m_query_graph->GetAdjacentEdgeRange(node); - }; - - // searches for a specific edge - EdgeID FindEdge(const NodeID from, const NodeID to) const override final - { - return m_query_graph->FindEdge(from, to); - } - - EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const override final - { - return m_query_graph->FindEdgeInEitherDirection(from, to); - } - - EdgeID - FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const override final - { - return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); - } - - // node and edge information access - FixedPointCoordinate GetCoordinateOfNode(const NodeID id) const override final - { - return m_coordinate_list->at(id); - }; - - virtual bool EdgeIsCompressed(const unsigned id) const override final - { - return m_edge_is_compressed.at(id); - } - - virtual void GetUncompressedGeometry(const unsigned id, - std::vector &result_nodes) const override final - { - const unsigned begin = m_geometry_indices.at(id); - const unsigned end = m_geometry_indices.at(id + 1); - - result_nodes.clear(); - result_nodes.insert(result_nodes.begin(), m_geometry_list.begin() + begin, - m_geometry_list.begin() + end); - } - - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final - { - return m_via_node_list.at(id); - } - - TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final - { - return m_turn_instruction_list.at(id); - } - - TravelMode GetTravelModeForEdgeID(const unsigned id) const override final - { - return m_travel_mode_list.at(id); - } - - std::vector - NearestPhantomNodesInRange(const FixedPointCoordinate &input_coordinate, - const float max_distance, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance, bearing, bearing_range); - } - - std::vector - NearestPhantomNodes(const FixedPointCoordinate &input_coordinate, - const unsigned max_results, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, bearing, bearing_range); - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const FixedPointCoordinate &input_coordinate, - const int bearing = 0, - const int bearing_range = 180) override final - { - if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) - { - LoadRTree(); - BOOST_ASSERT(m_geospatial_query.get()); - } - - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(input_coordinate, bearing, bearing_range); - } - - unsigned GetCheckSum() const override final { return m_check_sum; } - - unsigned GetNameIndexFromEdgeID(const unsigned id) const override final - { - return m_name_ID_list.at(id); - }; - - std::string get_name_for_id(const unsigned name_id) const override final - { - if (std::numeric_limits::max() == name_id) - { - return ""; - } - auto range = m_name_table->GetRange(name_id); - - std::string result; - result.reserve(range.size()); - if (range.begin() != range.end()) - { - result.resize(range.back() - range.front() + 1); - std::copy(m_names_char_list.begin() + range.front(), - m_names_char_list.begin() + range.back() + 1, result.begin()); - } - return result; - } - - bool IsCoreNode(const NodeID id) const override final - { - if (m_is_core_node.size() > 0) - { - return m_is_core_node.at(id); - } - - return false; - } - - virtual std::size_t GetCoreSize() const override final - { - return m_is_core_node.size(); - } - - std::string GetTimestamp() const override final { return m_timestamp; } -}; - -#endif // SHARED_DATAFACADE_HPP diff --git a/server/data_structures/shared_datatype.hpp b/server/data_structures/shared_datatype.hpp deleted file mode 100644 index c0ad7d45b..000000000 --- a/server/data_structures/shared_datatype.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef SHARED_DATA_TYPE_HPP -#define SHARED_DATA_TYPE_HPP - -#include "../../util/osrm_exception.hpp" -#include "../../util/simple_logger.hpp" - -#include - -#include - -namespace -{ -// Added at the start and end of each block as sanity check -static const char CANARY[] = "OSRM"; -} - -struct SharedDataLayout -{ - enum BlockID - { - NAME_OFFSETS = 0, - NAME_BLOCKS, - NAME_CHAR_LIST, - NAME_ID_LIST, - VIA_NODE_LIST, - GRAPH_NODE_LIST, - GRAPH_EDGE_LIST, - COORDINATE_LIST, - TURN_INSTRUCTION, - TRAVEL_MODE, - R_SEARCH_TREE, - GEOMETRIES_INDEX, - GEOMETRIES_LIST, - GEOMETRIES_INDICATORS, - HSGR_CHECKSUM, - TIMESTAMP, - FILE_INDEX_PATH, - CORE_MARKER, - NUM_BLOCKS - }; - - std::array num_entries; - std::array entry_size; - - SharedDataLayout() : num_entries(), entry_size() {} - - void PrintInformation() const - { - SimpleLogger().Write(logDEBUG) << "NAME_OFFSETS " - << ": " << GetBlockSize(NAME_OFFSETS); - SimpleLogger().Write(logDEBUG) << "NAME_BLOCKS " - << ": " << GetBlockSize(NAME_BLOCKS); - SimpleLogger().Write(logDEBUG) << "NAME_CHAR_LIST " - << ": " << GetBlockSize(NAME_CHAR_LIST); - SimpleLogger().Write(logDEBUG) << "NAME_ID_LIST " - << ": " << GetBlockSize(NAME_ID_LIST); - SimpleLogger().Write(logDEBUG) << "VIA_NODE_LIST " - << ": " << GetBlockSize(VIA_NODE_LIST); - SimpleLogger().Write(logDEBUG) << "GRAPH_NODE_LIST " - << ": " << GetBlockSize(GRAPH_NODE_LIST); - SimpleLogger().Write(logDEBUG) << "GRAPH_EDGE_LIST " - << ": " << GetBlockSize(GRAPH_EDGE_LIST); - SimpleLogger().Write(logDEBUG) << "COORDINATE_LIST " - << ": " << GetBlockSize(COORDINATE_LIST); - SimpleLogger().Write(logDEBUG) << "TURN_INSTRUCTION " - << ": " << GetBlockSize(TURN_INSTRUCTION); - SimpleLogger().Write(logDEBUG) << "TRAVEL_MODE " - << ": " << GetBlockSize(TRAVEL_MODE); - SimpleLogger().Write(logDEBUG) << "R_SEARCH_TREE " - << ": " << GetBlockSize(R_SEARCH_TREE); - SimpleLogger().Write(logDEBUG) << "GEOMETRIES_INDEX " - << ": " << GetBlockSize(GEOMETRIES_INDEX); - SimpleLogger().Write(logDEBUG) << "GEOMETRIES_LIST " - << ": " << GetBlockSize(GEOMETRIES_LIST); - SimpleLogger().Write(logDEBUG) << "GEOMETRIES_INDICATORS" - << ": " << GetBlockSize(GEOMETRIES_INDICATORS); - SimpleLogger().Write(logDEBUG) << "HSGR_CHECKSUM " - << ": " << GetBlockSize(HSGR_CHECKSUM); - SimpleLogger().Write(logDEBUG) << "TIMESTAMP " - << ": " << GetBlockSize(TIMESTAMP); - SimpleLogger().Write(logDEBUG) << "FILE_INDEX_PATH " - << ": " << GetBlockSize(FILE_INDEX_PATH); - SimpleLogger().Write(logDEBUG) << "CORE_MARKER " - << ": " << GetBlockSize(CORE_MARKER); - } - - template inline void SetBlockSize(BlockID bid, uint64_t entries) - { - num_entries[bid] = entries; - entry_size[bid] = sizeof(T); - } - - inline uint64_t GetBlockSize(BlockID bid) const - { - // special bit encoding - if (bid == GEOMETRIES_INDICATORS || bid == CORE_MARKER) - { - return (num_entries[bid] / 32 + 1) * - entry_size[bid]; - } - - return num_entries[bid] * entry_size[bid]; - } - - inline uint64_t GetSizeOfLayout() const - { - return GetBlockOffset(NUM_BLOCKS) + NUM_BLOCKS * 2 * sizeof(CANARY); - } - - inline uint64_t GetBlockOffset(BlockID bid) const - { - uint64_t result = sizeof(CANARY); - for (auto i = 0; i < bid; i++) - { - result += GetBlockSize((BlockID)i) + 2 * sizeof(CANARY); - } - return result; - } - - template - inline T *GetBlockPtr(char *shared_memory, BlockID bid) - { - T *ptr = (T *)(shared_memory + GetBlockOffset(bid)); - if (WRITE_CANARY) - { - char *start_canary_ptr = shared_memory + GetBlockOffset(bid) - sizeof(CANARY); - char *end_canary_ptr = shared_memory + GetBlockOffset(bid) + GetBlockSize(bid); - std::copy(CANARY, CANARY + sizeof(CANARY), start_canary_ptr); - std::copy(CANARY, CANARY + sizeof(CANARY), end_canary_ptr); - } - else - { - char *start_canary_ptr = shared_memory + GetBlockOffset(bid) - sizeof(CANARY); - char *end_canary_ptr = shared_memory + GetBlockOffset(bid) + GetBlockSize(bid); - bool start_canary_alive = std::equal(CANARY, CANARY + sizeof(CANARY), start_canary_ptr); - bool end_canary_alive = std::equal(CANARY, CANARY + sizeof(CANARY), end_canary_ptr); - if (!start_canary_alive) - { - throw osrm::exception("Start canary of block corrupted."); - } - if (!end_canary_alive) - { - throw osrm::exception("End canary of block corrupted."); - } - } - - return ptr; - } -}; - -enum SharedDataType -{ - CURRENT_REGIONS, - LAYOUT_1, - DATA_1, - LAYOUT_2, - DATA_2, - LAYOUT_NONE, - DATA_NONE -}; - -struct SharedDataTimestamp -{ - SharedDataType layout; - SharedDataType data; - unsigned timestamp; -}; - -#endif /* SHARED_DATA_TYPE_HPP */ diff --git a/server/http/header.hpp b/server/http/header.hpp deleted file mode 100644 index f2598ba1e..000000000 --- a/server/http/header.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef HEADER_HPP -#define HEADER_HPP - -#include -#include - -namespace http -{ -struct header -{ - // explicitly use default copy c'tor as adding move c'tor - header &operator=(const header &other) = default; - header(std::string name, std::string value) : name(std::move(name)), value(std::move(value)) {} - header(header &&other) : name(std::move(other.name)), value(std::move(other.value)) {} - - void clear() - { - name.clear(); - value.clear(); - } - - std::string name; - std::string value; -}; -} - -#endif // HEADER_HPP diff --git a/server/http/reply.hpp b/server/http/reply.hpp deleted file mode 100644 index 733818c2c..000000000 --- a/server/http/reply.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef REPLY_HPP -#define REPLY_HPP - -#include "header.hpp" - -#include - -#include - -namespace http -{ -class reply -{ - public: - enum status_type - { - ok = 200, - bad_request = 400, - internal_server_error = 500 - } status; - - std::vector
headers; - std::vector to_buffers(); - std::vector headers_to_buffers(); - std::vector content; - static reply stock_reply(const status_type status); - void set_size(const std::size_t size); - void set_uncompressed_size(); - - reply(); - - private: - std::string status_to_string(reply::status_type status); - boost::asio::const_buffer status_to_buffer(reply::status_type status); -}; -} - -#endif // REPLY_HPP diff --git a/server/request_handler.cpp b/server/request_handler.cpp deleted file mode 100644 index 54f039502..000000000 --- a/server/request_handler.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "request_handler.hpp" - -#include "api_grammar.hpp" -#include "http/reply.hpp" -#include "http/request.hpp" - -#include "../util/json_renderer.hpp" -#include "../util/simple_logger.hpp" -#include "../util/string_util.hpp" -#include "../util/xml_renderer.hpp" -#include "../typedefs.h" - -#include -#include -#include - -#include - -#include -#include -#include - -RequestHandler::RequestHandler() : routing_machine(nullptr) {} - -void RequestHandler::handle_request(const http::request ¤t_request, - http::reply ¤t_reply) -{ - osrm::json::Object json_result; - - // parse command - try - { - std::string request_string; - URIDecode(current_request.uri, request_string); - - // deactivated as GCC apparently does not implement that, not even in 4.9 - // std::time_t t = std::time(nullptr); - // SimpleLogger().Write() << std::put_time(std::localtime(&t), "%m-%d-%Y %H:%M:%S") << - // " " << current_request.endpoint.to_string() << " " << - // current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ") << - // current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") << - // request; - - time_t ltime; - struct tm *time_stamp; - - ltime = time(nullptr); - time_stamp = localtime(<ime); - - // log timestamp - SimpleLogger().Write() << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday - << "-" << (time_stamp->tm_mon + 1 < 10 ? "0" : "") - << (time_stamp->tm_mon + 1) << "-" << 1900 + time_stamp->tm_year - << " " << (time_stamp->tm_hour < 10 ? "0" : "") - << time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "") - << time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "") - << time_stamp->tm_sec << " " << current_request.endpoint.to_string() - << " " << current_request.referrer - << (0 == current_request.referrer.length() ? "- " : " ") - << current_request.agent - << (0 == current_request.agent.length() ? "- " : " ") - << request_string; - - RouteParameters route_parameters; - APIGrammarParser api_parser(&route_parameters); - - auto api_iterator = request_string.begin(); - const bool result = - boost::spirit::qi::parse(api_iterator, request_string.end(), api_parser); - - // check if the was an error with the request - if (result && api_iterator == request_string.end()) - { - // parsing done, lets call the right plugin to handle the request - BOOST_ASSERT_MSG(routing_machine != nullptr, "pointer not init'ed"); - - if (!route_parameters.jsonp_parameter.empty()) - { // prepend response with jsonp parameter - const std::string json_p = (route_parameters.jsonp_parameter + "("); - current_reply.content.insert(current_reply.content.end(), json_p.begin(), json_p.end()); - } - - const int return_code = routing_machine->RunQuery(route_parameters, json_result); - json_result.values["status"] = return_code; - // 4xx bad request return code - if (return_code / 100 == 4) - { - current_reply.status = http::reply::bad_request; - current_reply.content.clear(); - route_parameters.output_format.clear(); - } - else - { - // 2xx valid request - BOOST_ASSERT(return_code / 100 == 2); - } - } - else - { - const auto position = std::distance(request_string.begin(), api_iterator); - - current_reply.status = http::reply::bad_request; - json_result.values["status"] = http::reply::bad_request; - json_result.values["status_message"] = "Query string malformed close to position " + std::to_string(position); - } - - current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*"); - current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET"); - current_reply.headers.emplace_back("Access-Control-Allow-Headers", - "X-Requested-With, Content-Type"); - - // set headers - current_reply.headers.emplace_back("Content-Length", - std::to_string(current_reply.content.size())); - if ("gpx" == route_parameters.output_format) - { // gpx file - osrm::json::gpx_render(current_reply.content, json_result.values["route"]); - current_reply.headers.emplace_back("Content-Type", - "application/gpx+xml; charset=UTF-8"); - current_reply.headers.emplace_back("Content-Disposition", - "attachment; filename=\"route.gpx\""); - } - else if (route_parameters.jsonp_parameter.empty()) - { // json file - osrm::json::render(current_reply.content, json_result); - current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8"); - current_reply.headers.emplace_back("Content-Disposition", - "inline; filename=\"response.json\""); - } - else - { // jsonp - osrm::json::render(current_reply.content, json_result); - current_reply.headers.emplace_back("Content-Type", "text/javascript; charset=UTF-8"); - current_reply.headers.emplace_back("Content-Disposition", - "inline; filename=\"response.js\""); - } - if (!route_parameters.jsonp_parameter.empty()) - { // append brace to jsonp response - current_reply.content.push_back(')'); - } - } - catch (const std::exception &e) - { - current_reply = http::reply::stock_reply(http::reply::internal_server_error);; - SimpleLogger().Write(logWARNING) << "[server error] code: " << e.what() - << ", uri: " << current_request.uri; - } -} - -void RequestHandler::RegisterRoutingMachine(OSRM *osrm) { routing_machine = osrm; } diff --git a/server/request_handler.hpp b/server/request_handler.hpp deleted file mode 100644 index b4019db7d..000000000 --- a/server/request_handler.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef REQUEST_HANDLER_HPP -#define REQUEST_HANDLER_HPP - -#include - -template struct APIGrammar; -struct RouteParameters; -class OSRM; - -namespace http -{ -class reply; -struct request; -} - -class RequestHandler -{ - - public: - using APIGrammarParser = APIGrammar; - - RequestHandler(); - RequestHandler(const RequestHandler &) = delete; - - void handle_request(const http::request ¤t_request, http::reply ¤t_reply); - void RegisterRoutingMachine(OSRM *osrm); - - private: - OSRM *routing_machine; -}; - -#endif // REQUEST_HANDLER_HPP diff --git a/server/request_parser.cpp b/server/request_parser.cpp deleted file mode 100644 index b52d730d9..000000000 --- a/server/request_parser.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "request_parser.hpp" - -#include "http/compression_type.hpp" -#include "http/header.hpp" -#include "http/request.hpp" - -#include "../data_structures/tribool.hpp" - -#include - -#include - -namespace http -{ - -RequestParser::RequestParser() - : state(internal_state::method_start), current_header({"", ""}), - selected_compression(no_compression), is_post_header(false), - content_length(0) -{ -} - -std::tuple -RequestParser::parse(request ¤t_request, char *begin, char *end) -{ - while (begin != end) - { - osrm::tribool result = consume(current_request, *begin++); - if (result != osrm::tribool::indeterminate) - { - return std::make_tuple(result, selected_compression); - } - } - osrm::tribool result = osrm::tribool::indeterminate; - - if(state == internal_state::post_request && content_length <= 0) - { - result = osrm::tribool::yes; - } - return std::make_tuple(result, selected_compression); -} - -osrm::tribool RequestParser::consume(request ¤t_request, const char input) -{ - switch (state) - { - case internal_state::method_start: - if (!is_char(input) || is_CTL(input) || is_special(input)) - { - return osrm::tribool::no; - } - if(input == 'P') - { - state = internal_state::post_O; - return osrm::tribool::indeterminate; - } - state = internal_state::method; - return osrm::tribool::indeterminate; - case internal_state::post_O: - if(input == 'O') - { - state = internal_state::post_S; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::post_S: - if(input == 'S') - { - state = internal_state::post_T; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::post_T: - if(input == 'T') - { - is_post_header = true; - state = internal_state::method; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::post_request: - current_request.uri.push_back(input); - --content_length; - return osrm::tribool::indeterminate; - case internal_state::method: - if (input == ' ') - { - state = internal_state::uri; - return osrm::tribool::indeterminate; - } - if (!is_char(input) || is_CTL(input) || is_special(input)) - { - return osrm::tribool::no; - } - return osrm::tribool::indeterminate; - case internal_state::uri_start: - if (is_CTL(input)) - { - return osrm::tribool::no; - } - state = internal_state::uri; - current_request.uri.push_back(input); - return osrm::tribool::indeterminate; - case internal_state::uri: - if (input == ' ') - { - state = internal_state::http_version_h; - return osrm::tribool::indeterminate; - } - if (is_CTL(input)) - { - return osrm::tribool::no; - } - current_request.uri.push_back(input); - return osrm::tribool::indeterminate; - case internal_state::http_version_h: - if (input == 'H') - { - state = internal_state::http_version_t_1; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_t_1: - if (input == 'T') - { - state = internal_state::http_version_t_2; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_t_2: - if (input == 'T') - { - state = internal_state::http_version_p; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_p: - if (input == 'P') - { - state = internal_state::http_version_slash; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_slash: - if (input == '/') - { - state = internal_state::http_version_major_start; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_major_start: - if (is_digit(input)) - { - state = internal_state::http_version_major; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_major: - if (input == '.') - { - state = internal_state::http_version_minor_start; - return osrm::tribool::indeterminate; - } - if (is_digit(input)) - { - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_minor_start: - if (is_digit(input)) - { - state = internal_state::http_version_minor; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::http_version_minor: - if (input == '\r') - { - state = internal_state::expecting_newline_1; - return osrm::tribool::indeterminate; - } - if (is_digit(input)) - { - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::expecting_newline_1: - if (input == '\n') - { - state = internal_state::header_line_start; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::header_line_start: - if (boost::iequals(current_header.name, "Accept-Encoding")) - { - /* giving gzip precedence over deflate */ - if (boost::icontains(current_header.value, "deflate")) - { - selected_compression = deflate_rfc1951; - } - if (boost::icontains(current_header.value, "gzip")) - { - selected_compression = gzip_rfc1952; - } - } - - if (boost::iequals(current_header.name, "Referer")) - { - current_request.referrer = current_header.value; - } - - if (boost::iequals(current_header.name, "User-Agent")) - { - current_request.agent = current_header.value; - } - if (boost::iequals(current_header.name, "Content-Length")) - { - try - { - content_length = std::stoi(current_header.value); - } - catch (const std::exception &e) - { - // Ignore the header if the parameter isn't an int - } - } - if (boost::iequals(current_header.name, "Content-Type")) - { - if (!boost::icontains(current_header.value, "application/x-www-form-urlencoded")) - { - return osrm::tribool::no; - } - } - - if (input == '\r') - { - state = internal_state::expecting_newline_3; - return osrm::tribool::indeterminate; - } - if (!is_char(input) || is_CTL(input) || is_special(input)) - { - return osrm::tribool::no; - } - state = internal_state::header_name; - current_header.clear(); - current_header.name.push_back(input); - return osrm::tribool::indeterminate; - case internal_state::header_lws: - if (input == '\r') - { - state = internal_state::expecting_newline_2; - return osrm::tribool::indeterminate; - } - if (input == ' ' || input == '\t') - { - return osrm::tribool::indeterminate; - } - if (is_CTL(input)) - { - return osrm::tribool::no; - } - state = internal_state::header_value; - return osrm::tribool::indeterminate; - case internal_state::header_name: - if (input == ':') - { - state = internal_state::space_before_header_value; - return osrm::tribool::indeterminate; - } - if (!is_char(input) || is_CTL(input) || is_special(input)) - { - return osrm::tribool::no; - } - current_header.name.push_back(input); - return osrm::tribool::indeterminate; - case internal_state::space_before_header_value: - if (input == ' ') - { - state = internal_state::header_value; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::header_value: - if (input == '\r') - { - state = internal_state::expecting_newline_2; - return osrm::tribool::indeterminate; - } - if (is_CTL(input)) - { - return osrm::tribool::no; - } - current_header.value.push_back(input); - return osrm::tribool::indeterminate; - case internal_state::expecting_newline_2: - if (input == '\n') - { - state = internal_state::header_line_start; - return osrm::tribool::indeterminate; - } - return osrm::tribool::no; - case internal_state::expecting_newline_3: - if (input == '\n') - { - if (is_post_header) - { - if (content_length > 0) - { - current_request.uri.push_back('?'); - } - state = internal_state::post_request; - return osrm::tribool::indeterminate; - } - return osrm::tribool::yes; - } - return osrm::tribool::no; - default: // should never be reached - return input == '\n' ? osrm::tribool::yes : osrm::tribool::no; - } -} - -bool RequestParser::is_char(const int character) const -{ - return character >= 0 && character <= 127; -} - -bool RequestParser::is_CTL(const int character) const -{ - return (character >= 0 && character <= 31) || (character == 127); -} - -bool RequestParser::is_special(const int character) const -{ - switch (character) - { - case '(': - case ')': - case '<': - case '>': - case '@': - case ',': - case ';': - case ':': - case '\\': - case '"': - case '/': - case '[': - case ']': - case '?': - case '=': - case '{': - case '}': - case ' ': - case '\t': - return true; - default: - return false; - } -} - -bool RequestParser::is_digit(const int character) const -{ - return character >= '0' && character <= '9'; -} -} diff --git a/server/request_parser.hpp b/server/request_parser.hpp deleted file mode 100644 index 3724613a7..000000000 --- a/server/request_parser.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef REQUEST_PARSER_HPP -#define REQUEST_PARSER_HPP - -#include "http/compression_type.hpp" -#include "http/header.hpp" -#include "../data_structures/tribool.hpp" - -#include - -namespace http -{ - -struct request; - -class RequestParser -{ - public: - RequestParser(); - - std::tuple - parse(request ¤t_request, char *begin, char *end); - - private: - osrm::tribool consume(request ¤t_request, const char input); - - bool is_char(const int character) const; - - bool is_CTL(const int character) const; - - bool is_special(const int character) const; - - bool is_digit(const int character) const; - - enum class internal_state : unsigned char - { - method_start, - method, - uri_start, - uri, - http_version_h, - http_version_t_1, - http_version_t_2, - http_version_p, - http_version_slash, - http_version_major_start, - http_version_major, - http_version_minor_start, - http_version_minor, - expecting_newline_1, - header_line_start, - header_lws, - header_name, - space_before_header_value, - header_value, - expecting_newline_2, - expecting_newline_3, - post_O, - post_S, - post_T, - post_request - } state; - - header current_header; - compression_type selected_compression; - bool is_post_header; - int content_length; -}; - -} // namespace http - -#endif // REQUEST_PARSER_HPP diff --git a/src/benchmarks/CMakeLists.txt b/src/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..754dc7527 --- /dev/null +++ b/src/benchmarks/CMakeLists.txt @@ -0,0 +1,20 @@ +file(GLOB RTreeBenchmarkSources + *.cpp) + +add_executable(rtree-bench + EXCLUDE_FROM_ALL + ${RTreeBenchmarkSources} + $) + +target_include_directories(rtree-bench + PUBLIC + ${PROJECT_SOURCE_DIR}/unit_tests) + +target_link_libraries(rtree-bench + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${TBB_LIBRARIES}) + +add_custom_target(benchmarks + DEPENDS + rtree-bench) diff --git a/src/benchmarks/static_rtree.cpp b/src/benchmarks/static_rtree.cpp new file mode 100644 index 000000000..32929eb07 --- /dev/null +++ b/src/benchmarks/static_rtree.cpp @@ -0,0 +1,115 @@ +#include "engine/geospatial_query.hpp" +#include "extractor/query_node.hpp" +#include "extractor/edge_based_node.hpp" +#include "util/static_rtree.hpp" +#include "util/timing_util.hpp" +#include "util/coordinate.hpp" +#include "mocks/mock_datafacade.hpp" + +#include +#include + +#include + +namespace osrm +{ +namespace benchmarks +{ + +using namespace osrm::test; + +// Choosen by a fair W20 dice roll (this value is completely arbitrary) +constexpr unsigned RANDOM_SEED = 13; +constexpr int32_t WORLD_MIN_LAT = -90 * COORDINATE_PRECISION; +constexpr int32_t WORLD_MAX_LAT = 90 * COORDINATE_PRECISION; +constexpr int32_t WORLD_MIN_LON = -180 * COORDINATE_PRECISION; +constexpr int32_t WORLD_MAX_LON = 180 * COORDINATE_PRECISION; + +using RTreeLeaf = extractor::EdgeBasedNode; +using CoordinateListPtr = std::shared_ptr>; +using BenchStaticRTree = + util::StaticRTree::vector, false>; + +CoordinateListPtr loadCoordinates(const boost::filesystem::path &nodes_file) +{ + boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary); + + extractor::QueryNode current_node; + unsigned coordinate_count = 0; + nodes_input_stream.read((char *)&coordinate_count, sizeof(unsigned)); + auto coords = std::make_shared>(coordinate_count); + for (unsigned i = 0; i < coordinate_count; ++i) + { + nodes_input_stream.read((char *)¤t_node, sizeof(extractor::QueryNode)); + coords->at(i) = util::Coordinate(current_node.lon, current_node.lat); + } + return coords; +} + +template +void benchmarkQuery(const std::vector &queries, + const std::string &name, + QueryT query) +{ + std::cout << "Running " << name << " with " << queries.size() << " coordinates: " << std::flush; + + TIMER_START(query); + for (const auto &q : queries) + { + auto result = query(q); + (void)result; + } + TIMER_STOP(query); + + std::cout << "Took " << TIMER_SEC(query) << " seconds " + << "(" << TIMER_MSEC(query) << "ms" + << ") -> " << TIMER_MSEC(query) / queries.size() << " ms/query " + << "(" << TIMER_MSEC(query) << "ms" + << ")" << std::endl; +} + +void benchmark(BenchStaticRTree &rtree, unsigned num_queries) +{ + std::mt19937 mt_rand(RANDOM_SEED); + std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT); + std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON); + std::vector queries; + for (unsigned i = 0; i < num_queries; i++) + { + queries.emplace_back(util::FixedLongitude{lon_udist(mt_rand)}, + util::FixedLatitude{lat_udist(mt_rand)}); + } + + benchmarkQuery(queries, "raw RTree queries (1 result)", [&rtree](const util::Coordinate &q) + { + return rtree.Nearest(q, 1); + }); + benchmarkQuery(queries, "raw RTree queries (10 results)", [&rtree](const util::Coordinate &q) + { + return rtree.Nearest(q, 10); + }); +} +} +} + +int main(int argc, char **argv) +{ + if (argc < 4) + { + std::cout << "./rtree-bench file.ramIndex file.fileIndx file.nodes" + << "\n"; + return 1; + } + + const char *ram_path = argv[1]; + const char *file_path = argv[2]; + const char *nodes_path = argv[3]; + + auto coords = osrm::benchmarks::loadCoordinates(nodes_path); + + osrm::benchmarks::BenchStaticRTree rtree(ram_path, file_path, coords); + + osrm::benchmarks::benchmark(rtree, 10000); + + return 0; +} diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp new file mode 100644 index 000000000..72b2d34d4 --- /dev/null +++ b/src/contractor/contractor.cpp @@ -0,0 +1,686 @@ +#include "contractor/contractor.hpp" +#include "contractor/crc32_processor.hpp" +#include "contractor/graph_contractor.hpp" + +#include "extractor/node_based_edge.hpp" +#include "extractor/compressed_edge_container.hpp" + +#include "util/static_graph.hpp" +#include "util/static_rtree.hpp" +#include "util/graph_loader.hpp" +#include "util/io.hpp" +#include "util/integer_range.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/string_util.hpp" +#include "util/timing_util.hpp" +#include "util/typedefs.hpp" + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace std +{ + +template <> struct hash> +{ + std::size_t operator()(const std::pair &k) const + { + return static_cast(k.first) ^ (static_cast(k.second) << 12); + } +}; +} + +namespace osrm +{ +namespace contractor +{ + +int Contractor::Run() +{ +#ifdef WIN32 +#pragma message("Memory consumption on Windows can be higher due to different bit packing") +#else + static_assert(sizeof(extractor::NodeBasedEdge) == 20, + "changing extractor::NodeBasedEdge type has influence on memory consumption!"); + static_assert(sizeof(extractor::EdgeBasedEdge) == 16, + "changing EdgeBasedEdge type has influence on memory consumption!"); +#endif + + if (config.core_factor > 1.0 || config.core_factor < 0) + { + throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)"); + } + + TIMER_START(preparing); + + util::SimpleLogger().Write() << "Loading edge-expanded graph representation"; + + util::DeallocatingVector edge_based_edge_list; + + std::size_t max_edge_id = LoadEdgeExpandedGraph( + config.edge_based_graph_path, edge_based_edge_list, config.edge_segment_lookup_path, + config.edge_penalty_path, config.segment_speed_lookup_paths, config.node_based_graph_path, + config.geometry_path, config.datasource_names_path, config.datasource_indexes_path, + config.rtree_leaf_path); + + // Contracting the edge-expanded graph + + TIMER_START(contraction); + std::vector is_core_node; + std::vector node_levels; + if (config.use_cached_priority) + { + ReadNodeLevels(node_levels); + } + + util::SimpleLogger().Write() << "Reading node weights."; + std::vector node_weights; + std::string node_file_name = config.osrm_input_path.string() + ".enw"; + if (util::deserializeVector(node_file_name, node_weights)) + { + util::SimpleLogger().Write() << "Done reading node weights."; + } + else + { + throw util::exception("Failed reading node weights."); + } + + util::DeallocatingVector contracted_edge_list; + ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, std::move(node_weights), + is_core_node, node_levels); + TIMER_STOP(contraction); + + util::SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec"; + + std::size_t number_of_used_edges = WriteContractedGraph(max_edge_id, contracted_edge_list); + WriteCoreNodeMarker(std::move(is_core_node)); + if (!config.use_cached_priority) + { + WriteNodeLevels(std::move(node_levels)); + } + + TIMER_STOP(preparing); + + util::SimpleLogger().Write() << "Preprocessing : " << TIMER_SEC(preparing) << " seconds"; + util::SimpleLogger().Write() << "Contraction: " << ((max_edge_id + 1) / TIMER_SEC(contraction)) + << " nodes/sec and " + << number_of_used_edges / TIMER_SEC(contraction) << " edges/sec"; + + util::SimpleLogger().Write() << "finished preprocessing"; + + return 0; +} + +std::size_t Contractor::LoadEdgeExpandedGraph( + std::string const &edge_based_graph_filename, + util::DeallocatingVector &edge_based_edge_list, + const std::string &edge_segment_lookup_filename, + const std::string &edge_penalty_filename, + const std::vector &segment_speed_filenames, + const std::string &nodes_filename, + const std::string &geometry_filename, + const std::string &datasource_names_filename, + const std::string &datasource_indexes_filename, + const std::string &rtree_leaf_filename) +{ + util::SimpleLogger().Write() << "Opening " << edge_based_graph_filename; + boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary); + + const bool update_edge_weights = !segment_speed_filenames.empty(); + + boost::filesystem::ifstream edge_segment_input_stream; + boost::filesystem::ifstream edge_fixed_penalties_input_stream; + + if (update_edge_weights) + { + edge_segment_input_stream.open(edge_segment_lookup_filename, std::ios::binary); + edge_fixed_penalties_input_stream.open(edge_penalty_filename, std::ios::binary); + if (!edge_segment_input_stream || !edge_fixed_penalties_input_stream) + { + throw util::exception("Could not load .edge_segment_lookup or .edge_penalties, did you " + "run osrm-extract with '--generate-edge-lookup'?"); + } + } + + const util::FingerPrint fingerprint_valid = util::FingerPrint::GetValid(); + util::FingerPrint fingerprint_loaded; + input_stream.read((char *)&fingerprint_loaded, sizeof(util::FingerPrint)); + fingerprint_loaded.TestContractor(fingerprint_valid); + + // TODO std::size_t can vary on systems. Our files are not transferable, but we might want to + // consider using a fixed size type for I/O + std::size_t number_of_edges = 0; + std::size_t max_edge_id = SPECIAL_EDGEID; + input_stream.read((char *)&number_of_edges, sizeof(std::size_t)); + input_stream.read((char *)&max_edge_id, sizeof(std::size_t)); + + edge_based_edge_list.resize(number_of_edges); + util::SimpleLogger().Write() << "Reading " << number_of_edges + << " edges from the edge based graph"; + + std::unordered_map, std::pair> + segment_speed_lookup; + + // If we update the edge weights, this file will hold the datasource information + // for each segment + std::vector m_geometry_datasource; + + if (update_edge_weights) + { + uint8_t file_id = 1; + for (auto segment_speed_filename : segment_speed_filenames) + { + util::SimpleLogger().Write() + << "Segment speed data supplied, will update edge weights from " + << segment_speed_filename; + io::CSVReader<3> csv_in(segment_speed_filename); + csv_in.set_header("from_node", "to_node", "speed"); + uint64_t from_node_id{}; + uint64_t to_node_id{}; + unsigned speed{}; + while (csv_in.read_row(from_node_id, to_node_id, speed)) + { + segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id), + OSMNodeID(to_node_id))] = + std::make_pair(speed, file_id); + } + ++file_id; + + // Check for overflow + if (file_id == 0) + { + throw util::exception( + "Sorry, there's a limit of 254 segment speed files, you supplied too many"); + } + } + + std::vector internal_to_external_node_map; + + // Here, we have to update the compressed geometry weights + // First, we need the external-to-internal node lookup table + { + boost::filesystem::ifstream nodes_input_stream(nodes_filename, std::ios::binary); + + if (!nodes_input_stream) + { + throw util::exception("Failed to open " + nodes_filename); + } + + unsigned number_of_nodes = 0; + nodes_input_stream.read((char *)&number_of_nodes, sizeof(unsigned)); + internal_to_external_node_map.resize(number_of_nodes); + + // Load all the query nodes into a vector + nodes_input_stream.read(reinterpret_cast(&(internal_to_external_node_map[0])), + number_of_nodes * sizeof(extractor::QueryNode)); + } + + std::vector m_geometry_indices; + std::vector m_geometry_list; + + { + std::ifstream geometry_stream(geometry_filename, std::ios::binary); + if (!geometry_stream) + { + throw util::exception("Failed to open " + geometry_filename); + } + unsigned number_of_indices = 0; + unsigned number_of_compressed_geometries = 0; + + geometry_stream.read((char *)&number_of_indices, sizeof(unsigned)); + + m_geometry_indices.resize(number_of_indices); + if (number_of_indices > 0) + { + geometry_stream.read((char *)&(m_geometry_indices[0]), + number_of_indices * sizeof(unsigned)); + } + + geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned)); + + BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries); + m_geometry_list.resize(number_of_compressed_geometries); + + if (number_of_compressed_geometries > 0) + { + geometry_stream.read( + (char *)&(m_geometry_list[0]), + number_of_compressed_geometries * + sizeof(extractor::CompressedEdgeContainer::CompressedEdge)); + } + } + + // This is a list of the "data source id" for every segment in the compressed + // geometry container. We assume that everything so far has come from the + // profile (data source 0). Here, we replace the 0's with the index of the + // CSV file that supplied the value that gets used for that segment, then + // we write out this list so that it can be returned by the debugging + // vector tiles later on. + m_geometry_datasource.resize(m_geometry_list.size(), 0); + + // Now, we iterate over all the segments stored in the StaticRTree, updating + // the packed geometry weights in the `.geometries` file (note: we do not + // update the RTree itself, we just use the leaf nodes to iterate over all segments) + { + + using LeafNode = util::StaticRTree::LeafNode; + + std::ifstream leaf_node_file(rtree_leaf_filename, std::ios::binary | std::ios::in); + if (!leaf_node_file) + { + throw util::exception("Failed to open " + rtree_leaf_filename); + } + uint64_t m_element_count; + leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t)); + + LeafNode current_node; + while (m_element_count > 0) + { + leaf_node_file.read(reinterpret_cast(¤t_node), sizeof(current_node)); + + for (size_t i = 0; i < current_node.object_count; i++) + { + auto &leaf_object = current_node.objects[i]; + extractor::QueryNode *u; + extractor::QueryNode *v; + + if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID) + { + const unsigned forward_begin = + m_geometry_indices.at(leaf_object.forward_packed_geometry_id); + + if (leaf_object.fwd_segment_position == 0) + { + u = &(internal_to_external_node_map[leaf_object.u]); + v = &(internal_to_external_node_map[m_geometry_list[forward_begin] + .node_id]); + } + else + { + u = &(internal_to_external_node_map + [m_geometry_list[forward_begin + + leaf_object.fwd_segment_position - 1] + .node_id]); + v = &(internal_to_external_node_map + [m_geometry_list[forward_begin + + leaf_object.fwd_segment_position] + .node_id]); + } + const double segment_length = + util::coordinate_calculation::greatCircleDistance( + util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat}); + + auto forward_speed_iter = + segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id)); + if (forward_speed_iter != segment_speed_lookup.end()) + { + int new_segment_weight = + std::max(1, static_cast(std::floor( + (segment_length * 10.) / + (forward_speed_iter->second.first / 3.6) + + .5))); + m_geometry_list[forward_begin + leaf_object.fwd_segment_position] + .weight = new_segment_weight; + m_geometry_datasource[forward_begin + + leaf_object.fwd_segment_position] = + forward_speed_iter->second.second; + } + } + if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID) + { + const unsigned reverse_begin = + m_geometry_indices.at(leaf_object.reverse_packed_geometry_id); + const unsigned reverse_end = + m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1); + + int rev_segment_position = + (reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1; + if (rev_segment_position == 0) + { + u = &(internal_to_external_node_map[leaf_object.v]); + v = &(internal_to_external_node_map[m_geometry_list[reverse_begin] + .node_id]); + } + else + { + u = &(internal_to_external_node_map + [m_geometry_list[reverse_begin + rev_segment_position - 1] + .node_id]); + v = &( + internal_to_external_node_map[m_geometry_list[reverse_begin + + rev_segment_position] + .node_id]); + } + const double segment_length = + util::coordinate_calculation::greatCircleDistance( + util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat}); + + auto reverse_speed_iter = + segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id)); + if (reverse_speed_iter != segment_speed_lookup.end()) + { + int new_segment_weight = + std::max(1, static_cast(std::floor( + (segment_length * 10.) / + (reverse_speed_iter->second.first / 3.6) + + .5))); + m_geometry_list[reverse_begin + rev_segment_position].weight = + new_segment_weight; + m_geometry_datasource[reverse_begin + rev_segment_position] = + reverse_speed_iter->second.second; + } + } + } + m_element_count -= current_node.object_count; + } + } + + // Now save out the updated compressed geometries + { + std::ofstream geometry_stream(geometry_filename, std::ios::binary); + if (!geometry_stream) + { + throw util::exception("Failed to open " + geometry_filename + " for writing"); + } + const unsigned number_of_indices = m_geometry_indices.size(); + const unsigned number_of_compressed_geometries = m_geometry_list.size(); + geometry_stream.write(reinterpret_cast(&number_of_indices), + sizeof(unsigned)); + geometry_stream.write(reinterpret_cast(&(m_geometry_indices[0])), + number_of_indices * sizeof(unsigned)); + geometry_stream.write(reinterpret_cast(&number_of_compressed_geometries), + sizeof(unsigned)); + geometry_stream.write(reinterpret_cast(&(m_geometry_list[0])), + number_of_compressed_geometries * + sizeof(extractor::CompressedEdgeContainer::CompressedEdge)); + } + } + + { + std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary); + if (!datasource_stream) + { + throw util::exception("Failed to open " + datasource_indexes_filename + + " for writing"); + } + auto number_of_datasource_entries = m_geometry_datasource.size(); + datasource_stream.write(reinterpret_cast(&number_of_datasource_entries), + sizeof(number_of_datasource_entries)); + if (number_of_datasource_entries > 0) + { + datasource_stream.write(reinterpret_cast(&(m_geometry_datasource[0])), + number_of_datasource_entries * sizeof(uint8_t)); + } + } + + { + std::ofstream datasource_stream(datasource_names_filename, std::ios::binary); + if (!datasource_stream) + { + throw util::exception("Failed to open " + datasource_names_filename + + " for writing"); + } + datasource_stream << "lua profile" << std::endl; + for (auto const &name : segment_speed_filenames) + { + datasource_stream << name << std::endl; + } + } + + // TODO: can we read this in bulk? util::DeallocatingVector isn't necessarily + // all stored contiguously + for (; number_of_edges > 0; --number_of_edges) + { + extractor::EdgeBasedEdge inbuffer; + input_stream.read((char *)&inbuffer, sizeof(extractor::EdgeBasedEdge)); + if (update_edge_weights) + { + // Processing-time edge updates + unsigned fixed_penalty; + edge_fixed_penalties_input_stream.read(reinterpret_cast(&fixed_penalty), + sizeof(fixed_penalty)); + + int new_weight = 0; + + unsigned num_osm_nodes = 0; + edge_segment_input_stream.read(reinterpret_cast(&num_osm_nodes), + sizeof(num_osm_nodes)); + OSMNodeID previous_osm_node_id; + edge_segment_input_stream.read(reinterpret_cast(&previous_osm_node_id), + sizeof(previous_osm_node_id)); + OSMNodeID this_osm_node_id; + double segment_length; + int segment_weight; + --num_osm_nodes; + for (; num_osm_nodes != 0; --num_osm_nodes) + { + edge_segment_input_stream.read(reinterpret_cast(&this_osm_node_id), + sizeof(this_osm_node_id)); + edge_segment_input_stream.read(reinterpret_cast(&segment_length), + sizeof(segment_length)); + edge_segment_input_stream.read(reinterpret_cast(&segment_weight), + sizeof(segment_weight)); + + auto speed_iter = segment_speed_lookup.find( + std::make_pair(previous_osm_node_id, this_osm_node_id)); + if (speed_iter != segment_speed_lookup.end()) + { + // This sets the segment weight using the same formula as the + // EdgeBasedGraphFactory for consistency. The *why* of this formula + // is lost in the annals of time. + int new_segment_weight = std::max( + 1, static_cast(std::floor( + (segment_length * 10.) / (speed_iter->second.first / 3.6) + .5))); + new_weight += new_segment_weight; + } + else + { + // If no lookup found, use the original weight value for this segment + new_weight += segment_weight; + } + + previous_osm_node_id = this_osm_node_id; + } + + inbuffer.weight = fixed_penalty + new_weight; + } + + edge_based_edge_list.emplace_back(std::move(inbuffer)); + } + + util::SimpleLogger().Write() << "Done reading edges"; + return max_edge_id; +} + +void Contractor::ReadNodeLevels(std::vector &node_levels) const +{ + boost::filesystem::ifstream order_input_stream(config.level_output_path, std::ios::binary); + + unsigned level_size; + order_input_stream.read((char *)&level_size, sizeof(unsigned)); + node_levels.resize(level_size); + order_input_stream.read((char *)node_levels.data(), sizeof(float) * node_levels.size()); +} + +void Contractor::WriteNodeLevels(std::vector &&in_node_levels) const +{ + std::vector node_levels(std::move(in_node_levels)); + + boost::filesystem::ofstream order_output_stream(config.level_output_path, std::ios::binary); + + unsigned level_size = node_levels.size(); + order_output_stream.write((char *)&level_size, sizeof(unsigned)); + order_output_stream.write((char *)node_levels.data(), sizeof(float) * node_levels.size()); +} + +void Contractor::WriteCoreNodeMarker(std::vector &&in_is_core_node) const +{ + std::vector is_core_node(std::move(in_is_core_node)); + std::vector unpacked_bool_flags(std::move(is_core_node.size())); + for (auto i = 0u; i < is_core_node.size(); ++i) + { + unpacked_bool_flags[i] = is_core_node[i] ? 1 : 0; + } + + boost::filesystem::ofstream core_marker_output_stream(config.core_output_path, + std::ios::binary); + unsigned size = unpacked_bool_flags.size(); + core_marker_output_stream.write((char *)&size, sizeof(unsigned)); + core_marker_output_stream.write((char *)unpacked_bool_flags.data(), + sizeof(char) * unpacked_bool_flags.size()); +} + +std::size_t +Contractor::WriteContractedGraph(unsigned max_node_id, + const util::DeallocatingVector &contracted_edge_list) +{ + // Sorting contracted edges in a way that the static query graph can read some in in-place. + tbb::parallel_sort(contracted_edge_list.begin(), contracted_edge_list.end()); + const unsigned contracted_edge_count = contracted_edge_list.size(); + util::SimpleLogger().Write() << "Serializing compacted graph of " << contracted_edge_count + << " edges"; + + const util::FingerPrint fingerprint = util::FingerPrint::GetValid(); + boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary); + hsgr_output_stream.write((char *)&fingerprint, sizeof(util::FingerPrint)); + const unsigned max_used_node_id = [&contracted_edge_list] + { + unsigned tmp_max = 0; + for (const QueryEdge &edge : contracted_edge_list) + { + BOOST_ASSERT(SPECIAL_NODEID != edge.source); + BOOST_ASSERT(SPECIAL_NODEID != edge.target); + tmp_max = std::max(tmp_max, edge.source); + tmp_max = std::max(tmp_max, edge.target); + } + return tmp_max; + }(); + + util::SimpleLogger().Write(logDEBUG) << "input graph has " << (max_node_id + 1) << " nodes"; + util::SimpleLogger().Write(logDEBUG) << "contracted graph has " << (max_used_node_id + 1) + << " nodes"; + + std::vector::NodeArrayEntry> node_array; + // make sure we have at least one sentinel + node_array.resize(max_node_id + 2); + + util::SimpleLogger().Write() << "Building node array"; + util::StaticGraph::EdgeIterator edge = 0; + util::StaticGraph::EdgeIterator position = 0; + util::StaticGraph::EdgeIterator last_edge; + + // initializing 'first_edge'-field of nodes: + for (const auto node : util::irange(0u, max_used_node_id + 1)) + { + last_edge = edge; + while ((edge < contracted_edge_count) && (contracted_edge_list[edge].source == node)) + { + ++edge; + } + node_array[node].first_edge = position; //=edge + position += edge - last_edge; // remove + } + + for (const auto sentinel_counter : + util::irange(max_used_node_id + 1, node_array.size())) + { + // sentinel element, guarded against underflow + node_array[sentinel_counter].first_edge = contracted_edge_count; + } + + util::SimpleLogger().Write() << "Serializing node array"; + + RangebasedCRC32 crc32_calculator; + const unsigned edges_crc32 = crc32_calculator(contracted_edge_list); + util::SimpleLogger().Write() << "Writing CRC32: " << edges_crc32; + + const unsigned node_array_size = node_array.size(); + // serialize crc32, aka checksum + hsgr_output_stream.write((char *)&edges_crc32, sizeof(unsigned)); + // serialize number of nodes + hsgr_output_stream.write((char *)&node_array_size, sizeof(unsigned)); + // serialize number of edges + hsgr_output_stream.write((char *)&contracted_edge_count, sizeof(unsigned)); + // serialize all nodes + if (node_array_size > 0) + { + hsgr_output_stream.write((char *)&node_array[0], + sizeof(util::StaticGraph::NodeArrayEntry) * + node_array_size); + } + + // serialize all edges + util::SimpleLogger().Write() << "Building edge array"; + int number_of_used_edges = 0; + + util::StaticGraph::EdgeArrayEntry current_edge; + for (const auto edge : util::irange(0UL, contracted_edge_list.size())) + { + // some self-loops are required for oneway handling. Need to assertthat we only keep these + // (TODO) + // no eigen loops + // BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target || + // node_represents_oneway[contracted_edge_list[edge].source]); + current_edge.target = contracted_edge_list[edge].target; + current_edge.data = contracted_edge_list[edge].data; + + // every target needs to be valid + BOOST_ASSERT(current_edge.target <= max_used_node_id); +#ifndef NDEBUG + if (current_edge.data.distance <= 0) + { + util::SimpleLogger().Write(logWARNING) + << "Edge: " << edge << ",source: " << contracted_edge_list[edge].source + << ", target: " << contracted_edge_list[edge].target + << ", dist: " << current_edge.data.distance; + + util::SimpleLogger().Write(logWARNING) << "Failed at adjacency list of node " + << contracted_edge_list[edge].source << "/" + << node_array.size() - 1; + return 1; + } +#endif + hsgr_output_stream.write((char *)¤t_edge, + sizeof(util::StaticGraph::EdgeArrayEntry)); + + ++number_of_used_edges; + } + + return number_of_used_edges; +} + +/** + \brief Build contracted graph. + */ +void Contractor::ContractGraph( + const unsigned max_edge_id, + util::DeallocatingVector &edge_based_edge_list, + util::DeallocatingVector &contracted_edge_list, + std::vector &&node_weights, + std::vector &is_core_node, + std::vector &inout_node_levels) const +{ + std::vector node_levels; + node_levels.swap(inout_node_levels); + + GraphContractor graph_contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels), + std::move(node_weights)); + graph_contractor.Run(config.core_factor); + graph_contractor.GetEdges(contracted_edge_list); + graph_contractor.GetCoreMarker(is_core_node); + graph_contractor.GetNodeLevels(inout_node_levels); +} +} +} diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp new file mode 100644 index 000000000..4d5ff9b4e --- /dev/null +++ b/src/engine/api/json_factory.cpp @@ -0,0 +1,224 @@ +#include "engine/api/json_factory.hpp" + +#include "engine/hint.hpp" +#include "engine/polyline_compressor.hpp" +#include "util/integer_range.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +using TurnType = osrm::extractor::guidance::TurnType; +using DirectionModifier = osrm::extractor::guidance::DirectionModifier; +using TurnInstruction = osrm::extractor::guidance::TurnInstruction; + +namespace osrm +{ +namespace engine +{ +namespace api +{ +namespace json +{ +namespace detail +{ + +const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "slight right", + "straight", "slight left", "left", "sharp left"}; + +// translations of TurnTypes. Not all types are exposed to the outside world. +// invalid types should never be returned as part of the API +const constexpr char *turn_type_names[] = { + "invalid", "no turn", "invalid", "new name", "continue", "turn", + "turn", "turn", "turn", "turn", "merge", "ramp", + "ramp", "ramp", "ramp", "ramp", "fork", "end of road", + "roundabout", "invalid", "roundabout", "invalid", "rotary", "invalid", + "rotary", "invalid", "invalid", "restriction", "notification"}; +const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"}; + +// Check whether to include a modifier in the result of the API +inline bool isValidModifier(const guidance::StepManeuver maneuver) +{ + if (maneuver.waypoint_type != guidance::WaypointType::None && + maneuver.instruction.direction_modifier == DirectionModifier::UTurn) + return false; + return true; +} + +std::string instructionTypeToString(const TurnType type) +{ + return turn_type_names[static_cast(type)]; +} + +std::string instructionModifierToString(const DirectionModifier modifier) +{ + return modifier_names[static_cast(modifier)]; +} + +std::string waypointTypeToString(const guidance::WaypointType waypoint_type) +{ + return waypoint_type_names[static_cast(waypoint_type)]; +} + +util::json::Array coordinateToLonLat(const util::Coordinate coordinate) +{ + util::json::Array array; + array.values.push_back(static_cast(toFloating(coordinate.lon))); + array.values.push_back(static_cast(toFloating(coordinate.lat))); + return array; +} + +// FIXME this actually needs to be configurable from the profiles +std::string modeToString(const extractor::TravelMode mode) +{ + std::string token; + switch (mode) + { + case TRAVEL_MODE_INACCESSIBLE: + token = "inaccessible"; + break; + case TRAVEL_MODE_DRIVING: + token = "driving"; + break; + case TRAVEL_MODE_CYCLING: + token = "cycling"; + break; + case TRAVEL_MODE_WALKING: + token = "walking"; + break; + case TRAVEL_MODE_FERRY: + token = "ferry"; + break; + case TRAVEL_MODE_TRAIN: + token = "train"; + break; + case TRAVEL_MODE_PUSHING_BIKE: + token = "pushing bike"; + break; + case TRAVEL_MODE_STEPS_UP: + token = "steps up"; + break; + case TRAVEL_MODE_STEPS_DOWN: + token = "steps down"; + break; + case TRAVEL_MODE_RIVER_UP: + token = "river upstream"; + break; + case TRAVEL_MODE_RIVER_DOWN: + token = "river downstream"; + break; + case TRAVEL_MODE_ROUTE: + token = "route"; + break; + default: + token = "other"; + break; + } + return token; +} + +} // namespace detail + +util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver) +{ + util::json::Object step_maneuver; + if (maneuver.waypoint_type == guidance::WaypointType::None) + step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type); + else + step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type); + + if (detail::isValidModifier(maneuver)) + step_maneuver.values["modifier"] = + detail::instructionModifierToString(maneuver.instruction.direction_modifier); + + step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location); + step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before); + step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after); + if (maneuver.exit != 0) + step_maneuver.values["exit"] = maneuver.exit; + + // TODO currently we need this to comply with the api. + // We should move this to an additional entry, the moment we + // actually compute the correct locations of the intersections + if (!maneuver.intersections.empty() && maneuver.exit == 0) + step_maneuver.values["exit"] = maneuver.intersections.size(); + return step_maneuver; +} + +util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geometry) +{ + util::json::Object route_step; + route_step.values["distance"] = std::round(step.distance * 10) / 10.; + route_step.values["duration"] = std::round(step.duration * 10) / 10.; + route_step.values["name"] = std::move(step.name); + if (!step.rotary_name.empty()) + route_step.values["rotary_name"] = std::move(step.rotary_name); + + route_step.values["mode"] = detail::modeToString(std::move(step.mode)); + route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver)); + route_step.values["geometry"] = std::move(geometry); + return route_step; +} + +util::json::Object makeRoute(const guidance::Route &route, + util::json::Array legs, + boost::optional geometry) +{ + util::json::Object json_route; + json_route.values["distance"] = std::round(route.distance * 10) / 10.; + json_route.values["duration"] = std::round(route.duration * 10) / 10.; + json_route.values["legs"] = std::move(legs); + if (geometry) + { + json_route.values["geometry"] = *std::move(geometry); + } + return json_route; +} + +util::json::Object makeWaypoint(const util::Coordinate location, std::string name, const Hint &hint) +{ + util::json::Object waypoint; + waypoint.values["location"] = detail::coordinateToLonLat(location); + waypoint.values["name"] = std::move(name); + waypoint.values["hint"] = hint.ToBase64(); + return waypoint; +} + +util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps) +{ + util::json::Object route_leg; + route_leg.values["distance"] = std::round(leg.distance * 10) / 10.; + route_leg.values["duration"] = std::round(leg.duration * 10) / 10.; + route_leg.values["summary"] = std::move(leg.summary); + route_leg.values["steps"] = std::move(steps); + return route_leg; +} + +util::json::Array makeRouteLegs(std::vector legs, + std::vector step_geometries) +{ + util::json::Array json_legs; + auto step_geometry_iter = step_geometries.begin(); + for (const auto idx : util::irange(0UL, legs.size())) + { + auto leg = std::move(legs[idx]); + util::json::Array json_steps; + json_steps.values.reserve(leg.steps.size()); + std::transform( + std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()), + std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step) { + return makeRouteStep(std::move(step), std::move(*step_geometry_iter++)); + }); + json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps))); + } + return json_legs; +} +} // namespace json +} // namespace api +} // namespace engine +} // namespace osrm diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp new file mode 100644 index 000000000..a3eb762d7 --- /dev/null +++ b/src/engine/douglas_peucker.cpp @@ -0,0 +1,128 @@ +#include "engine/douglas_peucker.hpp" +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/integer_range.hpp" +#include "util/web_mercator.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +struct FastPerpendicularDistance +{ + FastPerpendicularDistance(const util::Coordinate start, const util::Coordinate target) + : projected_start(util::web_mercator::fromWGS84(start)), + projected_target(util::web_mercator::fromWGS84(target)) + { + } + + // Normed to the thresholds table + std::uint64_t operator()(const util::Coordinate coordinate) const + { + auto projected = util::web_mercator::fromWGS84(coordinate); + util::FloatCoordinate projected_point_on_segment; + std::tie(std::ignore, projected_point_on_segment) = + util::coordinate_calculation::projectPointOnSegment(projected_start, projected_target, + projected); + auto squared_distance = util::coordinate_calculation::squaredEuclideanDistance(projected, + projected_point_on_segment); + return squared_distance; + } + + const util::FloatCoordinate projected_start; + const util::FloatCoordinate projected_target; +}; + +std::vector douglasPeucker(std::vector::const_iterator begin, + std::vector::const_iterator end, + const unsigned zoom_level) +{ + BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE, + "unsupported zoom level"); + + const std::size_t size = std::distance(begin, end); + if (size < 2) + { + return {}; + } + + std::vector is_necessary(size, false); + BOOST_ASSERT(is_necessary.size() >= 2); + is_necessary.front() = true; + is_necessary.back() = true; + using GeometryRange = std::pair; + + std::stack recursion_stack; + + recursion_stack.emplace(0UL, size - 1); + + // mark locations as 'necessary' by divide-and-conquer + while (!recursion_stack.empty()) + { + // pop next element + const GeometryRange pair = recursion_stack.top(); + recursion_stack.pop(); + // sanity checks + BOOST_ASSERT_MSG(is_necessary[pair.first], "left border must be necessary"); + BOOST_ASSERT_MSG(is_necessary[pair.second], "right border must be necessary"); + BOOST_ASSERT_MSG(pair.second < size, "right border outside of geometry"); + BOOST_ASSERT_MSG(pair.first <= pair.second, "left border on the wrong side"); + + std::uint64_t max_distance = 0; + auto farthest_entry_index = pair.second; + + FastPerpendicularDistance perpendicular_distance(begin[pair.first], begin[pair.second]); + + // sweep over range to find the maximum + for (auto idx = pair.first + 1; idx != pair.second; ++idx) + { + using namespace util::coordinate_calculation; + const auto distance = perpendicular_distance(begin[idx]); + // found new feasible maximum? + if (distance > max_distance && + distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + { + farthest_entry_index = idx; + max_distance = distance; + } + } + + // check if maximum violates a zoom level dependent threshold + if (max_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + { + // mark idx as necessary + is_necessary[farthest_entry_index] = true; + if (pair.first < farthest_entry_index) + { + recursion_stack.emplace(pair.first, farthest_entry_index); + } + if (farthest_entry_index < pair.second) + { + recursion_stack.emplace(farthest_entry_index, pair.second); + } + } + } + + auto simplified_size = std::count(is_necessary.begin(), is_necessary.end(), true); + std::vector simplified_geometry; + simplified_geometry.reserve(simplified_size); + for (auto idx : util::irange(0UL, size)) + { + if (is_necessary[idx]) + { + simplified_geometry.push_back(begin[idx]); + } + } + return simplified_geometry; +} +} // ns engine +} // ns osrm diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 000000000..9207c44c0 --- /dev/null +++ b/src/engine/engine.cpp @@ -0,0 +1,190 @@ +#include "engine/engine.hpp" +#include "engine/engine_config.hpp" +#include "engine/api/route_parameters.hpp" +#include "engine/status.hpp" + +#include "engine/plugins/table.hpp" +#include "engine/plugins/nearest.hpp" +#include "engine/plugins/trip.hpp" +#include "engine/plugins/viaroute.hpp" +#include "engine/plugins/tile.hpp" +#include "engine/plugins/match.hpp" + +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/datafacade/internal_datafacade.hpp" +#include "engine/datafacade/shared_datafacade.hpp" + +#include "storage/shared_barriers.hpp" +#include "util/make_unique.hpp" +#include "util/simple_logger.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +struct Engine::EngineLock +{ + // will only be initialized if shared memory is used + storage::SharedBarriers barrier; + // decrease number of concurrent queries + void DecreaseQueryCount(); + // increase number of concurrent queries + void IncreaseQueryCount(); +}; + +// decrease number of concurrent queries +void Engine::EngineLock::DecreaseQueryCount() +{ + // lock query + boost::interprocess::scoped_lock query_lock( + barrier.query_mutex); + + // decrement query count + --(barrier.number_of_queries); + BOOST_ASSERT_MSG(0 <= barrier.number_of_queries, "invalid number of queries"); + + // notify all processes that were waiting for this condition + if (0 == barrier.number_of_queries) + { + barrier.no_running_queries_condition.notify_all(); + } +} + +// increase number of concurrent queries +void Engine::EngineLock::IncreaseQueryCount() +{ + // lock update pending + boost::interprocess::scoped_lock pending_lock( + barrier.pending_update_mutex); + + // lock query + boost::interprocess::scoped_lock query_lock( + barrier.query_mutex); + + // unlock update pending + pending_lock.unlock(); + + // increment query count + ++(barrier.number_of_queries); +} +} // ns engine +} // ns osrm + +namespace +{ +// Abstracted away the query locking into a template function +// Works the same for every plugin. +template +osrm::engine::Status RunQuery(const std::unique_ptr &lock, + osrm::engine::datafacade::BaseDataFacade &facade, + const ParameterT ¶meters, + PluginT &plugin, + ResultT &result) +{ + if (!lock) + { + return plugin.HandleRequest(parameters, result); + } + + BOOST_ASSERT(lock); + lock->IncreaseQueryCount(); + + auto &shared_facade = static_cast(facade); + shared_facade.CheckAndReloadFacade(); + // Get a shared data lock so that other threads won't update + // things while the query is running + boost::shared_lock data_lock{shared_facade.data_mutex}; + + osrm::engine::Status status = plugin.HandleRequest(parameters, result); + + lock->DecreaseQueryCount(); + return status; +} + +template +std::unique_ptr create(Facade &facade, Args... args) +{ + return osrm::util::make_unique(facade, std::forward(args)...); +} + +} // anon. ns + +namespace osrm +{ +namespace engine +{ + +Engine::Engine(EngineConfig &config) +{ + if (config.use_shared_memory) + { + lock = util::make_unique(); + query_data_facade = util::make_unique(); + } + else + { + if (!config.storage_config.IsValid()) + { + throw util::exception("Invalid file paths given!"); + } + query_data_facade = util::make_unique(config.storage_config); + } + + // Register plugins + using namespace plugins; + + route_plugin = create(*query_data_facade, config.max_locations_viaroute); + table_plugin = create(*query_data_facade, config.max_locations_distance_table); + nearest_plugin = create(*query_data_facade); + trip_plugin = create(*query_data_facade, config.max_locations_trip); + match_plugin = create(*query_data_facade, config.max_locations_map_matching); + tile_plugin = create(*query_data_facade); +} + +// make sure we deallocate the unique ptr at a position where we know the size of the plugins +Engine::~Engine() = default; +Engine::Engine(Engine &&) noexcept = default; +Engine &Engine::operator=(Engine &&) noexcept = default; + +Status Engine::Route(const api::RouteParameters ¶ms, util::json::Object &result) +{ + return RunQuery(lock, *query_data_facade, params, *route_plugin, result); +} + +Status Engine::Table(const api::TableParameters ¶ms, util::json::Object &result) +{ + return RunQuery(lock, *query_data_facade, params, *table_plugin, result); +} + +Status Engine::Nearest(const api::NearestParameters ¶ms, util::json::Object &result) +{ + return RunQuery(lock, *query_data_facade, params, *nearest_plugin, result); +} + +Status Engine::Trip(const api::TripParameters ¶ms, util::json::Object &result) +{ + return RunQuery(lock, *query_data_facade, params, *trip_plugin, result); +} + +Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &result) +{ + return RunQuery(lock, *query_data_facade, params, *match_plugin, result); +} + +Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) +{ + return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); +} + +} // engine ns +} // osrm ns diff --git a/src/engine/engine_config.cpp b/src/engine/engine_config.cpp new file mode 100644 index 000000000..e26a67eed --- /dev/null +++ b/src/engine/engine_config.cpp @@ -0,0 +1,27 @@ +#include "engine/engine_config.hpp" + +namespace osrm +{ +namespace engine +{ + +bool EngineConfig::IsValid() const +{ + const bool all_path_are_empty = + storage_config.ram_index_path.empty() && storage_config.file_index_path.empty() && + storage_config.hsgr_data_path.empty() && storage_config.nodes_data_path.empty() && + storage_config.edges_data_path.empty() && storage_config.core_data_path.empty() && + storage_config.geometries_path.empty() && storage_config.timestamp_path.empty() && + storage_config.datasource_names_path.empty() && + storage_config.datasource_indexes_path.empty() && storage_config.names_data_path.empty(); + + const bool limits_valid = + (max_locations_distance_table == -1 || max_locations_distance_table > 2) && + (max_locations_map_matching == -1 || max_locations_map_matching > 2) && + (max_locations_trip == -1 || max_locations_trip > 2) && + (max_locations_viaroute == -1 || max_locations_viaroute > 2); + + return ((use_shared_memory && all_path_are_empty) || storage_config.IsValid()) && limits_valid; +} +} +} diff --git a/src/engine/guidance/assemble_overview.cpp b/src/engine/guidance/assemble_overview.cpp new file mode 100644 index 000000000..aa25411fc --- /dev/null +++ b/src/engine/guidance/assemble_overview.cpp @@ -0,0 +1,103 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP + +#include "engine/guidance/leg_geometry.hpp" +#include "engine/douglas_peucker.hpp" +#include "util/viewport.hpp" + +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace +{ + +unsigned calculateOverviewZoomLevel(const std::vector &leg_geometries) +{ + util::Coordinate south_west{util::FixedLongitude{std::numeric_limits::max()}, util::FixedLatitude{std::numeric_limits::max()}}; + util::Coordinate north_east{util::FixedLongitude{std::numeric_limits::min()}, util::FixedLatitude{std::numeric_limits::min()}}; + + for (const auto &leg_geometry : leg_geometries) + { + for (const auto coord : leg_geometry.locations) + { + south_west.lon = std::min(south_west.lon, coord.lon); + south_west.lat = std::min(south_west.lat, coord.lat); + + north_east.lon = std::max(north_east.lon, coord.lon); + north_east.lat = std::max(north_east.lat, coord.lat); + } + } + + return util::viewport::getFittedZoom(south_west, north_east); +} + +std::vector simplifyGeometry(const std::vector &leg_geometries, + const unsigned zoom_level) +{ + std::vector overview_geometry; + auto leg_index = 0UL; + for (const auto geometry : leg_geometries) + { + auto simplified_geometry = + douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level); + // not the last leg + if (leg_index < leg_geometries.size() - 1) + { + simplified_geometry.pop_back(); + } + overview_geometry.insert(overview_geometry.end(), simplified_geometry.begin(), + simplified_geometry.end()); + } + return overview_geometry; +} +} + +std::vector assembleOverview(const std::vector &leg_geometries, + const bool use_simplification) +{ + if (use_simplification) + { + const auto zoom_level = std::min(18u, calculateOverviewZoomLevel(leg_geometries)); + return simplifyGeometry(leg_geometries, zoom_level); + } + BOOST_ASSERT(!use_simplification); + + auto overview_size = std::accumulate(leg_geometries.begin(), leg_geometries.end(), 0, + [](const std::size_t sum, const LegGeometry &leg_geometry) + { + return sum + leg_geometry.locations.size(); + }) - + leg_geometries.size() + 1; + std::vector overview_geometry; + overview_geometry.reserve(overview_size); + + auto leg_index = 0UL; + for (const auto geometry : leg_geometries) + { + auto begin = geometry.locations.begin(); + auto end = geometry.locations.end(); + if (leg_index < leg_geometries.size() - 1) + { + end = std::prev(end); + } + overview_geometry.insert(overview_geometry.end(), begin, end); + } + + return overview_geometry; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/src/engine/guidance/assemble_route.cpp b/src/engine/guidance/assemble_route.cpp new file mode 100644 index 000000000..cdc3a98f2 --- /dev/null +++ b/src/engine/guidance/assemble_route.cpp @@ -0,0 +1,30 @@ +#include "engine/guidance/assemble_route.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +Route assembleRoute(const std::vector &route_legs) +{ + auto distance = std::accumulate(route_legs.begin(), route_legs.end(), 0., + [](const double sum, const RouteLeg &leg) + { + return sum + leg.distance; + }); + auto duration = std::accumulate(route_legs.begin(), route_legs.end(), 0., + [](const double sum, const RouteLeg &leg) + { + return sum + leg.duration; + }); + + return Route{duration, distance}; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm diff --git a/src/engine/guidance/assemble_steps.cpp b/src/engine/guidance/assemble_steps.cpp new file mode 100644 index 000000000..207a6d287 --- /dev/null +++ b/src/engine/guidance/assemble_steps.cpp @@ -0,0 +1,82 @@ +#include "engine/guidance/assemble_steps.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace detail +{ + +StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction, + const WaypointType waypoint_type, + const LegGeometry &leg_geometry) +{ + BOOST_ASSERT(waypoint_type != WaypointType::None); + BOOST_ASSERT(leg_geometry.locations.size() >= 2); + + double pre_turn_bearing = 0, post_turn_bearing = 0; + Coordinate turn_coordinate; + if (waypoint_type == WaypointType::Depart) + { + turn_coordinate = leg_geometry.locations.front(); + const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1); + post_turn_bearing = + util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); + } + else + { + BOOST_ASSERT(waypoint_type == WaypointType::Arrive); + turn_coordinate = leg_geometry.locations.back(); + const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2); + pre_turn_bearing = + util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); + } + return StepManeuver{ + std::move(turn_coordinate), + pre_turn_bearing, + post_turn_bearing, + std::move(instruction), + waypoint_type, + INVALID_EXIT_NR, + {} // no intermediate intersections + }; +} + +StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction, + const LegGeometry &leg_geometry, + const std::size_t segment_index) +{ + auto turn_index = leg_geometry.BackIndex(segment_index); + BOOST_ASSERT(turn_index > 0); + BOOST_ASSERT(turn_index + 1 < leg_geometry.locations.size()); + + // TODO chose a bigger look-a-head to smooth complex geometry + const auto pre_turn_coordinate = leg_geometry.locations[turn_index - 1]; + const auto turn_coordinate = leg_geometry.locations[turn_index]; + const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1]; + + const double pre_turn_bearing = + util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); + const double post_turn_bearing = + util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); + + return StepManeuver{ + std::move(turn_coordinate), + pre_turn_bearing, + post_turn_bearing, + std::move(instruction), + WaypointType::None, + INVALID_EXIT_NR, + {} // no intermediate intersections + }; +} +} // ns detail +} // ns engine +} // ns guidance +} // ns detail diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp new file mode 100644 index 000000000..6d02a0747 --- /dev/null +++ b/src/engine/guidance/post_processing.cpp @@ -0,0 +1,503 @@ +#include "engine/guidance/post_processing.hpp" +#include "extractor/guidance/turn_instruction.hpp" + +#include "engine/guidance/assemble_steps.hpp" +#include "engine/guidance/toolkit.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using TurnInstruction = osrm::extractor::guidance::TurnInstruction; +using TurnType = osrm::extractor::guidance::TurnType; +using DirectionModifier = osrm::extractor::guidance::DirectionModifier; + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +namespace detail +{ +bool canMergeTrivially(const RouteStep &destination, const RouteStep &source) +{ + return destination.maneuver.exit == 0 && destination.name_id == source.name_id && + isSilent(source.maneuver.instruction); +} + +RouteStep forwardInto(RouteStep destination, const RouteStep &source) +{ + // Merge a turn into a silent turn + // Overwrites turn instruction and increases exit NR + destination.duration += source.duration; + destination.distance += source.distance; + destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin); + destination.geometry_end = std::max(destination.geometry_end, source.geometry_end); + return destination; +} + +void fixFinalRoundabout(std::vector &steps) +{ + for (std::size_t propagation_index = steps.size() - 1; propagation_index > 0; + --propagation_index) + { + auto &propagation_step = steps[propagation_index]; + if (propagation_index == 0 || entersRoundabout(propagation_step.maneuver.instruction)) + { + propagation_step.maneuver.exit = 0; + propagation_step.geometry_end = steps.back().geometry_begin; + + if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || + propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit) + propagation_step.rotary_name = propagation_step.name; + + break; + } + else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout) + { + // TODO this operates on the data that is in the instructions. + // We are missing out on the final segment after the last stay-on-roundabout + // instruction though. it is not contained somewhere until now + steps[propagation_index - 1] = + forwardInto(std::move(steps[propagation_index - 1]), propagation_step); + propagation_step.maneuver.instruction = + TurnInstruction::NO_TURN(); // mark intermediate instructions invalid + } + } +} + +bool setUpRoundabout(RouteStep &step) +{ + // basic entry into a roundabout + // Special case handling, if an entry is directly tied to an exit + const auto instruction = step.maneuver.instruction; + if (instruction.type == TurnType::EnterRotaryAtExit || + instruction.type == TurnType::EnterRoundaboutAtExit) + { + step.maneuver.exit = 1; + // prevent futher special case handling of these two. + if (instruction.type == TurnType::EnterRotaryAtExit) + step.maneuver.instruction.type = TurnType::EnterRotary; + else + step.maneuver.instruction.type = TurnType::EnterRoundabout; + } + + if (leavesRoundabout(instruction)) + { + step.maneuver.exit = 1; // count the otherwise missing exit + + // prevent futher special case handling of these two. + if (instruction.type == TurnType::EnterAndExitRotary) + step.maneuver.instruction.type = TurnType::EnterRotary; + else + step.maneuver.instruction.type = TurnType::EnterRoundabout; + return false; + } + else + { + return true; + } +} + +void closeOffRoundabout(const bool on_roundabout, + std::vector &steps, + const std::size_t step_index) +{ + auto &step = steps[step_index]; + step.maneuver.exit += 1; + if (!on_roundabout) + { + + // We reached a special case that requires the addition of a special route step in + // the beginning. + // We started in a roundabout, so to announce the exit, we move use the exit + // instruction and + // move it right to the beginning to make sure to immediately announce the exit. + BOOST_ASSERT(leavesRoundabout(steps[1].maneuver.instruction) || + steps[1].maneuver.instruction.type == TurnType::StayOnRoundabout); + steps[0].geometry_end = 1; + steps[1] = detail::forwardInto(steps[1], steps[0]); + steps[0].duration = 0; + steps[0].distance = 0; + steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary + ? TurnType::EnterRotary + : TurnType::EnterRoundabout; + if (steps[1].maneuver.instruction.type == TurnType::EnterRotary) + steps[1].rotary_name = steps[0].name; + } + + // Normal exit from the roundabout, or exit from a previously fixed roundabout. + // Propagate the index back to the entering + // location and + // prepare the current silent set of instructions for removal. + if (step_index > 1) + { + // The very first route-step is head, so we cannot iterate past that one + for (std::size_t propagation_index = step_index - 1; propagation_index > 0; + --propagation_index) + { + auto &propagation_step = steps[propagation_index]; + propagation_step = detail::forwardInto(propagation_step, steps[propagation_index + 1]); + if (entersRoundabout(propagation_step.maneuver.instruction)) + { + // TODO at this point, we can remember the additional name for a rotary + // This requires some initial thought on the data format, though + + propagation_step.maneuver.exit = step.maneuver.exit; + propagation_step.geometry_end = step.geometry_end; + // remember rotary name + if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || + propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit) + propagation_step.rotary_name = propagation_step.name; + + propagation_step.name = step.name; + propagation_step.name_id = step.name_id; + break; + } + else + { + BOOST_ASSERT(propagation_step.maneuver.instruction.type = + TurnType::StayOnRoundabout); + propagation_step.maneuver.instruction = + TurnInstruction::NO_TURN(); // mark intermediate instructions invalid + } + } + // remove exit + step.maneuver.instruction = TurnInstruction::NO_TURN(); + } +} +} // namespace detail + +void print(const std::vector &steps) +{ + std::cout << "Path\n"; + int segment = 0; + for (const auto &step : steps) + { + const auto type = static_cast(step.maneuver.instruction.type); + const auto modifier = static_cast(step.maneuver.instruction.direction_modifier); + + std::cout << "\t[" << ++segment << "]: " << type << " " << modifier + << " Duration: " << step.duration << " Distance: " << step.distance + << " Geometry: " << step.geometry_begin << " " << step.geometry_end + << " exit: " << step.maneuver.exit + << " Intersections: " << step.maneuver.intersections.size() << " ["; + + for (auto intersection : step.maneuver.intersections) + std::cout << "(" << intersection.duration << " " << intersection.distance << ")"; + + std::cout << "] name[" << step.name_id << "]: " << step.name << std::endl; + } +} + +// Every Step Maneuver consists of the information until the turn. +// This list contains a set of instructions, called silent, which should +// not be part of the final output. +// They are required for maintenance purposes. We can calculate the number +// of exits to pass in a roundabout and the number of intersections +// that we come across. + +std::vector postProcess(std::vector steps) +{ + // the steps should always include the first/last step in form of a location + BOOST_ASSERT(steps.size() >= 2); + if (steps.size() == 2) + return steps; + + // Count Street Exits forward + bool on_roundabout = false; + bool has_entered_roundabout = false; + + // adds an intersection to the initial route step + // It includes the length of the last step, until the intersection + // Also updates the length of the respective segment + auto addIntersection = [](RouteStep into, const RouteStep &last_step, + const RouteStep &intersection) { + into.maneuver.intersections.push_back( + {last_step.duration, last_step.distance, intersection.maneuver.location}); + + return detail::forwardInto(std::move(into), intersection); + }; + + // count the exits forward. if enter/exit roundabout happen both, no further treatment is + // required. We might end up with only one of them (e.g. starting within a roundabout) + // or having a via-point in the roundabout. + // In this case, exits are numbered from the start of the lag. + std::size_t last_valid_instruction = 0; + for (std::size_t step_index = 0; step_index < steps.size(); ++step_index) + { + auto &step = steps[step_index]; + const auto instruction = step.maneuver.instruction; + if (entersRoundabout(instruction)) + { + last_valid_instruction = step_index; + has_entered_roundabout = detail::setUpRoundabout(step); + + if (has_entered_roundabout && step_index + 1 < steps.size()) + steps[step_index + 1].maneuver.exit = step.maneuver.exit; + } + else if (instruction.type == TurnType::StayOnRoundabout) + { + on_roundabout = true; + // increase the exit number we require passing the exit + step.maneuver.exit += 1; + if (step_index + 1 < steps.size()) + steps[step_index + 1].maneuver.exit = step.maneuver.exit; + } + else if (leavesRoundabout(instruction)) + { + if (!has_entered_roundabout) + { + // in case the we are not on a roundabout, the very first instruction + // after the depart will be transformed into a roundabout and become + // the first valid instruction + last_valid_instruction = 1; + } + detail::closeOffRoundabout(has_entered_roundabout, steps, step_index); + has_entered_roundabout = false; + on_roundabout = false; + } + else if (instruction.type == TurnType::Suppressed) + { + // count intersections. We cannot use exit, since intersections can follow directly + // after a roundabout + steps[last_valid_instruction] = addIntersection( + std::move(steps[last_valid_instruction]), steps[step_index - 1], step); + step.maneuver.instruction = TurnInstruction::NO_TURN(); + } + else if (!isSilent(instruction)) + { + // Remember the last non silent instruction + last_valid_instruction = step_index; + } + } + // unterminated roundabout + // Move backwards through the instructions until the start and remove the exit number + // A roundabout without exit translates to enter-roundabout. + if (has_entered_roundabout || on_roundabout) + { + detail::fixFinalRoundabout(steps); + } + + // finally clean up the post-processed instructions. + // Remove all invalid instructions from the set of instructions. + // An instruction is invalid, if its NO_TURN and has WaypointType::None. + // Two valid NO_TURNs exist in each leg in the form of Depart/Arrive + + // keep valid instructions + const auto not_is_valid = [](const RouteStep &step) { + return step.maneuver.instruction == TurnInstruction::NO_TURN() && + step.maneuver.waypoint_type == WaypointType::None; + }; + + boost::remove_erase_if(steps, not_is_valid); + + return steps; +} + +void trimShortSegments(std::vector &steps, LegGeometry &geometry) +{ + // Doing this step in post-processing provides a few challenges we cannot overcome. + // The removal of an initial step imposes some copy overhead in the steps, moving all later + // steps to the front. + // In addition, we cannot reduce the travel time that is accumulated at a different location. + // As a direct implication, we have to keep the time of the initial/final turns (which adds a + // few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should + // usually not be as relevant. + + if (steps.size() < 2 || geometry.locations.size() <= 2) + return; + + // if phantom node is located at the connection of two segments, either one can be selected as + // turn + // + // a --- b + // | + // c + // + // If a route from b to c is requested, both a--b and b--c could be selected as start segment. + // In case of a--b, we end up with an unwanted turn saying turn-right onto b-c. + // These cases start off with an initial segment which is of zero length. + // We have to be careful though, since routing that starts in a roundabout has a valid. + // To catch these cases correctly, we have to perform trimming prior to the post-processing + + BOOST_ASSERT(geometry.locations.size() >= steps.size()); + // Look for distances under 1m + const bool zero_length_step = steps.front().distance <= 1; + const bool duplicated_coordinate = util::coordinate_calculation::haversineDistance( + geometry.locations[0], geometry.locations[1]) <= 1; + if (zero_length_step || duplicated_coordinate) + { + // fixup the coordinate + geometry.locations.erase(geometry.locations.begin()); + + // remove the initial distance value + geometry.segment_distances.erase(geometry.segment_distances.begin()); + + // We have to adjust the first step both for its name and the bearings + if (zero_length_step) + { + // move offsets to front + BOOST_ASSERT(geometry.segment_offsets[1] == 1); + // geometry offsets have to be adjusted. Move all offsets to the front and reduce by + // one. (This is an inplace forward one and reduce by one) + std::transform(geometry.segment_offsets.begin() + 1, geometry.segment_offsets.end(), + geometry.segment_offsets.begin(), + [](const std::size_t val) { return val - 1; }); + + geometry.segment_offsets.pop_back(); + const auto ¤t_depart = steps.front(); + auto &designated_depart = *(steps.begin() + 1); + + // FIXME this is required to be consistent with the route durations. The initial turn is + // not actually part of the route, though + designated_depart.duration += current_depart.duration; + + // update initial turn direction/bearings. Due to the duplicated first coordinate, the + // initial bearing is invalid + designated_depart.maneuver = detail::stepManeuverFromGeometry( + TurnInstruction::NO_TURN(), WaypointType::Depart, geometry); + + // finally remove the initial (now duplicated move) + steps.erase(steps.begin()); + } + else + { + steps.front().geometry_begin = 1; + // reduce all offsets by one (inplace) + std::transform(geometry.segment_offsets.begin(), geometry.segment_offsets.end(), + geometry.segment_offsets.begin(), + [](const std::size_t val) { return val - 1; }); + + steps.front().maneuver = detail::stepManeuverFromGeometry( + TurnInstruction::NO_TURN(), WaypointType::Depart, geometry); + } + + // and update the leg geometry indices for the removed entry + std::for_each(steps.begin(), steps.end(), [](RouteStep &step) { + --step.geometry_begin; + --step.geometry_end; + }); + } + + // make sure we still have enough segments + if (steps.size() < 2 || geometry.locations.size() == 2) + return; + + BOOST_ASSERT(geometry.locations.size() >= steps.size()); + auto &next_to_last_step = *(steps.end() - 2); + // in the end, the situation with the roundabout cannot occur. As a result, we can remove all + // zero-length instructions + if (next_to_last_step.distance <= 1) + { + geometry.locations.pop_back(); + geometry.segment_offsets.pop_back(); + BOOST_ASSERT(geometry.segment_distances.back() < 1); + geometry.segment_distances.pop_back(); + + next_to_last_step.maneuver = detail::stepManeuverFromGeometry( + TurnInstruction::NO_TURN(), WaypointType::Arrive, geometry); + steps.pop_back(); + + // Because we eliminated a really short segment, it was probably + // near an intersection. The convention is *not* to make the + // turn, so the `arrive` instruction should be on the same road + // as the segment before it. Thus, we have to copy the names + // and travel modes from the new next_to_last step. + auto &new_next_to_last = *(steps.end() - 2); + next_to_last_step.name = new_next_to_last.name; + next_to_last_step.name_id = new_next_to_last.name_id; + next_to_last_step.mode = new_next_to_last.mode; + // the geometry indices of the last step are already correct; + } + else if (util::coordinate_calculation::haversineDistance( + geometry.locations[geometry.locations.size() - 2], + geometry.locations[geometry.locations.size() - 1]) <= 1) + { + // correct steps but duplicated coordinate in the end. + // This can happen if the last coordinate snaps to a node in the unpacked geometry + geometry.locations.pop_back(); + geometry.segment_offsets.back()--; + // since the last geometry includes the location of arrival, the arrival instruction + // geometry overlaps with the previous segment + BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin + 1); + BOOST_ASSERT(next_to_last_step.geometry_begin < next_to_last_step.geometry_end); + next_to_last_step.geometry_end--; + steps.back().geometry_begin--; + steps.back().geometry_end--; + steps.back().maneuver = detail::stepManeuverFromGeometry(TurnInstruction::NO_TURN(), + WaypointType::Arrive, geometry); + } +} + +// assign relative locations to depart/arrive instructions +std::vector assignRelativeLocations(std::vector steps, + const LegGeometry &leg_geometry, + const PhantomNode &source_node, + const PhantomNode &target_node) +{ + // We report the relative position of source/target to the road only within a range that is + // sufficiently different but not full of the path + BOOST_ASSERT(steps.size() >= 2); + BOOST_ASSERT(leg_geometry.locations.size() >= 2); + const constexpr double MINIMAL_RELATIVE_DISTANCE = 5., MAXIMAL_RELATIVE_DISTANCE = 300.; + const auto distance_to_start = util::coordinate_calculation::haversineDistance( + source_node.input_location, leg_geometry.locations[0]); + const auto initial_modifier = + distance_to_start >= MINIMAL_RELATIVE_DISTANCE && + distance_to_start <= MAXIMAL_RELATIVE_DISTANCE + ? angleToDirectionModifier(util::coordinate_calculation::computeAngle( + source_node.input_location, leg_geometry.locations[0], leg_geometry.locations[1])) + : extractor::guidance::DirectionModifier::UTurn; + + steps.front().maneuver.instruction.direction_modifier = initial_modifier; + + const auto distance_from_end = util::coordinate_calculation::haversineDistance( + target_node.input_location, leg_geometry.locations.back()); + const auto final_modifier = + distance_from_end >= MINIMAL_RELATIVE_DISTANCE && + distance_from_end <= MAXIMAL_RELATIVE_DISTANCE + ? angleToDirectionModifier(util::coordinate_calculation::computeAngle( + leg_geometry.locations[leg_geometry.locations.size() - 2], + leg_geometry.locations[leg_geometry.locations.size() - 1], + target_node.input_location)) + : extractor::guidance::DirectionModifier::UTurn; + + steps.back().maneuver.instruction.direction_modifier = final_modifier; + return steps; +} + +LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector &steps) +{ + // The geometry uses an adjacency array-like structure for representation. + // To sync it back up with the steps, we cann add a segment for every step. + leg_geometry.segment_offsets.clear(); + leg_geometry.segment_distances.clear(); + leg_geometry.segment_offsets.push_back(0); + + for (const auto &step : steps) + { + leg_geometry.segment_distances.push_back(step.distance); + // the leg geometry does not follow the begin/end-convetion. So we have to subtract one + // to get the back-index. + leg_geometry.segment_offsets.push_back(step.geometry_end - 1); + } + + // remove the data from the reached-target step again + leg_geometry.segment_offsets.pop_back(); + leg_geometry.segment_distances.pop_back(); + + return leg_geometry; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm diff --git a/src/engine/hint.cpp b/src/engine/hint.cpp new file mode 100644 index 000000000..d031fbe29 --- /dev/null +++ b/src/engine/hint.cpp @@ -0,0 +1,59 @@ +#include "engine/hint.hpp" +#include "engine/base64.hpp" +#include "engine/datafacade/datafacade_base.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +bool Hint::IsValid(const util::Coordinate new_input_coordinates, + const datafacade::BaseDataFacade &facade) const +{ + auto is_same_input_coordinate = new_input_coordinates.lon == phantom.input_location.lon && + new_input_coordinates.lat == phantom.input_location.lat; + return is_same_input_coordinate && phantom.IsValid(facade.GetNumberOfNodes()) && + facade.GetCheckSum() == data_checksum; +} + +std::string Hint::ToBase64() const +{ + auto base64 = encodeBase64Bytewise(*this); + + // Make safe for usage as GET parameter in URLs + std::replace(begin(base64), end(base64), '+', '-'); + std::replace(begin(base64), end(base64), '/', '_'); + + return base64; +} + +Hint Hint::FromBase64(const std::string &base64Hint) +{ + BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_HINT_SIZE, "Hint has invalid size"); + + // We need mutability but don't want to change the API + auto encoded = base64Hint; + + // Reverses above encoding we need for GET parameters in URL + std::replace(begin(encoded), end(encoded), '-', '+'); + std::replace(begin(encoded), end(encoded), '_', '/'); + + return decodeBase64Bytewise(encoded); +} + +bool operator==(const Hint &lhs, const Hint &rhs) +{ + return std::tie(lhs.phantom, lhs.data_checksum) == std::tie(rhs.phantom, rhs.data_checksum); +} + +std::ostream &operator<<(std::ostream &out, const Hint &hint) { return out << hint.ToBase64(); } + +} // ns engine +} // ns osrm diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp new file mode 100644 index 000000000..bd3a970f7 --- /dev/null +++ b/src/engine/plugins/match.cpp @@ -0,0 +1,199 @@ +#include "engine/plugins/plugin_base.hpp" +#include "engine/plugins/match.hpp" + +#include "engine/map_matching/bayes_classifier.hpp" +#include "engine/api/match_parameters.hpp" +#include "engine/api/match_api.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/integer_range.hpp" +#include "util/json_logger.hpp" +#include "util/json_util.hpp" +#include "util/string_util.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +// Filters PhantomNodes to obtain a set of viable candiates +void filterCandidates(const std::vector &coordinates, + MatchPlugin::CandidateLists &candidates_lists) +{ + for (const auto current_coordinate : util::irange(0, coordinates.size())) + { + bool allow_uturn = false; + + if (coordinates.size() - 1 > current_coordinate && 0 < current_coordinate) + { + double turn_angle = util::coordinate_calculation::computeAngle( + coordinates[current_coordinate - 1], coordinates[current_coordinate], + coordinates[current_coordinate + 1]); + + // sharp turns indicate a possible uturn + if (turn_angle <= 90.0 || turn_angle >= 270.0) + { + allow_uturn = true; + } + } + + auto &candidates = candidates_lists[current_coordinate]; + if (candidates.empty()) + { + continue; + } + + // sort by forward id, then by reverse id and then by distance + std::sort( + candidates.begin(), candidates.end(), + [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) + { + return lhs.phantom_node.forward_segment_id.id < rhs.phantom_node.forward_segment_id.id || + (lhs.phantom_node.forward_segment_id.id == rhs.phantom_node.forward_segment_id.id && + (lhs.phantom_node.reverse_segment_id.id < rhs.phantom_node.reverse_segment_id.id || + (lhs.phantom_node.reverse_segment_id.id == rhs.phantom_node.reverse_segment_id.id && + lhs.distance < rhs.distance))); + }); + + auto new_end = std::unique( + candidates.begin(), candidates.end(), + [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) + { + return lhs.phantom_node.forward_segment_id.id == rhs.phantom_node.forward_segment_id.id && + lhs.phantom_node.reverse_segment_id.id == rhs.phantom_node.reverse_segment_id.id; + }); + candidates.resize(new_end - candidates.begin()); + + if (!allow_uturn) + { + const auto compact_size = candidates.size(); + for (const auto i : util::irange(0, compact_size)) + { + // Split edge if it is bidirectional and append reverse direction to end of list + if (candidates[i].phantom_node.forward_segment_id.enabled && + candidates[i].phantom_node.reverse_segment_id.enabled) + { + PhantomNode reverse_node(candidates[i].phantom_node); + reverse_node.forward_segment_id.enabled = false; + candidates.push_back( + PhantomNodeWithDistance{reverse_node, candidates[i].distance}); + + candidates[i].phantom_node.reverse_segment_id.enabled = false; + } + } + } + + // sort by distance to make pruning effective + std::sort(candidates.begin(), candidates.end(), + [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs) + { + return lhs.distance < rhs.distance; + }); + } +} + +Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, + util::json::Object &json_result) +{ + BOOST_ASSERT(parameters.IsValid()); + + // enforce maximum number of locations for performance reasons + if (max_locations_map_matching > 0 && + static_cast(parameters.coordinates.size()) > max_locations_map_matching) + { + return Error("TooBig", "Too many trace coordinates", json_result); + } + + if (!CheckAllCoordinates(parameters.coordinates)) + { + return Error("InvalidValue", "Invalid coordinate value.", json_result); + } + + // assuming radius is the standard deviation of a normal distribution + // that models GPS noise (in this model), x3 should give us the correct + // search radius with > 99% confidence + std::vector search_radiuses; + if (parameters.radiuses.empty()) + { + search_radiuses.resize(parameters.coordinates.size(), + DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER); + } + else + { + search_radiuses.resize(parameters.coordinates.size()); + std::transform(parameters.radiuses.begin(), parameters.radiuses.end(), + search_radiuses.begin(), [](const boost::optional &maybe_radius) + { + if (maybe_radius) + { + return *maybe_radius * RADIUS_MULTIPLIER; + } + else + { + return DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER; + } + + }); + } + + auto candidates_lists = GetPhantomNodesInRange(parameters, search_radiuses); + + filterCandidates(parameters.coordinates, candidates_lists); + if (std::all_of(candidates_lists.begin(), candidates_lists.end(), + [](const std::vector &candidates) + { + return candidates.empty(); + })) + { + return Error("NoSegment", + std::string("Could not find a matching segment for any coordinate."), + json_result); + } + + // call the actual map matching + SubMatchingList sub_matchings = map_matching(candidates_lists, parameters.coordinates, + parameters.timestamps, parameters.radiuses); + + if (sub_matchings.size() == 0) + { + return Error("NoMatch", "Could not match the trace.", json_result); + } + + std::vector sub_routes(sub_matchings.size()); + for (auto index : util::irange(0UL, sub_matchings.size())) + { + BOOST_ASSERT(sub_matchings[index].nodes.size() > 1); + + // FIXME we only run this to obtain the geometry + // The clean way would be to get this directly from the map matching plugin + PhantomNodes current_phantom_node_pair; + for (unsigned i = 0; i < sub_matchings[index].nodes.size() - 1; ++i) + { + current_phantom_node_pair.source_phantom = sub_matchings[index].nodes[i]; + current_phantom_node_pair.target_phantom = sub_matchings[index].nodes[i + 1]; + BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid()); + BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid()); + sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair); + } + // force uturns to be on, since we split the phantom nodes anyway and only have bi-directional + // phantom nodes for possible uturns + shortest_path(sub_routes[index].segment_end_coordinates, {true}, sub_routes[index]); + BOOST_ASSERT(sub_routes[index].shortest_path_length != INVALID_EDGE_WEIGHT); + } + + api::MatchAPI match_api{BasePlugin::facade, parameters}; + match_api.MakeResponse(sub_matchings, sub_routes, json_result); + + return Status::Ok; +} +} +} +} diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp new file mode 100644 index 000000000..96b63e3eb --- /dev/null +++ b/src/engine/plugins/nearest.cpp @@ -0,0 +1,49 @@ +#include "engine/plugins/nearest.hpp" +#include "engine/api/nearest_parameters.hpp" +#include "engine/api/nearest_api.hpp" +#include "engine/phantom_node.hpp" +#include "util/integer_range.hpp" + +#include +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +NearestPlugin::NearestPlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} + +Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms, + util::json::Object &json_result) +{ + BOOST_ASSERT(params.IsValid()); + + if (!CheckAllCoordinates(params.coordinates)) + return Error("InvalidOptions", "Coordinates are invalid", json_result); + + if (params.coordinates.size() != 1) + { + return Error("InvalidOptions", "Only one input coordinate is supported", json_result); + } + + auto phantom_nodes = GetPhantomNodes(params, params.number_of_results); + + if (phantom_nodes.front().size() == 0) + { + return Error("NoSegment", "Could not find a matching segments for coordinate", json_result); + } + BOOST_ASSERT(phantom_nodes.front().size() > 0); + + api::NearestAPI nearest_api(facade, params); + nearest_api.MakeResponse(phantom_nodes, json_result); + + return Status::Ok; +} +} +} +} diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp new file mode 100644 index 000000000..27e310054 --- /dev/null +++ b/src/engine/plugins/table.cpp @@ -0,0 +1,76 @@ +#include "engine/plugins/table.hpp" + +#include "engine/api/table_parameters.hpp" +#include "engine/api/table_api.hpp" +#include "engine/routing_algorithms/many_to_many.hpp" +#include "engine/search_engine_data.hpp" +#include "util/string_util.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +TablePlugin::TablePlugin(datafacade::BaseDataFacade &facade, const int max_locations_distance_table) + : BasePlugin{facade}, distance_table(&facade, heaps), + max_locations_distance_table(max_locations_distance_table) +{ +} + +Status TablePlugin::HandleRequest(const api::TableParameters ¶ms, util::json::Object &result) +{ + BOOST_ASSERT(params.IsValid()); + + if (!CheckAllCoordinates(params.coordinates)) + { + return Error("InvalidOptions", "Coordinates are invalid", result); + } + + if (params.bearings.size() > 0 && params.coordinates.size() != params.bearings.size()) + { + return Error("InvalidOptions", "Number of bearings does not match number of coordinates", + result); + } + + // Empty sources or destinations means the user wants all of them included, respectively + // The ManyToMany routing algorithm we dispatch to below already handles this perfectly. + const auto num_sources = + params.sources.empty() ? params.coordinates.size() : params.sources.size(); + const auto num_destinations = + params.destinations.empty() ? params.coordinates.size() : params.destinations.size(); + + if (max_locations_distance_table > 0 && + ((num_sources * num_destinations) > + static_cast(max_locations_distance_table * max_locations_distance_table))) + { + return Error("TooBig", "Too many table coordinates", result); + } + + auto snapped_phantoms = SnapPhantomNodes(GetPhantomNodes(params)); + auto result_table = distance_table(snapped_phantoms, params.sources, params.destinations); + + if (result_table.empty()) + { + return Error("NoTable", "No table found", result); + } + + api::TableAPI table_api{facade, params}; + table_api.MakeResponse(result_table, snapped_phantoms, result); + + return Status::Ok; +} +} +} +} diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp new file mode 100644 index 000000000..6ce85c71b --- /dev/null +++ b/src/engine/plugins/tile.cpp @@ -0,0 +1,457 @@ +#include "engine/plugins/plugin_base.hpp" +#include "engine/plugins/tile.hpp" + +#include "util/coordinate_calculation.hpp" +#include "util/web_mercator.hpp" +#include "util/vector_tile.hpp" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ +namespace detail +{ +// Simple container class for WGS84 coordinates +template struct Point final +{ + Point(T _x, T _y) : x(_x), y(_y) {} + + const T x; + const T y; +}; + +// from mapnik-vector-tile +namespace pbf +{ +inline unsigned encode_length(const unsigned len) { return (len << 3u) | 2u; } +} + +struct BBox final +{ + BBox(const double _minx, const double _miny, const double _maxx, const double _maxy) + : minx(_minx), miny(_miny), maxx(_maxx), maxy(_maxy) + { + } + + double width() const { return maxx - minx; } + double height() const { return maxy - miny; } + + const double minx; + const double miny; + const double maxx; + const double maxy; +}; + +// Simple container for integer coordinates (i.e. pixel coords) +struct point_type_i final +{ + point_type_i(std::int64_t _x, std::int64_t _y) : x(_x), y(_y) {} + + const std::int64_t x; + const std::int64_t y; +}; + +using FixedLine = std::vector>; +using FloatLine = std::vector>; + +typedef boost::geometry::model::point point_t; +typedef boost::geometry::model::linestring linestring_t; +typedef boost::geometry::model::box box_t; +typedef boost::geometry::model::multi_linestring multi_linestring_t; +const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_tile::BUFFER), + point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER, + util::vector_tile::EXTENT + util::vector_tile::BUFFER)); + +// from mapnik-vector-tile +// Encodes a linestring using protobuf zigzag encoding +inline bool encodeLinestring(const FixedLine &line, + protozero::packed_field_uint32 &geometry, + std::int32_t &start_x, + std::int32_t &start_y) +{ + const std::size_t line_size = line.size(); + if (line_size < 2) + { + return false; + } + + const unsigned line_to_length = static_cast(line_size) - 1; + + auto pt = line.begin(); + geometry.add_element(9); // move_to | (1 << 3) + geometry.add_element(protozero::encode_zigzag32(pt->x - start_x)); + geometry.add_element(protozero::encode_zigzag32(pt->y - start_y)); + start_x = pt->x; + start_y = pt->y; + geometry.add_element(detail::pbf::encode_length(line_to_length)); + for (++pt; pt != line.end(); ++pt) + { + const std::int32_t dx = pt->x - start_x; + const std::int32_t dy = pt->y - start_y; + geometry.add_element(protozero::encode_zigzag32(dx)); + geometry.add_element(protozero::encode_zigzag32(dy)); + start_x = pt->x; + start_y = pt->y; + } + return true; +} + +FixedLine coordinatesToTileLine(const util::Coordinate start, + const util::Coordinate target, + const detail::BBox &tile_bbox) +{ + FloatLine geo_line; + geo_line.emplace_back(static_cast(util::toFloating(start.lon)), + static_cast(util::toFloating(start.lat))); + geo_line.emplace_back(static_cast(util::toFloating(target.lon)), + static_cast(util::toFloating(target.lat))); + + linestring_t unclipped_line; + + for (auto const &pt : geo_line) + { + double px_merc = pt.x * util::web_mercator::DEGREE_TO_PX; + double py_merc = util::web_mercator::latToY(util::FloatLatitude(pt.y)) * + util::web_mercator::DEGREE_TO_PX; + // convert lon/lat to tile coordinates + const auto px = std::round( + ((px_merc - tile_bbox.minx) * util::web_mercator::TILE_SIZE / tile_bbox.width()) * + util::vector_tile::EXTENT / util::web_mercator::TILE_SIZE); + const auto py = std::round( + ((tile_bbox.maxy - py_merc) * util::web_mercator::TILE_SIZE / tile_bbox.height()) * + util::vector_tile::EXTENT / util::web_mercator::TILE_SIZE); + + boost::geometry::append(unclipped_line, point_t(px, py)); + } + + multi_linestring_t clipped_line; + + boost::geometry::intersection(clip_box, unclipped_line, clipped_line); + + FixedLine tile_line; + + // b::g::intersection might return a line with one point if the + // original line was very short and coords were dupes + if (!clipped_line.empty() && clipped_line[0].size() == 2) + { + if (clipped_line[0].size() == 2) + { + for (const auto &p : clipped_line[0]) + { + tile_line.emplace_back(p.get<0>(), p.get<1>()); + } + } + } + + return tile_line; +} +} + +Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer) +{ + BOOST_ASSERT(parameters.IsValid()); + + double min_lon, min_lat, max_lon, max_lat; + + // Convert the z,x,y mercator tile coordinates into WGS84 lon/lat values + util::web_mercator::xyzToWGS84(parameters.x, parameters.y, parameters.z, min_lon, min_lat, + max_lon, max_lat); + + util::Coordinate southwest{util::FloatLongitude(min_lon), util::FloatLatitude(min_lat)}; + util::Coordinate northeast{util::FloatLongitude(max_lon), util::FloatLatitude(max_lat)}; + + // Fetch all the segments that are in our bounding box. + // This hits the OSRM StaticRTree + const auto edges = facade.GetEdgesInBox(southwest, northeast); + + std::vector used_weights; + std::unordered_map weight_offsets; + uint8_t max_datasource_id = 0; + + // Loop over all edges once to tally up all the attributes we'll need. + // We need to do this so that we know the attribute offsets to use + // when we encode each feature in the tile. + for (const auto &edge : edges) + { + int forward_weight = 0, reverse_weight = 0; + uint8_t forward_datasource = 0; + uint8_t reverse_datasource = 0; + + if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector forward_weight_vector; + facade.GetUncompressedWeights(edge.forward_packed_geometry_id, forward_weight_vector); + forward_weight = forward_weight_vector[edge.fwd_segment_position]; + + std::vector forward_datasource_vector; + facade.GetUncompressedDatasources(edge.forward_packed_geometry_id, + forward_datasource_vector); + forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; + + if (weight_offsets.find(forward_weight) == weight_offsets.end()) + { + used_weights.push_back(forward_weight); + weight_offsets[forward_weight] = used_weights.size() - 1; + } + } + + if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector reverse_weight_vector; + facade.GetUncompressedWeights(edge.reverse_packed_geometry_id, reverse_weight_vector); + + BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); + + reverse_weight = + reverse_weight_vector[reverse_weight_vector.size() - edge.fwd_segment_position - 1]; + + if (weight_offsets.find(reverse_weight) == weight_offsets.end()) + { + used_weights.push_back(reverse_weight); + weight_offsets[reverse_weight] = used_weights.size() - 1; + } + std::vector reverse_datasource_vector; + facade.GetUncompressedDatasources(edge.reverse_packed_geometry_id, + reverse_datasource_vector); + reverse_datasource = reverse_datasource_vector[reverse_datasource_vector.size() - + edge.fwd_segment_position - 1]; + } + // Keep track of the highest datasource seen so that we don't write unnecessary + // data to the layer attribute values + max_datasource_id = std::max(max_datasource_id, forward_datasource); + max_datasource_id = std::max(max_datasource_id, reverse_datasource); + } + + // TODO: extract speed values for compressed and uncompressed geometries + + // Convert tile coordinates into mercator coordinates + util::web_mercator::xyzToMercator(parameters.x, parameters.y, parameters.z, min_lon, min_lat, + max_lon, max_lat); + const detail::BBox tile_bbox{min_lon, min_lat, max_lon, max_lat}; + + // Protobuf serialized blocks when objects go out of scope, hence + // the extra scoping below. + protozero::pbf_writer tile_writer{pbf_buffer}; + { + // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec (2.1) + protozero::pbf_writer layer_writer(tile_writer, util::vector_tile::LAYER_TAG); + // TODO: don't write a layer if there are no features + + layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version + // Field 1 is the "layer name" field, it's a string + layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name + // Field 5 is the tile extent. It's a uint32 and should be set to 4096 + // for normal vector tiles. + layer_writer.add_uint32(util::vector_tile::EXTEND_TAG, util::vector_tile::EXTENT); // extent + + // Begin the layer features block + { + // Each feature gets a unique id, starting at 1 + unsigned id = 1; + for (const auto &edge : edges) + { + // Get coordinates for start/end nodes of segmet (NodeIDs u and v) + const auto a = facade.GetCoordinateOfNode(edge.u); + const auto b = facade.GetCoordinateOfNode(edge.v); + // Calculate the length in meters + const double length = osrm::util::coordinate_calculation::haversineDistance(a, b); + + int forward_weight = 0; + int reverse_weight = 0; + + uint8_t forward_datasource = 0; + uint8_t reverse_datasource = 0; + + if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector forward_weight_vector; + facade.GetUncompressedWeights(edge.forward_packed_geometry_id, + forward_weight_vector); + forward_weight = forward_weight_vector[edge.fwd_segment_position]; + + std::vector forward_datasource_vector; + facade.GetUncompressedDatasources(edge.forward_packed_geometry_id, + forward_datasource_vector); + forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; + } + + if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) + { + std::vector reverse_weight_vector; + facade.GetUncompressedWeights(edge.reverse_packed_geometry_id, + reverse_weight_vector); + + BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); + + reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - + edge.fwd_segment_position - 1]; + + std::vector reverse_datasource_vector; + facade.GetUncompressedDatasources(edge.reverse_packed_geometry_id, + reverse_datasource_vector); + reverse_datasource = + reverse_datasource_vector[reverse_datasource_vector.size() - + edge.fwd_segment_position - 1]; + } + + // Keep track of the highest datasource seen so that we don't write unnecessary + // data to the layer attribute values + max_datasource_id = std::max(max_datasource_id, forward_datasource); + max_datasource_id = std::max(max_datasource_id, reverse_datasource); + + const auto encode_tile_line = [&layer_writer, &edge, &id, &max_datasource_id]( + const detail::FixedLine &tile_line, const std::uint32_t speed_kmh, + const std::size_t duration, const std::uint8_t datasource, + std::int32_t &start_x, std::int32_t &start_y) + { + // Here, we save the two attributes for our feature: the speed and the + // is_small + // boolean. We onl serve up speeds from 0-139, so all we do is save the + // first + protozero::pbf_writer feature_writer(layer_writer, + util::vector_tile::FEATURE_TAG); + // Field 3 is the "geometry type" field. Value 2 is "line" + feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG, + util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type + // Field 1 for the feature is the "id" field. + feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id + { + // When adding attributes to a feature, we have to write + // pairs of numbers. The first value is the index in the + // keys array (written later), and the second value is the + // index into the "values" array (also written later). We're + // not writing the actual speed or bool value here, we're saving + // an index into the "values" array. This means many features + // can share the same value data, leading to smaller tiles. + protozero::packed_field_uint32 field( + feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); + + field.add_element(0); // "speed" tag key offset + field.add_element( + std::min(speed_kmh, 127u)); // save the speed value, capped at 127 + field.add_element(1); // "is_small" tag key offset + field.add_element(128 + + (edge.component.is_tiny ? 0 : 1)); // is_small feature + field.add_element(2); // "datasource" tag key offset + field.add_element(130 + datasource); // datasource value offset + field.add_element(3); // "duration" tag key offset + field.add_element(130 + max_datasource_id + 1 + + duration); // duration value offset + } + { + + // Encode the geometry for the feature + protozero::packed_field_uint32 geometry( + feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); + encodeLinestring(tile_line, geometry, start_x, start_y); + } + }; + + // If this is a valid forward edge, go ahead and add it to the tile + if (forward_weight != 0 && edge.forward_segment_id.enabled) + { + std::int32_t start_x = 0; + std::int32_t start_y = 0; + + // Calculate the speed for this line + std::uint32_t speed_kmh = + static_cast(round(length / forward_weight * 10 * 3.6)); + + auto tile_line = coordinatesToTileLine(a, b, tile_bbox); + if (!tile_line.empty()) + { + encode_tile_line(tile_line, speed_kmh, weight_offsets[forward_weight], + forward_datasource, start_x, start_y); + } + } + + // Repeat the above for the coordinates reversed and using the `reverse` + // properties + if (reverse_weight != 0 && edge.reverse_segment_id.enabled) + { + std::int32_t start_x = 0; + std::int32_t start_y = 0; + + // Calculate the speed for this line + std::uint32_t speed_kmh = + static_cast(round(length / reverse_weight * 10 * 3.6)); + + auto tile_line = coordinatesToTileLine(b, a, tile_bbox); + if (!tile_line.empty()) + { + encode_tile_line(tile_line, speed_kmh, weight_offsets[reverse_weight], + reverse_datasource, start_x, start_y); + } + } + } + } + + // Field id 3 is the "keys" attribute + // We need two "key" fields, these are referred to with 0 and 1 (their array indexes) + // earlier + layer_writer.add_string(util::vector_tile::KEY_TAG, "speed"); + layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small"); + layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource"); + layer_writer.add_string(util::vector_tile::KEY_TAG, "duration"); + + // Now, we write out the possible speed value arrays and possible is_tiny + // values. Field type 4 is the "values" field. It's a variable type field, + // so requires a two-step write (create the field, then write its value) + for (std::size_t i = 0; i < 128; i++) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); + // Attribute value 5 == uin64 type + values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT32, i); + } + { + protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); + // Attribute value 7 == bool type + values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true); + } + { + protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); + // Attribute value 7 == bool type + values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false); + } + for (std::size_t i = 0; i <= max_datasource_id; i++) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); + // Attribute value 1 == string type + values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, + facade.GetDatasourceName(i)); + } + for (auto weight : used_weights) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); + // Attribute value 2 == float type + // Durations come out of OSRM in integer deciseconds, so we convert them + // to seconds with a simple /10 for display + values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, weight / 10.); + } + } + + return Status::Ok; +} +} +} +} diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp new file mode 100644 index 000000000..35c651440 --- /dev/null +++ b/src/engine/plugins/trip.cpp @@ -0,0 +1,245 @@ +#include "engine/plugins/trip.hpp" + +#include "extractor/tarjan_scc.hpp" + +#include "engine/api/trip_api.hpp" +#include "engine/api/trip_parameters.hpp" +#include "engine/trip/trip_nearest_neighbour.hpp" +#include "engine/trip/trip_farthest_insertion.hpp" +#include "engine/trip/trip_brute_force.hpp" +#include "util/dist_table_wrapper.hpp" // to access the dist table more easily +#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +// Object to hold all strongly connected components (scc) of a graph +// to access all graphs with component ID i, get the iterators by: +// auto start = std::begin(scc_component.component) + scc_component.range[i]; +// auto end = std::begin(scc_component.component) + scc_component.range[i+1]; +struct SCC_Component +{ + // in_component: all NodeIDs sorted by component ID + // in_range: index where a new component starts + // + // example: NodeID 0, 1, 2, 4, 5 are in component 0 + // NodeID 3, 6, 7, 8 are in component 1 + // => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8] + // => in_range = [0, 5] + SCC_Component(std::vector in_component_nodes, std::vector in_range) + : component(std::move(in_component_nodes)), range(std::move(in_range)) + { + BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component"); + BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) == component.size(), + "scc component ranges are out of bound"); + BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) == 0, + "invalid scc component range"); + BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)), + "invalid component ranges"); + } + + std::size_t GetNumberOfComponents() const + { + BOOST_ASSERT_MSG(range.size() > 0, "there's no range"); + return range.size() - 1; + } + + const std::vector component; + std::vector range; +}; + +// takes the number of locations and its duration matrix, +// identifies and splits the graph in its strongly connected components (scc) +// and returns an SCC_Component +SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations, + const util::DistTableWrapper &result_table) +{ + + if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) == + std::end(result_table)) + { + // whole graph is one scc + std::vector location_ids(number_of_locations); + std::iota(std::begin(location_ids), std::end(location_ids), 0); + std::vector range = {0, location_ids.size()}; + return SCC_Component(std::move(location_ids), std::move(range)); + } + + // Run TarjanSCC + auto wrapper = std::make_shared>(result_table.GetTable(), + number_of_locations); + auto scc = extractor::TarjanSCC>(wrapper); + scc.run(); + + const auto number_of_components = scc.get_number_of_components(); + + std::vector range_insertion; + std::vector range; + range_insertion.reserve(number_of_components); + range.reserve(number_of_components); + + std::vector components(number_of_locations, 0); + + std::size_t prefix = 0; + for (std::size_t j = 0; j < number_of_components; ++j) + { + range_insertion.push_back(prefix); + range.push_back(prefix); + prefix += scc.get_component_size(j); + } + // senitel + range.push_back(components.size()); + + for (std::size_t i = 0; i < number_of_locations; ++i) + { + components[range_insertion[scc.get_component_id(i)]] = i; + ++range_insertion[scc.get_component_id(i)]; + } + + return SCC_Component(std::move(components), std::move(range)); +} + +InternalRouteResult TripPlugin::ComputeRoute(const std::vector &snapped_phantoms, + const api::TripParameters ¶meters, + const std::vector &trip) +{ + InternalRouteResult min_route; + // given he final trip, compute total duration and return the route and location permutation + PhantomNodes viapoint; + const auto start = std::begin(trip); + const auto end = std::end(trip); + // computes a roundtrip from the nodes in trip + for (auto it = start; it != end; ++it) + { + const auto from_node = *it; + // if from_node is the last node, compute the route from the last to the first location + const auto to_node = std::next(it) != end ? *std::next(it) : *start; + + viapoint = PhantomNodes{snapped_phantoms[from_node], snapped_phantoms[to_node]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + } + BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); + + shortest_path(min_route.segment_end_coordinates, parameters.continue_straight, min_route); + + BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route"); + return min_route; +} + +Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, + util::json::Object &json_result) +{ + BOOST_ASSERT(parameters.IsValid()); + + // enforce maximum number of locations for performance reasons + if (max_locations_trip > 0 && + static_cast(parameters.coordinates.size()) > max_locations_trip) + { + return Error("TooBig", "Too many trip coordinates", json_result); + } + + if (!CheckAllCoordinates(parameters.coordinates)) + { + return Error("InvalidValue", "Invalid coordinate value.", json_result); + } + + auto phantom_node_pairs = GetPhantomNodes(parameters); + if (phantom_node_pairs.size() != parameters.coordinates.size()) + { + return Error("NoSegment", + std::string("Could not find a matching segment for coordinate ") + + std::to_string(phantom_node_pairs.size()), + json_result); + } + BOOST_ASSERT(phantom_node_pairs.size() == parameters.coordinates.size()); + + auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); + + const auto number_of_locations = snapped_phantoms.size(); + + // compute the duration table of all phantom nodes + const auto result_table = util::DistTableWrapper( + duration_table(snapped_phantoms, {}, {}), number_of_locations); + + if (result_table.size() == 0) + { + return Status::Error; + } + + const constexpr std::size_t BF_MAX_FEASABLE = 10; + BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations, + "Distance Table has wrong size"); + + // get scc components + SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table); + + std::vector> trips; + trips.reserve(scc.GetNumberOfComponents()); + // run Trip computation for every SCC + for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k) + { + const auto component_size = scc.range[k + 1] - scc.range[k]; + + BOOST_ASSERT_MSG(component_size > 0, "invalid component size"); + + std::vector scc_route; + auto route_begin = std::begin(scc.component) + scc.range[k]; + auto route_end = std::begin(scc.component) + scc.range[k + 1]; + + if (component_size > 1) + { + + if (component_size < BF_MAX_FEASABLE) + { + scc_route = + trip::BruteForceTrip(route_begin, route_end, number_of_locations, result_table); + } + else + { + scc_route = trip::FarthestInsertionTrip(route_begin, route_end, number_of_locations, + result_table); + } + } + else + { + scc_route = std::vector(route_begin, route_end); + } + + trips.push_back(std::move(scc_route)); + } + if (trips.empty()) + { + return Error("NoTrips", "Cannot find trips", json_result); + } + + // compute all round trip routes + std::vector routes; + routes.reserve(trips.size()); + for (const auto &trip : trips) + { + routes.push_back(ComputeRoute(snapped_phantoms, parameters, trip)); + } + + api::TripAPI trip_api{BasePlugin::facade, parameters}; + trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result); + + return Status::Ok; +} +} +} +} diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp new file mode 100644 index 000000000..748151ae6 --- /dev/null +++ b/src/engine/plugins/viaroute.cpp @@ -0,0 +1,131 @@ +#include "engine/plugins/viaroute.hpp" +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/api/route_api.hpp" +#include "engine/status.hpp" + +#include "util/for_each_pair.hpp" +#include "util/integer_range.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +ViaRoutePlugin::ViaRoutePlugin(datafacade::BaseDataFacade &facade_, int max_locations_viaroute) + : BasePlugin(facade_), shortest_path(&facade_, heaps), alternative_path(&facade_, heaps), + direct_shortest_path(&facade_, heaps), max_locations_viaroute(max_locations_viaroute) +{ +} + +Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameters, + util::json::Object &json_result) +{ + BOOST_ASSERT(route_parameters.IsValid()); + + if (max_locations_viaroute > 0 && + (static_cast(route_parameters.coordinates.size()) > max_locations_viaroute)) + { + return Error("TooBig", + "Number of entries " + std::to_string(route_parameters.coordinates.size()) + + " is higher than current maximum (" + + std::to_string(max_locations_viaroute) + ")", + json_result); + } + + if (!CheckAllCoordinates(route_parameters.coordinates)) + { + return Error("InvalidValue", "Invalid coordinate value.", json_result); + } + + auto phantom_node_pairs = GetPhantomNodes(route_parameters); + if (phantom_node_pairs.size() != route_parameters.coordinates.size()) + { + return Error("NoSegment", std::string("Could not find a matching segment for coordinate ") + + std::to_string(phantom_node_pairs.size()), + json_result); + } + BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size()); + + auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); + + const bool continue_straight_at_waypoint = route_parameters.continue_straight + ? *route_parameters.continue_straight + : facade.GetContinueStraightDefault(); + + InternalRouteResult raw_route; + auto build_phantom_pairs = [&raw_route, continue_straight_at_waypoint]( + const PhantomNode &first_node, const PhantomNode &second_node) + { + raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node}); + auto &last_inserted = raw_route.segment_end_coordinates.back(); + // enable forward direction if possible + if (last_inserted.source_phantom.forward_segment_id.id != SPECIAL_SEGMENTID) + { + last_inserted.source_phantom.forward_segment_id.enabled |= + !continue_straight_at_waypoint; + } + // enable reverse direction if possible + if (last_inserted.source_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID) + { + last_inserted.source_phantom.reverse_segment_id.enabled |= !continue_straight_at_waypoint; + } + }; + util::for_each_pair(snapped_phantoms, build_phantom_pairs); + + if (1 == raw_route.segment_end_coordinates.size()) + { + if (route_parameters.alternatives && facade.GetCoreSize() == 0) + { + alternative_path(raw_route.segment_end_coordinates.front(), raw_route); + } + else + { + direct_shortest_path(raw_route.segment_end_coordinates, raw_route); + } + } + else + { + shortest_path(raw_route.segment_end_coordinates, route_parameters.continue_straight, raw_route); + } + + // we can only know this after the fact, different SCC ids still + // allow for connection in one direction. + if (raw_route.is_valid()) + { + api::RouteAPI route_api{BasePlugin::facade, route_parameters}; + route_api.MakeResponse(raw_route, json_result); + } + else + { + auto first_component_id = snapped_phantoms.front().component.id; + auto not_in_same_component = std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(), + [first_component_id](const PhantomNode &node) + { + return node.component.id != first_component_id; + }); + + if (not_in_same_component) + { + return Error("NoRoute", "Impossible route between points", json_result); + } + else + { + return Error("NoRoute", "No route found between points", json_result); + } + } + + return Status::Ok; +} +} +} +} diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp new file mode 100644 index 000000000..4cbfe1c7f --- /dev/null +++ b/src/engine/polyline_compressor.cpp @@ -0,0 +1,128 @@ +#include "engine/polyline_compressor.hpp" + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace /*detail*/ // anonymous to keep TU local +{ + +std::string encode(int number_to_encode) +{ + std::string output; + while (number_to_encode >= 0x20) + { + const int next_value = (0x20 | (number_to_encode & 0x1f)) + 63; + output += static_cast(next_value); + number_to_encode >>= 5; + } + + number_to_encode += 63; + output += static_cast(number_to_encode); + return output; +} + +std::string encode(std::vector &numbers) +{ + std::string output; + for (auto &number : numbers) + { + bool isNegative = number < 0; + + if (isNegative) + { + const unsigned binary = std::llabs(number); + const unsigned twos = (~binary) + 1u; + number = twos; + } + + number <<= 1u; + + if (isNegative) + { + number = ~number; + } + } + for (const int number : numbers) + { + output += encode(number); + } + return output; +} +} // anonymous ns + +std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end) +{ + auto size = std::distance(begin, end); + if (size == 0) + { + return {}; + } + + std::vector delta_numbers; + BOOST_ASSERT(size > 0); + delta_numbers.reserve((size - 1) * 2); + int current_lat = 0; + int current_lon = 0; + std::for_each(begin, end, + [&delta_numbers, ¤t_lat, ¤t_lon](const util::Coordinate loc) + { + const int lat_diff = + std::round(static_cast(loc.lat) * detail::COORDINATE_TO_POLYLINE) - + current_lat; + const int lon_diff = + std::round(static_cast(loc.lon) * detail::COORDINATE_TO_POLYLINE) - + current_lon; + delta_numbers.emplace_back(lat_diff); + delta_numbers.emplace_back(lon_diff); + current_lat += lat_diff; + current_lon += lon_diff; + }); + return encode(delta_numbers); +} + +std::vector decodePolyline(const std::string &geometry_string) +{ + std::vector new_coordinates; + int index = 0, len = geometry_string.size(); + int lat = 0, lng = 0; + + while (index < len) + { + int b, shift = 0, result = 0; + do + { + b = geometry_string.at(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lat += dlat; + + shift = 0; + result = 0; + do + { + b = geometry_string.at(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lng += dlng; + + util::Coordinate p; + p.lat = util::FixedLatitude(lat * detail::POLYLINE_TO_COORDINATE); + p.lon = util::FixedLongitude(lng * detail::POLYLINE_TO_COORDINATE); + new_coordinates.push_back(p); + } + + return new_coordinates; +} +} +} diff --git a/src/engine/search_engine_data.cpp b/src/engine/search_engine_data.cpp new file mode 100644 index 000000000..72963bbba --- /dev/null +++ b/src/engine/search_engine_data.cpp @@ -0,0 +1,80 @@ +#include "engine/search_engine_data.hpp" + +#include "util/binary_heap.hpp" + +namespace osrm +{ +namespace engine +{ + +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3; + +void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes) +{ + if (forward_heap_1.get()) + { + forward_heap_1->Clear(); + } + else + { + forward_heap_1.reset(new QueryHeap(number_of_nodes)); + } + + if (reverse_heap_1.get()) + { + reverse_heap_1->Clear(); + } + else + { + reverse_heap_1.reset(new QueryHeap(number_of_nodes)); + } +} + +void SearchEngineData::InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes) +{ + if (forward_heap_2.get()) + { + forward_heap_2->Clear(); + } + else + { + forward_heap_2.reset(new QueryHeap(number_of_nodes)); + } + + if (reverse_heap_2.get()) + { + reverse_heap_2->Clear(); + } + else + { + reverse_heap_2.reset(new QueryHeap(number_of_nodes)); + } +} + +void SearchEngineData::InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes) +{ + if (forward_heap_3.get()) + { + forward_heap_3->Clear(); + } + else + { + forward_heap_3.reset(new QueryHeap(number_of_nodes)); + } + + if (reverse_heap_3.get()) + { + reverse_heap_3->Clear(); + } + else + { + reverse_heap_3.reset(new QueryHeap(number_of_nodes)); + } +} +} +} diff --git a/data_structures/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp similarity index 63% rename from data_structures/compressed_edge_container.cpp rename to src/extractor/compressed_edge_container.cpp index da916cee7..70b1f77cb 100644 --- a/data_structures/compressed_edge_container.cpp +++ b/src/extractor/compressed_edge_container.cpp @@ -1,32 +1,5 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "compressed_edge_container.hpp" -#include "../util/simple_logger.hpp" +#include "extractor/compressed_edge_container.hpp" +#include "util/simple_logger.hpp" #include #include @@ -37,6 +10,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +namespace osrm +{ +namespace extractor +{ + CompressedEdgeContainer::CompressedEdgeContainer() { m_free_list.reserve(100); @@ -81,7 +59,7 @@ void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) c { geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned)); - const std::vector ¤t_vector = elem; + const std::vector ¤t_vector = elem; const unsigned unpacked_size = current_vector.size(); BOOST_ASSERT(std::numeric_limits::max() != unpacked_size); prefix_sum_of_list_indices += unpacked_size; @@ -96,26 +74,32 @@ void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) c // write compressed geometries for (auto &elem : m_compressed_geometries) { - const std::vector ¤t_vector = elem; + const std::vector ¤t_vector = elem; const unsigned unpacked_size = current_vector.size(); control_sum += unpacked_size; BOOST_ASSERT(std::numeric_limits::max() != unpacked_size); - for (const CompressedNode current_node : current_vector) + for (const auto ¤t_node : current_vector) { - geometry_out_stream.write((char *)&(current_node.first), sizeof(NodeID)); + geometry_out_stream.write((char *)&(current_node), sizeof(CompressedEdge)); } } BOOST_ASSERT(control_sum == prefix_sum_of_list_indices); - // all done, let's close the resource - geometry_out_stream.close(); } +// Adds info for a compressed edge to the container. edge_id_2 +// has been removed from the graph, so we have to save These edges/nodes +// have already been trimmed from the graph, this function just stores +// the original data for unpacking later. +// +// edge_id_1 edge_id_2 +// ----------> via_node_id -----------> target_node_id +// weight_1 weight_2 void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, - const EdgeID edge_id_2, - const NodeID via_node_id, - const NodeID target_node_id, - const EdgeWeight weight1, - const EdgeWeight weight2) + const EdgeID edge_id_2, + const NodeID via_node_id, + const NodeID target_node_id, + const EdgeWeight weight1, + const EdgeWeight weight2) { // remove super-trivial geometries BOOST_ASSERT(SPECIAL_EDGEID != edge_id_1); @@ -153,13 +137,13 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1)); BOOST_ASSERT(edge_bucket_id1 < m_compressed_geometries.size()); - std::vector &edge_bucket_list1 = m_compressed_geometries[edge_bucket_id1]; + std::vector &edge_bucket_list1 = m_compressed_geometries[edge_bucket_id1]; // note we don't save the start coordinate: it is implicitly given by edge 1 // weight1 is the distance to the (currently) last coordinate in the bucket if (edge_bucket_list1.empty()) { - edge_bucket_list1.emplace_back(via_node_id, weight1); + edge_bucket_list1.emplace_back(CompressedEdge{via_node_id, weight1}); } BOOST_ASSERT(0 < edge_bucket_list1.size()); @@ -171,7 +155,7 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, const unsigned list_to_remove_index = GetPositionForID(edge_id_2); BOOST_ASSERT(list_to_remove_index < m_compressed_geometries.size()); - std::vector &edge_bucket_list2 = + std::vector &edge_bucket_list2 = m_compressed_geometries[list_to_remove_index]; // found an existing list, append it to the list of edge_id_1 @@ -190,7 +174,48 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, else { // we are certain that the second edge is atomic. - edge_bucket_list1.emplace_back(target_node_id, weight2); + edge_bucket_list1.emplace_back(CompressedEdge{target_node_id, weight2}); + } +} + +void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, + const NodeID target_node_id, + const EdgeWeight weight) +{ + // remove super-trivial geometries + BOOST_ASSERT(SPECIAL_EDGEID != edge_id); + BOOST_ASSERT(SPECIAL_NODEID != target_node_id); + BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight); + + // Add via node id. List is created if it does not exist + if (!HasEntryForID(edge_id)) + { + // create a new entry in the map + if (0 == m_free_list.size()) + { + // make sure there is a place to put the entries + IncreaseFreeList(); + } + BOOST_ASSERT(!m_free_list.empty()); + m_edge_id_to_list_index_map[edge_id] = m_free_list.back(); + m_free_list.pop_back(); + } + + // find bucket index + const auto iter = m_edge_id_to_list_index_map.find(edge_id); + BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end()); + const unsigned edge_bucket_id = iter->second; + BOOST_ASSERT(edge_bucket_id == GetPositionForID(edge_id)); + BOOST_ASSERT(edge_bucket_id < m_compressed_geometries.size()); + + std::vector &edge_bucket_list = m_compressed_geometries[edge_bucket_id]; + + // note we don't save the start coordinate: it is implicitly given by edge_id + // weight is the distance to the (currently) last coordinate in the bucket + // Don't re-add this if it's already in there. + if (edge_bucket_list.empty()) + { + edge_bucket_list.emplace_back(CompressedEdge{target_node_id, weight}); } } @@ -202,24 +227,23 @@ void CompressedEdgeContainer::PrintStatistics() const uint64_t compressed_geometries = 0; uint64_t longest_chain_length = 0; - for (const std::vector ¤t_vector : m_compressed_geometries) + for (const std::vector ¤t_vector : m_compressed_geometries) { compressed_geometries += current_vector.size(); longest_chain_length = std::max(longest_chain_length, (uint64_t)current_vector.size()); } - SimpleLogger().Write() << "Geometry successfully removed:" - "\n compressed edges: " << compressed_edges - << "\n compressed geometries: " << compressed_geometries - << "\n longest chain length: " << longest_chain_length - << "\n cmpr ratio: " << ((float)compressed_edges / - std::max(compressed_geometries, (uint64_t)1)) - << "\n avg chain length: " - << (float)compressed_geometries / - std::max((uint64_t)1, compressed_edges); + util::SimpleLogger().Write() + << "Geometry successfully removed:" + "\n compressed edges: " + << compressed_edges << "\n compressed geometries: " << compressed_geometries + << "\n longest chain length: " << longest_chain_length << "\n cmpr ratio: " + << ((float)compressed_edges / std::max(compressed_geometries, (uint64_t)1)) + << "\n avg chain length: " + << (float)compressed_geometries / std::max((uint64_t)1, compressed_edges); } -const CompressedEdgeContainer::EdgeBucket& +const CompressedEdgeContainer::EdgeBucket & CompressedEdgeContainer::GetBucketReference(const EdgeID edge_id) const { const unsigned index = m_edge_id_to_list_index_map.at(edge_id); @@ -230,11 +254,13 @@ NodeID CompressedEdgeContainer::GetFirstEdgeTargetID(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); BOOST_ASSERT(bucket.size() >= 2); - return bucket.front().first; + return bucket.front().node_id; } NodeID CompressedEdgeContainer::GetLastEdgeSourceID(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); BOOST_ASSERT(bucket.size() >= 2); - return bucket[bucket.size() - 2].first; + return bucket[bucket.size() - 2].node_id; +} +} } diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp new file mode 100644 index 000000000..6264fcd84 --- /dev/null +++ b/src/extractor/edge_based_graph_factory.cpp @@ -0,0 +1,482 @@ +#include "extractor/edge_based_edge.hpp" +#include "extractor/edge_based_graph_factory.hpp" +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/exception.hpp" +#include "util/integer_range.hpp" +#include "util/lua_util.hpp" +#include "util/percent.hpp" +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" + +#include "extractor/guidance/toolkit.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +// Configuration to find representative candidate for turn angle calculations + +EdgeBasedGraphFactory::EdgeBasedGraphFactory( + std::shared_ptr node_based_graph, + const CompressedEdgeContainer &compressed_edge_container, + const std::unordered_set &barrier_nodes, + const std::unordered_set &traffic_lights, + std::shared_ptr restriction_map, + const std::vector &node_info_list, + ProfileProperties profile_properties, + const util::NameTable &name_table) + : m_max_edge_id(0), m_node_info_list(node_info_list), + m_node_based_graph(std::move(node_based_graph)), + m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes), + m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container), + profile_properties(std::move(profile_properties)), name_table(name_table) +{ +} + +void EdgeBasedGraphFactory::GetEdgeBasedEdges( + util::DeallocatingVector &output_edge_list) +{ + BOOST_ASSERT_MSG(0 == output_edge_list.size(), "Vector is not empty"); + using std::swap; // Koenig swap + swap(m_edge_based_edge_list, output_edge_list); +} + +void EdgeBasedGraphFactory::GetEdgeBasedNodes(std::vector &nodes) +{ +#ifndef NDEBUG + for (const EdgeBasedNode &node : m_edge_based_node_list) + { + BOOST_ASSERT( + util::Coordinate(m_node_info_list[node.u].lon, m_node_info_list[node.u].lat).IsValid()); + BOOST_ASSERT( + util::Coordinate(m_node_info_list[node.v].lon, m_node_info_list[node.v].lat).IsValid()); + } +#endif + using std::swap; // Koenig swap + swap(nodes, m_edge_based_node_list); +} + +void EdgeBasedGraphFactory::GetStartPointMarkers(std::vector &node_is_startpoint) +{ + using std::swap; // Koenig swap + swap(m_edge_based_node_is_startpoint, node_is_startpoint); +} + +void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector &output_node_weights) +{ + using std::swap; // Koenig swap + swap(m_edge_based_node_weights, output_node_weights); +} + +unsigned EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; } + +void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v) +{ + // merge edges together into one EdgeBasedNode + BOOST_ASSERT(node_u != SPECIAL_NODEID); + BOOST_ASSERT(node_v != SPECIAL_NODEID); + + // find forward edge id and + const EdgeID edge_id_1 = m_node_based_graph->FindEdge(node_u, node_v); + BOOST_ASSERT(edge_id_1 != SPECIAL_EDGEID); + + const EdgeData &forward_data = m_node_based_graph->GetEdgeData(edge_id_1); + + // find reverse edge id and + const EdgeID edge_id_2 = m_node_based_graph->FindEdge(node_v, node_u); + BOOST_ASSERT(edge_id_2 != SPECIAL_EDGEID); + + const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(edge_id_2); + + if (forward_data.edge_id == SPECIAL_NODEID && reverse_data.edge_id == SPECIAL_NODEID) + { + return; + } + + if (forward_data.edge_id != SPECIAL_NODEID && reverse_data.edge_id == SPECIAL_NODEID) + m_edge_based_node_weights[forward_data.edge_id] = INVALID_EDGE_WEIGHT; + + BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) == + m_compressed_edge_container.HasEntryForID(edge_id_2)); + BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1)); + BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_2)); + const auto &forward_geometry = m_compressed_edge_container.GetBucketReference(edge_id_1); + BOOST_ASSERT(forward_geometry.size() == + m_compressed_edge_container.GetBucketReference(edge_id_2).size()); + const auto geometry_size = forward_geometry.size(); + + // There should always be some geometry + BOOST_ASSERT(0 != geometry_size); + + NodeID current_edge_source_coordinate_id = node_u; + + const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id) { + if (edge_based_node_id == SPECIAL_NODEID) + { + return SegmentID{SPECIAL_SEGMENTID, false}; + } + + return SegmentID{edge_based_node_id, true}; + }; + + // traverse arrays from start and end respectively + for (const auto i : util::irange(std::size_t{0}, geometry_size)) + { + BOOST_ASSERT( + current_edge_source_coordinate_id == + m_compressed_edge_container.GetBucketReference(edge_id_2)[geometry_size - 1 - i] + .node_id); + const NodeID current_edge_target_coordinate_id = forward_geometry[i].node_id; + BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id); + + // build edges + m_edge_based_node_list.emplace_back( + edge_id_to_segment_id(forward_data.edge_id), + edge_id_to_segment_id(reverse_data.edge_id), current_edge_source_coordinate_id, + current_edge_target_coordinate_id, forward_data.name_id, + m_compressed_edge_container.GetPositionForID(edge_id_1), + m_compressed_edge_container.GetPositionForID(edge_id_2), false, INVALID_COMPONENTID, i, + forward_data.travel_mode, reverse_data.travel_mode); + + m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || + reverse_data.startpoint); + current_edge_source_coordinate_id = current_edge_target_coordinate_id; + } + + BOOST_ASSERT(current_edge_source_coordinate_id == node_v); +} + +void EdgeBasedGraphFactory::FlushVectorToStream( + std::ofstream &edge_data_file, std::vector &original_edge_data_vector) const +{ + if (original_edge_data_vector.empty()) + { + return; + } + edge_data_file.write((char *)&(original_edge_data_vector[0]), + original_edge_data_vector.size() * sizeof(OriginalEdgeData)); + original_edge_data_vector.clear(); +} + +void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename, + lua_State *lua_state, + const std::string &edge_segment_lookup_filename, + const std::string &edge_penalty_filename, + const bool generate_edge_lookup) +{ + TIMER_START(renumber); + m_max_edge_id = RenumberEdges() - 1; + TIMER_STOP(renumber); + + TIMER_START(generate_nodes); + m_edge_based_node_weights.reserve(m_max_edge_id + 1); + GenerateEdgeExpandedNodes(); + TIMER_STOP(generate_nodes); + + TIMER_START(generate_edges); + GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state, edge_segment_lookup_filename, + edge_penalty_filename, generate_edge_lookup); + + TIMER_STOP(generate_edges); + + util::SimpleLogger().Write() << "Timing statistics for edge-expanded graph:"; + util::SimpleLogger().Write() << "Renumbering edges: " << TIMER_SEC(renumber) << "s"; + util::SimpleLogger().Write() << "Generating nodes: " << TIMER_SEC(generate_nodes) << "s"; + util::SimpleLogger().Write() << "Generating edges: " << TIMER_SEC(generate_edges) << "s"; +} + +/// Renumbers all _forward_ edges and sets the edge_id. +/// A specific numbering is not important. Any unique ID will do. +/// Returns the number of edge based nodes. +unsigned EdgeBasedGraphFactory::RenumberEdges() +{ + // renumber edge based node of outgoing edges + unsigned numbered_edges_count = 0; + for (const auto current_node : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) + { + for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node)) + { + EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge); + + // only number incoming edges + if (edge_data.reversed) + { + continue; + } + + // oneway streets always require this self-loop. Other streets only if a u-turn plus + // traversal + // of the street takes longer than the loop + m_edge_based_node_weights.push_back(edge_data.distance + + profile_properties.u_turn_penalty); + + BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges()); + edge_data.edge_id = numbered_edges_count; + ++numbered_edges_count; + + BOOST_ASSERT(SPECIAL_NODEID != edge_data.edge_id); + } + } + + return numbered_edges_count; +} + +/// Creates the nodes in the edge expanded graph from edges in the node-based graph. +void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() +{ + util::Percent progress(m_node_based_graph->GetNumberOfNodes()); + + // loop over all edges and generate new set of nodes + for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) + { + BOOST_ASSERT(node_u != SPECIAL_NODEID); + BOOST_ASSERT(node_u < m_node_based_graph->GetNumberOfNodes()); + progress.printStatus(node_u); + for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u)) + { + const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1); + BOOST_ASSERT(e1 != SPECIAL_EDGEID); + const NodeID node_v = m_node_based_graph->GetTarget(e1); + + BOOST_ASSERT(SPECIAL_NODEID != node_v); + // pick only every other edge, since we have every edge as an outgoing + // and incoming egde + if (node_u > node_v) + { + continue; + } + + BOOST_ASSERT(node_u < node_v); + + // if we found a non-forward edge reverse and try again + if (edge_data.edge_id == SPECIAL_NODEID) + { + InsertEdgeBasedNode(node_v, node_u); + } + else + { + InsertEdgeBasedNode(node_u, node_v); + } + } + } + + BOOST_ASSERT(m_edge_based_node_list.size() == m_edge_based_node_is_startpoint.size()); + BOOST_ASSERT(m_max_edge_id + 1 == m_edge_based_node_weights.size()); + + util::SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() + << " nodes in edge-expanded graph"; +} + +/// Actually it also generates OriginalEdgeData and serializes them... +void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( + const std::string &original_edge_data_filename, + lua_State *lua_state, + const std::string &edge_segment_lookup_filename, + const std::string &edge_fixed_penalties_filename, + const bool generate_edge_lookup) +{ + util::SimpleLogger().Write() << "generating edge-expanded edges"; + + BOOST_ASSERT(lua_state != nullptr); + const bool use_turn_function = util::luaFunctionExists(lua_state, "turn_function"); + + std::size_t node_based_edge_counter = 0; + std::size_t original_edges_counter = 0; + restricted_turns_counter = 0; + skipped_uturns_counter = 0; + skipped_barrier_turns_counter = 0; + + std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary); + std::ofstream edge_segment_file; + std::ofstream edge_penalty_file; + + if (generate_edge_lookup) + { + edge_segment_file.open(edge_segment_lookup_filename.c_str(), std::ios::binary); + edge_penalty_file.open(edge_fixed_penalties_filename.c_str(), std::ios::binary); + } + + // Writes a dummy value at the front that is updated later with the total length + const unsigned length_prefix_empty_space{0}; + edge_data_file.write(reinterpret_cast(&length_prefix_empty_space), + sizeof(length_prefix_empty_space)); + + std::vector original_edge_data_vector; + original_edge_data_vector.reserve(1024 * 1024); + + // Loop over all turns and generate new set of edges. + // Three nested loop look super-linear, but we are dealing with a (kind of) + // linear number of turns only. + util::Percent progress(m_node_based_graph->GetNumberOfNodes()); + guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_node_info_list, *m_restriction_map, + m_barrier_nodes, m_compressed_edge_container, name_table); + for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) + { + progress.printStatus(node_u); + for (const EdgeID edge_from_u : m_node_based_graph->GetAdjacentEdgeRange(node_u)) + { + if (m_node_based_graph->GetEdgeData(edge_from_u).reversed) + { + continue; + } + + ++node_based_edge_counter; + auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u); + + const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u); + + for (const auto turn : possible_turns) + { + const double turn_angle = turn.angle; + + // only add an edge if turn is not prohibited + const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(edge_from_u); + const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid); + + BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id); + BOOST_ASSERT(!edge_data1.reversed); + BOOST_ASSERT(!edge_data2.reversed); + + // the following is the core of the loop. + unsigned distance = edge_data1.distance; + if (m_traffic_lights.find(node_v) != m_traffic_lights.end()) + { + distance += profile_properties.traffic_signal_penalty; + } + + const int turn_penalty = + use_turn_function ? GetTurnPenalty(turn_angle, lua_state) : 0; + const auto turn_instruction = turn.instruction; + + if (guidance::isUturn(turn_instruction)) + { + distance += profile_properties.u_turn_penalty; + } + + distance += turn_penalty; + + BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_from_u)); + original_edge_data_vector.emplace_back( + m_compressed_edge_container.GetPositionForID(edge_from_u), edge_data1.name_id, + turn_instruction, edge_data1.travel_mode); + + ++original_edges_counter; + + if (original_edge_data_vector.size() > 1024 * 1024 * 10) + { + FlushVectorToStream(edge_data_file, original_edge_data_vector); + } + + BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); + BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); + + // NOTE: potential overflow here if we hit 2^32 routable edges + BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits::max()); + m_edge_based_edge_list.emplace_back(edge_data1.edge_id, edge_data2.edge_id, + m_edge_based_edge_list.size(), distance, true, + false); + + // Here is where we write out the mapping between the edge-expanded edges, and + // the node-based edges that are originally used to calculate the `distance` + // for the edge-expanded edges. About 40 lines back, there is: + // + // unsigned distance = edge_data1.distance; + // + // This tells us that the weight for an edge-expanded-edge is based on the weight + // of the *source* node-based edge. Therefore, we will look up the individual + // segments of the source node-based edge, and write out a mapping between + // those and the edge-based-edge ID. + // External programs can then use this mapping to quickly perform + // updates to the edge-expanded-edge based directly on its ID. + if (generate_edge_lookup) + { + unsigned fixed_penalty = distance - edge_data1.distance; + edge_penalty_file.write(reinterpret_cast(&fixed_penalty), + sizeof(fixed_penalty)); + const auto node_based_edges = + m_compressed_edge_container.GetBucketReference(edge_from_u); + NodeID previous = node_u; + + const unsigned node_count = node_based_edges.size() + 1; + edge_segment_file.write(reinterpret_cast(&node_count), + sizeof(node_count)); + const QueryNode &first_node = m_node_info_list[previous]; + edge_segment_file.write(reinterpret_cast(&first_node.node_id), + sizeof(first_node.node_id)); + + for (auto target_node : node_based_edges) + { + const QueryNode &from = m_node_info_list[previous]; + const QueryNode &to = m_node_info_list[target_node.node_id]; + const double segment_length = + util::coordinate_calculation::greatCircleDistance(from, to); + + edge_segment_file.write(reinterpret_cast(&to.node_id), + sizeof(to.node_id)); + edge_segment_file.write(reinterpret_cast(&segment_length), + sizeof(segment_length)); + edge_segment_file.write(reinterpret_cast(&target_node.weight), + sizeof(target_node.weight)); + previous = target_node.node_id; + } + } + } + } + } + + FlushVectorToStream(edge_data_file, original_edge_data_vector); + + // Finally jump back to the empty space at the beginning and write length prefix + edge_data_file.seekp(std::ios::beg); + + const auto length_prefix = boost::numeric_cast(original_edges_counter); + static_assert(sizeof(length_prefix_empty_space) == sizeof(length_prefix), "type mismatch"); + + edge_data_file.write(reinterpret_cast(&length_prefix), sizeof(length_prefix)); + + util::SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() + << " edge based nodes"; + util::SimpleLogger().Write() << "Node-based graph contains " << node_based_edge_counter + << " edges"; + util::SimpleLogger().Write() << "Edge-expanded graph ..."; + util::SimpleLogger().Write() << " contains " << m_edge_based_edge_list.size() << " edges"; + util::SimpleLogger().Write() << " skips " << restricted_turns_counter << " turns, " + "defined by " + << m_restriction_map->size() << " restrictions"; + util::SimpleLogger().Write() << " skips " << skipped_uturns_counter << " U turns"; + util::SimpleLogger().Write() << " skips " << skipped_barrier_turns_counter + << " turns over barriers"; +} + +int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const +{ + BOOST_ASSERT(lua_state != nullptr); + try + { + // call lua profile to compute turn penalty + double penalty = luabind::call_function(lua_state, "turn_function", 180. - angle); + BOOST_ASSERT(penalty < std::numeric_limits::max()); + BOOST_ASSERT(penalty > std::numeric_limits::min()); + return boost::numeric_cast(penalty); + } + catch (const luabind::error &er) + { + util::SimpleLogger().Write(logWARNING) << er.what(); + } + return 0; +} + +} // namespace extractor +} // namespace osrm diff --git a/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp similarity index 74% rename from extractor/extraction_containers.cpp rename to src/extractor/extraction_containers.cpp index 48a626b1c..5106ca982 100644 --- a/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -1,48 +1,20 @@ -/* +#include "extractor/extraction_containers.hpp" +#include "extractor/extraction_way.hpp" -Copyright (c) 2015, Project OSRM contributors -All rights reserved. +#include "util/coordinate_calculation.hpp" +#include "util/range_table.hpp" -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "extraction_containers.hpp" -#include "extraction_way.hpp" - -#include "../algorithms/coordinate_calculation.hpp" -#include "../data_structures/node_id.hpp" -#include "../data_structures/range_table.hpp" - -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" -#include "../util/fingerprint.hpp" -#include "../util/lua_util.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" +#include "util/fingerprint.hpp" +#include "util/lua_util.hpp" #include #include #include -#include #include +#include #include @@ -51,6 +23,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace +{ +// Needed for STXXL comparison - STXXL requires max_value(), min_value(), so we can not use +// std::less{}. Anonymous namespace to keep translation unit local. +struct OSMNodeIDSTXXLLess +{ + using value_type = OSMNodeID; + bool operator()(const value_type left, const value_type right) const { return left < right; } + value_type max_value() { return MAX_OSM_NODEID; } + value_type min_value() { return MIN_OSM_NODEID; } +}; +} + +namespace osrm +{ +namespace extractor +{ + static const int WRITE_BLOCK_BUFFER_SIZE = 8000; ExtractionContainers::ExtractionContainers() @@ -92,16 +82,14 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name, { std::ofstream file_out_stream; file_out_stream.open(output_file_name.c_str(), std::ios::binary); - const FingerPrint fingerprint = FingerPrint::GetValid(); - file_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); + const util::FingerPrint fingerprint = util::FingerPrint::GetValid(); + file_out_stream.write((char *)&fingerprint, sizeof(util::FingerPrint)); PrepareNodes(); WriteNodes(file_out_stream); PrepareEdges(segment_state); WriteEdges(file_out_stream); - file_out_stream.close(); - PrepareRestrictions(); WriteRestrictions(restrictions_file_name); @@ -113,7 +101,7 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name, } } -void ExtractionContainers::WriteNames(const std::string& names_file_name) const +void ExtractionContainers::WriteNames(const std::string &names_file_name) const { std::cout << "[extractor] writing street name index ... " << std::flush; TIMER_START(write_name_index); @@ -127,12 +115,11 @@ void ExtractionContainers::WriteNames(const std::string& names_file_name) const } // builds and writes the index - RangeTable<> name_index_range(name_lengths); + util::RangeTable<> name_index_range(name_lengths); name_file_stream << name_index_range; name_file_stream.write((char *)&total_length, sizeof(unsigned)); - // write all chars consecutively char write_buffer[WRITE_BLOCK_BUFFER_SIZE]; unsigned buffer_len = 0; @@ -150,7 +137,6 @@ void ExtractionContainers::WriteNames(const std::string& names_file_name) const name_file_stream.write(write_buffer, buffer_len); - name_file_stream.close(); TIMER_STOP(write_name_index); std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl; } @@ -159,7 +145,8 @@ void ExtractionContainers::PrepareNodes() { std::cout << "[extractor] Sorting used nodes ... " << std::flush; TIMER_START(sorting_used_nodes); - stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), Cmp(), stxxl_memory); + stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), OSMNodeIDSTXXLLess(), + stxxl_memory); TIMER_STOP(sorting_used_nodes); std::cout << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s" << std::endl; @@ -210,12 +197,12 @@ void ExtractionContainers::PrepareNodes() } if (internal_id > std::numeric_limits::max()) { - throw osrm::exception("There are too many nodes remaining after filtering, OSRM only supports 2^32 unique nodes"); + throw util::exception("There are too many nodes remaining after filtering, OSRM only " + "supports 2^32 unique nodes"); } max_internal_node_id = boost::numeric_cast(internal_id); TIMER_STOP(id_map); std::cout << "ok, after " << TIMER_SEC(id_map) << "s" << std::endl; - } void ExtractionContainers::PrepareEdges(lua_State *segment_state) @@ -240,7 +227,8 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) { if (edge_iterator->result.osm_source_id < node_iterator->node_id) { - SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " << edge_iterator->result.source; + util::SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " + << edge_iterator->result.source; edge_iterator->result.source = SPECIAL_NODEID; ++edge_iterator; continue; @@ -276,8 +264,8 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) // them. This happens when using osmosis with bbox or polygon to extract smaller areas. auto markSourcesInvalid = [](InternalExtractorEdge &edge) { - SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " - << edge.result.source; + util::SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " + << edge.result.source; edge.result.source = SPECIAL_NODEID; edge.result.osm_source_id = SPECIAL_OSM_NODEID; }; @@ -288,8 +276,7 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) // Sort Edges by target std::cout << "[extractor] Sorting edges by target ... " << std::flush; TIMER_START(sort_edges_by_target); - stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMTargetID(), - stxxl_memory); + stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMTargetID(), stxxl_memory); TIMER_STOP(sort_edges_by_target); std::cout << "ok, after " << TIMER_SEC(sort_edges_by_target) << "s" << std::endl; @@ -301,6 +288,8 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) const auto all_edges_list_end_ = all_edges_list.end(); const auto all_nodes_list_end_ = all_nodes_list.end(); + const auto has_segment_function = util::luaFunctionExists(segment_state, "segment_function"); + while (edge_iterator != all_edges_list_end_ && node_iterator != all_nodes_list_end_) { // skip all invalid edges @@ -312,7 +301,9 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) if (edge_iterator->result.osm_target_id < node_iterator->node_id) { - SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " << OSMNodeID_to_uint64_t(edge_iterator->result.osm_target_id); + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Found invalid node reference " + << static_cast(edge_iterator->result.osm_target_id); edge_iterator->result.target = SPECIAL_NODEID; ++edge_iterator; continue; @@ -325,40 +316,40 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) BOOST_ASSERT(edge_iterator->result.osm_target_id == node_iterator->node_id); BOOST_ASSERT(edge_iterator->weight_data.speed >= 0); - BOOST_ASSERT(edge_iterator->source_coordinate.lat != std::numeric_limits::min()); - BOOST_ASSERT(edge_iterator->source_coordinate.lon != std::numeric_limits::min()); + BOOST_ASSERT(edge_iterator->source_coordinate.lat != + util::FixedLatitude(std::numeric_limits::min())); + BOOST_ASSERT(edge_iterator->source_coordinate.lon != + util::FixedLongitude(std::numeric_limits::min())); - const double distance = coordinate_calculation::great_circle_distance( - edge_iterator->source_coordinate.lat, edge_iterator->source_coordinate.lon, - node_iterator->lat, node_iterator->lon); + const double distance = util::coordinate_calculation::greatCircleDistance( + edge_iterator->source_coordinate, + util::Coordinate(node_iterator->lon, node_iterator->lat)); - if (lua_function_exists(segment_state, "segment_function")) + if (has_segment_function) { luabind::call_function( - segment_state, "segment_function", - boost::cref(edge_iterator->source_coordinate), - boost::cref(*node_iterator), - distance, - boost::ref(edge_iterator->weight_data)); + segment_state, "segment_function", boost::cref(edge_iterator->source_coordinate), + boost::cref(*node_iterator), distance, boost::ref(edge_iterator->weight_data)); } - const double weight = [distance](const InternalExtractorEdge::WeightData& data) { + const double weight = [distance](const InternalExtractorEdge::WeightData &data) + { switch (data.type) { - case InternalExtractorEdge::WeightType::EDGE_DURATION: - case InternalExtractorEdge::WeightType::WAY_DURATION: - return data.duration * 10.; - break; - case InternalExtractorEdge::WeightType::SPEED: - return (distance * 10.) / (data.speed / 3.6); - break; - case InternalExtractorEdge::WeightType::INVALID: - osrm::exception("invalid weight type"); + case InternalExtractorEdge::WeightType::EDGE_DURATION: + case InternalExtractorEdge::WeightType::WAY_DURATION: + return data.duration * 10.; + break; + case InternalExtractorEdge::WeightType::SPEED: + return (distance * 10.) / (data.speed / 3.6); + break; + case InternalExtractorEdge::WeightType::INVALID: + util::exception("invalid weight type"); } return -1.0; }(edge_iterator->weight_data); - auto& edge = edge_iterator->result; + auto &edge = edge_iterator->result; edge.weight = std::max(1, static_cast(std::floor(weight + .5))); // assign new node id @@ -384,8 +375,8 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) // them. This happens when using osmosis with bbox or polygon to extract smaller areas. auto markTargetsInvalid = [](InternalExtractorEdge &edge) { - SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " - << edge.result.target; + util::SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " + << edge.result.target; edge.result.target = SPECIAL_NODEID; }; std::for_each(edge_iterator, all_edges_list_end_, markTargetsInvalid); @@ -395,7 +386,8 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) // Sort edges by start. std::cout << "[extractor] Sorting edges by renumbered start ... " << std::flush; TIMER_START(sort_edges_by_renumbered_start); - stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByInternalStartThenInternalTargetID(), stxxl_memory); + stxxl::sort(all_edges_list.begin(), all_edges_list.end(), + CmpEdgeByInternalStartThenInternalTargetID(), stxxl_memory); TIMER_STOP(sort_edges_by_renumbered_start); std::cout << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s" << std::endl; @@ -427,11 +419,13 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) while (all_edges_list[i].result.source == source && all_edges_list[i].result.target == target) { - if (all_edges_list[i].result.forward && all_edges_list[i].result.weight < min_forward_weight) + if (all_edges_list[i].result.forward && + all_edges_list[i].result.weight < min_forward_weight) { min_forward_idx = i; } - if (all_edges_list[i].result.backward && all_edges_list[i].result.weight < min_backward_weight) + if (all_edges_list[i].result.backward && + all_edges_list[i].result.weight < min_backward_weight) { min_backward_idx = i; } @@ -440,8 +434,10 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) i++; } - BOOST_ASSERT(min_forward_idx == std::numeric_limits::max() || min_forward_idx < i); - BOOST_ASSERT(min_backward_idx == std::numeric_limits::max() || min_backward_idx < i); + BOOST_ASSERT(min_forward_idx == std::numeric_limits::max() || + min_forward_idx < i); + BOOST_ASSERT(min_backward_idx == std::numeric_limits::max() || + min_backward_idx < i); BOOST_ASSERT(min_backward_idx != std::numeric_limits::max() || min_forward_idx != std::numeric_limits::max()); @@ -484,7 +480,7 @@ void ExtractionContainers::PrepareEdges(lua_State *segment_state) } } -void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const +void ExtractionContainers::WriteEdges(std::ofstream &file_out_stream) const { std::cout << "[extractor] Writing used edges ... " << std::flush; TIMER_START(write_edges); @@ -495,7 +491,7 @@ void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const auto start_position = file_out_stream.tellp(); file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned)); - for (const auto& edge : all_edges_list) + for (const auto &edge : all_edges_list) { if (edge.result.source == SPECIAL_NODEID || edge.result.target == SPECIAL_NODEID) { @@ -505,13 +501,13 @@ void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const // IMPORTANT: here, we're using slicing to only write the data from the base // class of NodeBasedEdgeWithOSM NodeBasedEdge tmp = edge.result; - file_out_stream.write((char*) &tmp, sizeof(NodeBasedEdge)); + file_out_stream.write((char *)&tmp, sizeof(NodeBasedEdge)); used_edges_counter++; } if (used_edges_counter > std::numeric_limits::max()) { - throw osrm::exception("There are too many edges, OSRM only supports 2^32"); + throw util::exception("There are too many edges, OSRM only supports 2^32"); } TIMER_STOP(write_edges); std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; @@ -524,10 +520,10 @@ void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned)); std::cout << "ok" << std::endl; - SimpleLogger().Write() << "Processed " << used_edges_counter << " edges"; + util::SimpleLogger().Write() << "Processed " << used_edges_counter << " edges"; } -void ExtractionContainers::WriteNodes(std::ofstream& file_out_stream) const +void ExtractionContainers::WriteNodes(std::ofstream &file_out_stream) const { // write dummy value, will be overwritten later std::cout << "[extractor] setting number of nodes ... " << std::flush; @@ -564,18 +560,17 @@ void ExtractionContainers::WriteNodes(std::ofstream& file_out_stream) const TIMER_STOP(write_nodes); std::cout << "ok, after " << TIMER_SEC(write_nodes) << "s" << std::endl; - - SimpleLogger().Write() << "Processed " << max_internal_node_id << " nodes"; + util::SimpleLogger().Write() << "Processed " << max_internal_node_id << " nodes"; } -void ExtractionContainers::WriteRestrictions(const std::string& path) const +void ExtractionContainers::WriteRestrictions(const std::string &path) const { // serialize restrictions std::ofstream restrictions_out_stream; unsigned written_restriction_count = 0; restrictions_out_stream.open(path.c_str(), std::ios::binary); - const FingerPrint fingerprint = FingerPrint::GetValid(); - restrictions_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); + const util::FingerPrint fingerprint = util::FingerPrint::GetValid(); + restrictions_out_stream.write((char *)&fingerprint, sizeof(util::FingerPrint)); const auto count_position = restrictions_out_stream.tellp(); restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); @@ -592,8 +587,7 @@ void ExtractionContainers::WriteRestrictions(const std::string& path) const } restrictions_out_stream.seekp(count_position); restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); - restrictions_out_stream.close(); - SimpleLogger().Write() << "usable restrictions: " << written_restriction_count; + util::SimpleLogger().Write() << "usable restrictions: " << written_restriction_count; } void ExtractionContainers::PrepareRestrictions() @@ -605,11 +599,11 @@ void ExtractionContainers::PrepareRestrictions() TIMER_STOP(sort_ways); std::cout << "ok, after " << TIMER_SEC(sort_ways) << "s" << std::endl; - std::cout << "[extractor] Sorting " << restrictions_list.size() - << " restriction. by from... " << std::flush; + std::cout << "[extractor] Sorting " << restrictions_list.size() << " restriction. by from... " + << std::flush; TIMER_START(sort_restrictions); - stxxl::sort(restrictions_list.begin(), restrictions_list.end(), - CmpRestrictionContainerByFrom(), stxxl_memory); + stxxl::sort(restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByFrom(), + stxxl_memory); TIMER_STOP(sort_restrictions); std::cout << "ok, after " << TIMER_SEC(sort_restrictions) << "s" << std::endl; @@ -623,15 +617,19 @@ void ExtractionContainers::PrepareRestrictions() while (way_start_and_end_iterator != way_start_end_id_list_end && restrictions_iterator != restrictions_list_end) { - if (way_start_and_end_iterator->way_id < OSMWayID(restrictions_iterator->restriction.from.way)) + if (way_start_and_end_iterator->way_id < + OSMWayID(restrictions_iterator->restriction.from.way)) { ++way_start_and_end_iterator; continue; } - if (way_start_and_end_iterator->way_id > OSMWayID(restrictions_iterator->restriction.from.way)) + if (way_start_and_end_iterator->way_id > + OSMWayID(restrictions_iterator->restriction.from.way)) { - SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.from.way; + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Restriction references invalid way: " + << restrictions_iterator->restriction.from.way; restrictions_iterator->restriction.from.node = SPECIAL_NODEID; ++restrictions_iterator; continue; @@ -644,9 +642,11 @@ void ExtractionContainers::PrepareRestrictions() // check if via is actually valid, if not invalidate auto via_id_iter = external_to_internal_node_id_map.find(via_node_id); - if(via_id_iter == external_to_internal_node_id_map.end()) + if (via_id_iter == external_to_internal_node_id_map.end()) { - SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid node: " << restrictions_iterator->restriction.via.node; + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Restriction references invalid node: " + << restrictions_iterator->restriction.via.node; restrictions_iterator->restriction.via.node = SPECIAL_NODEID; ++restrictions_iterator; continue; @@ -656,16 +656,34 @@ void ExtractionContainers::PrepareRestrictions() { // assign new from node id auto id_iter = external_to_internal_node_id_map.find( - OSMNodeID(way_start_and_end_iterator->first_segment_target_id)); - BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + OSMNodeID(way_start_and_end_iterator->first_segment_target_id)); + if (id_iter == external_to_internal_node_id_map.end()) + { + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Way references invalid node: " + << way_start_and_end_iterator->first_segment_target_id; + restrictions_iterator->restriction.from.node = SPECIAL_NODEID; + ++restrictions_iterator; + ++way_start_and_end_iterator; + continue; + } restrictions_iterator->restriction.from.node = id_iter->second; } else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id) { // assign new from node id auto id_iter = external_to_internal_node_id_map.find( - OSMNodeID(way_start_and_end_iterator->last_segment_source_id)); - BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + OSMNodeID(way_start_and_end_iterator->last_segment_source_id)); + if (id_iter == external_to_internal_node_id_map.end()) + { + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Way references invalid node: " + << way_start_and_end_iterator->last_segment_target_id; + restrictions_iterator->restriction.from.node = SPECIAL_NODEID; + ++restrictions_iterator; + ++way_start_and_end_iterator; + continue; + } restrictions_iterator->restriction.from.node = id_iter->second; } ++restrictions_iterator; @@ -676,8 +694,8 @@ void ExtractionContainers::PrepareRestrictions() std::cout << "[extractor] Sorting restrictions. by to ... " << std::flush; TIMER_START(sort_restrictions_to); - stxxl::sort(restrictions_list.begin(), restrictions_list.end(), - CmpRestrictionContainerByTo(), stxxl_memory); + stxxl::sort(restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByTo(), + stxxl_memory); TIMER_STOP(sort_restrictions_to); std::cout << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s" << std::endl; @@ -691,7 +709,8 @@ void ExtractionContainers::PrepareRestrictions() while (way_start_and_end_iterator != way_start_end_id_list_end_ && restrictions_iterator != restrictions_list_end_) { - if (way_start_and_end_iterator->way_id < OSMWayID(restrictions_iterator->restriction.to.way)) + if (way_start_and_end_iterator->way_id < + OSMWayID(restrictions_iterator->restriction.to.way)) { ++way_start_and_end_iterator; continue; @@ -702,9 +721,12 @@ void ExtractionContainers::PrepareRestrictions() ++restrictions_iterator; continue; } - if (way_start_and_end_iterator->way_id > OSMWayID(restrictions_iterator->restriction.to.way)) + if (way_start_and_end_iterator->way_id > + OSMWayID(restrictions_iterator->restriction.to.way)) { - SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.to.way; + util::SimpleLogger().Write(LogLevel::logDEBUG) + << "Restriction references invalid way: " + << restrictions_iterator->restriction.to.way; restrictions_iterator->restriction.to.way = SPECIAL_NODEID; ++restrictions_iterator; continue; @@ -721,15 +743,33 @@ void ExtractionContainers::PrepareRestrictions() if (OSMNodeID(way_start_and_end_iterator->first_segment_source_id) == via_node_id) { auto to_id_iter = external_to_internal_node_id_map.find( - OSMNodeID(way_start_and_end_iterator->first_segment_target_id)); - BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end()); + OSMNodeID(way_start_and_end_iterator->first_segment_target_id)); + if (to_id_iter == external_to_internal_node_id_map.end()) + { + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Way references invalid node: " + << way_start_and_end_iterator->first_segment_source_id; + restrictions_iterator->restriction.to.node = SPECIAL_NODEID; + ++restrictions_iterator; + ++way_start_and_end_iterator; + continue; + } restrictions_iterator->restriction.to.node = to_id_iter->second; } else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id) { auto to_id_iter = external_to_internal_node_id_map.find( - OSMNodeID(way_start_and_end_iterator->last_segment_source_id)); - BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end()); + OSMNodeID(way_start_and_end_iterator->last_segment_source_id)); + if (to_id_iter == external_to_internal_node_id_map.end()) + { + util::SimpleLogger().Write(LogLevel::logWARNING) + << "Way references invalid node: " + << way_start_and_end_iterator->last_segment_source_id; + restrictions_iterator->restriction.to.node = SPECIAL_NODEID; + ++restrictions_iterator; + ++way_start_and_end_iterator; + continue; + } restrictions_iterator->restriction.to.node = to_id_iter->second; } ++restrictions_iterator; @@ -737,3 +777,5 @@ void ExtractionContainers::PrepareRestrictions() TIMER_STOP(fix_restriction_ends); std::cout << "ok, after " << TIMER_SEC(fix_restriction_ends) << "s" << std::endl; } +} +} diff --git a/extractor/extractor.cpp b/src/extractor/extractor.cpp similarity index 57% rename from extractor/extractor.cpp rename to src/extractor/extractor.cpp index 80bd33d1c..977a59ce7 100644 --- a/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -1,55 +1,30 @@ -/* +#include "extractor/extractor.hpp" -Copyright (c) 2015, Project OSRM contributors -All rights reserved. +#include "extractor/edge_based_edge.hpp" +#include "extractor/extraction_containers.hpp" +#include "extractor/extraction_node.hpp" +#include "extractor/extraction_way.hpp" +#include "extractor/extractor_callbacks.hpp" +#include "extractor/restriction_parser.hpp" +#include "extractor/scripting_environment.hpp" -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +#include "extractor/raster_source.hpp" +#include "util/io.hpp" +#include "util/make_unique.hpp" +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" +#include "util/lua_util.hpp" +#include "util/graph_loader.hpp" +#include "util/name_table.hpp" -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. +#include "util/typedefs.hpp" -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "util/static_graph.hpp" +#include "util/static_rtree.hpp" +#include "extractor/compressed_edge_container.hpp" +#include "extractor/restriction_map.hpp" -*/ - -#include "extractor.hpp" - -#include "extraction_containers.hpp" -#include "extraction_node.hpp" -#include "extraction_way.hpp" -#include "extractor_callbacks.hpp" -#include "restriction_parser.hpp" -#include "scripting_environment.hpp" - -#include "../data_structures/raster_source.hpp" -#include "../util/make_unique.hpp" -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" -#include "../util/lua_util.hpp" -#include "../util/graph_loader.hpp" - -#include "../typedefs.h" - -#include "../data_structures/static_graph.hpp" -#include "../data_structures/static_rtree.hpp" -#include "../data_structures/restriction_map.hpp" -#include "../data_structures/compressed_edge_container.hpp" - -#include "../algorithms/tarjan_scc.hpp" -#include "../algorithms/crc32_processor.hpp" +#include "extractor/tarjan_scc.hpp" #include #include @@ -72,6 +47,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include + +namespace osrm +{ +namespace extractor +{ /** * TODO: Refactor this function into smaller functions for better readability. @@ -87,16 +69,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The result of this process are the following files: * .names : Names of all streets, stored as long consecutive string with prefix sum based index - * .osrm : Nodes and edges in a intermediate format that easy to digest for osrm-prepare - * .restrictions : Turn restrictions that are used my osrm-prepare to construct the edge-expanded + * .osrm : Nodes and edges in a intermediate format that easy to digest for osrm-contract + * .restrictions : Turn restrictions that are used by osrm-contract to construct the edge-expanded * graph * */ -int extractor::run() +int Extractor::run() { + // setup scripting environment + ScriptingEnvironment scripting_environment(config.profile_path.string().c_str()); + try { - LogPolicy::GetInstance().Unmute(); + util::LogPolicy::GetInstance().Unmute(); TIMER_START(extracting); const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); @@ -104,15 +89,12 @@ int extractor::run() std::min(recommended_num_threads, config.requested_num_threads); tbb::task_scheduler_init init(number_of_threads); - SimpleLogger().Write() << "Input file: " << config.input_path.filename().string(); - SimpleLogger().Write() << "Profile: " << config.profile_path.filename().string(); - SimpleLogger().Write() << "Threads: " << number_of_threads; - - // setup scripting environment - ScriptingEnvironment scripting_environment(config.profile_path.string().c_str()); + util::SimpleLogger().Write() << "Input file: " << config.input_path.filename().string(); + util::SimpleLogger().Write() << "Profile: " << config.profile_path.filename().string(); + util::SimpleLogger().Write() << "Threads: " << number_of_threads; ExtractionContainers extraction_containers; - auto extractor_callbacks = osrm::make_unique(extraction_containers); + auto extractor_callbacks = util::make_unique(extraction_containers); const osmium::io::File input_file(config.input_path.string()); osmium::io::Reader reader(input_file); @@ -123,18 +105,15 @@ int extractor::run() std::atomic number_of_relations{0}; std::atomic number_of_others{0}; - SimpleLogger().Write() << "Parsing in progress.."; + util::SimpleLogger().Write() << "Parsing in progress.."; TIMER_START(parsing); - lua_State *segment_state = scripting_environment.get_lua_state(); + auto &main_context = scripting_environment.GetContex(); - if (lua_function_exists(segment_state, "source_function")) + // setup raster sources + if (util::luaFunctionExists(main_context.state, "source_function")) { - // bind a single instance of SourceContainer class to relevant lua state - SourceContainer sources; - luabind::globals(segment_state)["sources"] = sources; - - luabind::call_function(segment_state, "source_function"); + luabind::call_function(main_context.state, "source_function"); } std::string generator = header.get("generator"); @@ -142,7 +121,7 @@ int extractor::run() { generator = "unknown tool"; } - SimpleLogger().Write() << "input file generated by " << generator; + util::SimpleLogger().Write() << "input file generated by " << generator; // write .timestamp data file std::string timestamp = header.get("osmosis_replication_timestamp"); @@ -150,11 +129,10 @@ int extractor::run() { timestamp = "n/a"; } - SimpleLogger().Write() << "timestamp: " << timestamp; + util::SimpleLogger().Write() << "timestamp: " << timestamp; boost::filesystem::ofstream timestamp_out(config.timestamp_file_name); timestamp_out.write(timestamp.c_str(), timestamp.length()); - timestamp_out.close(); // initialize vectors holding parsed objects tbb::concurrent_vector> resulting_nodes; @@ -162,7 +140,7 @@ int extractor::run() tbb::concurrent_vector> resulting_restrictions; // setup restriction parser - const RestrictionParser restriction_parser(scripting_environment.get_lua_state()); + const RestrictionParser restriction_parser(main_context.state, main_context.properties); while (const osmium::memory::Buffer buffer = reader.read()) { @@ -185,7 +163,7 @@ int extractor::run() { ExtractionNode result_node; ExtractionWay result_way; - lua_State *local_state = scripting_environment.get_lua_state(); + auto &local_context = scripting_environment.GetContex(); for (auto x = range.begin(), end = range.end(); x != end; ++x) { @@ -197,7 +175,7 @@ int extractor::run() result_node.clear(); ++number_of_nodes; luabind::call_function( - local_state, "node_function", + local_context.state, "node_function", boost::cref(static_cast(*entity)), boost::ref(result_node)); resulting_nodes.push_back(std::make_pair(x, result_node)); @@ -206,7 +184,7 @@ int extractor::run() result_way.clear(); ++number_of_ways; luabind::call_function( - local_state, "way_function", + local_context.state, "way_function", boost::cref(static_cast(*entity)), boost::ref(result_way)); resulting_ways.push_back(std::make_pair(x, result_way)); @@ -241,134 +219,117 @@ int extractor::run() } } TIMER_STOP(parsing); - SimpleLogger().Write() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds"; + util::SimpleLogger().Write() << "Parsing finished after " << TIMER_SEC(parsing) + << " seconds"; - SimpleLogger().Write() << "Raw input contains " << number_of_nodes.load() << " nodes, " - << number_of_ways.load() << " ways, and " - << number_of_relations.load() << " relations, and " - << number_of_others.load() << " unknown entities"; + util::SimpleLogger().Write() << "Raw input contains " << number_of_nodes.load() + << " nodes, " << number_of_ways.load() << " ways, and " + << number_of_relations.load() << " relations, and " + << number_of_others.load() << " unknown entities"; extractor_callbacks.reset(); if (extraction_containers.all_edges_list.empty()) { - SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; + util::SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; return 1; } extraction_containers.PrepareData(config.output_file_name, config.restriction_file_name, - config.names_file_name, segment_state); + config.names_file_name, main_context.state); + + WriteProfileProperties(config.profile_properties_output_path, main_context.properties); TIMER_STOP(extracting); - SimpleLogger().Write() << "extraction finished after " << TIMER_SEC(extracting) << "s"; + util::SimpleLogger().Write() << "extraction finished after " << TIMER_SEC(extracting) + << "s"; } + // we do this for scoping + // TODO move to own functions catch (const std::exception &e) { - SimpleLogger().Write(logWARNING) << e.what(); + util::SimpleLogger().Write(logWARNING) << e.what(); return 1; } - try { // Transform the node-based graph that OSM is based on into an edge-based graph // that is better for routing. Every edge becomes a node, and every valid // movement (e.g. turn from A->B, and B->A) becomes an edge // - // - // // Create a new lua state - SimpleLogger().Write() << "Generating edge-expanded graph representation"; + auto &main_context = scripting_environment.GetContex(); + + util::SimpleLogger().Write() << "Generating edge-expanded graph representation"; TIMER_START(expansion); - std::vector node_based_edge_list; - DeallocatingVector edge_based_edge_list; + std::vector edge_based_node_list; + util::DeallocatingVector edge_based_edge_list; std::vector node_is_startpoint; + std::vector edge_based_node_weights; std::vector internal_to_external_node_map; - auto graph_size = - BuildEdgeExpandedGraph(internal_to_external_node_map, node_based_edge_list, - node_is_startpoint, edge_based_edge_list); + auto graph_size = BuildEdgeExpandedGraph(main_context.state, main_context.properties, + internal_to_external_node_map, + edge_based_node_list, node_is_startpoint, + edge_based_node_weights, edge_based_edge_list); auto number_of_node_based_nodes = graph_size.first; auto max_edge_id = graph_size.second; TIMER_STOP(expansion); - SimpleLogger().Write() << "building r-tree ..."; + util::SimpleLogger().Write() << "Saving edge-based node weights to file."; + TIMER_START(timer_write_node_weights); + util::serializeVector(config.edge_based_node_weights_output_path, edge_based_node_weights); + TIMER_STOP(timer_write_node_weights); + util::SimpleLogger().Write() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) + << ")"; + + util::SimpleLogger().Write() << "building r-tree ..."; TIMER_START(rtree); - FindComponents(max_edge_id, edge_based_edge_list, node_based_edge_list); + FindComponents(max_edge_id, edge_based_edge_list, edge_based_node_list); - BuildRTree(std::move(node_based_edge_list), std::move(node_is_startpoint), + BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint), internal_to_external_node_map); TIMER_STOP(rtree); - SimpleLogger().Write() << "writing node map ..."; + util::SimpleLogger().Write() << "writing node map ..."; WriteNodeMapping(internal_to_external_node_map); WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list); - SimpleLogger().Write() << "Expansion : " - << (number_of_node_based_nodes / TIMER_SEC(expansion)) - << " nodes/sec and " << ((max_edge_id + 1) / TIMER_SEC(expansion)) - << " edges/sec"; - SimpleLogger().Write() << "To prepare the data for routing, run: " - << "./osrm-prepare " << config.output_file_name << std::endl; + util::SimpleLogger().Write() + << "Expansion : " << (number_of_node_based_nodes / TIMER_SEC(expansion)) + << " nodes/sec and " << ((max_edge_id + 1) / TIMER_SEC(expansion)) << " edges/sec"; + util::SimpleLogger().Write() << "To prepare the data for routing, run: " + << "./osrm-contract " << config.output_file_name << std::endl; } catch (const std::exception &e) { - SimpleLogger().Write(logWARNING) << e.what(); + util::SimpleLogger().Write(logWARNING) << e.what(); return 1; } return 0; } -/** - \brief Setups scripting environment (lua-scripting) - Also initializes speed profile. -*/ -void extractor::SetupScriptingEnvironment(lua_State *lua_state, - SpeedProfileProperties &speed_profile) +void Extractor::WriteProfileProperties(const std::string &output_path, + const ProfileProperties &properties) const { - // open utility libraries string library; - luaL_openlibs(lua_state); - - // adjust lua load path - luaAddScriptFolderToLoadPath(lua_state, config.profile_path.string().c_str()); - - // Now call our function in a lua script - if (0 != luaL_dofile(lua_state, config.profile_path.string().c_str())) + boost::filesystem::ofstream out_stream(output_path); + if (!out_stream) { - std::stringstream msg; - msg << lua_tostring(lua_state, -1) << " occured in scripting block"; - throw osrm::exception(msg.str()); + throw util::exception("Could not open " + output_path + " for writing."); } - if (0 != luaL_dostring(lua_state, "return traffic_signal_penalty\n")) - { - std::stringstream msg; - msg << lua_tostring(lua_state, -1) << " occured in scripting block"; - throw osrm::exception(msg.str()); - } - speed_profile.traffic_signal_penalty = 10 * lua_tointeger(lua_state, -1); - SimpleLogger().Write(logDEBUG) << "traffic_signal_penalty: " - << speed_profile.traffic_signal_penalty; - - if (0 != luaL_dostring(lua_state, "return u_turn_penalty\n")) - { - std::stringstream msg; - msg << lua_tostring(lua_state, -1) << " occured in scripting block"; - throw osrm::exception(msg.str()); - } - - speed_profile.u_turn_penalty = 10 * lua_tointeger(lua_state, -1); - speed_profile.has_turn_penalty_function = lua_function_exists(lua_state, "turn_function"); + out_stream.write(reinterpret_cast(&properties), sizeof(properties)); } -void extractor::FindComponents(unsigned max_edge_id, - const DeallocatingVector &input_edge_list, +void Extractor::FindComponents(unsigned max_edge_id, + const util::DeallocatingVector &input_edge_list, std::vector &input_nodes) const { struct UncontractedEdgeData @@ -390,7 +351,7 @@ void extractor::FindComponents(unsigned max_edge_id, return source == rhs.source && target == rhs.target; } }; - using UncontractedGraph = StaticGraph; + using UncontractedGraph = util::StaticGraph; std::vector edges; edges.reserve(input_edge_list.size() * 2); @@ -398,6 +359,8 @@ void extractor::FindComponents(unsigned max_edge_id, { BOOST_ASSERT_MSG(static_cast(std::max(edge.weight, 1)) > 0, "edge distance < 1"); + BOOST_ASSERT(edge.source <= max_edge_id); + BOOST_ASSERT(edge.target <= max_edge_id); if (edge.forward) { edges.push_back({edge.source, edge.target, {}}); @@ -412,10 +375,12 @@ void extractor::FindComponents(unsigned max_edge_id, // connect forward and backward nodes of each edge for (const auto &node : input_nodes) { - if (node.reverse_edge_based_node_id != SPECIAL_NODEID) + if (node.reverse_segment_id.enabled) { - edges.push_back({node.forward_edge_based_node_id, node.reverse_edge_based_node_id, {}}); - edges.push_back({node.reverse_edge_based_node_id, node.forward_edge_based_node_id, {}}); + BOOST_ASSERT(node.forward_segment_id.id <= max_edge_id); + BOOST_ASSERT(node.reverse_segment_id.id <= max_edge_id); + edges.push_back({node.forward_segment_id.id, node.reverse_segment_id.id, {}}); + edges.push_back({node.reverse_segment_id.id, node.forward_segment_id.id, {}}); } } @@ -431,10 +396,10 @@ void extractor::FindComponents(unsigned max_edge_id, for (auto &node : input_nodes) { - auto forward_component = component_search.get_component_id(node.forward_edge_based_node_id); - BOOST_ASSERT(node.reverse_edge_based_node_id == SPECIAL_EDGEID || + auto forward_component = component_search.get_component_id(node.forward_segment_id.id); + BOOST_ASSERT(!node.reverse_segment_id.enabled || forward_component == - component_search.get_component_id(node.reverse_edge_based_node_id)); + component_search.get_component_id(node.reverse_segment_id.id)); const unsigned component_size = component_search.get_component_size(forward_component); node.component.is_tiny = component_size < config.small_component_size; @@ -445,15 +410,15 @@ void extractor::FindComponents(unsigned max_edge_id, /** \brief Build load restrictions from .restriction file */ -std::shared_ptr extractor::LoadRestrictionMap() +std::shared_ptr Extractor::LoadRestrictionMap() { boost::filesystem::ifstream input_stream(config.restriction_file_name, std::ios::in | std::ios::binary); std::vector restriction_list; - loadRestrictionsFromFile(input_stream, restriction_list); + util::loadRestrictionsFromFile(input_stream, restriction_list); - SimpleLogger().Write() << " - " << restriction_list.size() << " restrictions."; + util::SimpleLogger().Write() << " - " << restriction_list.size() << " restrictions."; return std::make_shared(restriction_list); } @@ -461,8 +426,8 @@ std::shared_ptr extractor::LoadRestrictionMap() /** \brief Load node based graph from .osrm file */ -std::shared_ptr -extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, +std::shared_ptr +Extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, std::unordered_set &traffic_lights, std::vector &internal_to_external_node_map) { @@ -473,11 +438,11 @@ extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, std::vector barrier_list; std::vector traffic_light_list; - NodeID number_of_node_based_nodes = loadNodesFromFile( + NodeID number_of_node_based_nodes = util::loadNodesFromFile( input_stream, barrier_list, traffic_light_list, internal_to_external_node_map); - SimpleLogger().Write() << " - " << barrier_list.size() << " bollard nodes, " - << traffic_light_list.size() << " traffic lights"; + util::SimpleLogger().Write() << " - " << barrier_list.size() << " bollard nodes, " + << traffic_light_list.size() << " traffic lights"; // insert into unordered sets for fast lookup barrier_nodes.insert(barrier_list.begin(), barrier_list.end()); @@ -488,32 +453,29 @@ extractor::LoadNodeBasedGraph(std::unordered_set &barrier_nodes, traffic_light_list.clear(); traffic_light_list.shrink_to_fit(); - loadEdgesFromFile(input_stream, edge_list); + util::loadEdgesFromFile(input_stream, edge_list); if (edge_list.empty()) { - SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; - return std::shared_ptr(); + util::SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; + return std::shared_ptr(); } - return NodeBasedDynamicGraphFromEdges(number_of_node_based_nodes, edge_list); + return util::NodeBasedDynamicGraphFromEdges(number_of_node_based_nodes, edge_list); } /** \brief Building an edge-expanded graph from node-based input and turn restrictions */ std::pair -extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, +Extractor::BuildEdgeExpandedGraph(lua_State *lua_state, + const ProfileProperties &profile_properties, + std::vector &internal_to_external_node_map, std::vector &node_based_edge_list, std::vector &node_is_startpoint, - DeallocatingVector &edge_based_edge_list) + std::vector &edge_based_node_weights, + util::DeallocatingVector &edge_based_edge_list) { - lua_State *lua_state = luaL_newstate(); - luabind::open(lua_state); - - SpeedProfileProperties speed_profile; - SetupScriptingEnvironment(lua_state, speed_profile); - std::unordered_set barrier_nodes; std::unordered_set traffic_lights; @@ -522,30 +484,27 @@ extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_n LoadNodeBasedGraph(barrier_nodes, traffic_lights, internal_to_external_node_map); CompressedEdgeContainer compressed_edge_container; - GraphCompressor graph_compressor(speed_profile); + GraphCompressor graph_compressor; graph_compressor.Compress(barrier_nodes, traffic_lights, *restriction_map, *node_based_graph, compressed_edge_container); + compressed_edge_container.SerializeInternalVector(config.geometry_output_path); + + util::NameTable name_table(config.names_file_name); + EdgeBasedGraphFactory edge_based_graph_factory( node_based_graph, compressed_edge_container, barrier_nodes, traffic_lights, std::const_pointer_cast(restriction_map), - internal_to_external_node_map, speed_profile); - - compressed_edge_container.SerializeInternalVector(config.geometry_output_path); + internal_to_external_node_map, profile_properties, name_table); edge_based_graph_factory.Run(config.edge_output_path, lua_state, config.edge_segment_lookup_path, config.edge_penalty_path, - config.generate_edge_lookup -#ifdef DEBUG_GEOMETRY - , - config.debug_turns_path -#endif - ); - lua_close(lua_state); + config.generate_edge_lookup); edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); + edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights); auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID(); const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes(); @@ -555,7 +514,7 @@ extractor::BuildEdgeExpandedGraph(std::vector &internal_to_external_n /** \brief Writing info on original (node-based) nodes */ -void extractor::WriteNodeMapping(const std::vector &internal_to_external_node_map) +void Extractor::WriteNodeMapping(const std::vector &internal_to_external_node_map) { boost::filesystem::ofstream node_stream(config.node_output_path, std::ios::binary); const unsigned size_of_mapping = internal_to_external_node_map.size(); @@ -565,7 +524,6 @@ void extractor::WriteNodeMapping(const std::vector &internal_to_exter node_stream.write((char *)internal_to_external_node_map.data(), size_of_mapping * sizeof(QueryNode)); } - node_stream.close(); } /** @@ -573,20 +531,20 @@ void extractor::WriteNodeMapping(const std::vector &internal_to_exter Saves tree into '.ramIndex' and leaves into '.fileIndex'. */ -void extractor::BuildRTree(std::vector node_based_edge_list, +void Extractor::BuildRTree(std::vector node_based_edge_list, std::vector node_is_startpoint, const std::vector &internal_to_external_node_map) { - SimpleLogger().Write() << "constructing r-tree of " << node_based_edge_list.size() - << " edge elements build on-top of " - << internal_to_external_node_map.size() << " coordinates"; + util::SimpleLogger().Write() << "constructing r-tree of " << node_based_edge_list.size() + << " edge elements build on-top of " + << internal_to_external_node_map.size() << " coordinates"; BOOST_ASSERT(node_is_startpoint.size() == node_based_edge_list.size()); // Filter node based edges based on startpoint auto out_iter = node_based_edge_list.begin(); auto in_iter = node_based_edge_list.begin(); - for (auto index : osrm::irange(0, node_is_startpoint.size())) + for (auto index : util::irange(0UL, node_is_startpoint.size())) { BOOST_ASSERT(in_iter != node_based_edge_list.end()); if (node_is_startpoint[index]) @@ -597,28 +555,36 @@ void extractor::BuildRTree(std::vector node_based_edge_list, in_iter++; } auto new_size = out_iter - node_based_edge_list.begin(); + if (new_size == 0) + { + throw util::exception("There are no snappable edges left after processing. Are you " + "setting travel modes correctly in the profile? Cannot continue."); + } node_based_edge_list.resize(new_size); TIMER_START(construction); - StaticRTree(node_based_edge_list, config.rtree_nodes_output_path, - config.rtree_leafs_output_path, internal_to_external_node_map); + util::StaticRTree rtree(node_based_edge_list, config.rtree_nodes_output_path, + config.rtree_leafs_output_path, + internal_to_external_node_map); TIMER_STOP(construction); - SimpleLogger().Write() << "finished r-tree construction in " << TIMER_SEC(construction) - << " seconds"; + util::SimpleLogger().Write() << "finished r-tree construction in " << TIMER_SEC(construction) + << " seconds"; } -void extractor::WriteEdgeBasedGraph(std::string const &output_file_filename, - size_t const max_edge_id, - DeallocatingVector const &edge_based_edge_list) +void Extractor::WriteEdgeBasedGraph( + std::string const &output_file_filename, + size_t const max_edge_id, + util::DeallocatingVector const &edge_based_edge_list) { std::ofstream file_out_stream; file_out_stream.open(output_file_filename.c_str(), std::ios::binary); - const FingerPrint fingerprint = FingerPrint::GetValid(); - file_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); + const util::FingerPrint fingerprint = util::FingerPrint::GetValid(); + file_out_stream.write((char *)&fingerprint, sizeof(util::FingerPrint)); - std::cout << "[extractor] Writing edge-based-graph egdes ... " << std::flush; + util::SimpleLogger().Write() << "[extractor] Writing edge-based-graph edges ... " + << std::flush; TIMER_START(write_edges); size_t number_of_used_edges = edge_based_edge_list.size(); @@ -631,8 +597,9 @@ void extractor::WriteEdgeBasedGraph(std::string const &output_file_filename, } TIMER_STOP(write_edges); - std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; + util::SimpleLogger().Write() << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; - SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges"; - file_out_stream.close(); + util::SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges"; +} +} } diff --git a/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp similarity index 65% rename from extractor/extractor_callbacks.cpp rename to src/extractor/extractor_callbacks.cpp index 2eb1a9b46..0e07d933e 100644 --- a/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -1,50 +1,29 @@ -/* +#include "extractor/extractor_callbacks.hpp" +#include "extractor/extraction_containers.hpp" +#include "extractor/extraction_node.hpp" +#include "extractor/extraction_way.hpp" -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "extractor_callbacks.hpp" -#include "extraction_containers.hpp" -#include "extraction_node.hpp" -#include "extraction_way.hpp" - -#include "../data_structures/external_memory_node.hpp" -#include "../data_structures/restriction.hpp" -#include "../util/container.hpp" -#include "../util/simple_logger.hpp" +#include "extractor/external_memory_node.hpp" +#include "extractor/restriction.hpp" +#include "util/simple_logger.hpp" +#include "util/for_each_pair.hpp" #include #include -#include +#include "osrm/coordinate.hpp" +#include #include #include #include +namespace osrm +{ +namespace extractor +{ + ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers) : external_memory(extraction_containers) { @@ -61,11 +40,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node, const ExtractionNode &result_node) { external_memory.all_nodes_list.push_back( - {static_cast(input_node.location().lat() * COORDINATE_PRECISION), - static_cast(input_node.location().lon() * COORDINATE_PRECISION), - OSMNodeID(input_node.id()), - result_node.barrier, - result_node.traffic_lights}); + {util::toFixed(util::FloatLongitude(input_node.location().lon())), + util::toFixed(util::FloatLatitude(input_node.location().lat())), + OSMNodeID(input_node.id()), result_node.barrier, result_node.traffic_lights}); } void ExtractorCallbacks::ProcessRestriction( @@ -74,7 +51,7 @@ void ExtractorCallbacks::ProcessRestriction( if (restriction) { external_memory.restrictions_list.push_back(restriction.get()); - // SimpleLogger().Write() << "from: " << restriction.get().restriction.from.node << + // util::SimpleLogger().Write() << "from: " << restriction.get().restriction.from.node << // ",via: " << restriction.get().restriction.via.node << // ", to: " << restriction.get().restriction.to.node << // ", only: " << (restriction.get().restriction.flags.is_only ? @@ -108,8 +85,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti if (std::numeric_limits::max() == input_way.id()) { - SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id() - << " of size " << input_way.nodes().size(); + util::SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id() + << " of size " << input_way.nodes().size(); return; } @@ -147,17 +124,27 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID && backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID) { - SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << input_way.id(); + util::SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " + << input_way.id(); return; } + // FIXME this need to be moved into the profiles + const char *data = input_way.get_value_by_key("highway"); + guidance::RoadClassificationData road_classification; + if (data) + { + road_classification.road_class = guidance::functionalRoadClassFromTag(data); + } + // Get the unique identifier for the street name const auto &string_map_iterator = string_map.find(parsed_way.name); unsigned name_id = external_memory.name_lengths.size(); if (string_map.end() == string_map_iterator) { auto name_length = std::min(255u, parsed_way.name.size()); - std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, std::back_inserter(external_memory.name_char_data)); + std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, + std::back_inserter(external_memory.name_char_data)); external_memory.name_lengths.push_back(name_length); string_map.insert(std::make_pair(parsed_way.name, name_id)); } @@ -187,55 +174,56 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti { BOOST_ASSERT(split_edge == false); BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); - osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(), + util::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(), [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { external_memory.all_edges_list.push_back(InternalExtractorEdge( - OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, - backward_weight_data, true, false, parsed_way.roundabout, - parsed_way.is_access_restricted, parsed_way.is_startpoint, - parsed_way.backward_travel_mode, false)); + OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), + name_id, backward_weight_data, true, false, + parsed_way.roundabout, parsed_way.is_access_restricted, + parsed_way.is_startpoint, parsed_way.backward_travel_mode, + false, road_classification)); }); external_memory.way_start_end_id_list.push_back( - {OSMWayID(input_way.id()), - OSMNodeID(input_way.nodes().back().ref()), + {OSMWayID(input_way.id()), OSMNodeID(input_way.nodes().back().ref()), OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()), - OSMNodeID(input_way.nodes()[1].ref()), - OSMNodeID(input_way.nodes()[0].ref())}); + OSMNodeID(input_way.nodes()[1].ref()), OSMNodeID(input_way.nodes()[0].ref())}); } else { const bool forward_only = split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode; - osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(), + util::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(), [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { external_memory.all_edges_list.push_back(InternalExtractorEdge( - OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, forward_weight_data, - true, !forward_only, parsed_way.roundabout, - parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.forward_travel_mode, - split_edge)); + OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), + name_id, forward_weight_data, true, !forward_only, + parsed_way.roundabout, parsed_way.is_access_restricted, + parsed_way.is_startpoint, parsed_way.forward_travel_mode, + split_edge, road_classification)); }); if (split_edge) { BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); - osrm::for_each_pair( + util::for_each_pair( input_way.nodes().cbegin(), input_way.nodes().cend(), [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { external_memory.all_edges_list.push_back(InternalExtractorEdge( - OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, backward_weight_data, false, - true, parsed_way.roundabout, parsed_way.is_access_restricted, - parsed_way.is_startpoint, parsed_way.backward_travel_mode, true)); + OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, + backward_weight_data, false, true, parsed_way.roundabout, + parsed_way.is_access_restricted, parsed_way.is_startpoint, + parsed_way.backward_travel_mode, true, road_classification)); }); } external_memory.way_start_end_id_list.push_back( - {OSMWayID(input_way.id()), - OSMNodeID(input_way.nodes().back().ref()), + {OSMWayID(input_way.id()), OSMNodeID(input_way.nodes().back().ref()), OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()), - OSMNodeID(input_way.nodes()[1].ref()), - OSMNodeID(input_way.nodes()[0].ref())}); + OSMNodeID(input_way.nodes()[1].ref()), OSMNodeID(input_way.nodes()[0].ref())}); } } +} +} diff --git a/algorithms/graph_compressor.cpp b/src/extractor/graph_compressor.cpp similarity index 65% rename from algorithms/graph_compressor.cpp rename to src/extractor/graph_compressor.cpp index f3b5f8dd9..6df9a38cc 100644 --- a/algorithms/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -1,31 +1,30 @@ -#include "graph_compressor.hpp" +#include "extractor/graph_compressor.hpp" -#include "../data_structures/compressed_edge_container.hpp" -#include "../data_structures/dynamic_graph.hpp" -#include "../data_structures/node_based_graph.hpp" -#include "../data_structures/restriction_map.hpp" -#include "../data_structures/percent.hpp" +#include "extractor/compressed_edge_container.hpp" +#include "extractor/restriction_map.hpp" +#include "util/dynamic_graph.hpp" +#include "util/node_based_graph.hpp" +#include "util/percent.hpp" -#include "../util/simple_logger.hpp" +#include "util/simple_logger.hpp" -GraphCompressor::GraphCompressor(SpeedProfileProperties speed_profile) - : speed_profile(std::move(speed_profile)) +namespace osrm +{ +namespace extractor { -} - -void GraphCompressor::Compress(const std::unordered_set& barrier_nodes, - const std::unordered_set& traffic_lights, - RestrictionMap& restriction_map, - NodeBasedDynamicGraph& graph, - CompressedEdgeContainer& geometry_compressor) +void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, + const std::unordered_set &traffic_lights, + RestrictionMap &restriction_map, + util::NodeBasedDynamicGraph &graph, + CompressedEdgeContainer &geometry_compressor) { const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); - Percent progress(original_number_of_nodes); + util::Percent progress(original_number_of_nodes); - for (const NodeID node_v : osrm::irange(0u, original_number_of_nodes)) + for (const NodeID node_v : util::irange(0u, original_number_of_nodes)) { progress.printStatus(node_v); @@ -64,12 +63,10 @@ void GraphCompressor::Compress(const std::unordered_set& barrier_nodes, const bool reverse_edge_order = graph.GetEdgeData(graph.BeginEdges(node_v)).reversed; const EdgeID forward_e2 = graph.BeginEdges(node_v) + reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != forward_e2); - BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) && - forward_e2 < graph.EndEdges(node_v)); + BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) && forward_e2 < graph.EndEdges(node_v)); const EdgeID reverse_e2 = graph.BeginEdges(node_v) + 1 - reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2); - BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) && - reverse_e2 < graph.EndEdges(node_v)); + BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) && reverse_e2 < graph.EndEdges(node_v)); const EdgeData &fwd_edge_data2 = graph.GetEdgeData(forward_e2); const EdgeData &rev_edge_data2 = graph.GetEdgeData(reverse_e2); @@ -103,13 +100,24 @@ void GraphCompressor::Compress(const std::unordered_set& barrier_nodes, continue; } - if (fwd_edge_data1.IsCompatibleTo(fwd_edge_data2) && rev_edge_data1.IsCompatibleTo(rev_edge_data2)) + if (fwd_edge_data1.IsCompatibleTo(fwd_edge_data2) && + rev_edge_data1.IsCompatibleTo(rev_edge_data2)) { BOOST_ASSERT(graph.GetEdgeData(forward_e1).name_id == graph.GetEdgeData(reverse_e1).name_id); BOOST_ASSERT(graph.GetEdgeData(forward_e2).name_id == graph.GetEdgeData(reverse_e2).name_id); + // Do not compress edge if it crosses a traffic signal. + // This can't be done in IsCompatibleTo, becase we only store the + // traffic signals in the `traffic_lights` list, which EdgeData + // doesn't have access to. + const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end(); + if (has_node_penalty) + { + continue; + } + // Get distances before graph is modified const int forward_weight1 = graph.GetEdgeData(forward_e1).distance; const int forward_weight2 = graph.GetEdgeData(forward_e2).distance; @@ -123,18 +131,9 @@ void GraphCompressor::Compress(const std::unordered_set& barrier_nodes, BOOST_ASSERT(0 != reverse_weight1); BOOST_ASSERT(0 != reverse_weight2); - const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end(); - // add weight of e2's to e1 graph.GetEdgeData(forward_e1).distance += fwd_edge_data2.distance; graph.GetEdgeData(reverse_e1).distance += rev_edge_data2.distance; - if (has_node_penalty) - { - graph.GetEdgeData(forward_e1).distance += - speed_profile.traffic_signal_penalty; - graph.GetEdgeData(reverse_e1).distance += - speed_profile.traffic_signal_penalty; - } // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); @@ -152,28 +151,38 @@ void GraphCompressor::Compress(const std::unordered_set& barrier_nodes, restriction_map.FixupArrivingTurnRestriction(node_w, node_v, node_u, graph); // store compressed geometry in container - geometry_compressor.CompressEdge( - forward_e1, forward_e2, node_v, node_w, - forward_weight1 + (has_node_penalty ? speed_profile.traffic_signal_penalty : 0), - forward_weight2); - geometry_compressor.CompressEdge( - reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, - reverse_weight2 + (has_node_penalty ? speed_profile.traffic_signal_penalty : 0)); + geometry_compressor.CompressEdge(forward_e1, forward_e2, node_v, node_w, + forward_weight1, forward_weight2); + geometry_compressor.CompressEdge(reverse_e1, reverse_e2, node_v, node_u, + reverse_weight1, reverse_weight2); } } PrintStatistics(original_number_of_nodes, original_number_of_edges, graph); + + // Repeate the loop, but now add all edges as uncompressed values. + // The function AddUncompressedEdge does nothing if the edge is already + // in the CompressedEdgeContainer. + for (const NodeID node_u : util::irange(0u, original_number_of_nodes)) + { + for (const auto edge_id : util::irange(graph.BeginEdges(node_u), graph.EndEdges(node_u))) + { + const EdgeData &data = graph.GetEdgeData(edge_id); + const NodeID target = graph.GetTarget(edge_id); + geometry_compressor.AddUncompressedEdge(edge_id, target, data.distance); + } + } } void GraphCompressor::PrintStatistics(unsigned original_number_of_nodes, unsigned original_number_of_edges, - const NodeBasedDynamicGraph& graph) const + const util::NodeBasedDynamicGraph &graph) const { unsigned new_node_count = 0; unsigned new_edge_count = 0; - for (const auto i : osrm::irange(0u, graph.GetNumberOfNodes())) + for (const auto i : util::irange(0u, graph.GetNumberOfNodes())) { if (graph.GetOutDegree(i) > 0) { @@ -181,8 +190,10 @@ void GraphCompressor::PrintStatistics(unsigned original_number_of_nodes, new_edge_count += (graph.EndEdges(i) - graph.BeginEdges(i)); } } - SimpleLogger().Write() << "Node compression ratio: " - << new_node_count / (double)original_number_of_nodes; - SimpleLogger().Write() << "Edge compression ratio: " - << new_edge_count / (double)original_number_of_edges; + util::SimpleLogger().Write() << "Node compression ratio: " + << new_node_count / (double)original_number_of_nodes; + util::SimpleLogger().Write() << "Edge compression ratio: " + << new_edge_count / (double)original_number_of_edges; +} +} } diff --git a/src/extractor/guidance/classification_data.cpp b/src/extractor/guidance/classification_data.cpp new file mode 100644 index 000000000..70ccaff1c --- /dev/null +++ b/src/extractor/guidance/classification_data.cpp @@ -0,0 +1,53 @@ +#include "extractor/guidance/classification_data.hpp" +#include "util/simple_logger.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +FunctionalRoadClass functionalRoadClassFromTag(std::string const &value) +{ + // FIXME at some point this should be part of the profiles + const static auto class_hash = [] + { + std::unordered_map hash; + hash["motorway"] = FunctionalRoadClass::MOTORWAY; + hash["motorway_link"] = FunctionalRoadClass::MOTORWAY_LINK; + hash["trunk"] = FunctionalRoadClass::TRUNK; + hash["trunk_link"] = FunctionalRoadClass::TRUNK_LINK; + hash["primary"] = FunctionalRoadClass::PRIMARY; + hash["primary_link"] = FunctionalRoadClass::PRIMARY_LINK; + hash["secondary"] = FunctionalRoadClass::SECONDARY; + hash["secondary_link"] = FunctionalRoadClass::SECONDARY_LINK; + hash["tertiary"] = FunctionalRoadClass::TERTIARY; + hash["tertiary_link"] = FunctionalRoadClass::TERTIARY_LINK; + hash["unclassified"] = FunctionalRoadClass::UNCLASSIFIED; + hash["residential"] = FunctionalRoadClass::RESIDENTIAL; + hash["service"] = FunctionalRoadClass::SERVICE; + hash["living_street"] = FunctionalRoadClass::LIVING_STREET; + hash["track"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["road"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["path"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["driveway"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + return hash; + }(); + + if (class_hash.find(value) != class_hash.end()) + { + return class_hash.find(value)->second; + } + else + { + util::SimpleLogger().Write(logDEBUG) << "Unknown road class encountered: " << value; + return FunctionalRoadClass::UNKNOWN; + } +} + +} // ns guidance +} // ns extractor +} // ns osrm diff --git a/src/extractor/guidance/intersection.cpp b/src/extractor/guidance/intersection.cpp new file mode 100644 index 000000000..8902ba4a0 --- /dev/null +++ b/src/extractor/guidance/intersection.cpp @@ -0,0 +1,31 @@ +#include "extractor/guidance/intersection.hpp" + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +ConnectedRoad::ConnectedRoad(const TurnOperation turn, const bool entry_allowed) + : entry_allowed(entry_allowed), turn(turn) +{ +} + +std::string toString(const ConnectedRoad &road) +{ + std::string result = "[connection] "; + result += std::to_string(road.turn.eid); + result += " allows entry: "; + result += std::to_string(road.entry_allowed); + result += " angle: "; + result += std::to_string(road.turn.angle); + result += " instruction: "; + result += std::to_string(static_cast(road.turn.instruction.type)) + " " + + std::to_string(static_cast(road.turn.instruction.direction_modifier)); + return result; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp new file mode 100644 index 000000000..5f01cda6b --- /dev/null +++ b/src/extractor/guidance/intersection_generator.cpp @@ -0,0 +1,255 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/intersection_generator.hpp" +#include "extractor/guidance/toolkit.hpp" + +#include +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +IntersectionGenerator::IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const std::vector &node_info_list, + const CompressedEdgeContainer &compressed_edge_container) + : node_based_graph(node_based_graph), restriction_map(restriction_map), + barrier_nodes(barrier_nodes), node_info_list(node_info_list), + compressed_edge_container(compressed_edge_container) +{ +} + +Intersection IntersectionGenerator::operator()(const NodeID from_node, const EdgeID via_eid) const +{ + return getConnectedRoads(from_node, via_eid); +} + +// a +// | +// | +// v +// For an intersection from_node --via_edi--> turn_node ----> c +// ^ +// | +// | +// b +// This functions returns _all_ turns as if the graph was undirected. +// That means we not only get (from_node, turn_node, c) in the above example +// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are +// marked as invalid and only needed for intersection classification. +Intersection IntersectionGenerator::getConnectedRoads(const NodeID from_node, + const EdgeID via_eid) const +{ + Intersection intersection; + const NodeID turn_node = node_based_graph.GetTarget(via_eid); + const NodeID only_restriction_to_node = + restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node); + const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end(); + + bool has_uturn_edge = false; + for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node)) + { + BOOST_ASSERT(onto_edge != SPECIAL_EDGEID); + const NodeID to_node = node_based_graph.GetTarget(onto_edge); + + bool turn_is_valid = + // reverse edges are never valid turns because the resulting turn would look like this: + // from_node --via_edge--> turn_node <--onto_edge-- to_node + // however we need this for capture intersection shape for incoming one-ways + !node_based_graph.GetEdgeData(onto_edge).reversed && + // we are not turning over a barrier + (!is_barrier_node || from_node == to_node) && + // We are at an only_-restriction but not at the right turn. + (only_restriction_to_node == SPECIAL_NODEID || to_node == only_restriction_to_node) && + // the turn is not restricted + !restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node); + + auto angle = 0.; + if (from_node == to_node) + { + if (turn_is_valid && !is_barrier_node) + { + // we only add u-turns for dead-end streets. + if (node_based_graph.GetOutDegree(turn_node) > 1) + { + auto number_of_emmiting_bidirectional_edges = 0; + for (auto edge : node_based_graph.GetAdjacentEdgeRange(turn_node)) + { + auto target = node_based_graph.GetTarget(edge); + auto reverse_edge = node_based_graph.FindEdge(target, turn_node); + BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID); + if (!node_based_graph.GetEdgeData(reverse_edge).reversed) + { + ++number_of_emmiting_bidirectional_edges; + } + } + // is a dead-end + turn_is_valid = number_of_emmiting_bidirectional_edges <= 1; + } + } + has_uturn_edge = true; + BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits::epsilon()); + } + else + { + // unpack first node of second segment if packed + const auto first_coordinate = getRepresentativeCoordinate( + from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list); + const auto third_coordinate = getRepresentativeCoordinate( + turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list); + angle = util::coordinate_calculation::computeAngle( + first_coordinate, node_info_list[turn_node], third_coordinate); + if (angle < std::numeric_limits::epsilon()) + has_uturn_edge = true; + } + + intersection.push_back(ConnectedRoad( + TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}}, + turn_is_valid)); + } + + // We hit the case of a street leading into nothing-ness. Since the code here assumes that this + // will + // never happen we add an artificial invalid uturn in this case. + if (!has_uturn_edge) + { + intersection.push_back( + {TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false}); + } + + const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) { + return first.turn.angle < second.turn.angle; + }; + std::sort(std::begin(intersection), std::end(intersection), ByAngle); + + BOOST_ASSERT(intersection[0].turn.angle >= 0. && + intersection[0].turn.angle < std::numeric_limits::epsilon()); + + return mergeSegregatedRoads(std::move(intersection)); +} + +/* + * Segregated Roads often merge onto a single intersection. + * While technically representing different roads, they are + * often looked at as a single road. + * Due to the merging, turn Angles seem off, wenn we compute them from the + * initial positions. + * + * bb>b>b(2)>b>b>b + * + * Would be seen as a slight turn going fro a to (2). A Sharp turn going from + * (1) to (2). + * + * In cases like these, we megre this segregated roads into a single road to + * end up with a case like: + * + * aaaaa-bbbbbb + * + * for the turn representation. + * Anything containing the first u-turn in a merge affects all other angles + * and is handled separately from all others. + */ +Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersection) const +{ + const auto getRight = [&](std::size_t index) { + return (index + intersection.size() - 1) % intersection.size(); + }; + + const auto mergable = [&](std::size_t first, std::size_t second) -> bool { + const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid); + const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid); + + return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id && + !first_data.roundabout && !second_data.roundabout && + first_data.travel_mode == second_data.travel_mode && + first_data.road_classification == second_data.road_classification && + // compatible threshold + angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) < + 60 && + first_data.reversed != second_data.reversed; + }; + + const auto merge = [](const ConnectedRoad &first, + const ConnectedRoad &second) -> ConnectedRoad { + if (!first.entry_allowed) + { + ConnectedRoad result = second; + result.turn.angle = (first.turn.angle + second.turn.angle) / 2; + if (first.turn.angle - second.turn.angle > 180) + result.turn.angle += 180; + if (result.turn.angle > 360) + result.turn.angle -= 360; + + return result; + } + else + { + BOOST_ASSERT(!second.entry_allowed); + ConnectedRoad result = first; + result.turn.angle = (first.turn.angle + second.turn.angle) / 2; + + if (first.turn.angle - second.turn.angle > 180) + result.turn.angle += 180; + if (result.turn.angle > 360) + result.turn.angle -= 360; + + return result; + } + }; + if (intersection.size() == 1) + return intersection; + + // check for merges including the basic u-turn + // these result in an adjustment of all other angles + if (mergable(0, intersection.size() - 1)) + { + const double correction_factor = + (360 - intersection[intersection.size() - 1].turn.angle) / 2; + for (std::size_t i = 1; i + 1 < intersection.size(); ++i) + intersection[i].turn.angle += correction_factor; + intersection[0] = merge(intersection.front(), intersection.back()); + intersection[0].turn.angle = 0; + intersection.pop_back(); + } + + else if (mergable(0, 1)) + { + const double correction_factor = (intersection[1].turn.angle) / 2; + for (std::size_t i = 2; i < intersection.size(); ++i) + intersection[i].turn.angle += correction_factor; + intersection[0] = merge(intersection[0], intersection[1]); + intersection[0].turn.angle = 0; + intersection.erase(intersection.begin() + 1); + } + + // a merge including the first u-turn requres an adjustment of the turn angles + // therefore these are handled prior to this step + for (std::size_t index = 2; index < intersection.size(); ++index) + { + if (mergable(index, getRight(index))) + { + intersection[getRight(index)] = + merge(intersection[getRight(index)], intersection[index]); + intersection.erase(intersection.begin() + index); + --index; + } + } + + const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) { + return first.turn.angle < second.turn.angle; + }; + std::sort(std::begin(intersection), std::end(intersection), ByAngle); + return intersection; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp new file mode 100644 index 000000000..b77f4edc7 --- /dev/null +++ b/src/extractor/guidance/intersection_handler.cpp @@ -0,0 +1,307 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/intersection_handler.hpp" +#include "extractor/guidance/toolkit.hpp" + +#include + +using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData; + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +namespace detail +{ +inline bool requiresAnnouncement(const EdgeData &from, const EdgeData &to) +{ + return !from.IsCompatibleTo(to); +} +} + +IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table) + : node_based_graph(node_based_graph), node_info_list(node_info_list), name_table(name_table) +{ +} + +IntersectionHandler::~IntersectionHandler() {} + +std::size_t IntersectionHandler::countValid(const Intersection &intersection) const +{ + return std::count_if(intersection.begin(), intersection.end(), + [](const ConnectedRoad &road) { return road.entry_allowed; }); +} + +TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge, + const ConnectedRoad &road) const +{ + + const auto &in_data = node_based_graph.GetEdgeData(via_edge); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + + bool on_ramp = isRampClass(in_data.road_classification.road_class); + + bool onto_ramp = isRampClass(out_data.road_classification.road_class); + + if (!on_ramp && onto_ramp) + return TurnType::Ramp; + + if (in_data.name_id == out_data.name_id && in_data.name_id != INVALID_NAME_ID) + { + return TurnType::Continue; + } + + return TurnType::Turn; +} + +TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t num_roads, + const EdgeID via_edge, + const bool through_street, + const ConnectedRoad &road) const +{ + const auto type = findBasicTurnType(via_edge, road); + // handle travel modes: + const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode; + const auto out_mode = node_based_graph.GetEdgeData(road.turn.eid).travel_mode; + if (type == TurnType::Ramp) + { + return {TurnType::Ramp, getTurnDirection(road.turn.angle)}; + } + + if (angularDeviation(road.turn.angle, 0) < 0.01) + { + return {TurnType::Turn, DirectionModifier::UTurn}; + } + if (type == TurnType::Turn) + { + const auto &in_data = node_based_graph.GetEdgeData(via_edge); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + if (in_data.name_id != out_data.name_id && + requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), + name_table.GetNameForID(out_data.name_id))) + { + // obvious turn onto a through street is a merge + if (through_street) + { + return {TurnType::Merge, road.turn.angle > STRAIGHT_ANGLE + ? DirectionModifier::SlightRight + : DirectionModifier::SlightLeft}; + } + else + { + return {TurnType::NewName, getTurnDirection(road.turn.angle)}; + } + } + else + { + if (in_mode == out_mode) + return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; + else + return {TurnType::Notification, getTurnDirection(road.turn.angle)}; + } + } + BOOST_ASSERT(type == TurnType::Continue); + if (in_mode != out_mode) + { + return {TurnType::Notification, getTurnDirection(road.turn.angle)}; + } + if (num_roads > 2) + { + return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; + } + else + { + return {TurnType::NoTurn, getTurnDirection(road.turn.angle)}; + } +} + +void IntersectionHandler::assignFork(const EdgeID via_edge, + ConnectedRoad &left, + ConnectedRoad &right) const +{ + const auto &in_data = node_based_graph.GetEdgeData(via_edge); + const bool low_priority_left = isLowPriorityRoadClass( + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class); + const bool low_priority_right = isLowPriorityRoadClass( + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class); + if ((angularDeviation(left.turn.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + angularDeviation(right.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)) + { + // left side is actually straight + const auto &out_data = node_based_graph.GetEdgeData(left.turn.eid); + if (detail::requiresAnnouncement(in_data, out_data)) + { + if (low_priority_right && !low_priority_left) + { + left.turn.instruction = getInstructionForObvious(3, via_edge, false, left); + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + else + { + if (low_priority_left && !low_priority_right) + { + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + else + { + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + } + } + } + else + { + left.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + } + else if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) + { + // right side is actually straight + const auto &out_data = node_based_graph.GetEdgeData(right.turn.eid); + if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) + { + if (detail::requiresAnnouncement(in_data, out_data)) + { + if (low_priority_left && !low_priority_right) + { + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + right.turn.instruction = getInstructionForObvious(3, via_edge, false, right); + } + else + { + if (low_priority_right && !low_priority_left) + { + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + else + { + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + } + } + } + else + { + right.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + } + } + } + // left side of fork + if (low_priority_right && !low_priority_left) + left.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; + else + { + if (low_priority_left && !low_priority_right) + left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft}; + else + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + } + + // right side of fork + if (low_priority_left && !low_priority_right) + right.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; + else + { + if (low_priority_right && !low_priority_left) + right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight}; + else + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + } +} + +void IntersectionHandler::assignFork(const EdgeID via_edge, + ConnectedRoad &left, + ConnectedRoad ¢er, + ConnectedRoad &right) const +{ + // TODO handle low priority road classes in a reasonable way + if (left.entry_allowed && center.entry_allowed && right.entry_allowed) + { + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + const auto &in_data = node_based_graph.GetEdgeData(via_edge); + const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid); + if (detail::requiresAnnouncement(in_data, out_data)) + { + center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; + } + else + { + center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + } + } + else + { + center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; + } + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + } + else if (left.entry_allowed) + { + if (right.entry_allowed) + assignFork(via_edge, left, right); + else if (center.entry_allowed) + assignFork(via_edge, left, center); + else + left.turn.instruction = {findBasicTurnType(via_edge, left), + getTurnDirection(left.turn.angle)}; + } + else if (right.entry_allowed) + { + if (center.entry_allowed) + assignFork(via_edge, center, right); + else + right.turn.instruction = {findBasicTurnType(via_edge, right), + getTurnDirection(right.turn.angle)}; + } + else + { + if (center.entry_allowed) + center.turn.instruction = {findBasicTurnType(via_edge, center), + getTurnDirection(center.turn.angle)}; + } +} + +bool IntersectionHandler::isThroughStreet(const std::size_t index, + const Intersection &intersection) const +{ + if (node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id == INVALID_NAME_ID) + return false; + for (const auto &road : intersection) + { + // a through street cannot start at our own position + if (road.turn.angle < std::numeric_limits::epsilon()) + continue; + if (angularDeviation(road.turn.angle, intersection[index].turn.angle) > + (STRAIGHT_ANGLE - NARROW_TURN_ANGLE) && + node_based_graph.GetEdgeData(road.turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[index].turn.eid).name_id) + return true; + } + return false; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp new file mode 100644 index 000000000..c855b21b4 --- /dev/null +++ b/src/extractor/guidance/motorway_handler.cpp @@ -0,0 +1,524 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/motorway_handler.hpp" +#include "extractor/guidance/toolkit.hpp" + +#include "util/simple_logger.hpp" + +#include +#include + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ +namespace detail +{ +inline bool isMotorwayClass(const FunctionalRoadClass road_class) +{ + return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK; +} + +inline bool isMotorwayClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph) +{ + return isMotorwayClass(node_based_graph.GetEdgeData(eid).road_classification.road_class); +} +inline FunctionalRoadClass roadClass(const ConnectedRoad &road, + const util::NodeBasedDynamicGraph &graph) +{ + return graph.GetEdgeData(road.turn.eid).road_classification.road_class; +} + +inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph) +{ + return isRampClass(node_based_graph.GetEdgeData(eid).road_classification.road_class); +} +} + +MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table) + : IntersectionHandler(node_based_graph, node_info_list, name_table) +{ +} + +MotorwayHandler::~MotorwayHandler() {} + +bool MotorwayHandler::canProcess(const NodeID, + const EdgeID via_eid, + const Intersection &intersection) const +{ + bool has_motorway = false; + bool has_normal_roads = false; + + for (const auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + // not merging or forking? + if (road.entry_allowed && angularDeviation(road.turn.angle, STRAIGHT_ANGLE) > 60) + return false; + else if (out_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY || + out_data.road_classification.road_class == FunctionalRoadClass::TRUNK) + { + if (road.entry_allowed) + has_motorway = true; + } + else if (!isRampClass(out_data.road_classification.road_class)) + has_normal_roads = true; + } + + if (has_normal_roads) + return false; + + const auto &in_data = node_based_graph.GetEdgeData(via_eid); + return has_motorway || + in_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY || + in_data.road_classification.road_class == FunctionalRoadClass::TRUNK; +} + +Intersection MotorwayHandler:: +operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const +{ + const auto &in_data = node_based_graph.GetEdgeData(via_eid); + + // coming from motorway + if (detail::isMotorwayClass(in_data.road_classification.road_class)) + { + return fromMotorway(via_eid, std::move(intersection)); + } + else // coming from a ramp + { + return fromRamp(via_eid, std::move(intersection)); + // ramp merging straight onto motorway + } +} + +Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection intersection) const +{ + const auto &in_data = node_based_graph.GetEdgeData(via_eid); + BOOST_ASSERT(detail::isMotorwayClass(in_data.road_classification.road_class)); + + const auto countExitingMotorways = [this](const Intersection &intersection) { + unsigned count = 0; + for (const auto &road : intersection) + { + if (road.entry_allowed && detail::isMotorwayClass(road.turn.eid, node_based_graph)) + ++count; + } + return count; + }; + + // find the angle that continues on our current highway + const auto getContinueAngle = [this, in_data](const Intersection &intersection) { + for (const auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + if (road.turn.angle != 0 && in_data.name_id == out_data.name_id && + in_data.name_id != INVALID_NAME_ID && + detail::isMotorwayClass(out_data.road_classification.road_class)) + return road.turn.angle; + } + return intersection[0].turn.angle; + }; + + const auto getMostLikelyContinue = [this, in_data](const Intersection &intersection) { + double angle = intersection[0].turn.angle; + double best = 180; + for (const auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + if (detail::isMotorwayClass(out_data.road_classification.road_class) && + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best) + { + best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE); + angle = road.turn.angle; + } + } + return angle; + }; + + const auto findBestContinue = [&]() { + const double continue_angle = getContinueAngle(intersection); + if (continue_angle != intersection[0].turn.angle) + return continue_angle; + else + return getMostLikelyContinue(intersection); + }; + + // find continue angle + const double continue_angle = findBestContinue(); + // highway does not continue and has no obvious choice + if (continue_angle == intersection[0].turn.angle) + { + if (intersection.size() == 2) + { + // do not announce ramps at the end of a highway + intersection[1].turn.instruction = {TurnType::NoTurn, + getTurnDirection(intersection[1].turn.angle)}; + } + else if (intersection.size() == 3) + { + // splitting ramp at the end of a highway + if (intersection[1].entry_allowed && intersection[2].entry_allowed) + { + assignFork(via_eid, intersection[2], intersection[1]); + } + else + { + // ending in a passing ramp + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = { + TurnType::NoTurn, getTurnDirection(intersection[1].turn.angle)}; + else + intersection[2].turn.instruction = { + TurnType::NoTurn, getTurnDirection(intersection[2].turn.angle)}; + } + } + else if (intersection.size() == 4 && + detail::roadClass(intersection[1], node_based_graph) == + detail::roadClass(intersection[2], node_based_graph) && + detail::roadClass(intersection[2], node_based_graph) == + detail::roadClass(intersection[3], node_based_graph)) + { + // tripple fork at the end + assignFork(via_eid, intersection[3], intersection[2], intersection[1]); + } + + else if (countValid(intersection) > 0) // check whether turns exist at all + { + // FALLBACK, this should hopefully never be reached + util::SimpleLogger().Write(logDEBUG) + << "Fallback reached from motorway, no continue angle, " << intersection.size() + << " roads, " << countValid(intersection) << " valid ones."; + return fallback(std::move(intersection)); + } + } + else + { + const unsigned exiting_motorways = countExitingMotorways(intersection); + + if (exiting_motorways == 0) + { + // Ending in Ramp + for (auto &road : intersection) + { + if (road.entry_allowed) + { + BOOST_ASSERT(detail::isRampClass(road.turn.eid, node_based_graph)); + road.turn.instruction = + TurnInstruction::SUPPRESSED(getTurnDirection(road.turn.angle)); + } + } + } + else if (exiting_motorways == 1) + { + // normal motorway passing some ramps or mering onto another motorway + if (intersection.size() == 2) + { + BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph)); + + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_eid, + isThroughStreet(1,intersection), intersection[1]); + } + else + { + // Normal Highway exit or merge + for (auto &road : intersection) + { + // ignore invalid uturns/other + if (!road.entry_allowed) + continue; + + if (road.turn.angle == continue_angle) + { + road.turn.instruction = getInstructionForObvious( + intersection.size(), via_eid, isThroughStreet(1,intersection), road); + } + else if (road.turn.angle < continue_angle) + { + road.turn.instruction = { + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp + : TurnType::Turn, + (road.turn.angle < 145) ? DirectionModifier::Right + : DirectionModifier::SlightRight}; + } + else if (road.turn.angle > continue_angle) + { + road.turn.instruction = { + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp + : TurnType::Turn, + (road.turn.angle > 215) ? DirectionModifier::Left + : DirectionModifier::SlightLeft}; + } + } + } + } + // handle motorway forks + else if (exiting_motorways > 1) + { + if (exiting_motorways == 2 && intersection.size() == 2) + { + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_eid, + isThroughStreet(1,intersection), intersection[1]); + util::SimpleLogger().Write(logDEBUG) << "Disabled U-Turn on a freeway"; + intersection[0].entry_allowed = false; // UTURN on the freeway + } + else if (exiting_motorways == 2) + { + // standard fork + std::size_t first_valid = std::numeric_limits::max(), + second_valid = std::numeric_limits::max(); + for (std::size_t i = 0; i < intersection.size(); ++i) + { + if (intersection[i].entry_allowed && + detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph)) + { + if (first_valid < intersection.size()) + { + second_valid = i; + break; + } + else + { + first_valid = i; + } + } + } + assignFork(via_eid, intersection[second_valid], intersection[first_valid]); + } + else if (exiting_motorways == 3) + { + // triple fork + std::size_t first_valid = std::numeric_limits::max(), + second_valid = std::numeric_limits::max(), + third_valid = std::numeric_limits::max(); + for (std::size_t i = 0; i < intersection.size(); ++i) + { + if (intersection[i].entry_allowed && + detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph)) + { + if (second_valid < intersection.size()) + { + third_valid = i; + break; + } + else if (first_valid < intersection.size()) + { + second_valid = i; + } + else + { + first_valid = i; + } + } + } + assignFork(via_eid, intersection[third_valid], intersection[second_valid], + intersection[first_valid]); + } + else + { + util::SimpleLogger().Write(logDEBUG) << "Found motorway junction with more than " + "2 exiting motorways or additional ramps"; + return fallback(std::move(intersection)); + } + } // done for more than one highway exit + } + return intersection; +} + +Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection intersection) const +{ + auto num_valid_turns = countValid(intersection); + // ramp straight into a motorway/ramp + if (intersection.size() == 2 && num_valid_turns == 1) + { + BOOST_ASSERT(!intersection[0].entry_allowed); + BOOST_ASSERT(detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph)); + + intersection[1].turn.instruction = getInstructionForObvious( + intersection.size(), via_eid, isThroughStreet(1,intersection), intersection[1]); + } + else if (intersection.size() == 3) + { + // merging onto a passing highway / or two ramps merging onto the same highway + if (num_valid_turns == 1) + { + BOOST_ASSERT(!intersection[0].entry_allowed); + // check order of highways + // 4 + // 5 3 + // + // 6 2 + // + // 7 1 + // 0 + if (intersection[1].entry_allowed) + { + if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) && + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id != + INVALID_NAME_ID && + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id) + { + // circular order indicates a merge to the left (0-3 onto 4 + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < + NARROW_TURN_ANGLE) + intersection[1].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightLeft}; + else // fallback + intersection[1].turn.instruction = { + TurnType::Merge, getTurnDirection(intersection[1].turn.angle)}; + } + else // passing by the end of a motorway + { + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_eid, + isThroughStreet(1,intersection), intersection[1]); + } + } + else + { + BOOST_ASSERT(intersection[2].entry_allowed); + if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph) && + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id != + INVALID_NAME_ID && + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[0].turn.eid).name_id) + { + // circular order (5-0) onto 4 + if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < + NARROW_TURN_ANGLE) + intersection[2].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightRight}; + else // fallback + intersection[2].turn.instruction = { + TurnType::Merge, getTurnDirection(intersection[2].turn.angle)}; + } + else // passing the end of a highway + { + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_eid, + isThroughStreet(2,intersection), intersection[2]); + } + } + } + else + { + BOOST_ASSERT(num_valid_turns == 2); + // UTurn on ramps is not possible + BOOST_ASSERT(!intersection[0].entry_allowed); + BOOST_ASSERT(intersection[1].entry_allowed); + BOOST_ASSERT(intersection[2].entry_allowed); + // two motorways starting at end of ramp (fork) + // M M + // \ / + // | + // R + if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) && + detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph)) + { + assignFork(via_eid, intersection[2], intersection[1]); + } + else + { + // continued ramp passing motorway entry + // M R + // M R + // | / + // R + if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid) + .road_classification.road_class)) + { + intersection[1].turn.instruction = {TurnType::Turn, + DirectionModifier::SlightRight}; + intersection[2].turn.instruction = {TurnType::Continue, + DirectionModifier::SlightLeft}; + } + else + { + assignFork(via_eid, intersection[2], intersection[1]); + } + } + } + } + // On - Off Ramp on passing Motorway, Ramp onto Fork(?) + else if (intersection.size() == 4) + { + bool passed_highway_entry = false; + for (auto &road : intersection) + { + const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid); + if (!road.entry_allowed && + detail::isMotorwayClass(edge_data.road_classification.road_class)) + { + passed_highway_entry = true; + } + else if (detail::isMotorwayClass(edge_data.road_classification.road_class)) + { + road.turn.instruction = {TurnType::Merge, passed_highway_entry + ? DirectionModifier::SlightRight + : DirectionModifier::SlightLeft}; + } + else + { + BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class)); + road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)}; + } + } + } + else + { // FALLBACK, hopefully this should never been reached + util::SimpleLogger().Write(logDEBUG) << "Reached fallback on motorway ramp with " + << intersection.size() << " roads and " + << countValid(intersection) << " valid turns."; + return fallback(std::move(intersection)); + } + return intersection; +} + +Intersection MotorwayHandler::fallback(Intersection intersection) const +{ + for (auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + + util::SimpleLogger().Write(logDEBUG) + << "road: " << toString(road) << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class; + + if (!road.entry_allowed) + continue; + + const auto type = detail::isMotorwayClass(out_data.road_classification.road_class) + ? TurnType::Merge + : TurnType::Turn; + + if (type == TurnType::Turn) + { + if (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) + road.turn.instruction = {type, DirectionModifier::Straight}; + else + { + road.turn.instruction = {type, road.turn.angle > STRAIGHT_ANGLE + ? DirectionModifier::SlightLeft + : DirectionModifier::SlightRight}; + } + } + else + { + road.turn.instruction = {type, road.turn.angle < STRAIGHT_ANGLE + ? DirectionModifier::SlightLeft + : DirectionModifier::SlightRight}; + } + } + return intersection; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp new file mode 100644 index 000000000..6bd72dca0 --- /dev/null +++ b/src/extractor/guidance/roundabout_handler.cpp @@ -0,0 +1,257 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/roundabout_handler.hpp" +#include "extractor/guidance/toolkit.hpp" + +#include "util/simple_logger.hpp" + +#include +#include +#include + +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table) + : IntersectionHandler(node_based_graph, node_info_list, name_table) +{ +} + +RoundaboutHandler::~RoundaboutHandler() {} + +bool RoundaboutHandler::canProcess(const NodeID from_nid, + const EdgeID via_eid, + const Intersection &intersection) const +{ + const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection); + return flags.on_roundabout || flags.can_enter; +} + +Intersection RoundaboutHandler:: +operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const +{ + const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection); + const bool is_rotary = isRotary(node_based_graph.GetTarget(via_eid)); + // find the radius of the roundabout + return handleRoundabouts(is_rotary, via_eid, flags.on_roundabout, flags.can_exit_separately, + std::move(intersection)); +} + +detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags( + const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const +{ + const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid); + bool on_roundabout = in_edge_data.roundabout; + bool can_enter_roundabout = false; + bool can_exit_roundabout_separately = false; + for (const auto &road : intersection) + { + const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid); + // only check actual outgoing edges + if (edge_data.reversed) + continue; + + if (edge_data.roundabout) + { + can_enter_roundabout = true; + } + // Exiting roundabouts at an entry point is technically a data-modelling issue. + // This workaround handles cases in which an exit follows the entry. + // To correctly represent perceived exits, we only count exits leading to a + // separate vertex than the one we are coming from that are in the direction of + // the roundabout. + // The sorting of the angles represents a problem for left-sided driving, though. + // FIXME in case of left-sided driving, we have to check whether we can enter the + // roundabout later in the cycle, rather than prior. + // FIXME requires consideration of crossing the roundabout + else if (node_based_graph.GetTarget(road.turn.eid) != from_nid && !can_enter_roundabout) + { + can_exit_roundabout_separately = true; + } + } + return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately}; +} + +bool RoundaboutHandler::isRotary(const NodeID nid) const +{ + // translate a node ID into its respective coordinate stored in the node_info_list + const auto getCoordinate = [this](const NodeID node) { + return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat); + }; + + unsigned roundabout_name_id = 0; + std::unordered_set connected_names; + + const auto getNextOnRoundabout = [this, &roundabout_name_id, + &connected_names](const NodeID node) { + EdgeID continue_edge = SPECIAL_EDGEID; + for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) + { + const auto &edge_data = node_based_graph.GetEdgeData(edge); + if (!edge_data.reversed && edge_data.roundabout) + { + if (SPECIAL_EDGEID != continue_edge) + { + // fork in roundabout + return SPECIAL_EDGEID; + } + // roundabout does not keep its name + if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id && + requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id), + name_table.GetNameForID(edge_data.name_id))) + { + return SPECIAL_EDGEID; + } + + roundabout_name_id = edge_data.name_id; + + continue_edge = edge; + } + else if (!edge_data.roundabout) + { + // remember all connected road names + connected_names.insert(edge_data.name_id); + } + } + return continue_edge; + }; + // the roundabout radius has to be the same for all locations we look at it from + // to guarantee this, we search the full roundabout for its vertices + // and select the three smalles ids + std::set roundabout_nodes; // needs to be sorted + + // this value is a hard abort to deal with potential self-loops + NodeID last_node = nid; + while (0 == roundabout_nodes.count(last_node)) + { + roundabout_nodes.insert(last_node); + const auto eid = getNextOnRoundabout(last_node); + + if (eid == SPECIAL_EDGEID) + { + util::SimpleLogger().Write(logDEBUG) << "Non-Loop Roundabout found."; + return false; + } + + last_node = node_based_graph.GetTarget(eid); + } + + // do we have a dedicated name for the rotary, if not its a roundabout + // This function can theoretically fail if the roundabout name is partly + // used with a reference and without. This will be fixed automatically + // when we handle references separately or if the useage is more consistent + if (roundabout_name_id == 0 || connected_names.count(roundabout_name_id)) + { + return false; + } + + if (roundabout_nodes.size() <= 1) + { + return false; + } + // calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal + // circle + // with both vertices right at the other side (so half their distance in meters). + // Otherwise, we construct a circle through the first tree vertices. + const auto getRadius = [&roundabout_nodes, &getCoordinate]() { + auto node_itr = roundabout_nodes.begin(); + if (roundabout_nodes.size() == 2) + { + const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++); + return 0.5 * util::coordinate_calculation::haversineDistance(first, second); + } + else + { + const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++), + third = getCoordinate(*node_itr++); + return util::coordinate_calculation::circleRadius(first, second, third); + } + }; + const double radius = getRadius(); + + // check whether the circle computation has gone wrong + // The radius computation can result in infinity, if the three coordinates are non-distinct. + // To stay on the safe side, we say its not a rotary + if (std::isinf(radius)) + return false; + + return radius > MAX_ROUNDABOUT_RADIUS; +} + +Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary, + const EdgeID via_eid, + const bool on_roundabout, + const bool can_exit_roundabout_separately, + Intersection intersection) const +{ + // TODO requires differentiation between roundabouts and rotaries + // detect via radius (get via circle through three vertices) + NodeID node_v = node_based_graph.GetTarget(via_eid); + if (on_roundabout) + { + // Shoule hopefully have only a single exit and continue + // at least for cars. How about bikes? + for (auto &road : intersection) + { + auto &turn = road.turn; + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + if (out_data.roundabout) + { + // TODO can forks happen in roundabouts? E.g. required lane changes + if (1 == node_based_graph.GetDirectedOutDegree(node_v)) + { + // No turn possible. + turn.instruction = TurnInstruction::NO_TURN(); + } + else + { + turn.instruction = + TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle)); + } + } + else + { + turn.instruction = + TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle)); + } + } + return intersection; + } + else + for (auto &road : intersection) + { + if (!road.entry_allowed) + continue; + auto &turn = road.turn; + const auto &out_data = node_based_graph.GetEdgeData(turn.eid); + if (out_data.roundabout) + { + turn.instruction = + TurnInstruction::ENTER_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle)); + if (can_exit_roundabout_separately) + { + if (turn.instruction.type == TurnType::EnterRotary) + turn.instruction.type = TurnType::EnterRotaryAtExit; + if (turn.instruction.type == TurnType::EnterRoundabout) + turn.instruction.type = TurnType::EnterRoundaboutAtExit; + } + } + else + { + turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT( + is_rotary, getTurnDirection(turn.angle)); + } + } + return intersection; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp new file mode 100644 index 000000000..9413f87f7 --- /dev/null +++ b/src/extractor/guidance/turn_analysis.cpp @@ -0,0 +1,100 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/turn_analysis.hpp" + +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/simple_logger.hpp" + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ + +using EdgeData = util::NodeBasedDynamicGraph::EdgeData; + +bool requiresAnnouncement(const EdgeData &from, const EdgeData &to) +{ + return !from.IsCompatibleTo(to); +} + +TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const CompressedEdgeContainer &compressed_edge_container, + const util::NameTable &name_table) + : node_based_graph(node_based_graph), intersection_generator(node_based_graph, + restriction_map, + barrier_nodes, + node_info_list, + compressed_edge_container), + roundabout_handler(node_based_graph, node_info_list, name_table), + motorway_handler(node_based_graph, node_info_list, name_table), + turn_handler(node_based_graph, node_info_list, name_table) +{ +} + +std::vector TurnAnalysis::getTurns(const NodeID from_nid, const EdgeID via_eid) const +{ + auto intersection = intersection_generator(from_nid, via_eid); + + // Roundabouts are a main priority. If there is a roundabout instruction present, we process the + // turn as a roundabout + if (roundabout_handler.canProcess(from_nid, via_eid, intersection)) + { + intersection = roundabout_handler(from_nid, via_eid, std::move(intersection)); + } + else + { + // set initial defaults for normal turns and modifier based on angle + intersection = setTurnTypes(from_nid, via_eid, std::move(intersection)); + if (motorway_handler.canProcess(from_nid, via_eid, intersection)) + { + intersection = motorway_handler(from_nid, via_eid, std::move(intersection)); + } + else + { + BOOST_ASSERT(turn_handler.canProcess(from_nid, via_eid, intersection)); + intersection = turn_handler(from_nid, via_eid, std::move(intersection)); + } + } + + std::vector turns; + for (auto road : intersection) + if (road.entry_allowed) + turns.emplace_back(road.turn); + + return turns; +} + +// Sets basic turn types as fallback for otherwise unhandled turns +Intersection TurnAnalysis::setTurnTypes(const NodeID from_nid, + const EdgeID, + Intersection intersection) const +{ + for (auto &road : intersection) + { + if (!road.entry_allowed) + continue; + + const EdgeID onto_edge = road.turn.eid; + const NodeID to_nid = node_based_graph.GetTarget(onto_edge); + + road.turn.instruction = {TurnType::Turn, (from_nid == to_nid) + ? DirectionModifier::UTurn + : getTurnDirection(road.turn.angle)}; + } + return intersection; +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp new file mode 100644 index 000000000..57a29d681 --- /dev/null +++ b/src/extractor/guidance/turn_handler.cpp @@ -0,0 +1,1171 @@ +#include "extractor/guidance/constants.hpp" +#include "extractor/guidance/toolkit.hpp" +#include "extractor/guidance/turn_handler.hpp" + +#include "util/simple_logger.hpp" + +#include +#include + +#include + +using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData; + +namespace osrm +{ +namespace extractor +{ +namespace guidance +{ +namespace detail +{ +inline FunctionalRoadClass roadClass(const ConnectedRoad &road, + const util::NodeBasedDynamicGraph &graph) +{ + return graph.GetEdgeData(road.turn.eid).road_classification.road_class; +} + +inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_based_graph) +{ + return isRampClass(node_based_graph.GetEdgeData(eid).road_classification.road_class); +} +} + +TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph, + const std::vector &node_info_list, + const util::NameTable &name_table) + : IntersectionHandler(node_based_graph, node_info_list, name_table) +{ +} + +TurnHandler::~TurnHandler() {} + +bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) const +{ + return true; +} + +Intersection TurnHandler:: +operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const +{ + if (intersection.size() == 1) + return handleOneWayTurn(std::move(intersection)); + + if (intersection[0].entry_allowed) + { + intersection[0].turn.instruction = {findBasicTurnType(via_eid, intersection[0]), + DirectionModifier::UTurn}; + } + + if (intersection.size() == 2) + return handleTwoWayTurn(via_eid, std::move(intersection)); + + if (intersection.size() == 3) + return handleThreeWayTurn(via_eid, std::move(intersection)); + + return handleComplexTurn(via_eid, std::move(intersection)); +} + +Intersection TurnHandler::handleOneWayTurn(Intersection intersection) const +{ + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + return intersection; +} + +Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const +{ + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]); + + if (intersection[1].turn.instruction.type == TurnType::Suppressed) + intersection[1].turn.instruction.type = TurnType::NoTurn; + + return intersection; +} + +Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const +{ + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) { + const auto first_class = + node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class; + const bool is_ramp = isRampClass(first_class); + const auto second_class = + node_based_graph.GetEdgeData(other.turn.eid).road_classification.road_class; + const bool is_narrow_turn = + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE; + const bool other_turn_is_at_least_orthogonal = + angularDeviation(other.turn.angle, STRAIGHT_ANGLE) > 85; + const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < + std::numeric_limits::epsilon(); + const bool is_obvious_by_road_class = + (!is_ramp && (2 * getPriority(first_class) < getPriority(second_class))) || + (!isLowPriorityRoadClass(first_class) && isLowPriorityRoadClass(second_class)); + const bool is_much_narrower_than_other = + angularDeviation(other.turn.angle, STRAIGHT_ANGLE) / + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) > + INCREASES_BY_FOURTY_PERCENT; + return (is_narrow_turn && other_turn_is_at_least_orthogonal) || + turn_is_perfectly_straight || is_much_narrower_than_other || + is_obvious_by_road_class; + }; + + /* Two nearly straight turns -> FORK + OOOOOOO + / + IIIIII + \ + OOOOOOO + */ + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + { + if (intersection[1].entry_allowed && intersection[2].entry_allowed) + { + const auto left_class = node_based_graph.GetEdgeData(intersection[2].turn.eid) + .road_classification.road_class; + const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid) + .road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + { + assignFork(via_edge, intersection[2], intersection[1]); + } + else if (isObviousOfTwo(intersection[1], intersection[2])) + { + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]); + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::SlightLeft}; + } + else if (isObviousOfTwo(intersection[2], intersection[1])) + { + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]); + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SlightRight}; + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SlightRight}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::SlightLeft}; + } + } + else + { + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]); + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]); + } + } + /* T Intersection + + OOOOOOO T OOOOOOOO + I + I + I + */ + else if (angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > + NARROW_TURN_ANGLE) + { + if (intersection[1].entry_allowed) + { + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1])) + intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right}; + else + intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Right}; + } + if (intersection[2].entry_allowed) + { + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[2])) + + intersection[2].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Left}; + else + intersection[2].turn.instruction = {TurnType::Ramp, DirectionModifier::Left}; + } + } + /* T Intersection, Cross left + O + O + O + IIIIIIII - OOOOOOOOOO + */ + else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > + NARROW_TURN_ANGLE) + { + if (intersection[1].entry_allowed) + { + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1])) + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]); + else + intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Straight}; + } + if (intersection[2].entry_allowed) + { + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::Left}; + } + } + /* T Intersection, Cross right + + IIIIIIII T OOOOOOOOOO + O + O + O + */ + else if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > + NARROW_TURN_ANGLE) + { + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]); + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::Right}; + } + // merge onto a through street + else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id && + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id) + { + const auto findTurn = [isObviousOfTwo](const ConnectedRoad turn, + const ConnectedRoad other) -> TurnInstruction { + if (isObviousOfTwo(turn, other)) + { + return {TurnType::Merge, turn.turn.angle < STRAIGHT_ANGLE + ? DirectionModifier::SlightLeft + : DirectionModifier::SlightRight}; + } + else + { + return {TurnType::Turn, getTurnDirection(turn.turn.angle)}; + } + }; + intersection[1].turn.instruction = findTurn(intersection[1], intersection[2]); + intersection[2].turn.instruction = findTurn(intersection[2], intersection[1]); + } + // other street merges from the left + else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id && + node_based_graph.GetEdgeData(via_edge).name_id == + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id) + { + if (isObviousOfTwo(intersection[1], intersection[2])) + { + intersection[1].turn.instruction = + TurnInstruction::SUPPRESSED(DirectionModifier::Straight); + } + else + { + intersection[1].turn.instruction = {TurnType::Continue, + getTurnDirection(intersection[1].turn.angle)}; + } + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + getTurnDirection(intersection[2].turn.angle)}; + } + // other street merges from the right + else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id && + node_based_graph.GetEdgeData(via_edge).name_id == + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id) + { + if (isObviousOfTwo(intersection[2], intersection[1])) + { + intersection[2].turn.instruction = + TurnInstruction::SUPPRESSED(DirectionModifier::Straight); + } + else + { + intersection[2].turn.instruction = {TurnType::Continue, + getTurnDirection(intersection[2].turn.angle)}; + } + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + getTurnDirection(intersection[1].turn.angle)}; + } + else + { + if (isObviousOfTwo(intersection[1], intersection[2])) + { + intersection[1].turn.instruction = + getInstructionForObvious(3, via_edge, false, intersection[1]); + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + getTurnDirection(intersection[1].turn.angle)}; + } + + if (isObviousOfTwo(intersection[2], intersection[1])) + { + intersection[2].turn.instruction = + getInstructionForObvious(3, via_edge, false, intersection[2]); + } + else + { + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + getTurnDirection(intersection[2].turn.angle)}; + } + } + return intersection; +} + +Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const +{ + static int fallback_count = 0; + const std::size_t obvious_index = findObviousTurn(via_edge, intersection); + const auto fork_range = findFork(intersection); + std::size_t straightmost_turn = 0; + double straightmost_deviation = 180; + for (std::size_t i = 0; i < intersection.size(); ++i) + { + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (deviation < straightmost_deviation) + { + straightmost_deviation = deviation; + straightmost_turn = i; + } + } + + // check whether the obvious choice is actually a through street + if (obvious_index != 0) + { + intersection[obvious_index].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, isThroughStreet(obvious_index,intersection), + intersection[obvious_index]); + + // assign left/right turns + intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index); + } + else if (fork_range.first != 0 && fork_range.second - fork_range.first <= 2) // found fork + { + if (fork_range.second - fork_range.first == 1) + { + auto &left = intersection[fork_range.second]; + auto &right = intersection[fork_range.first]; + const auto left_class = + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class; + const auto right_class = + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + assignFork(via_edge, left, right); + else if (getPriority(left_class) > getPriority(right_class)) + { + right.turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, right); + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + } + else + { + left.turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, false, left); + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + } + else if (fork_range.second - fork_range.second == 2) + { + assignFork(via_edge, intersection[fork_range.second], + intersection[fork_range.first + 1], intersection[fork_range.first]); + } + // assign left/right turns + intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), fork_range.first); + } + else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE && + !intersection[straightmost_turn].entry_allowed) + { + // invalid straight turn + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn); + } + // no straight turn + else if (intersection[straightmost_turn].turn.angle > 180) + { + // at most three turns on either side + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn); + } + else if (intersection[straightmost_turn].turn.angle < 180) + { + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1); + } + else + { + if (fallback_count++ < 10) + { + util::SimpleLogger().Write(logWARNING) + << "Resolved to keep fallback on complex turn assignment" + << "Straightmost: " << straightmost_turn; + ; + for (const auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "road: " << toString(road) << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class; + } + } + } + return intersection; +} + +std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge, + const Intersection &intersection) const +{ + // no obvious road + if (intersection.size() == 1) + return 0; + + // a single non u-turn is obvious + if (intersection.size() == 2) + return 1; + + // at least three roads + std::size_t best = 0; + double best_deviation = 180; + + std::size_t best_continue = 0; + double best_continue_deviation = 180; + + const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge); + for (std::size_t i = 1; i < intersection.size(); ++i) + { + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (intersection[i].entry_allowed && deviation < best_deviation) + { + best_deviation = deviation; + best = i; + } + + const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid); + if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id && + deviation < best_continue_deviation) + { + best_continue_deviation = deviation; + best_continue = i; + } + } + + if (best == 0) + return 0; + + if (best_deviation >= 2 * NARROW_TURN_ANGLE) + return 0; + + // TODO incorporate road class in decision + if (best != 0 && best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + return best; + } + + // has no obvious continued road + if (best_continue == 0 || true) + { + // Find left/right deviation + const double left_deviation = angularDeviation( + intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE); + const double right_deviation = + angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE); + + if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE) + return best; + + // other narrow turns? + if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <= + FUZZY_ANGLE_DIFFERENCE) + return 0; + if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle, + STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE) + return 0; + + // Well distinct turn that is nearly straight + if (left_deviation / best_deviation >= DISTINCTION_RATIO && + right_deviation / best_deviation >= DISTINCTION_RATIO) + { + return best; + } + } + + return 0; // no obvious turn +} + +// Can only assign three turns +Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t starting_at) const +{ + const auto count_valid = [&intersection, starting_at]() { + std::size_t count = 0; + for (std::size_t i = starting_at; i < intersection.size(); ++i) + if (intersection[i].entry_allowed) + ++count; + return count; + }; + if (starting_at == intersection.size() || count_valid() == 0) + return intersection; + // handle single turn + if (intersection.size() - starting_at == 1) + { + if (!intersection[starting_at].entry_allowed) + return intersection; + + if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) > + NARROW_TURN_ANGLE && + angularDeviation(intersection[starting_at].turn.angle, 0) > NARROW_TURN_ANGLE) + { + // assign left turn + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), DirectionModifier::Left}; + } + else if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) <= + NARROW_TURN_ANGLE) + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SlightLeft}; + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SharpLeft}; + } + } + // two turns on at the side + else if (intersection.size() - starting_at == 2) + { + const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle); + const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle); + if (first_direction == second_direction) + { + // conflict + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + } + } + else if (intersection.size() - starting_at == 3) + { + const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle); + const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle); + const auto third_direction = getTurnDirection(intersection[starting_at + 2].turn.angle); + if (first_direction != second_direction && second_direction != third_direction) + { + // implies first != third, based on the angles and clockwise order + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction}; + } + else if (2 >= (intersection[starting_at].entry_allowed + + intersection[starting_at + 1].entry_allowed + + intersection[starting_at + 2].entry_allowed)) + { + + // at least one invalid turn + if (!intersection[starting_at].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at + 1]); + } + else if (!intersection[starting_at + 1].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at]); + } + else + { + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + } + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) >= NARROW_TURN_ANGLE && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= NARROW_TURN_ANGLE) + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SlightLeft}; + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), + DirectionModifier::Left}; + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), + DirectionModifier::SharpLeft}; + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + ((first_direction == second_direction && second_direction == third_direction) || + (third_direction == second_direction && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) < GROUP_ANGLE) || + (second_direction == first_direction && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) < GROUP_ANGLE))) + { + intersection[starting_at].turn.instruction = { + detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph) + ? FirstRamp + : FirstTurn, + second_direction}; + intersection[starting_at + 1].turn.instruction = { + detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph) + ? SecondRamp + : SecondTurn, + second_direction}; + intersection[starting_at + 2].turn.instruction = { + detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph) + ? ThirdRamp + : ThirdTurn, + second_direction}; + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + ((third_direction == second_direction && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) >= GROUP_ANGLE) || + (second_direction == first_direction && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE))) + { + // conflict one side with an additional very sharp turn + if (angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE) + { + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction}; + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at + 1]); + } + } + else if ((first_direction == second_direction && + intersection[starting_at].entry_allowed != + intersection[starting_at + 1].entry_allowed) || + (second_direction == third_direction && + intersection[starting_at + 1].entry_allowed != + intersection[starting_at + 2].entry_allowed)) + { + // no conflict, due to conflict being restricted to valid/invalid + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction}; + } + else + { + util::SimpleLogger().Write(logWARNING) << "Reached fallback for left turns, size 3"; + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << toString(road) << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class; + } + + for (std::size_t i = starting_at; i < intersection.size(); ++i) + if (intersection[i].entry_allowed) + intersection[i].turn.instruction = { + findBasicTurnType(via_edge, intersection[i]), + getTurnDirection(intersection[i].turn.angle)}; + } + } + else if (intersection.size() - starting_at == 4) + { + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph) + ? FirstRamp + : FirstTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph) + ? SecondRamp + : SecondTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph) + ? ThirdRamp + : ThirdTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 3].entry_allowed) + intersection[starting_at + 3].turn.instruction = { + detail::isRampClass(intersection[starting_at + 3].turn.eid, node_based_graph) + ? FourthRamp + : FourthTurn, + DirectionModifier::Left}; + } + else + { + for (auto &road : intersection) + { + if (!road.entry_allowed) + continue; + road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp + : Turn, + getTurnDirection(road.turn.angle)}; + } + } + return intersection; +} + +// can only assign three turns +Intersection TurnHandler::assignRightTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t up_to) const +{ + BOOST_ASSERT(up_to <= intersection.size()); + const auto count_valid = [&intersection, up_to]() { + std::size_t count = 0; + for (std::size_t i = 1; i < up_to; ++i) + if (intersection[i].entry_allowed) + ++count; + return count; + }; + if (up_to <= 1 || count_valid() == 0) + return intersection; + // handle single turn + if (up_to == 2) + { + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, 0) > NARROW_TURN_ANGLE) + { + // assign left turn + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::Right}; + } + else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <= NARROW_TURN_ANGLE) + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SlightRight}; + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SharpRight}; + } + } + else if (up_to == 3) + { + const auto first_direction = getTurnDirection(intersection[1].turn.angle); + const auto second_direction = getTurnDirection(intersection[2].turn.angle); + if (first_direction == second_direction) + { + // conflict + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + } + } + else if (up_to == 4) + { + const auto first_direction = getTurnDirection(intersection[1].turn.angle); + const auto second_direction = getTurnDirection(intersection[2].turn.angle); + const auto third_direction = getTurnDirection(intersection[3].turn.angle); + if (first_direction != second_direction && second_direction != third_direction) + { + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; + } + else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed + + intersection[3].entry_allowed)) + { + // at least a single invalid + if (!intersection[3].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + } + else if (!intersection[1].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[3], intersection[2]); + } + else // handles one-valid as well as two valid (1,3) + { + handleDistinctConflict(via_edge, intersection[3], intersection[1]); + } + } + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >= + NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + NARROW_TURN_ANGLE) + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SharpRight}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::Right}; + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + DirectionModifier::SlightRight}; + } + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + ((first_direction == second_direction && second_direction == third_direction) || + (first_direction == second_direction && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) < + GROUP_ANGLE) || + (second_direction == third_direction && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) < + GROUP_ANGLE))) + { + intersection[1].turn.instruction = { + detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? ThirdRamp + : ThirdTurn, + second_direction}; + intersection[2].turn.instruction = { + detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? SecondRamp + : SecondTurn, + second_direction}; + intersection[3].turn.instruction = { + detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? FirstRamp + : FirstTurn, + second_direction}; + } + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + ((first_direction == second_direction && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + GROUP_ANGLE) || + (second_direction == third_direction && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >= + GROUP_ANGLE))) + { + if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + GROUP_ANGLE) + { + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + handleDistinctConflict(via_edge, intersection[3], intersection[2]); + } + } + else if ((first_direction == second_direction && + intersection[1].entry_allowed != intersection[2].entry_allowed) || + (second_direction == third_direction && + intersection[2].entry_allowed != intersection[3].entry_allowed)) + { + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; + } + else + { + util::SimpleLogger().Write(logWARNING) + << "Reached fallback for right turns, size 3 " + << " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed + + intersection[3].entry_allowed); + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << toString(road) << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class; + } + + for (std::size_t i = 1; i < up_to; ++i) + if (intersection[i].entry_allowed) + intersection[i].turn.instruction = { + findBasicTurnType(via_edge, intersection[i]), + getTurnDirection(intersection[i].turn.angle)}; + } + } + else if (up_to == 5) + { + if (intersection[4].entry_allowed) + intersection[4].turn.instruction = { + detail::isRampClass(intersection[4].turn.eid, node_based_graph) ? FirstRamp + : FirstTurn, + DirectionModifier::Right}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = { + detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? SecondRamp + : SecondTurn, + DirectionModifier::Right}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = { + detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? ThirdRamp + : ThirdTurn, + DirectionModifier::Right}; + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = { + detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? FourthRamp + : FourthTurn, + DirectionModifier::Right}; + } + else + { + for (std::size_t i = 1; i < up_to; ++i) + { + auto &road = intersection[i]; + if (!road.entry_allowed) + continue; + road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp + : Turn, + getTurnDirection(road.turn.angle)}; + } + } + return intersection; +} + +std::pair TurnHandler::findFork(const Intersection &intersection) const +{ + + std::size_t best = 0; + double best_deviation = 180; + + // TODO handle road classes + for (std::size_t i = 1; i < intersection.size(); ++i) + { + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (intersection[i].entry_allowed && deviation < best_deviation) + { + best_deviation = deviation; + best = i; + } + } + if (best_deviation <= NARROW_TURN_ANGLE) + { + std::size_t left = best, right = best; + if (intersection[left].turn.angle >= 180) + { + // due to best > 1, we can safely decrement right + --right; + if (angularDeviation(intersection[right].turn.angle, STRAIGHT_ANGLE) > + NARROW_TURN_ANGLE) + return std::make_pair(std::size_t{0}, std::size_t{0}); + } + else + { + ++left; + if (left >= intersection.size() || + angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE) + return std::make_pair(std::size_t{0}, std::size_t{0}); + } + while (left + 1 < intersection.size() && + angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) < + NARROW_TURN_ANGLE) + ++left; + while (right > 1 && + angularDeviation(intersection[right].turn.angle, + intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE) + --right; + + // TODO check whether 2*NARROW_TURN is too large + if (right < left && + angularDeviation(intersection[left].turn.angle, + intersection[(left + 1) % intersection.size()].turn.angle) >= + 2 * NARROW_TURN_ANGLE && + angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >= + 2 * NARROW_TURN_ANGLE) + return std::make_pair(right, left); + } + return std::make_pair(std::size_t{0}, std::size_t{0}); +} + +void TurnHandler::handleDistinctConflict(const EdgeID via_edge, + ConnectedRoad &left, + ConnectedRoad &right) const +{ + // single turn of both is valid (don't change the valid one) + // or multiple identical angles -> bad OSM intersection + if ((!left.entry_allowed || !right.entry_allowed) || (left.turn.angle == right.turn.angle)) + { + if (left.entry_allowed) + left.turn.instruction = {findBasicTurnType(via_edge, left), + getTurnDirection(left.turn.angle)}; + if (right.entry_allowed) + right.turn.instruction = {findBasicTurnType(via_edge, right), + getTurnDirection(right.turn.angle)}; + return; + } + + if (getTurnDirection(left.turn.angle) == DirectionModifier::Straight || + getTurnDirection(left.turn.angle) == DirectionModifier::SlightLeft || + getTurnDirection(right.turn.angle) == DirectionModifier::SlightRight) + { + const auto left_class = + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class; + const auto right_class = + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + assignFork(via_edge, left, right); + else if (getPriority(left_class) > getPriority(right_class)) + { + // FIXME this should possibly know about the actual roads? + // here we don't know about the intersection size. To be on the save side, + // we declare it + // as complex (at least size 4) + right.turn.instruction = getInstructionForObvious(4, via_edge, false, right); + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + } + else + { + // FIXME this should possibly know about the actual roads? + // here we don't know about the intersection size. To be on the save side, + // we declare it + // as complex (at least size 4) + left.turn.instruction = getInstructionForObvious(4, via_edge, false, left); + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; + } + } + const auto left_type = findBasicTurnType(via_edge, left); + const auto right_type = findBasicTurnType(via_edge, right); + // Two Right Turns + if (angularDeviation(left.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // Keep left perfect, shift right + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + return; + } + if (angularDeviation(right.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // Keep Right perfect, shift left + left.turn.instruction = {left_type, DirectionModifier::SlightRight}; + right.turn.instruction = {right_type, DirectionModifier::Right}; + return; + } + // Two Right Turns + if (angularDeviation(left.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // Keep left perfect, shift right + left.turn.instruction = {left_type, DirectionModifier::Left}; + right.turn.instruction = {right_type, DirectionModifier::SlightLeft}; + return; + } + if (angularDeviation(right.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // Keep Right perfect, shift left + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + return; + } + // Both turns? + if (TurnType::Ramp != left_type && TurnType::Ramp != right_type) + { + if (left.turn.angle < STRAIGHT_ANGLE) + { + left.turn.instruction = {TurnType::FirstTurn, getTurnDirection(left.turn.angle)}; + right.turn.instruction = {TurnType::SecondTurn, getTurnDirection(right.turn.angle)}; + } + else + { + left.turn.instruction = {TurnType::SecondTurn, getTurnDirection(left.turn.angle)}; + right.turn.instruction = {TurnType::FirstTurn, getTurnDirection(right.turn.angle)}; + } + return; + } + // Shift the lesser penalty + if (getTurnDirection(left.turn.angle) == DirectionModifier::SharpLeft) + { + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + return; + } + if (getTurnDirection(right.turn.angle) == DirectionModifier::SharpRight) + { + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + return; + } + + if (getTurnDirection(left.turn.angle) == DirectionModifier::Right) + { + if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90)) + { + left.turn.instruction = {left_type, DirectionModifier::SlightRight}; + right.turn.instruction = {right_type, DirectionModifier::Right}; + } + else + { + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + } + } + else + { + if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270)) + { + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + } + else + { + left.turn.instruction = {left_type, DirectionModifier::Left}; + right.turn.instruction = {right_type, DirectionModifier::SlightLeft}; + } + } +} + +} // namespace guidance +} // namespace extractor +} // namespace osrm diff --git a/data_structures/raster_source.cpp b/src/extractor/raster_source.cpp similarity index 59% rename from data_structures/raster_source.cpp rename to src/extractor/raster_source.cpp index 9e880a218..16e41f987 100644 --- a/data_structures/raster_source.cpp +++ b/src/extractor/raster_source.cpp @@ -1,39 +1,15 @@ -/* +#include "extractor/raster_source.hpp" -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "raster_source.hpp" - -#include "../util/simple_logger.hpp" -#include "../util/timing_util.hpp" - -#include +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" #include +namespace osrm +{ +namespace extractor +{ + RasterSource::RasterSource(RasterGrid _raster_data, std::size_t _width, std::size_t _height, @@ -106,28 +82,28 @@ int SourceContainer::loadRasterSource(const std::string &path_string, std::size_t nrows, std::size_t ncols) { - const auto _xmin = static_cast(xmin * COORDINATE_PRECISION); - const auto _xmax = static_cast(xmax * COORDINATE_PRECISION); - const auto _ymin = static_cast(ymin * COORDINATE_PRECISION); - const auto _ymax = static_cast(ymax * COORDINATE_PRECISION); + const auto _xmin = static_cast(util::toFixed(util::FloatLongitude(xmin))); + const auto _xmax = static_cast(util::toFixed(util::FloatLongitude(xmax))); + const auto _ymin = static_cast(util::toFixed(util::FloatLatitude(ymin))); + const auto _ymax = static_cast(util::toFixed(util::FloatLatitude(ymax))); const auto itr = LoadedSourcePaths.find(path_string); if (itr != LoadedSourcePaths.end()) { - SimpleLogger().Write() << "[source loader] Already loaded source '" << path_string - << "' at source_id " << itr->second; + util::SimpleLogger().Write() << "[source loader] Already loaded source '" << path_string + << "' at source_id " << itr->second; return itr->second; } int source_id = static_cast(LoadedSources.size()); - SimpleLogger().Write() << "[source loader] Loading from " << path_string << " ... "; + util::SimpleLogger().Write() << "[source loader] Loading from " << path_string << " ... "; TIMER_START(loading_source); boost::filesystem::path filepath(path_string); if (!boost::filesystem::exists(filepath)) { - throw osrm::exception("error reading: no such path"); + throw util::exception("error reading: no such path"); } RasterGrid rasterData{filepath, ncols, nrows}; @@ -137,42 +113,47 @@ int SourceContainer::loadRasterSource(const std::string &path_string, LoadedSourcePaths.emplace(path_string, source_id); LoadedSources.push_back(std::move(source)); - SimpleLogger().Write() << "[source loader] ok, after " << TIMER_SEC(loading_source) << "s"; + util::SimpleLogger().Write() << "[source loader] ok, after " << TIMER_SEC(loading_source) + << "s"; return source_id; } // External function for looking up nearest data point from a specified source -RasterDatum SourceContainer::getRasterDataFromSource(unsigned int source_id, int lon, int lat) +RasterDatum SourceContainer::getRasterDataFromSource(unsigned int source_id, double lon, double lat) { if (LoadedSources.size() < source_id + 1) { - throw osrm::exception("error reading: no such loaded source"); + throw util::exception("error reading: no such loaded source"); } - BOOST_ASSERT(lat < (90 * COORDINATE_PRECISION)); - BOOST_ASSERT(lat > (-90 * COORDINATE_PRECISION)); - BOOST_ASSERT(lon < (180 * COORDINATE_PRECISION)); - BOOST_ASSERT(lon > (-180 * COORDINATE_PRECISION)); + BOOST_ASSERT(lat < 90); + BOOST_ASSERT(lat > -90); + BOOST_ASSERT(lon < 180); + BOOST_ASSERT(lon > -180); const auto &found = LoadedSources[source_id]; - return found.getRasterData(lon, lat); + return found.getRasterData(static_cast(util::toFixed(util::FloatLongitude(lon))), + static_cast(util::toFixed(util::FloatLatitude(lat)))); } // External function for looking up interpolated data from a specified source RasterDatum -SourceContainer::getRasterInterpolateFromSource(unsigned int source_id, int lon, int lat) +SourceContainer::getRasterInterpolateFromSource(unsigned int source_id, double lon, double lat) { if (LoadedSources.size() < source_id + 1) { - throw osrm::exception("error reading: no such loaded source"); + throw util::exception("error reading: no such loaded source"); } - BOOST_ASSERT(lat < (90 * COORDINATE_PRECISION)); - BOOST_ASSERT(lat > (-90 * COORDINATE_PRECISION)); - BOOST_ASSERT(lon < (180 * COORDINATE_PRECISION)); - BOOST_ASSERT(lon > (-180 * COORDINATE_PRECISION)); + BOOST_ASSERT(lat < 90); + BOOST_ASSERT(lat > -90); + BOOST_ASSERT(lon < 180); + BOOST_ASSERT(lon > -180); const auto &found = LoadedSources[source_id]; - return found.getRasterInterpolate(lon, lat); + return found.getRasterInterpolate(static_cast(util::toFixed(util::FloatLongitude(lon))), + static_cast(util::toFixed(util::FloatLatitude(lat)))); +} +} } diff --git a/data_structures/restriction_map.cpp b/src/extractor/restriction_map.cpp similarity index 78% rename from data_structures/restriction_map.cpp rename to src/extractor/restriction_map.cpp index eb685be80..d41b1c497 100644 --- a/data_structures/restriction_map.cpp +++ b/src/extractor/restriction_map.cpp @@ -1,31 +1,9 @@ -/* +#include "extractor/restriction_map.hpp" -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "restriction_map.hpp" +namespace osrm +{ +namespace extractor +{ RestrictionMap::RestrictionMap(const std::vector &restriction_list) : m_count(0) { @@ -42,7 +20,8 @@ RestrictionMap::RestrictionMap(const std::vector &restriction_l m_no_turn_via_node_set.insert(restriction.via.node); // This explicit downcasting is also OK for the same reason. - RestrictionSource restriction_source = {static_cast(restriction.from.node), static_cast(restriction.via.node)}; + RestrictionSource restriction_source = {static_cast(restriction.from.node), + static_cast(restriction.via.node)}; std::size_t index; auto restriction_iter = m_restriction_map.find(restriction_source); @@ -176,9 +155,7 @@ bool RestrictionMap::CheckIfTurnIsRestricted(const NodeID node_u, // check of node is the start of any restriction bool RestrictionMap::IsSourceNode(const NodeID node) const { - if (m_restriction_start_nodes.find(node) == m_restriction_start_nodes.end()) - { - return false; - } - return true; + return m_restriction_start_nodes.find(node) != m_restriction_start_nodes.end(); +} +} } diff --git a/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp similarity index 62% rename from extractor/restriction_parser.cpp rename to src/extractor/restriction_parser.cpp index afb194738..66cd25903 100644 --- a/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -1,256 +1,213 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "restriction_parser.hpp" -#include "extraction_way.hpp" - -#include "../data_structures/external_memory_node.hpp" -#include "../util/lua_util.hpp" -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -namespace -{ -int lua_error_callback(lua_State *lua_state) -{ - std::string error_msg = lua_tostring(lua_state, -1); - throw osrm::exception("ERROR occured in profile script:\n" + error_msg); -} -} - -RestrictionParser::RestrictionParser(lua_State *lua_state) : use_turn_restrictions(true) -{ - ReadUseRestrictionsSetting(lua_state); - - if (use_turn_restrictions) - { - ReadRestrictionExceptions(lua_state); - } -} - -void RestrictionParser::ReadUseRestrictionsSetting(lua_State *lua_state) -{ - if (0 == luaL_dostring(lua_state, "return use_turn_restrictions\n") && - lua_isboolean(lua_state, -1)) - { - use_turn_restrictions = lua_toboolean(lua_state, -1); - } - - if (use_turn_restrictions) - { - SimpleLogger().Write() << "Using turn restrictions"; - } - else - { - SimpleLogger().Write() << "Ignoring turn restrictions"; - } -} - -void RestrictionParser::ReadRestrictionExceptions(lua_State *lua_state) -{ - if (lua_function_exists(lua_state, "get_exceptions")) - { - luabind::set_pcall_callback(&lua_error_callback); - // get list of turn restriction exceptions - luabind::call_function(lua_state, "get_exceptions", - boost::ref(restriction_exceptions)); - const unsigned exception_count = restriction_exceptions.size(); - SimpleLogger().Write() << "Found " << exception_count - << " exceptions to turn restrictions:"; - for (const std::string &str : restriction_exceptions) - { - SimpleLogger().Write() << " " << str; - } - } - else - { - SimpleLogger().Write() << "Found no exceptions to turn restrictions"; - } -} - -/** - * Tries to parse an relation as turn restriction. This can fail for a number of - * reasons, this the return type is a boost::optional. - * - * Some restrictions can also be ignored: See the ```get_exceptions``` function - * in the corresponding profile. - */ -boost::optional -RestrictionParser::TryParse(const osmium::Relation &relation) const -{ - // return if turn restrictions should be ignored - if (!use_turn_restrictions) - { - return {}; - } - - osmium::tags::KeyPrefixFilter filter(false); - filter.add(true, "restriction"); - - const osmium::TagList &tag_list = relation.tags(); - - osmium::tags::KeyPrefixFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); - osmium::tags::KeyPrefixFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); - - // if it's a restriction, continue; - if (std::distance(fi_begin, fi_end) == 0) - { - return {}; - } - - // check if the restriction should be ignored - const char *except = relation.get_value_by_key("except"); - if (except != nullptr && ShouldIgnoreRestriction(except)) - { - return {}; - } - - bool is_only_restriction = false; - - for (; fi_begin != fi_end; ++fi_begin) - { - const std::string key(fi_begin->key()); - const std::string value(fi_begin->value()); - - if (value.find("only_") == 0) - { - is_only_restriction = true; - } - - // if the "restriction*" key is longer than 11 chars, it is a conditional exception (i.e. - // "restriction:") - if (key.size() > 11) - { - const auto ex_suffix = [&](const std::string &exception) - { - return boost::algorithm::ends_with(key, exception); - }; - bool is_actually_restricted = - std::any_of(begin(restriction_exceptions), end(restriction_exceptions), ex_suffix); - - if (!is_actually_restricted) - { - return {}; - } - } - } - - InputRestrictionContainer restriction_container(is_only_restriction); - - for (const auto &member : relation.members()) - { - const char *role = member.role(); - if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0) - { - continue; - } - - switch (member.type()) - { - case osmium::item_type::node: - // Make sure nodes appear only in the role if a via node - if (0 == strcmp("from", role) || 0 == strcmp("to", role)) - { - continue; - } - BOOST_ASSERT(0 == strcmp("via", role)); - - // set via node id - restriction_container.restriction.via.node = member.ref(); - break; - - case osmium::item_type::way: - BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) || - 0 == strcmp("via", role)); - if (0 == strcmp("from", role)) - { - restriction_container.restriction.from.way = member.ref(); - } - else if (0 == strcmp("to", role)) - { - restriction_container.restriction.to.way = member.ref(); - } - // else if (0 == strcmp("via", role)) - // { - // not yet suppported - // restriction_container.restriction.via.way = member.ref(); - // } - break; - case osmium::item_type::relation: - // not yet supported, but who knows what the future holds... - break; - default: - // shouldn't ever happen - break; - } - } - return boost::make_optional(std::move(restriction_container)); -} - -bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const -{ - // should this restriction be ignored? yes if there's an overlap between: - // a) the list of modes in the except tag of the restriction - // (except_tag_string), eg: except=bus;bicycle - // b) the lua profile defines a hierachy of modes, - // eg: [access, vehicle, bicycle] - - if (except_tag_string.empty()) - { - return false; - } - - // Be warned, this is quadratic work here, but we assume that - // only a few exceptions are actually defined. - std::vector exceptions; - boost::algorithm::split_regex(exceptions, except_tag_string, boost::regex("[;][ ]*")); - - return std::any_of(std::begin(exceptions), std::end(exceptions), - [&](const std::string ¤t_string) - { - if (std::end(restriction_exceptions) != - std::find(std::begin(restriction_exceptions), - std::end(restriction_exceptions), current_string)) - { - return true; - } - return false; - }); -} +#include "extractor/restriction_parser.hpp" +#include "extractor/profile_properties.hpp" + +#include "extractor/external_memory_node.hpp" +#include "util/lua_util.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +namespace +{ +int luaErrorCallback(lua_State *lua_state) +{ + std::string error_msg = lua_tostring(lua_state, -1); + throw util::exception("ERROR occurred in profile script:\n" + error_msg); +} +} + +RestrictionParser::RestrictionParser(lua_State *lua_state, const ProfileProperties &properties) + : use_turn_restrictions(properties.use_turn_restrictions) +{ + if (use_turn_restrictions) + { + ReadRestrictionExceptions(lua_state); + } +} + +void RestrictionParser::ReadRestrictionExceptions(lua_State *lua_state) +{ + if (util::luaFunctionExists(lua_state, "get_exceptions")) + { + luabind::set_pcall_callback(&luaErrorCallback); + // get list of turn restriction exceptions + luabind::call_function(lua_state, "get_exceptions", + boost::ref(restriction_exceptions)); + const unsigned exception_count = restriction_exceptions.size(); + util::SimpleLogger().Write() << "Found " << exception_count + << " exceptions to turn restrictions:"; + for (const std::string &str : restriction_exceptions) + { + util::SimpleLogger().Write() << " " << str; + } + } + else + { + util::SimpleLogger().Write() << "Found no exceptions to turn restrictions"; + } +} + +/** + * Tries to parse an relation as turn restriction. This can fail for a number of + * reasons, this the return type is a boost::optional. + * + * Some restrictions can also be ignored: See the ```get_exceptions``` function + * in the corresponding profile. + */ +boost::optional +RestrictionParser::TryParse(const osmium::Relation &relation) const +{ + // return if turn restrictions should be ignored + if (!use_turn_restrictions) + { + return {}; + } + + osmium::tags::KeyPrefixFilter filter(false); + filter.add(true, "restriction"); + + const osmium::TagList &tag_list = relation.tags(); + + osmium::tags::KeyPrefixFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); + osmium::tags::KeyPrefixFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); + + // if it's a restriction, continue; + if (std::distance(fi_begin, fi_end) == 0) + { + return {}; + } + + // check if the restriction should be ignored + const char *except = relation.get_value_by_key("except"); + if (except != nullptr && ShouldIgnoreRestriction(except)) + { + return {}; + } + + bool is_only_restriction = false; + + for (; fi_begin != fi_end; ++fi_begin) + { + const std::string key(fi_begin->key()); + const std::string value(fi_begin->value()); + + if (value.find("only_") == 0) + { + is_only_restriction = true; + } + + // if the "restriction*" key is longer than 11 chars, it is a conditional exception (i.e. + // "restriction:") + if (key.size() > 11) + { + const auto ex_suffix = [&](const std::string &exception) + { + return boost::algorithm::ends_with(key, exception); + }; + bool is_actually_restricted = + std::any_of(begin(restriction_exceptions), end(restriction_exceptions), ex_suffix); + + if (!is_actually_restricted) + { + return {}; + } + } + } + + InputRestrictionContainer restriction_container(is_only_restriction); + + for (const auto &member : relation.members()) + { + const char *role = member.role(); + if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0) + { + continue; + } + + switch (member.type()) + { + case osmium::item_type::node: + // Make sure nodes appear only in the role if a via node + if (0 == strcmp("from", role) || 0 == strcmp("to", role)) + { + continue; + } + BOOST_ASSERT(0 == strcmp("via", role)); + + // set via node id + restriction_container.restriction.via.node = member.ref(); + break; + + case osmium::item_type::way: + BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) || + 0 == strcmp("via", role)); + if (0 == strcmp("from", role)) + { + restriction_container.restriction.from.way = member.ref(); + } + else if (0 == strcmp("to", role)) + { + restriction_container.restriction.to.way = member.ref(); + } + // else if (0 == strcmp("via", role)) + // { + // not yet suppported + // restriction_container.restriction.via.way = member.ref(); + // } + break; + case osmium::item_type::relation: + // not yet supported, but who knows what the future holds... + break; + default: + // shouldn't ever happen + break; + } + } + return boost::make_optional(std::move(restriction_container)); +} + +bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const +{ + // should this restriction be ignored? yes if there's an overlap between: + // a) the list of modes in the except tag of the restriction + // (except_tag_string), eg: except=bus;bicycle + // b) the lua profile defines a hierarchy of modes, + // eg: [access, vehicle, bicycle] + + if (except_tag_string.empty()) + { + return false; + } + + // Be warned, this is quadratic work here, but we assume that + // only a few exceptions are actually defined. + std::vector exceptions; + boost::algorithm::split_regex(exceptions, except_tag_string, boost::regex("[;][ ]*")); + + return std::any_of(std::begin(exceptions), std::end(exceptions), + [&](const std::string ¤t_string) + { + return std::end(restriction_exceptions) != + std::find(std::begin(restriction_exceptions), + std::end(restriction_exceptions), current_string); + }); +} +} +} diff --git a/src/extractor/scripting_environment.cpp b/src/extractor/scripting_environment.cpp new file mode 100644 index 000000000..0840f7d1d --- /dev/null +++ b/src/extractor/scripting_environment.cpp @@ -0,0 +1,183 @@ +#include "extractor/scripting_environment.hpp" + +#include "extractor/extraction_helper_functions.hpp" +#include "extractor/extraction_node.hpp" +#include "extractor/extraction_way.hpp" +#include "extractor/internal_extractor_edge.hpp" +#include "extractor/external_memory_node.hpp" +#include "extractor/raster_source.hpp" +#include "extractor/profile_properties.hpp" +#include "util/lua_util.hpp" +#include "util/make_unique.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/typedefs.hpp" + +#include +#include + +#include + +#include + +namespace osrm +{ +namespace extractor +{ +namespace +{ +// wrapper method as luabind doesn't automatically overload funcs w/ default parameters +template +auto get_value_by_key(T const &object, const char *key) -> decltype(object.get_value_by_key(key)) +{ + return object.get_value_by_key(key, ""); +} + +template double latToDouble(T const &object) +{ + return static_cast(util::toFloating(object.lat)); +} + +template double lonToDouble(T const &object) +{ + return static_cast(util::toFloating(object.lon)); +} + +// Error handler +int luaErrorCallback(lua_State *state) +{ + std::string error_msg = lua_tostring(state, -1); + std::ostringstream error_stream; + error_stream << error_msg; + throw util::exception("ERROR occurred in profile script:\n" + error_stream.str()); +} +} + +ScriptingEnvironment::ScriptingEnvironment(const std::string &file_name) : file_name(file_name) +{ + util::SimpleLogger().Write() << "Using script " << file_name; +} + +void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context) +{ + typedef double (osmium::Location::*location_member_ptr_type)() const; + + luabind::open(context.state); + // open utility libraries string library; + luaL_openlibs(context.state); + + util::luaAddScriptFolderToLoadPath(context.state, file_name.c_str()); + + // Add our function to the state's global scope + luabind::module(context.state) + [luabind::def("durationIsValid", durationIsValid), + luabind::def("parseDuration", parseDuration), + luabind::class_("mode") + .enum_("enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE), + luabind::value("driving", TRAVEL_MODE_DRIVING), + luabind::value("cycling", TRAVEL_MODE_CYCLING), + luabind::value("walking", TRAVEL_MODE_WALKING), + luabind::value("ferry", TRAVEL_MODE_FERRY), + luabind::value("train", TRAVEL_MODE_TRAIN), + luabind::value("pushing_bike", TRAVEL_MODE_PUSHING_BIKE), + luabind::value("steps_up", TRAVEL_MODE_STEPS_UP), + luabind::value("steps_down", TRAVEL_MODE_STEPS_DOWN), + luabind::value("river_up", TRAVEL_MODE_RIVER_UP), + luabind::value("river_down", TRAVEL_MODE_RIVER_DOWN), + luabind::value("route", TRAVEL_MODE_ROUTE)], + luabind::class_("sources") + .def(luabind::constructor<>()) + .def("load", &SourceContainer::loadRasterSource) + .def("query", &SourceContainer::getRasterDataFromSource) + .def("interpolate", &SourceContainer::getRasterInterpolateFromSource), + luabind::class_("constants") + .enum_("enums")[luabind::value("precision", COORDINATE_PRECISION)], + + luabind::class_("ProfileProperties") + .def(luabind::constructor<>()) + .property("traffic_signal_penalty", &ProfileProperties::GetTrafficSignalPenalty, + &ProfileProperties::SetTrafficSignalPenalty) + .property("u_turn_penalty", &ProfileProperties::GetUturnPenalty, + &ProfileProperties::SetUturnPenalty) + .def_readwrite("use_turn_restrictions", &ProfileProperties::use_turn_restrictions) + .def_readwrite("continue_straight_at_waypoint", &ProfileProperties::continue_straight_at_waypoint), + + luabind::class_>("vector") + .def("Add", static_cast::*)(const std::string &)>( + &std::vector::push_back)), + + luabind::class_("Location") + .def("lat", &osmium::Location::lat) + .def("lon", &osmium::Location::lon), + + luabind::class_("Node") + // .def("tags", &osmium::Node::tags) + .def("location", &osmium::Node::location) + .def("get_value_by_key", &osmium::Node::get_value_by_key) + .def("get_value_by_key", &get_value_by_key) + .def("id", &osmium::Node::id), + + luabind::class_("ResultNode") + .def_readwrite("traffic_lights", &ExtractionNode::traffic_lights) + .def_readwrite("barrier", &ExtractionNode::barrier), + + luabind::class_("ResultWay") + // .def(luabind::constructor<>()) + .def_readwrite("forward_speed", &ExtractionWay::forward_speed) + .def_readwrite("backward_speed", &ExtractionWay::backward_speed) + .def_readwrite("name", &ExtractionWay::name) + .def_readwrite("roundabout", &ExtractionWay::roundabout) + .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted) + .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint) + .def_readwrite("duration", &ExtractionWay::duration) + .property("forward_mode", &ExtractionWay::get_forward_mode, + &ExtractionWay::set_forward_mode) + .property("backward_mode", &ExtractionWay::get_backward_mode, + &ExtractionWay::set_backward_mode), + luabind::class_("Way") + .def("get_value_by_key", &osmium::Way::get_value_by_key) + .def("get_value_by_key", &get_value_by_key) + .def("id", &osmium::Way::id), + luabind::class_("EdgeSource") + .def_readonly("source_coordinate", &InternalExtractorEdge::source_coordinate) + .def_readwrite("weight_data", &InternalExtractorEdge::weight_data), + luabind::class_("WeightData") + .def_readwrite("speed", &InternalExtractorEdge::WeightData::speed), + luabind::class_("EdgeTarget") + .property("lon", &lonToDouble) + .property("lat", &latToDouble), + luabind::class_("Coordinate") + .property("lon", &lonToDouble) + .property("lat", &latToDouble), + luabind::class_("RasterDatum") + .def_readonly("datum", &RasterDatum::datum) + .def("invalid_data", &RasterDatum::get_invalid)]; + + luabind::globals(context.state)["properties"] = &context.properties; + luabind::globals(context.state)["sources"] = &context.sources; + + if (0 != luaL_dofile(context.state, file_name.c_str())) + { + luabind::object error_msg(luabind::from_stack(context.state, -1)); + std::ostringstream error_stream; + error_stream << error_msg; + throw util::exception("ERROR occurred in profile script:\n" + error_stream.str()); + } +} + +ScriptingEnvironment::Context &ScriptingEnvironment::GetContex() +{ + std::lock_guard lock(init_mutex); + bool initialized = false; + auto &ref = script_contexts.local(initialized); + if (!initialized) + { + ref = util::make_unique(); + InitContext(*ref); + } + luabind::set_pcall_callback(&luaErrorCallback); + + return *ref; +} +} +} diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp new file mode 100644 index 000000000..9a69e2e14 --- /dev/null +++ b/src/osrm/osrm.cpp @@ -0,0 +1,54 @@ +#include "osrm/osrm.hpp" +#include "engine/api/route_parameters.hpp" +#include "engine/api/table_parameters.hpp" +#include "engine/api/nearest_parameters.hpp" +#include "engine/api/trip_parameters.hpp" +#include "engine/api/match_parameters.hpp" +#include "engine/engine.hpp" +#include "engine/status.hpp" +#include "engine/engine_config.hpp" +#include "util/make_unique.hpp" + +namespace osrm +{ + +// Pimpl idiom + +OSRM::OSRM(engine::EngineConfig &config) : engine_(util::make_unique(config)) {} +OSRM::~OSRM() = default; +OSRM::OSRM(OSRM &&) noexcept = default; +OSRM &OSRM::operator=(OSRM &&) noexcept = default; + +// Forward to implementation + +engine::Status OSRM::Route(const engine::api::RouteParameters ¶ms, util::json::Object &result) +{ + return engine_->Route(params, result); +} + +engine::Status OSRM::Table(const engine::api::TableParameters ¶ms, json::Object &result) +{ + return engine_->Table(params, result); +} + +engine::Status OSRM::Nearest(const engine::api::NearestParameters ¶ms, json::Object &result) +{ + return engine_->Nearest(params, result); +} + +engine::Status OSRM::Trip(const engine::api::TripParameters ¶ms, json::Object &result) +{ + return engine_->Trip(params, result); +} + +engine::Status OSRM::Match(const engine::api::MatchParameters ¶ms, json::Object &result) +{ + return engine_->Match(params, result); +} + +engine::Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string &result) +{ + return engine_->Tile(params, result); +} + +} // ns osrm diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp new file mode 100644 index 000000000..1d4485a03 --- /dev/null +++ b/src/server/api/parameters_parser.cpp @@ -0,0 +1,101 @@ +#include "server/api/parameters_parser.hpp" + +#include "server/api/match_parameter_grammar.hpp" +#include "server/api/nearest_parameter_grammar.hpp" +#include "server/api/route_parameters_grammar.hpp" +#include "server/api/table_parameter_grammar.hpp" +#include "server/api/tile_parameter_grammar.hpp" +#include "server/api/trip_parameter_grammar.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace detail +{ +template +using is_grammar_t = std::integral_constant::value || + std::is_same::value>; + +template ::value, int>::type = 0, + typename std::enable_if::value, int>::type = 0> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + using It = std::decay::type; + + GrammarT grammar; + + try + { + const auto ok = boost::spirit::qi::parse(iter, end, grammar); + + // return move(a.b) is needed to move b out of a and then return the rvalue by implicit move + if (ok && iter == end) + return std::move(grammar.parameters); + } + catch (const qi::expectation_failure &failure) + { + // The grammar above using expectation parsers ">" does not automatically increment the + // iterator to the failing position. Extract the position from the exception ourselves. + iter = failure.first; + } + + return boost::none; +} +} // ns detail + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, end); +} + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, end); +} + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, + end); +} + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, end); +} + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, end); +} + +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters(iter, end); +} + +} // ns api +} // ns server +} // ns osrm diff --git a/src/server/api/url_parser.cpp b/src/server/api/url_parser.cpp new file mode 100644 index 000000000..7ef6db001 --- /dev/null +++ b/src/server/api/url_parser.cpp @@ -0,0 +1,86 @@ +#include "server/api/url_parser.hpp" +#include "engine/polyline_compressor.hpp" + +//#define BOOST_SPIRIT_DEBUG +#include + +#include +#include + +// Keep impl. TU local +namespace +{ +namespace qi = boost::spirit::qi; + +template // +struct URLParser final : qi::grammar +{ + URLParser() : URLParser::base_type(start) + { + alpha_numeral = qi::char_("a-zA-Z0-9"); + polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^"); + all_chars = polyline_chars | qi::char_("=,;:&()."); + + service = +alpha_numeral; + version = qi::uint_; + profile = +alpha_numeral; + query = +all_chars; + + // Example input: /route/v1/driving/7.416351,43.731205;7.420363,43.736189 + + start = qi::lit('/') > service // + > qi::lit('/') > qi::lit('v') > version // + > qi::lit('/') > profile // + > qi::lit('/') > query; // + + BOOST_SPIRIT_DEBUG_NODES((start)(service)(version)(profile)(query)) + } + + qi::rule start; + + qi::rule service; + qi::rule version; + qi::rule profile; + qi::rule query; + + qi::rule alpha_numeral; + qi::rule all_chars; + qi::rule polyline_chars; +}; + +} // anon. + +namespace osrm +{ +namespace server +{ +namespace api +{ + +boost::optional parseURL(std::string::iterator &iter, const std::string::iterator end) +{ + using It = std::decay::type; + + static URLParser const parser; + ParsedURL out; + + try + { + const auto ok = boost::spirit::qi::parse(iter, end, parser, out); + + if (ok && iter == end) + return boost::make_optional(out); + } + catch (const qi::expectation_failure &failure) + { + // The grammar above using expectation parsers ">" does not automatically increment the + // iterator to the failing position. Extract the position from the exception ourselves. + iter = failure.first; + } + + return boost::none; +} + +} // api +} // server +} // osrm diff --git a/server/connection.cpp b/src/server/connection.cpp similarity index 70% rename from server/connection.cpp rename to src/server/connection.cpp index dbf85fd2a..0fa31e638 100644 --- a/server/connection.cpp +++ b/src/server/connection.cpp @@ -1,43 +1,19 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "connection.hpp" -#include "request_handler.hpp" -#include "request_parser.hpp" +#include "server/connection.hpp" +#include "server/request_handler.hpp" +#include "server/request_parser.hpp" #include #include #include #include +#include #include #include -namespace http +namespace osrm +{ +namespace server { Connection::Connection(boost::asio::io_service &io_service, RequestHandler &handler) @@ -65,25 +41,22 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t } // no error detected, let's parse the request - compression_type compression_type(no_compression); - osrm::tribool result; + http::compression_type compression_type(http::no_compression); + RequestParser::RequestStatus result; std::tie(result, compression_type) = request_parser.parse(current_request, incoming_data_buffer.data(), incoming_data_buffer.data() + bytes_transferred); // the request has been parsed - if (result == osrm::tribool::yes) + if (result == RequestParser::RequestStatus::valid) { current_request.endpoint = TCP_socket.remote_endpoint().address(); - request_handler.handle_request(current_request, current_reply); - - // Header compression_header; - std::vector output_buffer; + request_handler.HandleRequest(current_request, current_reply); // compress the result w/ gzip/deflate if requested switch (compression_type) { - case deflate_rfc1951: + case http::deflate_rfc1951: // use deflate for compression current_reply.headers.insert(current_reply.headers.begin(), {"Content-Encoding", "deflate"}); @@ -92,7 +65,7 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t output_buffer = current_reply.headers_to_buffers(); output_buffer.push_back(boost::asio::buffer(compressed_output)); break; - case gzip_rfc1952: + case http::gzip_rfc1952: // use gzip for compression current_reply.headers.insert(current_reply.headers.begin(), {"Content-Encoding", "gzip"}); @@ -101,7 +74,7 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t output_buffer = current_reply.headers_to_buffers(); output_buffer.push_back(boost::asio::buffer(compressed_output)); break; - case no_compression: + case http::no_compression: // don't use any compression current_reply.set_uncompressed_size(); output_buffer = current_reply.to_buffers(); @@ -113,9 +86,9 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t strand.wrap(boost::bind(&Connection::handle_write, this->shared_from_this(), boost::asio::placeholders::error))); } - else if (result == osrm::tribool::no) + else if (result == RequestParser::RequestStatus::invalid) { // request is not parseable - current_reply = reply::stock_reply(reply::bad_request); + current_reply = http::reply::stock_reply(http::reply::bad_request); boost::asio::async_write( TCP_socket, current_reply.to_buffers(), @@ -145,14 +118,14 @@ void Connection::handle_write(const boost::system::error_code &error) } std::vector Connection::compress_buffers(const std::vector &uncompressed_data, - const compression_type compression_type) + const http::compression_type compression_type) { boost::iostreams::gzip_params compression_parameters; // there's a trade-off between speed and size. speed wins compression_parameters.level = boost::iostreams::zlib::best_speed; // check which compression flavor is used - if (deflate_rfc1951 == compression_type) + if (http::deflate_rfc1951 == compression_type) { compression_parameters.noheader = true; } @@ -168,3 +141,4 @@ std::vector Connection::compress_buffers(const std::vector &uncompre return compressed_data; } } +} diff --git a/server/http/reply.cpp b/src/server/http/reply.cpp similarity index 66% rename from server/http/reply.cpp rename to src/server/http/reply.cpp index a3d3d9624..d279417e0 100644 --- a/server/http/reply.cpp +++ b/src/server/http/reply.cpp @@ -1,41 +1,18 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "reply.hpp" +#include "server/http/reply.hpp" #include +namespace osrm +{ +namespace server +{ namespace http { const char ok_html[] = ""; -const char bad_request_html[] = "{\"status\": 400,\"status_message\":\"Bad Request\"}"; +const char bad_request_html[] = ""; const char internal_server_error_html[] = - "{\"status\": 500,\"status_message\":\"Internal Server Error\"}"; + "{\"code\": \"InternalError\",\"message\":\"Internal Server Error\"}"; const char seperators[] = {':', ' '}; const char crlf[] = {'\r', '\n'}; const std::string http_ok_string = "HTTP/1.0 200 OK\r\n"; @@ -126,5 +103,11 @@ boost::asio::const_buffer reply::status_to_buffer(const reply::status_type statu return boost::asio::buffer(http_bad_request_string); } -reply::reply() : status(ok) {} +reply::reply() : status(ok) +{ + // We do not currently support keep alive. Always set 'Connection: close'. + headers.emplace_back("Connection", "close"); +} +} +} } diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp new file mode 100644 index 000000000..cfe74f257 --- /dev/null +++ b/src/server/request_handler.cpp @@ -0,0 +1,151 @@ +#include "server/request_handler.hpp" +#include "server/service_handler.hpp" + +#include "server/api/url_parser.hpp" +#include "server/http/reply.hpp" +#include "server/http/request.hpp" + +#include "util/json_renderer.hpp" +#include "util/simple_logger.hpp" +#include "util/string_util.hpp" +#include "util/typedefs.hpp" + +#include "engine/status.hpp" +#include "util/json_container.hpp" +#include "osrm/osrm.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace server +{ + +void RequestHandler::RegisterServiceHandler(std::unique_ptr service_handler_) +{ + service_handler = std::move(service_handler_); +} + +void RequestHandler::HandleRequest(const http::request ¤t_request, http::reply ¤t_reply) +{ + if (!service_handler) + { + current_reply = http::reply::stock_reply(http::reply::internal_server_error); + util::SimpleLogger().Write(logWARNING) << "No service handler registered." << std::endl; + return; + } + + // parse command + try + { + std::string request_string; + util::URIDecode(current_request.uri, request_string); + + // deactivated as GCC apparently does not implement that, not even in 4.9 + // std::time_t t = std::time(nullptr); + // util::SimpleLogger().Write() << std::put_time(std::localtime(&t), "%m-%d-%Y %H:%M:%S") << + // " " << current_request.endpoint.to_string() << " " << + // current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ") << + // current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") << + // request; + + time_t ltime; + struct tm *time_stamp; + + ltime = time(nullptr); + time_stamp = localtime(<ime); + + // log timestamp + util::SimpleLogger().Write() + << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-" + << (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1) << "-" + << 1900 + time_stamp->tm_year << " " << (time_stamp->tm_hour < 10 ? "0" : "") + << time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "") + << time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "") + << time_stamp->tm_sec << " " << current_request.endpoint.to_string() << " " + << current_request.referrer << (0 == current_request.referrer.length() ? "- " : " ") + << current_request.agent << (0 == current_request.agent.length() ? "- " : " ") + << request_string; + + auto api_iterator = request_string.begin(); + auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end()); + ServiceHandler::ResultT result; + + // check if the was an error with the request + if (maybe_parsed_url && api_iterator == request_string.end()) + { + + const engine::Status status = + service_handler->RunQuery(*std::move(maybe_parsed_url), result); + if (status != engine::Status::Ok) + { + // 4xx bad request return code + current_reply.status = http::reply::bad_request; + } + else + { + BOOST_ASSERT(status == engine::Status::Ok); + } + } + else + { + const auto position = std::distance(request_string.begin(), api_iterator); + BOOST_ASSERT(position >= 0); + const auto context_begin = request_string.begin() + ((position < 3) ? 0 : (position - 3UL)); + BOOST_ASSERT(context_begin >= request_string.begin()); + const auto context_end = + request_string.begin() + std::min(position + 3UL, request_string.size()); + BOOST_ASSERT(context_end <= request_string.end()); + std::string context(context_begin, context_end); + + current_reply.status = http::reply::bad_request; + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidUrl"; + json_result.values["message"] = "URL string malformed close to position " + + std::to_string(position) + ": \"" + context + "\""; + } + + current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*"); + current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET"); + current_reply.headers.emplace_back("Access-Control-Allow-Headers", + "X-Requested-With, Content-Type"); + if (result.is()) + { + current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8"); + current_reply.headers.emplace_back("Content-Disposition", + "inline; filename=\"response.json\""); + + util::json::render(current_reply.content, result.get()); + } + else + { + BOOST_ASSERT(result.is()); + std::copy(result.get().cbegin(), result.get().cend(), + std::back_inserter(current_reply.content)); + + current_reply.headers.emplace_back("Content-Type", "application/x-protobuf"); + } + + // set headers + current_reply.headers.emplace_back("Content-Length", + std::to_string(current_reply.content.size())); + } + catch (const std::exception &e) + { + current_reply = http::reply::stock_reply(http::reply::internal_server_error); + util::SimpleLogger().Write(logWARNING) << "[server error] code: " << e.what() + << ", uri: " << current_request.uri; + } +} +} +} diff --git a/src/server/request_parser.cpp b/src/server/request_parser.cpp new file mode 100644 index 000000000..e408a792f --- /dev/null +++ b/src/server/request_parser.cpp @@ -0,0 +1,299 @@ +#include "server/request_parser.hpp" + +#include "server/http/compression_type.hpp" +#include "server/http/header.hpp" +#include "server/http/request.hpp" + +#include + +#include + +namespace osrm +{ +namespace server +{ + +RequestParser::RequestParser() + : state(internal_state::method_start), current_header({"", ""}), + selected_compression(http::no_compression) +{ +} + +std::tuple +RequestParser::parse(http::request ¤t_request, char *begin, char *end) +{ + while (begin != end) + { + RequestStatus result = consume(current_request, *begin++); + if (result != RequestStatus::indeterminate) + { + return std::make_tuple(result, selected_compression); + } + } + RequestStatus result = RequestStatus::indeterminate; + + return std::make_tuple(result, selected_compression); +} + +RequestParser::RequestStatus RequestParser::consume(http::request ¤t_request, + const char input) +{ + switch (state) + { + case internal_state::method_start: + if (!is_char(input) || is_CTL(input) || is_special(input)) + { + return RequestStatus::invalid; + } + state = internal_state::method; + return RequestStatus::indeterminate; + case internal_state::method: + if (input == ' ') + { + state = internal_state::uri; + return RequestStatus::indeterminate; + } + if (!is_char(input) || is_CTL(input) || is_special(input)) + { + return RequestStatus::invalid; + } + return RequestStatus::indeterminate; + case internal_state::uri_start: + if (is_CTL(input)) + { + return RequestStatus::invalid; + } + state = internal_state::uri; + current_request.uri.push_back(input); + return RequestStatus::indeterminate; + case internal_state::uri: + if (input == ' ') + { + state = internal_state::http_version_h; + return RequestStatus::indeterminate; + } + if (is_CTL(input)) + { + return RequestStatus::invalid; + } + current_request.uri.push_back(input); + return RequestStatus::indeterminate; + case internal_state::http_version_h: + if (input == 'H') + { + state = internal_state::http_version_t_1; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_t_1: + if (input == 'T') + { + state = internal_state::http_version_t_2; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_t_2: + if (input == 'T') + { + state = internal_state::http_version_p; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_p: + if (input == 'P') + { + state = internal_state::http_version_slash; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_slash: + if (input == '/') + { + state = internal_state::http_version_major_start; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_major_start: + if (is_digit(input)) + { + state = internal_state::http_version_major; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_major: + if (input == '.') + { + state = internal_state::http_version_minor_start; + return RequestStatus::indeterminate; + } + if (is_digit(input)) + { + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_minor_start: + if (is_digit(input)) + { + state = internal_state::http_version_minor; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::http_version_minor: + if (input == '\r') + { + state = internal_state::expecting_newline_1; + return RequestStatus::indeterminate; + } + if (is_digit(input)) + { + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::expecting_newline_1: + if (input == '\n') + { + state = internal_state::header_line_start; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::header_line_start: + if (boost::iequals(current_header.name, "Accept-Encoding")) + { + /* giving gzip precedence over deflate */ + if (boost::icontains(current_header.value, "deflate")) + { + selected_compression = http::deflate_rfc1951; + } + if (boost::icontains(current_header.value, "gzip")) + { + selected_compression = http::gzip_rfc1952; + } + } + + if (boost::iequals(current_header.name, "Referer")) + { + current_request.referrer = current_header.value; + } + + if (boost::iequals(current_header.name, "User-Agent")) + { + current_request.agent = current_header.value; + } + + if (input == '\r') + { + state = internal_state::expecting_newline_3; + return RequestStatus::indeterminate; + } + if (!is_char(input) || is_CTL(input) || is_special(input)) + { + return RequestStatus::invalid; + } + state = internal_state::header_name; + current_header.clear(); + current_header.name.push_back(input); + return RequestStatus::indeterminate; + case internal_state::header_lws: + if (input == '\r') + { + state = internal_state::expecting_newline_2; + return RequestStatus::indeterminate; + } + if (input == ' ' || input == '\t') + { + return RequestStatus::indeterminate; + } + if (is_CTL(input)) + { + return RequestStatus::invalid; + } + state = internal_state::header_value; + return RequestStatus::indeterminate; + case internal_state::header_name: + if (input == ':') + { + state = internal_state::space_before_header_value; + return RequestStatus::indeterminate; + } + if (!is_char(input) || is_CTL(input) || is_special(input)) + { + return RequestStatus::invalid; + } + current_header.name.push_back(input); + return RequestStatus::indeterminate; + case internal_state::space_before_header_value: + if (input == ' ') + { + state = internal_state::header_value; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + case internal_state::header_value: + if (input == '\r') + { + state = internal_state::expecting_newline_2; + return RequestStatus::indeterminate; + } + if (is_CTL(input)) + { + return RequestStatus::invalid; + } + current_header.value.push_back(input); + return RequestStatus::indeterminate; + case internal_state::expecting_newline_2: + if (input == '\n') + { + state = internal_state::header_line_start; + return RequestStatus::indeterminate; + } + return RequestStatus::invalid; + default: // expecting_newline_3 + return input == '\n' ? RequestStatus::valid : RequestStatus::invalid; + } +} + +bool RequestParser::is_char(const int character) const +{ + return character >= 0 && character <= 127; +} + +bool RequestParser::is_CTL(const int character) const +{ + return (character >= 0 && character <= 31) || (character == 127); +} + +bool RequestParser::is_special(const int character) const +{ + switch (character) + { + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '\\': + case '"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + case ' ': + case '\t': + return true; + default: + return false; + } +} + +bool RequestParser::is_digit(const int character) const +{ + return character >= '0' && character <= '9'; +} +} +} diff --git a/src/server/service/match_service.cpp b/src/server/service/match_service.cpp new file mode 100644 index 000000000..dec8006e1 --- /dev/null +++ b/src/server/service/match_service.cpp @@ -0,0 +1,73 @@ +#include "server/service/match_service.hpp" + +#include "engine/api/match_parameters.hpp" +#include "server/api/parameters_parser.hpp" +#include "server/service/utils.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ +namespace +{ +std::string getWrongOptionHelp(const engine::api::MatchParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "timestamps", + parameters.timestamps, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status MatchService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + + BOOST_ASSERT(parameters); + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Match(*parameters, json_result); +} +} +} +} diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp new file mode 100644 index 000000000..1ea1fa89b --- /dev/null +++ b/src/server/service/nearest_service.cpp @@ -0,0 +1,72 @@ +#include "server/service/nearest_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/nearest_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +namespace +{ +std::string getWrongOptionHelp(const engine::api::NearestParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status NearestService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Nearest(*parameters, json_result); +} +} +} +} diff --git a/src/server/service/route_service.cpp b/src/server/service/route_service.cpp new file mode 100644 index 000000000..f06cbdbd3 --- /dev/null +++ b/src/server/service/route_service.cpp @@ -0,0 +1,69 @@ +#include "server/service/route_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/route_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +namespace osrm +{ +namespace server +{ +namespace service +{ +namespace +{ +std::string getWrongOptionHelp(const engine::api::RouteParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status RouteService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Route(*parameters, json_result); +} +} +} +} diff --git a/src/server/service/table_service.cpp b/src/server/service/table_service.cpp new file mode 100644 index 000000000..9b0f817b8 --- /dev/null +++ b/src/server/service/table_service.cpp @@ -0,0 +1,90 @@ +#include "server/service/table_service.hpp" + +#include "engine/api/table_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +namespace +{ + +const constexpr char PARAMETER_SIZE_MISMATCH_MSG[] = + "Number of elements in %1% size %2% does not match coordinate size %3%"; + +template +bool constrainParamSize(const char *msg_template, + const char *name, + const ParamT ¶m, + const std::size_t target_size, + std::string &help) +{ + if (param.size() > 0 && param.size() != target_size) + { + help = (boost::format(msg_template) % name % param.size() % target_size).str(); + return true; + } + return false; +} + +std::string getWrongOptionHelp(const engine::api::TableParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status TableService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Table(*parameters, json_result); +} +} +} +} diff --git a/src/server/service/tile_service.cpp b/src/server/service/tile_service.cpp new file mode 100644 index 000000000..d8f1ba8a5 --- /dev/null +++ b/src/server/service/tile_service.cpp @@ -0,0 +1,51 @@ +#include "server/service/tile_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/tile_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +engine::Status TileService::RunQuery(std::string &query, ResultT &result) +{ + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = "Invalid coodinates. Only zoomlevel 12+ is supported"; + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + result = std::string(); + auto &string_result = result.get(); + return BaseService::routing_machine.Tile(*parameters, string_result); +} +} +} +} diff --git a/src/server/service/trip_service.cpp b/src/server/service/trip_service.cpp new file mode 100644 index 000000000..82a1bfdd2 --- /dev/null +++ b/src/server/service/trip_service.cpp @@ -0,0 +1,73 @@ +#include "server/service/trip_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/trip_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ +namespace +{ +std::string getWrongOptionHelp(const engine::api::TripParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status TripService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = + api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Trip(*parameters, json_result); +} +} +} +} diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp new file mode 100644 index 000000000..98ba6a617 --- /dev/null +++ b/src/server/service_handler.cpp @@ -0,0 +1,54 @@ +#include "server/service_handler.hpp" + +#include "server/service/route_service.hpp" +#include "server/service/table_service.hpp" +#include "server/service/nearest_service.hpp" +#include "server/service/trip_service.hpp" +#include "server/service/match_service.hpp" +#include "server/service/tile_service.hpp" + +#include "server/api/parsed_url.hpp" +#include "util/json_util.hpp" +#include "util/make_unique.hpp" + +namespace osrm +{ +namespace server +{ +ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(config) +{ + service_map["route"] = util::make_unique(routing_machine); + service_map["table"] = util::make_unique(routing_machine); + service_map["nearest"] = util::make_unique(routing_machine); + service_map["trip"] = util::make_unique(routing_machine); + service_map["match"] = util::make_unique(routing_machine); + service_map["tile"] = util::make_unique(routing_machine); +} + +engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, + service::BaseService::ResultT &result) +{ + const auto &service_iter = service_map.find(parsed_url.service); + if (service_iter == service_map.end()) + { + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidService"; + json_result.values["message"] = "Service " + parsed_url.service + " not found!"; + return engine::Status::Error; + } + auto &service = service_iter->second; + + if (service->GetVersion() != parsed_url.version) + { + result = util::json::Object(); + auto &json_result = result.get(); + json_result.values["code"] = "InvalidVersion"; + json_result.values["message"] = "Service " + parsed_url.service + " not found!"; + return engine::Status::Error; + } + + return service->RunQuery(parsed_url.query, result); +} +} +} diff --git a/datastore.cpp b/src/storage/storage.cpp similarity index 53% rename from datastore.cpp rename to src/storage/storage.cpp index 504dd3d44..67fd122ff 100644 --- a/datastore.cpp +++ b/src/storage/storage.cpp @@ -1,54 +1,24 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "data_structures/original_edge_data.hpp" -#include "data_structures/range_table.hpp" -#include "data_structures/query_edge.hpp" -#include "data_structures/query_node.hpp" -#include "data_structures/shared_memory_factory.hpp" -#include "data_structures/shared_memory_vector_wrapper.hpp" -#include "data_structures/static_graph.hpp" -#include "data_structures/static_rtree.hpp" -#include "data_structures/travel_mode.hpp" -#include "data_structures/turn_instructions.hpp" -#include "server/data_structures/datafacade_base.hpp" -#include "server/data_structures/shared_datatype.hpp" -#include "server/data_structures/shared_barriers.hpp" -#include "util/datastore_options.hpp" -#include "util/simple_logger.hpp" -#include "util/osrm_exception.hpp" +#include "extractor/original_edge_data.hpp" +#include "util/range_table.hpp" +#include "contractor/query_edge.hpp" +#include "extractor/query_node.hpp" +#include "extractor/profile_properties.hpp" +#include "extractor/compressed_edge_container.hpp" +#include "util/shared_memory_vector_wrapper.hpp" +#include "util/static_graph.hpp" +#include "util/static_rtree.hpp" +#include "engine/datafacade/datafacade_base.hpp" +#include "extractor/travel_mode.hpp" +#include "extractor/guidance/turn_instruction.hpp" +#include "storage/storage.hpp" +#include "storage/shared_datatype.hpp" +#include "storage/shared_barriers.hpp" +#include "storage/shared_memory.hpp" #include "util/fingerprint.hpp" -#include "typedefs.h" - -#include - -using RTreeLeaf = BaseDataFacade::RTreeLeaf; -using RTreeNode = StaticRTree::vector, true>::TreeNode; -using QueryGraph = StaticGraph; +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/typedefs.hpp" +#include "util/coordinate.hpp" #ifdef __linux__ #include @@ -60,11 +30,22 @@ using QueryGraph = StaticGraph; #include #include -#include +#include #include +#include + +namespace osrm +{ +namespace storage +{ + +using RTreeLeaf = typename engine::datafacade::BaseDataFacade::RTreeLeaf; +using RTreeNode = + util::StaticRTree::vector, true>::TreeNode; +using QueryGraph = util::StaticGraph; // delete a shared memory region. report warning if it could not be deleted -void delete_region(const SharedDataType region) +void deleteRegion(const SharedDataType region) { if (SharedMemory::RegionExists(region) && !SharedMemory::Remove(region)) { @@ -89,13 +70,17 @@ void delete_region(const SharedDataType region) } }(); - SimpleLogger().Write(logWARNING) << "could not delete shared memory region " << name; + util::SimpleLogger().Write(logWARNING) << "could not delete shared memory region " << name; } } -int main(const int argc, const char *argv[]) try +Storage::Storage(StorageConfig config_) : config(std::move(config_)) {} + +int Storage::Run() { - LogPolicy::GetInstance().Unmute(); + BOOST_ASSERT_MSG(config.IsValid(), "Invalid storage config"); + + util::LogPolicy::GetInstance().Unmute(); SharedBarriers barrier; #ifdef __linux__ @@ -103,7 +88,7 @@ int main(const int argc, const char *argv[]) try const bool lock_flags = MCL_CURRENT | MCL_FUTURE; if (-1 == mlockall(lock_flags)) { - SimpleLogger().Write(logWARNING) << "Process " << argv[0] << " could not request RAM lock"; + util::SimpleLogger().Write(logWARNING) << "Could not request RAM lock"; } #endif @@ -118,122 +103,47 @@ int main(const int argc, const char *argv[]) try barrier.pending_update_mutex.unlock(); } - SimpleLogger().Write(logDEBUG) << "Checking input parameters"; - - std::unordered_map server_paths; - if (!GenerateDataStoreOptions(argc, argv, server_paths)) - { - return EXIT_SUCCESS; - } - - if (server_paths.find("hsgrdata") == server_paths.end()) - { - throw osrm::exception("no hsgr file found"); - } - if (server_paths.find("ramindex") == server_paths.end()) - { - throw osrm::exception("no ram index file found"); - } - if (server_paths.find("fileindex") == server_paths.end()) - { - throw osrm::exception("no leaf index file found"); - } - if (server_paths.find("nodesdata") == server_paths.end()) - { - throw osrm::exception("no nodes file found"); - } - if (server_paths.find("edgesdata") == server_paths.end()) - { - throw osrm::exception("no edges file found"); - } - if (server_paths.find("namesdata") == server_paths.end()) - { - throw osrm::exception("no names file found"); - } - if (server_paths.find("geometry") == server_paths.end()) - { - throw osrm::exception("no geometry file found"); - } - if (server_paths.find("core") == server_paths.end()) - { - throw osrm::exception("no core file found"); - } - - auto paths_iterator = server_paths.find("hsgrdata"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &hsgr_path = paths_iterator->second; - paths_iterator = server_paths.find("timestamp"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path ×tamp_path = paths_iterator->second; - paths_iterator = server_paths.find("ramindex"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &ram_index_path = paths_iterator->second; - paths_iterator = server_paths.find("fileindex"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path index_file_path_absolute = - boost::filesystem::canonical(paths_iterator->second); - const std::string &file_index_path = index_file_path_absolute.string(); - paths_iterator = server_paths.find("nodesdata"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &nodes_data_path = paths_iterator->second; - paths_iterator = server_paths.find("edgesdata"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &edges_data_path = paths_iterator->second; - paths_iterator = server_paths.find("namesdata"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &names_data_path = paths_iterator->second; - paths_iterator = server_paths.find("geometry"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &geometries_data_path = paths_iterator->second; - paths_iterator = server_paths.find("core"); - BOOST_ASSERT(server_paths.end() != paths_iterator); - BOOST_ASSERT(!paths_iterator->second.empty()); - const boost::filesystem::path &core_marker_path = paths_iterator->second; - // determine segment to use bool segment2_in_use = SharedMemory::RegionExists(LAYOUT_2); - const SharedDataType layout_region = [&] + const storage::SharedDataType layout_region = [&] { return segment2_in_use ? LAYOUT_1 : LAYOUT_2; }(); - const SharedDataType data_region = [&] + const storage::SharedDataType data_region = [&] { return segment2_in_use ? DATA_1 : DATA_2; }(); - const SharedDataType previous_layout_region = [&] + const storage::SharedDataType previous_layout_region = [&] { return segment2_in_use ? LAYOUT_2 : LAYOUT_1; }(); - const SharedDataType previous_data_region = [&] + const storage::SharedDataType previous_data_region = [&] { return segment2_in_use ? DATA_2 : DATA_1; }(); // Allocate a memory layout in shared memory, deallocate previous - auto *layout_memory = SharedMemoryFactory::Get(layout_region, sizeof(SharedDataLayout)); - auto *shared_layout_ptr = new (layout_memory->Ptr()) SharedDataLayout(); + auto *layout_memory = makeSharedMemory(layout_region, sizeof(SharedDataLayout)); + auto shared_layout_ptr = new (layout_memory->Ptr()) SharedDataLayout(); + auto absolute_file_index_path = boost::filesystem::absolute(config.file_index_path); shared_layout_ptr->SetBlockSize(SharedDataLayout::FILE_INDEX_PATH, - file_index_path.length() + 1); + absolute_file_index_path.string().length() + 1); // collect number of elements to store in shared memory object - SimpleLogger().Write() << "load names from: " << names_data_path; + util::SimpleLogger().Write() << "load names from: " << config.names_data_path; // number of entries in name index - boost::filesystem::ifstream name_stream(names_data_path, std::ios::binary); + boost::filesystem::ifstream name_stream(config.names_data_path, std::ios::binary); + if (!name_stream) + { + throw util::exception("Could not open " + config.names_data_path.string() + " for reading."); + } unsigned name_blocks = 0; name_stream.read((char *)&name_blocks, sizeof(unsigned)); shared_layout_ptr->SetBlockSize(SharedDataLayout::NAME_OFFSETS, name_blocks); - shared_layout_ptr->SetBlockSize::BlockT>( + shared_layout_ptr->SetBlockSize::BlockT>( SharedDataLayout::NAME_BLOCKS, name_blocks); - SimpleLogger().Write() << "name offsets size: " << name_blocks; + util::SimpleLogger().Write() << "name offsets size: " << name_blocks; BOOST_ASSERT_MSG(0 != name_blocks, "name file broken"); unsigned number_of_chars = 0; @@ -241,7 +151,11 @@ int main(const int argc, const char *argv[]) try shared_layout_ptr->SetBlockSize(SharedDataLayout::NAME_CHAR_LIST, number_of_chars); // Loading information for original edges - boost::filesystem::ifstream edges_input_stream(edges_data_path, std::ios::binary); + boost::filesystem::ifstream edges_input_stream(config.edges_data_path, std::ios::binary); + if (!edges_input_stream) + { + throw util::exception("Could not open " + config.edges_data_path.string() + " for reading."); + } unsigned number_of_original_edges = 0; edges_input_stream.read((char *)&number_of_original_edges, sizeof(unsigned)); @@ -250,27 +164,28 @@ int main(const int argc, const char *argv[]) try number_of_original_edges); shared_layout_ptr->SetBlockSize(SharedDataLayout::NAME_ID_LIST, number_of_original_edges); - shared_layout_ptr->SetBlockSize(SharedDataLayout::TRAVEL_MODE, - number_of_original_edges); - shared_layout_ptr->SetBlockSize(SharedDataLayout::TURN_INSTRUCTION, - number_of_original_edges); - // note: there are 32 geometry indicators in one unsigned block - shared_layout_ptr->SetBlockSize(SharedDataLayout::GEOMETRIES_INDICATORS, - number_of_original_edges); + shared_layout_ptr->SetBlockSize(SharedDataLayout::TRAVEL_MODE, + number_of_original_edges); + shared_layout_ptr->SetBlockSize( + SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges); - boost::filesystem::ifstream hsgr_input_stream(hsgr_path, std::ios::binary); + boost::filesystem::ifstream hsgr_input_stream(config.hsgr_data_path, std::ios::binary); + if (!hsgr_input_stream) + { + throw util::exception("Could not open " + config.hsgr_data_path.string() + " for reading."); + } - FingerPrint fingerprint_valid = FingerPrint::GetValid(); - FingerPrint fingerprint_loaded; - hsgr_input_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); + util::FingerPrint fingerprint_valid = util::FingerPrint::GetValid(); + util::FingerPrint fingerprint_loaded; + hsgr_input_stream.read((char *)&fingerprint_loaded, sizeof(util::FingerPrint)); if (fingerprint_loaded.TestGraphUtil(fingerprint_valid)) { - SimpleLogger().Write(logDEBUG) << "Fingerprint checked out ok"; + util::SimpleLogger().Write(logDEBUG) << "Fingerprint checked out ok"; } else { - SimpleLogger().Write(logWARNING) << ".hsgr was prepared with different build. " - "Reprocess to get rid of this warning."; + util::SimpleLogger().Write(logWARNING) << ".hsgr was prepared with different build. " + "Reprocess to get rid of this warning."; } // load checksum @@ -293,39 +208,27 @@ int main(const int argc, const char *argv[]) try number_of_graph_edges); // load rsearch tree size - boost::filesystem::ifstream tree_node_file(ram_index_path, std::ios::binary); + boost::filesystem::ifstream tree_node_file(config.ram_index_path, std::ios::binary); uint32_t tree_size = 0; tree_node_file.read((char *)&tree_size, sizeof(uint32_t)); shared_layout_ptr->SetBlockSize(SharedDataLayout::R_SEARCH_TREE, tree_size); + // load profile properties + shared_layout_ptr->SetBlockSize(SharedDataLayout::PROPERTIES, 1); + // load timestamp size + boost::filesystem::ifstream timestamp_stream(config.timestamp_path); std::string m_timestamp; - if (boost::filesystem::exists(timestamp_path)) - { - boost::filesystem::ifstream timestamp_stream(timestamp_path); - if (!timestamp_stream) - { - SimpleLogger().Write(logWARNING) << timestamp_path << " not found. setting to default"; - } - else - { - getline(timestamp_stream, m_timestamp); - timestamp_stream.close(); - } - } - if (m_timestamp.empty()) - { - m_timestamp = "n/a"; - } - if (25 < m_timestamp.length()) - { - m_timestamp.resize(25); - } + getline(timestamp_stream, m_timestamp); shared_layout_ptr->SetBlockSize(SharedDataLayout::TIMESTAMP, m_timestamp.length()); // load core marker size - boost::filesystem::ifstream core_marker_file(core_marker_path, std::ios::binary); + boost::filesystem::ifstream core_marker_file(config.core_data_path, std::ios::binary); + if (!core_marker_file) + { + throw util::exception("Could not open " + config.core_data_path.string() + " for reading."); + } uint32_t number_of_core_markers = 0; core_marker_file.read((char *)&number_of_core_markers, sizeof(uint32_t)); @@ -333,14 +236,22 @@ int main(const int argc, const char *argv[]) try number_of_core_markers); // load coordinate size - boost::filesystem::ifstream nodes_input_stream(nodes_data_path, std::ios::binary); + boost::filesystem::ifstream nodes_input_stream(config.nodes_data_path, std::ios::binary); + if (!nodes_input_stream) + { + throw util::exception("Could not open " + config.core_data_path.string() + " for reading."); + } unsigned coordinate_list_size = 0; nodes_input_stream.read((char *)&coordinate_list_size, sizeof(unsigned)); - shared_layout_ptr->SetBlockSize(SharedDataLayout::COORDINATE_LIST, - coordinate_list_size); + shared_layout_ptr->SetBlockSize(SharedDataLayout::COORDINATE_LIST, + coordinate_list_size); // load geometries sizes - std::ifstream geometry_input_stream(geometries_data_path.string().c_str(), std::ios::binary); + boost::filesystem::ifstream geometry_input_stream(config.geometries_path, std::ios::binary); + if (!geometry_input_stream) + { + throw util::exception("Could not open " + config.geometries_path.string() + " for reading."); + } unsigned number_of_geometries_indices = 0; unsigned number_of_compressed_geometries = 0; @@ -350,13 +261,59 @@ int main(const int argc, const char *argv[]) try boost::iostreams::seek(geometry_input_stream, number_of_geometries_indices * sizeof(unsigned), BOOST_IOS::cur); geometry_input_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned)); - shared_layout_ptr->SetBlockSize(SharedDataLayout::GEOMETRIES_LIST, - number_of_compressed_geometries); + shared_layout_ptr->SetBlockSize( + SharedDataLayout::GEOMETRIES_LIST, number_of_compressed_geometries); + + // load datasource sizes. This file is optional, and it's non-fatal if it doesn't + // exist. + boost::filesystem::ifstream geometry_datasource_input_stream(config.datasource_indexes_path, + std::ios::binary); + if (!geometry_datasource_input_stream) + { + throw util::exception("Could not open " + config.datasource_indexes_path.string() + " for reading."); + } + std::size_t number_of_compressed_datasources = 0; + if (geometry_datasource_input_stream) + { + geometry_datasource_input_stream.read( + reinterpret_cast(&number_of_compressed_datasources), sizeof(std::size_t)); + } + shared_layout_ptr->SetBlockSize(SharedDataLayout::DATASOURCES_LIST, + number_of_compressed_datasources); + + // Load datasource name sizes. This file is optional, and it's non-fatal if it doesn't + // exist + boost::filesystem::ifstream datasource_names_input_stream(config.datasource_names_path, + std::ios::binary); + if (!datasource_names_input_stream) + { + throw util::exception("Could not open " + config.datasource_names_path.string() + " for reading."); + } + std::vector m_datasource_name_data; + std::vector m_datasource_name_offsets; + std::vector m_datasource_name_lengths; + if (datasource_names_input_stream) + { + std::string name; + while (std::getline(datasource_names_input_stream, name)) + { + m_datasource_name_offsets.push_back(m_datasource_name_data.size()); + std::copy(name.c_str(), name.c_str() + name.size(), + std::back_inserter(m_datasource_name_data)); + m_datasource_name_lengths.push_back(name.size()); + } + } + shared_layout_ptr->SetBlockSize(SharedDataLayout::DATASOURCE_NAME_DATA, + m_datasource_name_data.size()); + shared_layout_ptr->SetBlockSize(SharedDataLayout::DATASOURCE_NAME_OFFSETS, + m_datasource_name_offsets.size()); + shared_layout_ptr->SetBlockSize(SharedDataLayout::DATASOURCE_NAME_LENGTHS, + m_datasource_name_lengths.size()); + // allocate shared memory block - SimpleLogger().Write() << "allocating shared memory of " << shared_layout_ptr->GetSizeOfLayout() - << " bytes"; - SharedMemory *shared_memory = - SharedMemoryFactory::Get(data_region, shared_layout_ptr->GetSizeOfLayout()); + util::SimpleLogger().Write() << "allocating shared memory of " + << shared_layout_ptr->GetSizeOfLayout() << " bytes"; + auto *shared_memory = makeSharedMemory(data_region, shared_layout_ptr->GetSizeOfLayout()); char *shared_memory_ptr = static_cast(shared_memory->Ptr()); // read actual data into shared memory object // @@ -374,7 +331,7 @@ int main(const int argc, const char *argv[]) try file_index_path_ptr + shared_layout_ptr->GetBlockSize(SharedDataLayout::FILE_INDEX_PATH), 0); - std::copy(file_index_path.begin(), file_index_path.end(), file_index_path_ptr); + std::copy(absolute_file_index_path.string().begin(), absolute_file_index_path.string().end(), file_index_path_ptr); // Loading street names unsigned *name_offsets_ptr = shared_layout_ptr->GetBlockPtr( @@ -417,39 +374,22 @@ int main(const int argc, const char *argv[]) try unsigned *name_id_ptr = shared_layout_ptr->GetBlockPtr( shared_memory_ptr, SharedDataLayout::NAME_ID_LIST); - TravelMode *travel_mode_ptr = shared_layout_ptr->GetBlockPtr( - shared_memory_ptr, SharedDataLayout::TRAVEL_MODE); + extractor::TravelMode *travel_mode_ptr = + shared_layout_ptr->GetBlockPtr(shared_memory_ptr, + SharedDataLayout::TRAVEL_MODE); - TurnInstruction *turn_instructions_ptr = shared_layout_ptr->GetBlockPtr( - shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION); + extractor::guidance::TurnInstruction *turn_instructions_ptr = + shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION); - unsigned *geometries_indicator_ptr = shared_layout_ptr->GetBlockPtr( - shared_memory_ptr, SharedDataLayout::GEOMETRIES_INDICATORS); - - OriginalEdgeData current_edge_data; + extractor::OriginalEdgeData current_edge_data; for (unsigned i = 0; i < number_of_original_edges; ++i) { - edges_input_stream.read((char *)&(current_edge_data), sizeof(OriginalEdgeData)); + edges_input_stream.read((char *)&(current_edge_data), sizeof(extractor::OriginalEdgeData)); via_node_ptr[i] = current_edge_data.via_node; name_id_ptr[i] = current_edge_data.name_id; travel_mode_ptr[i] = current_edge_data.travel_mode; turn_instructions_ptr[i] = current_edge_data.turn_instruction; - - const unsigned bucket = i / 32; - const unsigned offset = i % 32; - const unsigned value = [&] - { - unsigned return_value = 0; - if (0 != offset) - { - return_value = geometries_indicator_ptr[bucket]; - } - return return_value; - }(); - if (current_edge_data.compressed_geometry) - { - geometries_indicator_ptr[bucket] = (value | (1 << offset)); - } } edges_input_stream.close(); @@ -468,8 +408,9 @@ int main(const int argc, const char *argv[]) try (char *)geometries_index_ptr, shared_layout_ptr->GetBlockSize(SharedDataLayout::GEOMETRIES_INDEX)); } - unsigned *geometries_list_ptr = shared_layout_ptr->GetBlockPtr( - shared_memory_ptr, SharedDataLayout::GEOMETRIES_LIST); + extractor::CompressedEdgeContainer::CompressedEdge *geometries_list_ptr = + shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::GEOMETRIES_LIST); geometry_input_stream.read((char *)&temporary_value, sizeof(unsigned)); BOOST_ASSERT(temporary_value == @@ -482,16 +423,52 @@ int main(const int argc, const char *argv[]) try shared_layout_ptr->GetBlockSize(SharedDataLayout::GEOMETRIES_LIST)); } - // Loading list of coordinates - FixedPointCoordinate *coordinates_ptr = - shared_layout_ptr->GetBlockPtr( - shared_memory_ptr, SharedDataLayout::COORDINATE_LIST); + // load datasource information (if it exists) + uint8_t *datasources_list_ptr = shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::DATASOURCES_LIST); + if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCES_LIST) > 0) + { + geometry_datasource_input_stream.read( + reinterpret_cast(datasources_list_ptr), + shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCES_LIST)); + } - QueryNode current_node; + // load datasource name information (if it exists) + char *datasource_name_data_ptr = shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_DATA); + if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_DATA) > 0) + { + std::cout << "Copying " << (m_datasource_name_data.end() - m_datasource_name_data.begin()) + << " chars into name data ptr\n"; + std::copy(m_datasource_name_data.begin(), m_datasource_name_data.end(), + datasource_name_data_ptr); + } + + auto datasource_name_offsets_ptr = shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_OFFSETS); + if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_OFFSETS) > 0) + { + std::copy(m_datasource_name_offsets.begin(), m_datasource_name_offsets.end(), + datasource_name_offsets_ptr); + } + + auto datasource_name_lengths_ptr = shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::DATASOURCE_NAME_LENGTHS); + if (shared_layout_ptr->GetBlockSize(SharedDataLayout::DATASOURCE_NAME_LENGTHS) > 0) + { + std::copy(m_datasource_name_lengths.begin(), m_datasource_name_lengths.end(), + datasource_name_lengths_ptr); + } + + // Loading list of coordinates + util::Coordinate *coordinates_ptr = shared_layout_ptr->GetBlockPtr( + shared_memory_ptr, SharedDataLayout::COORDINATE_LIST); + + extractor::QueryNode current_node; for (unsigned i = 0; i < coordinate_list_size; ++i) { - nodes_input_stream.read((char *)¤t_node, sizeof(QueryNode)); - coordinates_ptr[i] = FixedPointCoordinate(current_node.lat, current_node.lon); + nodes_input_stream.read((char *)¤t_node, sizeof(extractor::QueryNode)); + coordinates_ptr[i] = util::Coordinate(current_node.lon, current_node.lat); } nodes_input_stream.close(); @@ -536,7 +513,7 @@ int main(const int argc, const char *argv[]) try return return_value; }(); - core_marker_ptr[bucket] = (value | (1 << offset)); + core_marker_ptr[bucket] = (value | (1u << offset)); } } @@ -561,9 +538,18 @@ int main(const int argc, const char *argv[]) try } hsgr_input_stream.close(); + // load profile properties + auto profile_properties_ptr = shared_layout_ptr->GetBlockPtr(shared_memory_ptr, SharedDataLayout::PROPERTIES); + boost::filesystem::ifstream profile_properties_stream(config.properties_path); + if (!profile_properties_stream) + { + util::exception("Could not open " + config.properties_path.string() + " for reading!"); + } + profile_properties_stream.read(reinterpret_cast(profile_properties_ptr), sizeof(extractor::ProfileProperties)); + // acquire lock SharedMemory *data_type_memory = - SharedMemoryFactory::Get(CURRENT_REGIONS, sizeof(SharedDataTimestamp), true, false); + makeSharedMemory(CURRENT_REGIONS, sizeof(SharedDataTimestamp), true, false); SharedDataTimestamp *data_timestamp_ptr = static_cast(data_type_memory->Ptr()); @@ -579,20 +565,11 @@ int main(const int argc, const char *argv[]) try data_timestamp_ptr->layout = layout_region; data_timestamp_ptr->data = data_region; data_timestamp_ptr->timestamp += 1; - delete_region(previous_data_region); - delete_region(previous_layout_region); - SimpleLogger().Write() << "all data loaded"; + deleteRegion(previous_data_region); + deleteRegion(previous_layout_region); + util::SimpleLogger().Write() << "all data loaded"; - shared_layout_ptr->PrintInformation(); + return EXIT_SUCCESS; } -catch (const std::bad_alloc &e) -{ - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - SimpleLogger().Write(logWARNING) << "Please provide more memory or disable locking the virtual " - "address space (note: this makes OSRM swap, i.e. slow)"; - return EXIT_FAILURE; } -catch (const std::exception &e) -{ - SimpleLogger().Write(logWARNING) << "caught exception: " << e.what(); } diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp new file mode 100644 index 000000000..6ade34a0c --- /dev/null +++ b/src/storage/storage_config.cpp @@ -0,0 +1,38 @@ +#include "storage/storage_config.hpp" + +#include + +namespace osrm +{ +namespace storage +{ + +StorageConfig::StorageConfig(const boost::filesystem::path &base) + : ram_index_path{base.string() + ".ramIndex"}, file_index_path{base.string() + ".fileIndex"}, + hsgr_data_path{base.string() + ".hsgr"}, nodes_data_path{base.string() + ".nodes"}, + edges_data_path{base.string() + ".edges"}, core_data_path{base.string() + ".core"}, + geometries_path{base.string() + ".geometry"}, timestamp_path{base.string() + ".timestamp"}, + datasource_names_path{base.string() + ".datasource_names"}, + datasource_indexes_path{base.string() + ".datasource_indexes"}, + names_data_path{base.string() + ".names"}, + properties_path{base.string() + ".properties"} +{ +} + +bool StorageConfig::IsValid() const +{ + return boost::filesystem::is_regular_file(ram_index_path) && + boost::filesystem::is_regular_file(file_index_path) && + boost::filesystem::is_regular_file(hsgr_data_path) && + boost::filesystem::is_regular_file(nodes_data_path) && + boost::filesystem::is_regular_file(edges_data_path) && + boost::filesystem::is_regular_file(core_data_path) && + boost::filesystem::is_regular_file(geometries_path) && + boost::filesystem::is_regular_file(timestamp_path) && + boost::filesystem::is_regular_file(datasource_names_path) && + boost::filesystem::is_regular_file(datasource_indexes_path) && + boost::filesystem::is_regular_file(names_data_path) && + boost::filesystem::is_regular_file(properties_path); +} +} +} diff --git a/tools/.gitignore b/src/tools/.gitignore similarity index 100% rename from tools/.gitignore rename to src/tools/.gitignore diff --git a/src/tools/components.cpp b/src/tools/components.cpp new file mode 100644 index 000000000..45131a85c --- /dev/null +++ b/src/tools/components.cpp @@ -0,0 +1,233 @@ +#include "util/typedefs.hpp" +#include "extractor/tarjan_scc.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/dynamic_graph.hpp" +#include "util/static_graph.hpp" +#include "util/fingerprint.hpp" +#include "util/graph_loader.hpp" +#include "util/make_unique.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" + +#include + +#if defined(__APPLE__) || defined(_WIN32) +#include +#include +#else +#include +#include +#endif + +#include "osrm/coordinate.hpp" + +#include +#include +#include +#include + +namespace osrm +{ +namespace tools +{ + +struct TarjanEdgeData +{ + TarjanEdgeData() : distance(INVALID_EDGE_WEIGHT), name_id(INVALID_NAMEID) {} + TarjanEdgeData(unsigned distance, unsigned name_id) : distance(distance), name_id(name_id) {} + unsigned distance; + unsigned name_id; +}; + +using TarjanGraph = util::StaticGraph; +using TarjanEdge = TarjanGraph::InputEdge; + +void deleteFileIfExists(const std::string &file_name) +{ + if (boost::filesystem::exists(file_name)) + { + boost::filesystem::remove(file_name); + } +} + +std::size_t loadGraph(const char *path, + std::vector &coordinate_list, + std::vector &graph_edge_list) +{ + std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); + if (!input_stream.is_open()) + { + throw util::exception("Cannot open osrm file"); + } + + // load graph data + std::vector edge_list; + std::vector traffic_light_node_list; + std::vector barrier_node_list; + + auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, + traffic_light_node_list, coordinate_list); + + util::loadEdgesFromFile(input_stream, edge_list); + + traffic_light_node_list.clear(); + traffic_light_node_list.shrink_to_fit(); + + // Building an node-based graph + for (const auto &input_edge : edge_list) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + if (input_edge.forward) + { + graph_edge_list.emplace_back(input_edge.source, input_edge.target, + (std::max)(input_edge.weight, 1), input_edge.name_id); + } + if (input_edge.backward) + { + graph_edge_list.emplace_back(input_edge.target, input_edge.source, + (std::max)(input_edge.weight, 1), input_edge.name_id); + } + } + + return number_of_nodes; +} +} +} + +int main(int argc, char *argv[]) try +{ + std::vector coordinate_list; + osrm::util::LogPolicy::GetInstance().Unmute(); + + // enable logging + if (argc < 2) + { + osrm::util::SimpleLogger().Write(logWARNING) << "usage:\n" << argv[0] << " "; + return EXIT_FAILURE; + } + + std::vector graph_edge_list; + auto number_of_nodes = osrm::tools::loadGraph(argv[1], coordinate_list, graph_edge_list); + + tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); + const auto graph = std::make_shared(number_of_nodes, graph_edge_list); + graph_edge_list.clear(); + graph_edge_list.shrink_to_fit(); + + osrm::util::SimpleLogger().Write() << "Starting SCC graph traversal"; + + auto tarjan = + osrm::util::make_unique>(graph); + tarjan->run(); + osrm::util::SimpleLogger().Write() << "identified: " << tarjan->get_number_of_components() + << " many components"; + osrm::util::SimpleLogger().Write() << "identified " << tarjan->get_size_one_count() + << " size 1 SCCs"; + + // output + TIMER_START(SCC_RUN_SETUP); + + // remove files from previous run if exist + osrm::tools::deleteFileIfExists("component.dbf"); + osrm::tools::deleteFileIfExists("component.shx"); + osrm::tools::deleteFileIfExists("component.shp"); + + osrm::util::Percent percentage(graph->GetNumberOfNodes()); + + OGRRegisterAll(); + + const char *psz_driver_name = "ESRI Shapefile"; + auto *po_driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(psz_driver_name); + if (nullptr == po_driver) + { + throw osrm::util::exception("ESRI Shapefile driver not available"); + } + auto *po_datasource = po_driver->CreateDataSource("component.shp", nullptr); + + if (nullptr == po_datasource) + { + throw osrm::util::exception("Creation of output file failed"); + } + + auto *po_srs = new OGRSpatialReference(); + po_srs->importFromEPSG(4326); + + auto *po_layer = po_datasource->CreateLayer("component", po_srs, wkbLineString, nullptr); + + if (nullptr == po_layer) + { + throw osrm::util::exception("Layer creation failed."); + } + TIMER_STOP(SCC_RUN_SETUP); + osrm::util::SimpleLogger().Write() << "shapefile setup took " + << TIMER_MSEC(SCC_RUN_SETUP) / 1000. << "s"; + + uint64_t total_network_length = 0; + percentage.reinit(graph->GetNumberOfNodes()); + TIMER_START(SCC_OUTPUT); + for (const NodeID source : osrm::util::irange(0u, graph->GetNumberOfNodes())) + { + percentage.printIncrement(); + for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) + { + const auto target = graph->GetTarget(current_edge); + + if (source < target || SPECIAL_EDGEID == graph->FindEdge(target, source)) + { + total_network_length += + 100 * osrm::util::coordinate_calculation::greatCircleDistance( + coordinate_list[source], coordinate_list[target]); + + BOOST_ASSERT(current_edge != SPECIAL_EDGEID); + BOOST_ASSERT(source != SPECIAL_NODEID); + BOOST_ASSERT(target != SPECIAL_NODEID); + + const unsigned size_of_containing_component = + std::min(tarjan->get_component_size(tarjan->get_component_id(source)), + tarjan->get_component_size(tarjan->get_component_id(target))); + + // edges that end on bollard nodes may actually be in two distinct components + if (size_of_containing_component < 1000) + { + OGRLineString line_string; + line_string.addPoint( + static_cast(osrm::util::toFloating(coordinate_list[source].lon)), + static_cast(osrm::util::toFloating(coordinate_list[source].lat))); + line_string.addPoint( + static_cast(osrm::util::toFloating(coordinate_list[target].lon)), + static_cast(osrm::util::toFloating(coordinate_list[target].lat))); + + OGRFeature *po_feature = OGRFeature::CreateFeature(po_layer->GetLayerDefn()); + + po_feature->SetGeometry(&line_string); + if (OGRERR_NONE != po_layer->CreateFeature(po_feature)) + { + throw osrm::util::exception("Failed to create feature in shapefile."); + } + OGRFeature::DestroyFeature(po_feature); + } + } + } + } + OGRSpatialReference::DestroySpatialReference(po_srs); + OGRDataSource::DestroyDataSource(po_datasource); + TIMER_STOP(SCC_OUTPUT); + osrm::util::SimpleLogger().Write() + << "generating output took: " << TIMER_MSEC(SCC_OUTPUT) / 1000. << "s"; + + osrm::util::SimpleLogger().Write() + << "total network distance: " << static_cast(total_network_length / 100 / 1000.) + << " km"; + + osrm::util::SimpleLogger().Write() << "finished component analysis"; + return EXIT_SUCCESS; +} +catch (const std::exception &e) +{ + osrm::util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/contract.cpp b/src/tools/contract.cpp new file mode 100644 index 000000000..c9460db18 --- /dev/null +++ b/src/tools/contract.cpp @@ -0,0 +1,160 @@ +#include "contractor/contractor.hpp" +#include "contractor/contractor_config.hpp" +#include "util/simple_logger.hpp" +#include "util/version.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace osrm; + +enum class return_code : unsigned +{ + ok, + fail, + exit +}; + +return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig &contractor_config) +{ + // declare a group of options that will be allowed only on command line + boost::program_options::options_description generic_options("Options"); + generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message"); + + // declare a group of options that will be allowed on command line + boost::program_options::options_description config_options("Configuration"); + config_options.add_options()( + "threads,t", + boost::program_options::value(&contractor_config.requested_num_threads) + ->default_value(tbb::task_scheduler_init::default_num_threads()), + "Number of threads to use")( + "core,k", + boost::program_options::value(&contractor_config.core_factor)->default_value(1.0), + "Percentage of the graph (in vertices) to contract [0..1]")( + "segment-speed-file", boost::program_options::value>( + &contractor_config.segment_speed_lookup_paths) + ->composing(), + "Lookup files containing nodeA, nodeB, speed data to adjust edge weights")( + "level-cache,o", boost::program_options::value(&contractor_config.use_cached_priority) + ->default_value(false), + "Use .level file to retain the contaction level for each node from the last run."); + + // hidden options, will be allowed on command line, but will not be shown to the user + boost::program_options::options_description hidden_options("Hidden options"); + hidden_options.add_options()("input,i", boost::program_options::value( + &contractor_config.osrm_input_path), + "Input file in .osm, .osm.bz2 or .osm.pbf format"); + + // positional option + boost::program_options::positional_options_description positional_options; + positional_options.add("input", 1); + + // combine above options for parsing + boost::program_options::options_description cmdline_options; + cmdline_options.add(generic_options).add(config_options).add(hidden_options); + + const auto* executable = argv[0]; + boost::program_options::options_description visible_options( + "Usage: " + boost::filesystem::path(executable).filename().string() + " [options]"); + visible_options.add(generic_options).add(config_options); + + // parse command line options + boost::program_options::variables_map option_variables; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv) + .options(cmdline_options) + .positional(positional_options) + .run(), + option_variables); + + if (option_variables.count("version")) + { + util::SimpleLogger().Write() << OSRM_VERSION; + return return_code::exit; + } + + if (option_variables.count("help")) + { + util::SimpleLogger().Write() << visible_options; + return return_code::exit; + } + + boost::program_options::notify(option_variables); + + if (!option_variables.count("input")) + { + util::SimpleLogger().Write() << visible_options; + return return_code::fail; + } + + return return_code::ok; +} + +int main(int argc, char *argv[]) try +{ + util::LogPolicy::GetInstance().Unmute(); + contractor::ContractorConfig contractor_config; + + const return_code result = parseArguments(argc, argv, contractor_config); + + if (return_code::fail == result) + { + return EXIT_FAILURE; + } + + if (return_code::exit == result) + { + return EXIT_SUCCESS; + } + + contractor_config.UseDefaultOutputNames(); + + if (1 > contractor_config.requested_num_threads) + { + util::SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; + return EXIT_FAILURE; + } + + const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); + + if (recommended_num_threads != contractor_config.requested_num_threads) + { + util::SimpleLogger().Write(logWARNING) + << "The recommended number of threads is " << recommended_num_threads + << "! This setting may have performance side-effects."; + } + + if (!boost::filesystem::is_regular_file(contractor_config.osrm_input_path)) + { + util::SimpleLogger().Write(logWARNING) + << "Input file " << contractor_config.osrm_input_path.string() << " not found!"; + return EXIT_FAILURE; + } + + util::SimpleLogger().Write() << "Input file: " + << contractor_config.osrm_input_path.filename().string(); + util::SimpleLogger().Write() << "Threads: " << contractor_config.requested_num_threads; + + tbb::task_scheduler_init init(contractor_config.requested_num_threads); + + return contractor::Contractor(contractor_config).Run(); +} +catch (const std::bad_alloc &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + util::SimpleLogger().Write(logWARNING) + << "Please provide more memory or consider using a larger swapfile"; + return EXIT_FAILURE; +} +catch (const std::exception &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp new file mode 100644 index 000000000..672ada2fb --- /dev/null +++ b/src/tools/extract.cpp @@ -0,0 +1,161 @@ +#include "extractor/extractor.hpp" +#include "extractor/extractor_config.hpp" +#include "util/simple_logger.hpp" +#include "util/version.hpp" + +#include + +#include +#include + +#include +#include +#include + +using namespace osrm; + +enum class return_code : unsigned +{ + ok, + fail, + exit +}; + +return_code parseArguments(int argc, char *argv[], extractor::ExtractorConfig &extractor_config) +{ + // declare a group of options that will be allowed only on command line + boost::program_options::options_description generic_options("Options"); + generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message"); + + // declare a group of options that will be allowed both on command line + boost::program_options::options_description config_options("Configuration"); + config_options.add_options()( + "profile,p", + boost::program_options::value(&extractor_config.profile_path) + ->default_value("profile.lua"), + "Path to LUA routing profile")( + "threads,t", + boost::program_options::value(&extractor_config.requested_num_threads) + ->default_value(tbb::task_scheduler_init::default_num_threads()), + "Number of threads to use")( + "generate-edge-lookup", + boost::program_options::value(&extractor_config.generate_edge_lookup) + ->implicit_value(true) + ->default_value(false), + "Generate a lookup table for internal edge-expanded-edge IDs to OSM node pairs")( + "small-component-size", + boost::program_options::value(&extractor_config.small_component_size) + ->default_value(1000), + "Number of nodes required before a strongly-connected-componennt is considered big " + "(affects nearest neighbor snapping)"); + + // hidden options, will be allowed on command line, but will not be + // shown to the user + boost::program_options::options_description hidden_options("Hidden options"); + hidden_options.add_options()("input,i", boost::program_options::value( + &extractor_config.input_path), + "Input file in .osm, .osm.bz2 or .osm.pbf format"); + + // positional option + boost::program_options::positional_options_description positional_options; + positional_options.add("input", 1); + + // combine above options for parsing + boost::program_options::options_description cmdline_options; + cmdline_options.add(generic_options).add(config_options).add(hidden_options); + + const auto* executable = argv[0]; + boost::program_options::options_description visible_options( + boost::filesystem::path(executable).filename().string() + " [options]"); + visible_options.add(generic_options).add(config_options); + + // parse command line options + try + { + boost::program_options::variables_map option_variables; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv) + .options(cmdline_options) + .positional(positional_options) + .run(), + option_variables); + if (option_variables.count("version")) + { + util::SimpleLogger().Write() << OSRM_VERSION; + return return_code::exit; + } + + if (option_variables.count("help")) + { + util::SimpleLogger().Write() << visible_options; + return return_code::exit; + } + + boost::program_options::notify(option_variables); + + if (!option_variables.count("input")) + { + util::SimpleLogger().Write() << visible_options; + return return_code::exit; + } + } + catch (std::exception &e) + { + util::SimpleLogger().Write(logWARNING) << e.what(); + return return_code::fail; + } + + return return_code::ok; +} + +int main(int argc, char *argv[]) try +{ + util::LogPolicy::GetInstance().Unmute(); + extractor::ExtractorConfig extractor_config; + + const auto result = parseArguments(argc, argv, extractor_config); + + if (return_code::fail == result) + { + return EXIT_FAILURE; + } + + if (return_code::exit == result) + { + return EXIT_SUCCESS; + } + + extractor_config.UseDefaultOutputNames(); + + if (1 > extractor_config.requested_num_threads) + { + util::SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; + return EXIT_FAILURE; + } + + if (!boost::filesystem::is_regular_file(extractor_config.input_path)) + { + util::SimpleLogger().Write(logWARNING) + << "Input file " << extractor_config.input_path.string() << " not found!"; + return EXIT_FAILURE; + } + + if (!boost::filesystem::is_regular_file(extractor_config.profile_path)) + { + util::SimpleLogger().Write(logWARNING) + << "Profile " << extractor_config.profile_path.string() << " not found!"; + return EXIT_FAILURE; + } + return extractor::Extractor(extractor_config).run(); +} +catch (const std::bad_alloc &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + util::SimpleLogger().Write(logWARNING) + << "Please provide more memory or consider using a larger swapfile"; + return EXIT_FAILURE; +} +catch (const std::exception &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/io-benchmark.cpp b/src/tools/io-benchmark.cpp new file mode 100644 index 000000000..0030740a2 --- /dev/null +++ b/src/tools/io-benchmark.cpp @@ -0,0 +1,325 @@ +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/timing_util.hpp" + +#include +#include + +#include +#include +#include +#ifdef __linux__ +#include +#endif + +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace tools +{ + +const unsigned NUMBER_OF_ELEMENTS = 268435456; + +struct Statistics +{ + double min, max, med, mean, dev; +}; + +void runStatistics(std::vector &timings_vector, Statistics &stats) +{ + std::sort(timings_vector.begin(), timings_vector.end()); + stats.min = timings_vector.front(); + stats.max = timings_vector.back(); + stats.med = timings_vector[timings_vector.size() / 2]; + double primary_sum = std::accumulate(timings_vector.begin(), timings_vector.end(), 0.0); + stats.mean = primary_sum / timings_vector.size(); + + double primary_sq_sum = std::inner_product(timings_vector.begin(), timings_vector.end(), + timings_vector.begin(), 0.0); + stats.dev = std::sqrt(primary_sq_sum / timings_vector.size() - (stats.mean * stats.mean)); +} +} +} + +boost::filesystem::path test_path; + +int main(int argc, char *argv[]) try +{ + +#ifdef __FreeBSD__ + osrm::util::SimpleLogger().Write() << "Not supported on FreeBSD"; + return 0; +#endif +#ifdef _WIN32 + osrm::util::SimpleLogger().Write() << "Not supported on Windows"; + return 0; +#else + + osrm::util::LogPolicy::GetInstance().Unmute(); + if (1 == argc) + { + osrm::util::SimpleLogger().Write(logWARNING) << "usage: " << argv[0] << " /path/on/device"; + return -1; + } + + test_path = boost::filesystem::path(argv[1]); + test_path /= "osrm.tst"; + osrm::util::SimpleLogger().Write(logDEBUG) << "temporary file: " << test_path.string(); + + // create files for testing + if (2 == argc) + { + // create file to test + if (boost::filesystem::exists(test_path)) + { + throw osrm::util::exception("Data file already exists"); + } + + int *random_array = new int[osrm::tools::NUMBER_OF_ELEMENTS]; + std::generate(random_array, random_array + osrm::tools::NUMBER_OF_ELEMENTS, std::rand); +#ifdef __APPLE__ + FILE *fd = fopen(test_path.string().c_str(), "w"); + fcntl(fileno(fd), F_NOCACHE, 1); + fcntl(fileno(fd), F_RDAHEAD, 0); + TIMER_START(write_1gb); + write(fileno(fd), (char *)random_array, osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)); + TIMER_STOP(write_1gb); + fclose(fd); +#endif +#ifdef __linux__ + int file_desc = + open(test_path.string().c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_SYNC, S_IRWXU); + if (-1 == file_desc) + { + throw osrm::util::exception("Could not open random data file"); + } + TIMER_START(write_1gb); + int ret = + write(file_desc, random_array, osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)); + if (0 > ret) + { + throw osrm::util::exception("could not write random data file"); + } + TIMER_STOP(write_1gb); + close(file_desc); +#endif + delete[] random_array; + osrm::util::SimpleLogger().Write(logDEBUG) << "writing raw 1GB took " + << TIMER_SEC(write_1gb) << "s"; + osrm::util::SimpleLogger().Write() << "raw write performance: " << std::setprecision(5) + << std::fixed << 1024 * 1024 / TIMER_SEC(write_1gb) + << "MB/sec"; + + osrm::util::SimpleLogger().Write(logDEBUG) + << "finished creation of random data. Flush disk cache now!"; + } + else + { + // Run Non-Cached I/O benchmarks + if (!boost::filesystem::exists(test_path)) + { + throw osrm::util::exception("data file does not exist"); + } + + // volatiles do not get optimized + osrm::tools::Statistics stats; + +#ifdef __APPLE__ + volatile unsigned single_block[1024]; + char *raw_array = new char[osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)]; + FILE *fd = fopen(test_path.string().c_str(), "r"); + fcntl(fileno(fd), F_NOCACHE, 1); + fcntl(fileno(fd), F_RDAHEAD, 0); +#endif +#ifdef __linux__ + char *single_block = (char *)memalign(512, 1024 * sizeof(unsigned)); + + int file_desc = open(test_path.string().c_str(), O_RDONLY | O_DIRECT | O_SYNC); + if (-1 == file_desc) + { + osrm::util::SimpleLogger().Write(logDEBUG) << "opened, error: " << strerror(errno); + return -1; + } + char *raw_array = (char *)memalign(512, osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)); +#endif + TIMER_START(read_1gb); +#ifdef __APPLE__ + read(fileno(fd), raw_array, osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)); + close(fileno(fd)); + fd = fopen(test_path.string().c_str(), "r"); +#endif +#ifdef __linux__ + int ret = read(file_desc, raw_array, osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned)); + osrm::util::SimpleLogger().Write(logDEBUG) << "read " << ret + << " bytes, error: " << strerror(errno); + close(file_desc); + file_desc = open(test_path.string().c_str(), O_RDONLY | O_DIRECT | O_SYNC); + osrm::util::SimpleLogger().Write(logDEBUG) << "opened, error: " << strerror(errno); +#endif + TIMER_STOP(read_1gb); + + osrm::util::SimpleLogger().Write(logDEBUG) << "reading raw 1GB took " << TIMER_SEC(read_1gb) + << "s"; + osrm::util::SimpleLogger().Write() << "raw read performance: " << std::setprecision(5) + << std::fixed << 1024 * 1024 / TIMER_SEC(read_1gb) + << "MB/sec"; + + std::vector timing_results_raw_random; + osrm::util::SimpleLogger().Write(logDEBUG) << "running 1000 random I/Os of 4KB"; + +#ifdef __APPLE__ + fseek(fd, 0, SEEK_SET); +#endif +#ifdef __linux__ + lseek(file_desc, 0, SEEK_SET); +#endif + // make 1000 random access, time each I/O seperately + unsigned number_of_blocks = (osrm::tools::NUMBER_OF_ELEMENTS * sizeof(unsigned) - 1) / 4096; + std::random_device rd; + std::default_random_engine e1(rd()); + std::uniform_int_distribution uniform_dist(0, number_of_blocks - 1); + for (unsigned i = 0; i < 1000; ++i) + { + unsigned block_to_read = uniform_dist(e1); + off_t current_offset = block_to_read * 4096; + TIMER_START(random_access); +#ifdef __APPLE__ + int ret1 = fseek(fd, current_offset, SEEK_SET); + int ret2 = read(fileno(fd), (char *)&single_block[0], 4096); +#endif + +#ifdef __FreeBSD__ + int ret1 = 0; + int ret2 = 0; +#endif + +#ifdef __linux__ + int ret1 = lseek(file_desc, current_offset, SEEK_SET); + int ret2 = read(file_desc, (char *)single_block, 4096); +#endif + TIMER_STOP(random_access); + if (((off_t)-1) == ret1) + { + osrm::util::SimpleLogger().Write(logWARNING) << "offset: " << current_offset; + osrm::util::SimpleLogger().Write(logWARNING) << "seek error " << strerror(errno); + throw osrm::util::exception("seek error"); + } + if (-1 == ret2) + { + osrm::util::SimpleLogger().Write(logWARNING) << "offset: " << current_offset; + osrm::util::SimpleLogger().Write(logWARNING) << "read error " << strerror(errno); + throw osrm::util::exception("read error"); + } + timing_results_raw_random.push_back(TIMER_SEC(random_access)); + } + + // Do statistics + osrm::util::SimpleLogger().Write(logDEBUG) << "running raw random I/O statistics"; + std::ofstream random_csv("random.csv", std::ios::trunc); + for (unsigned i = 0; i < timing_results_raw_random.size(); ++i) + { + random_csv << i << ", " << timing_results_raw_random[i] << std::endl; + } + osrm::tools::runStatistics(timing_results_raw_random, stats); + + osrm::util::SimpleLogger().Write() << "raw random I/O: " << std::setprecision(5) + << std::fixed << "min: " << stats.min << "ms, " + << "mean: " << stats.mean << "ms, " + << "med: " << stats.med << "ms, " + << "max: " << stats.max << "ms, " + << "dev: " << stats.dev << "ms"; + + std::vector timing_results_raw_seq; +#ifdef __APPLE__ + fseek(fd, 0, SEEK_SET); +#endif +#ifdef __linux__ + lseek(file_desc, 0, SEEK_SET); +#endif + + // read every 100th block + for (unsigned i = 0; i < 1000; ++i) + { + off_t current_offset = i * 4096; + TIMER_START(read_every_100); +#ifdef __APPLE__ + int ret1 = fseek(fd, current_offset, SEEK_SET); + int ret2 = read(fileno(fd), (char *)&single_block, 4096); +#endif + +#ifdef __FreeBSD__ + int ret1 = 0; + int ret2 = 0; +#endif + +#ifdef __linux__ + int ret1 = lseek(file_desc, current_offset, SEEK_SET); + + int ret2 = read(file_desc, (char *)single_block, 4096); +#endif + TIMER_STOP(read_every_100); + if (((off_t)-1) == ret1) + { + osrm::util::SimpleLogger().Write(logWARNING) << "offset: " << current_offset; + osrm::util::SimpleLogger().Write(logWARNING) << "seek error " << strerror(errno); + throw osrm::util::exception("seek error"); + } + if (-1 == ret2) + { + osrm::util::SimpleLogger().Write(logWARNING) << "offset: " << current_offset; + osrm::util::SimpleLogger().Write(logWARNING) << "read error " << strerror(errno); + throw osrm::util::exception("read error"); + } + timing_results_raw_seq.push_back(TIMER_SEC(read_every_100)); + } +#ifdef __APPLE__ + fclose(fd); + // free(single_element); + free(raw_array); +// free(single_block); +#endif +#ifdef __linux__ + close(file_desc); +#endif + // Do statistics + osrm::util::SimpleLogger().Write(logDEBUG) << "running sequential I/O statistics"; + // print simple statistics: min, max, median, variance + std::ofstream seq_csv("sequential.csv", std::ios::trunc); + for (unsigned i = 0; i < timing_results_raw_seq.size(); ++i) + { + seq_csv << i << ", " << timing_results_raw_seq[i] << std::endl; + } + osrm::tools::runStatistics(timing_results_raw_seq, stats); + osrm::util::SimpleLogger().Write() << "raw sequential I/O: " << std::setprecision(5) + << std::fixed << "min: " << stats.min << "ms, " + << "mean: " << stats.mean << "ms, " + << "med: " << stats.med << "ms, " + << "max: " << stats.max << "ms, " + << "dev: " << stats.dev << "ms"; + + if (boost::filesystem::exists(test_path)) + { + boost::filesystem::remove(test_path); + osrm::util::SimpleLogger().Write(logDEBUG) << "removing temporary files"; + } + } + return EXIT_SUCCESS; +#endif +} +catch (const std::exception &e) +{ + osrm::util::SimpleLogger().Write(logWARNING) << "caught exception: " << e.what(); + osrm::util::SimpleLogger().Write(logWARNING) << "cleaning up, and exiting"; + if (boost::filesystem::exists(test_path)) + { + boost::filesystem::remove(test_path); + osrm::util::SimpleLogger().Write(logWARNING) << "removing temporary files"; + } + return EXIT_FAILURE; +} diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp new file mode 100644 index 000000000..831a9cd25 --- /dev/null +++ b/src/tools/routed.cpp @@ -0,0 +1,349 @@ +#include "server/server.hpp" +#include "util/make_unique.hpp" +#include "util/simple_logger.hpp" +#include "util/version.hpp" + +#include "osrm/osrm.hpp" +#include "osrm/engine_config.hpp" +#include "osrm/storage_config.hpp" + +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +boost::function0 console_ctrl_function; + +BOOL WINAPI console_ctrl_handler(DWORD ctrl_type) +{ + switch (ctrl_type) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_SHUTDOWN_EVENT: + console_ctrl_function(); + return TRUE; + default: + return FALSE; + } +} +#endif + +using namespace osrm; + +const static unsigned INIT_OK_START_ENGINE = 0; +const static unsigned INIT_OK_DO_NOT_START_ENGINE = 1; +const static unsigned INIT_FAILED = -1; + +// generate boost::program_options object for the routing part +inline unsigned +generateServerProgramOptions(const int argc, + const char *argv[], + boost::filesystem::path &base_path, + std::string &ip_address, + int &ip_port, + int &requested_num_threads, + bool &use_shared_memory, + bool &trial, + int &max_locations_trip, + int &max_locations_viaroute, + int &max_locations_distance_table, + int &max_locations_map_matching) +{ + using boost::program_options::value; + using boost::filesystem::path; + + // declare a group of options that will be allowed only on command line + boost::program_options::options_description generic_options("Options"); + generic_options.add_options() // + ("version,v", "Show version")("help,h", "Show this help message") // + ("trial", value(&trial)->implicit_value(true), "Quit after initialization"); + + // declare a group of options that will be allowed on command line + boost::program_options::options_description config_options("Configuration"); + config_options.add_options() // + ("ip,i", value(&ip_address)->default_value("0.0.0.0"), + "IP address") // + ("port,p", value(&ip_port)->default_value(5000), + "TCP/IP port") // + ("threads,t", value(&requested_num_threads)->default_value(8), + "Number of threads to use") // + ("shared-memory,s", + value(&use_shared_memory)->implicit_value(true)->default_value(false), + "Load data from shared memory") // + ("max-viaroute-size", value(&max_locations_viaroute)->default_value(500), + "Max. locations supported in viaroute query") // + ("max-trip-size", value(&max_locations_trip)->default_value(100), + "Max. locations supported in trip query") // + ("max-table-size", value(&max_locations_distance_table)->default_value(100), + "Max. locations supported in distance table query") // + ("max-matching-size", value(&max_locations_map_matching)->default_value(100), + "Max. locations supported in map matching query"); + + // hidden options, will be allowed on command line, but will not be shown to the user + boost::program_options::options_description hidden_options("Hidden options"); + hidden_options.add_options()("base,b", value(&base_path), + "base path to .osrm file"); + + // positional option + boost::program_options::positional_options_description positional_options; + positional_options.add("base", 1); + + // combine above options for parsing + boost::program_options::options_description cmdline_options; + cmdline_options.add(generic_options).add(config_options).add(hidden_options); + + const auto* executable = argv[0]; + boost::program_options::options_description visible_options( + boost::filesystem::path(executable).filename().string() + " []"); + visible_options.add(generic_options).add(config_options); + + // parse command line options + boost::program_options::variables_map option_variables; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv) + .options(cmdline_options) + .positional(positional_options) + .run(), + option_variables); + + if (option_variables.count("version")) + { + util::SimpleLogger().Write() << OSRM_VERSION; + return INIT_OK_DO_NOT_START_ENGINE; + } + + if (option_variables.count("help")) + { + util::SimpleLogger().Write() << visible_options; + return INIT_OK_DO_NOT_START_ENGINE; + } + + boost::program_options::notify(option_variables); + + if (!use_shared_memory && option_variables.count("base")) + { + return INIT_OK_START_ENGINE; + } + else if (use_shared_memory && !option_variables.count("base")) + { + return INIT_OK_START_ENGINE; + } + else if (use_shared_memory && option_variables.count("base")) + { + util::SimpleLogger().Write(logWARNING) << "Shared memory settings conflict with path settings."; + } + + util::SimpleLogger().Write() << visible_options; + return INIT_OK_DO_NOT_START_ENGINE; +} + +int main(int argc, const char *argv[]) try +{ + util::LogPolicy::GetInstance().Unmute(); + + bool trial_run = false; + std::string ip_address; + int ip_port, requested_thread_num; + + EngineConfig config; + boost::filesystem::path base_path; + const unsigned init_result = generateServerProgramOptions( + argc, argv, base_path, ip_address, ip_port, requested_thread_num, + config.use_shared_memory, trial_run, config.max_locations_trip, + config.max_locations_viaroute, config.max_locations_distance_table, + config.max_locations_map_matching); + if (init_result == INIT_OK_DO_NOT_START_ENGINE) + { + return EXIT_SUCCESS; + } + if (init_result == INIT_FAILED) + { + return EXIT_FAILURE; + } + if (!base_path.empty()) + { + config.storage_config = storage::StorageConfig(base_path); + } + if(!config.IsValid()) + { + if (base_path.empty() != config.use_shared_memory) + { + util::SimpleLogger().Write(logWARNING) << "Path settings and shared memory conflicts."; + } + else + { + if(!boost::filesystem::is_regular_file(config.storage_config.ram_index_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.file_index_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.edges_data_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.core_data_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.geometries_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.timestamp_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_names_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_indexes_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.names_data_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path << " is not found"; + } + if(!boost::filesystem::is_regular_file(config.storage_config.properties_path)) + { + util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path << " is not found"; + } + } + return EXIT_FAILURE; + } + +#ifdef __linux__ + struct MemoryLocker final + { + explicit MemoryLocker(bool should_lock) : should_lock(should_lock) + { + if (should_lock && -1 == mlockall(MCL_CURRENT | MCL_FUTURE)) + { + could_lock = false; + util::SimpleLogger().Write(logWARNING) << "memory could not be locked to RAM"; + } + } + ~MemoryLocker() + { + if (should_lock && could_lock) + (void)munlockall(); + } + bool should_lock = false, could_lock = true; + } memory_locker(config.use_shared_memory); +#endif + util::SimpleLogger().Write() << "starting up engines, " << OSRM_VERSION; + + if (config.use_shared_memory) + { + util::SimpleLogger().Write() << "Loading from shared memory"; + } + + util::SimpleLogger().Write() << "Threads: " << requested_thread_num; + util::SimpleLogger().Write() << "IP address: " << ip_address; + util::SimpleLogger().Write() << "IP port: " << ip_port; + +#ifndef _WIN32 + int sig = 0; + sigset_t new_mask; + sigset_t old_mask; + sigfillset(&new_mask); + pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask); +#endif + + auto routing_server = server::Server::CreateServer(ip_address, ip_port, requested_thread_num); + auto service_handler = util::make_unique(config); + + routing_server->RegisterServiceHandler(std::move(service_handler)); + + if (trial_run) + { + util::SimpleLogger().Write() << "trial run, quitting after successful initialization"; + } + else + { + std::packaged_task server_task([&] + { + routing_server->Run(); + return 0; + }); + auto future = server_task.get_future(); + std::thread server_thread(std::move(server_task)); + +#ifndef _WIN32 + sigset_t wait_mask; + pthread_sigmask(SIG_SETMASK, &old_mask, nullptr); + sigemptyset(&wait_mask); + sigaddset(&wait_mask, SIGINT); + sigaddset(&wait_mask, SIGQUIT); + sigaddset(&wait_mask, SIGTERM); + pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); + util::SimpleLogger().Write() << "running and waiting for requests"; + sigwait(&wait_mask, &sig); +#else + // Set console control handler to allow server to be stopped. + console_ctrl_function = std::bind(&server::Server::Stop, routing_server); + SetConsoleCtrlHandler(console_ctrl_handler, TRUE); + util::SimpleLogger().Write() << "running and waiting for requests"; + routing_server->Run(); +#endif + util::SimpleLogger().Write() << "initiating shutdown"; + routing_server->Stop(); + util::SimpleLogger().Write() << "stopping threads"; + + auto status = future.wait_for(std::chrono::seconds(2)); + + if (status == std::future_status::ready) + { + server_thread.join(); + } + else + { + util::SimpleLogger().Write(logWARNING) << "Didn't exit within 2 seconds. Hard abort!"; + server_task.reset(); // just kill it + } + } + + util::SimpleLogger().Write() << "freeing objects"; + routing_server.reset(); + util::SimpleLogger().Write() << "shutdown completed"; +} +catch (const std::bad_alloc &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + util::SimpleLogger().Write(logWARNING) + << "Please provide more memory or consider using a larger swapfile"; + return EXIT_FAILURE; +} +catch (const std::exception &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/springclean.cpp b/src/tools/springclean.cpp new file mode 100644 index 000000000..69a7cc175 --- /dev/null +++ b/src/tools/springclean.cpp @@ -0,0 +1,82 @@ +#include + +#include "storage/shared_memory.hpp" +#include "storage/shared_datatype.hpp" +#include "util/simple_logger.hpp" + +namespace osrm +{ +namespace tools +{ + +// FIXME remove after folding back into datastore +using namespace storage; + +void deleteRegion(const SharedDataType region) +{ + if (SharedMemory::RegionExists(region) && !SharedMemory::Remove(region)) + { + const std::string name = [&] + { + switch (region) + { + case CURRENT_REGIONS: + return "CURRENT_REGIONS"; + case LAYOUT_1: + return "LAYOUT_1"; + case DATA_1: + return "DATA_1"; + case LAYOUT_2: + return "LAYOUT_2"; + case DATA_2: + return "DATA_2"; + case LAYOUT_NONE: + return "LAYOUT_NONE"; + default: // DATA_NONE: + return "DATA_NONE"; + } + }(); + + util::SimpleLogger().Write(logWARNING) << "could not delete shared memory region " << name; + } +} + +// find all existing shmem regions and remove them. +void springclean() +{ + util::SimpleLogger().Write() << "spring-cleaning all shared memory regions"; + deleteRegion(DATA_1); + deleteRegion(LAYOUT_1); + deleteRegion(DATA_2); + deleteRegion(LAYOUT_2); + deleteRegion(CURRENT_REGIONS); +} +} +} + +int main() try +{ + osrm::util::LogPolicy::GetInstance().Unmute(); + osrm::util::SimpleLogger().Write() << "Releasing all locks"; + osrm::util::SimpleLogger().Write() << "ATTENTION! BE CAREFUL!"; + osrm::util::SimpleLogger().Write() << "----------------------"; + osrm::util::SimpleLogger().Write() << "This tool may put osrm-routed into an undefined state!"; + osrm::util::SimpleLogger().Write() + << "Type 'Y' to acknowledge that you know what your are doing."; + osrm::util::SimpleLogger().Write() << "\n\nDo you want to purge all shared memory allocated " + << "by osrm-datastore? [type 'Y' to confirm]"; + + const auto letter = getchar(); + if (letter != 'Y') + { + osrm::util::SimpleLogger().Write() << "aborted."; + return EXIT_SUCCESS; + } + osrm::tools::springclean(); + return EXIT_SUCCESS; +} +catch (const std::exception &e) +{ + osrm::util::SimpleLogger().Write(logWARNING) << "[excpetion] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/store.cpp b/src/tools/store.cpp new file mode 100644 index 000000000..9e7cbbc5e --- /dev/null +++ b/src/tools/store.cpp @@ -0,0 +1,105 @@ +#include "storage/storage.hpp" +#include "util/exception.hpp" +#include "util/simple_logger.hpp" +#include "util/typedefs.hpp" +#include "util/version.hpp" + +#include +#include + +using namespace osrm; + +// generate boost::program_options object for the routing part +bool generateDataStoreOptions(const int argc, const char *argv[], boost::filesystem::path& base_path) +{ + // declare a group of options that will be allowed only on command line + boost::program_options::options_description generic_options("Options"); + generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")( + "springclean,s", "Remove all regions in shared memory"); + + // declare a group of options that will be allowed both on command line + // as well as in a config file + boost::program_options::options_description config_options("Configuration"); + + // hidden options, will be allowed on command line but will not be shown to the user + boost::program_options::options_description hidden_options("Hidden options"); + hidden_options.add_options()( + "base,b", boost::program_options::value(&base_path), + "base path to .osrm file"); + + // positional option + boost::program_options::positional_options_description positional_options; + positional_options.add("base", 1); + + // combine above options for parsing + boost::program_options::options_description cmdline_options; + cmdline_options.add(generic_options).add(config_options).add(hidden_options); + + const auto* executable = argv[0]; + boost::program_options::options_description visible_options( + boost::filesystem::path(executable).filename().string() + " [] "); + visible_options.add(generic_options).add(config_options); + + // print help options if no infile is specified + if (argc < 2) + { + util::SimpleLogger().Write() << visible_options; + return false; + } + + // parse command line options + boost::program_options::variables_map option_variables; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv) + .options(cmdline_options) + .positional(positional_options) + .run(), + option_variables); + + if (option_variables.count("version")) + { + util::SimpleLogger().Write() << OSRM_VERSION; + return false; + } + + if (option_variables.count("help")) + { + util::SimpleLogger().Write() << visible_options; + return false; + } + + boost::program_options::notify(option_variables); + + return true; +} + +int main(const int argc, const char *argv[]) try +{ + util::LogPolicy::GetInstance().Unmute(); + + boost::filesystem::path base_path; + if (!generateDataStoreOptions(argc, argv, base_path)) + { + return EXIT_SUCCESS; + } + storage::StorageConfig config(base_path); + if (!config.IsValid()) + { + util::SimpleLogger().Write(logWARNING) << "Invalid file path given!"; + return EXIT_FAILURE; + } + storage::Storage storage(std::move(config)); + return storage.Run(); +} +catch (const std::bad_alloc &e) +{ + util::SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); + util::SimpleLogger().Write(logWARNING) + << "Please provide more memory or disable locking the virtual " + "address space (note: this makes OSRM swap, i.e. slow)"; + return EXIT_FAILURE; +} +catch (const std::exception &e) +{ + util::SimpleLogger().Write(logWARNING) << "caught exception: " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/tools/unlock_all_mutexes.cpp b/src/tools/unlock_all_mutexes.cpp new file mode 100644 index 000000000..2af8477ab --- /dev/null +++ b/src/tools/unlock_all_mutexes.cpp @@ -0,0 +1,20 @@ +#include "util/simple_logger.hpp" +#include "storage/shared_barriers.hpp" + +#include + +int main() try +{ + osrm::util::LogPolicy::GetInstance().Unmute(); + osrm::util::SimpleLogger().Write() << "Releasing all locks"; + osrm::storage::SharedBarriers barrier; + barrier.pending_update_mutex.unlock(); + barrier.query_mutex.unlock(); + barrier.update_mutex.unlock(); + return 0; +} +catch (const std::exception &e) +{ + osrm::util::SimpleLogger().Write(logWARNING) << "[excpetion] " << e.what(); + return EXIT_FAILURE; +} diff --git a/src/util/assert.cpp b/src/util/assert.cpp new file mode 100644 index 000000000..70517840a --- /dev/null +++ b/src/util/assert.cpp @@ -0,0 +1,29 @@ +#include "util/assert.hpp" + +#include + +namespace +{ +// We throw to guarantee for stack-unwinding and therefore our destructors being called. +void assertion_failed_msg_helper( + char const *expr, char const *msg, char const *function, char const *file, long line) +{ + std::ostringstream fmt; + fmt << file << ":" << line << "\nin: " << function << ": " << expr << "\n" << msg; + throw osrm::util::assertionError{fmt.str().c_str()}; +} +} + +// Boost.Assert only declares the following two functions and let's us define them here. +namespace boost +{ +void assertion_failed(char const *expr, char const *function, char const *file, long line) +{ + ::assertion_failed_msg_helper(expr, "", function, file, line); +} +void assertion_failed_msg( + char const *expr, char const *msg, char const *function, char const *file, long line) +{ + ::assertion_failed_msg_helper(expr, msg, function, file, line); +} +} diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp new file mode 100644 index 000000000..9ccbf17f3 --- /dev/null +++ b/src/util/coordinate.cpp @@ -0,0 +1,100 @@ +#include "util/coordinate_calculation.hpp" + +#ifndef NDEBUG +#include "util/simple_logger.hpp" +#endif +#include "osrm/coordinate.hpp" + +#ifndef NDEBUG +#include +#endif +#include +#include +#include + +namespace osrm +{ +namespace util +{ + +Coordinate::Coordinate() + : lon(std::numeric_limits::min()), lat(std::numeric_limits::min()) +{ +} + +Coordinate::Coordinate(const FloatCoordinate &other) + : Coordinate(toFixed(other.lon), toFixed(other.lat)) +{ +} + +Coordinate::Coordinate(const FloatLongitude lon_, const FloatLatitude lat_) + : Coordinate(toFixed(lon_), toFixed(lat_)) +{ +} + +Coordinate::Coordinate(const FixedLongitude lon_, const FixedLatitude lat_) : lon(lon_), lat(lat_) +{ +} + +bool Coordinate::IsValid() const +{ + return !(lat > FixedLatitude(90 * COORDINATE_PRECISION) || + lat < FixedLatitude(-90 * COORDINATE_PRECISION) || + lon > FixedLongitude(180 * COORDINATE_PRECISION) || + lon < FixedLongitude(-180 * COORDINATE_PRECISION)); +} + +FloatCoordinate::FloatCoordinate() + : lon(std::numeric_limits::min()), lat(std::numeric_limits::min()) +{ +} + +FloatCoordinate::FloatCoordinate(const Coordinate other) + : FloatCoordinate(toFloating(other.lon), toFloating(other.lat)) +{ +} + +FloatCoordinate::FloatCoordinate(const FixedLongitude lon_, const FixedLatitude lat_) + : FloatCoordinate(toFloating(lon_), toFloating(lat_)) +{ +} + +FloatCoordinate::FloatCoordinate(const FloatLongitude lon_, const FloatLatitude lat_) : lon(lon_), lat(lat_) +{ +} + +bool FloatCoordinate::IsValid() const +{ + return !(lat > FloatLatitude(90) || + lat < FloatLatitude(-90) || + lon > FloatLongitude(180) || + lon < FloatLongitude(-180)); +} + + +bool operator==(const Coordinate lhs, const Coordinate rhs) +{ + return lhs.lat == rhs.lat && lhs.lon == rhs.lon; +} +bool operator==(const FloatCoordinate lhs, const FloatCoordinate rhs) +{ + return lhs.lat == rhs.lat && lhs.lon == rhs.lon; +} + +bool operator!=(const Coordinate lhs, const Coordinate rhs) { return !(lhs == rhs); } +bool operator!=(const FloatCoordinate lhs, const FloatCoordinate rhs) { return !(lhs == rhs); } + +std::ostream &operator<<(std::ostream &out, const Coordinate coordinate) +{ + out << std::setprecision(12) << "(lon:" << toFloating(coordinate.lon) + << ", lat:" << toFloating(coordinate.lat) << ")"; + return out; +} +std::ostream &operator<<(std::ostream &out, const FloatCoordinate coordinate) +{ + out << std::setprecision(12) << "(lon:" << coordinate.lon + << ", lat:" << coordinate.lat << ")"; + return out; +} +} +} diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp new file mode 100644 index 000000000..24b280fef --- /dev/null +++ b/src/util/coordinate_calculation.cpp @@ -0,0 +1,326 @@ +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/trigonometry_table.hpp" +#include "util/web_mercator.hpp" + +#include + +#include + +#include +#include + +namespace osrm +{ +namespace util +{ + +namespace coordinate_calculation +{ + +// Does not project the coordinates! +std::uint64_t squaredEuclideanDistance(const Coordinate &lhs, const Coordinate &rhs) +{ + const std::uint64_t dx = static_cast(lhs.lon - rhs.lon); + const std::uint64_t dy = static_cast(lhs.lat - rhs.lat); + + return dx * dx + dy * dy; +} + +double haversineDistance(const Coordinate coordinate_1, const Coordinate coordinate_2) +{ + auto lon1 = static_cast(coordinate_1.lon); + auto lat1 = static_cast(coordinate_1.lat); + auto lon2 = static_cast(coordinate_2.lon); + auto lat2 = static_cast(coordinate_2.lat); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + const double lt1 = lat1 / COORDINATE_PRECISION; + const double ln1 = lon1 / COORDINATE_PRECISION; + const double lt2 = lat2 / COORDINATE_PRECISION; + const double ln2 = lon2 / COORDINATE_PRECISION; + const double dlat1 = lt1 * detail::DEGREE_TO_RAD; + + const double dlong1 = ln1 * detail::DEGREE_TO_RAD; + const double dlat2 = lt2 * detail::DEGREE_TO_RAD; + const double dlong2 = ln2 * detail::DEGREE_TO_RAD; + + const double dlong = dlong1 - dlong2; + const double dlat = dlat1 - dlat2; + + const double aharv = std::pow(std::sin(dlat / 2.0), 2.0) + + std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dlong / 2.), 2); + const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv)); + return detail::EARTH_RADIUS * charv; +} + +double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coordinate_2) +{ + auto lon1 = static_cast(coordinate_1.lon); + auto lat1 = static_cast(coordinate_1.lat); + auto lon2 = static_cast(coordinate_2.lon); + auto lat2 = static_cast(coordinate_2.lat); + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + + const double float_lat1 = (lat1 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD; + const double float_lon1 = (lon1 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD; + const double float_lat2 = (lat2 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD; + const double float_lon2 = (lon2 / COORDINATE_PRECISION) * detail::DEGREE_TO_RAD; + + const double x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.0); + const double y_value = float_lat2 - float_lat1; + return std::hypot(x_value, y_value) * detail::EARTH_RADIUS; +} + +std::pair projectPointOnSegment(const FloatCoordinate &source, + const FloatCoordinate &target, + const FloatCoordinate &coordinate) +{ + const FloatCoordinate slope_vector{target.lon - source.lon, target.lat - source.lat}; + const FloatCoordinate rel_coordinate{coordinate.lon - source.lon, coordinate.lat - source.lat}; + // dot product of two un-normed vectors + const auto unnormed_ratio = static_cast(slope_vector.lon * rel_coordinate.lon) + + static_cast(slope_vector.lat * rel_coordinate.lat); + // squared length of the slope vector + const auto squared_length = static_cast(slope_vector.lon * slope_vector.lon) + + static_cast(slope_vector.lat * slope_vector.lat); + + if (squared_length < std::numeric_limits::epsilon()) + { + return {0, source}; + } + + const double normed_ratio = unnormed_ratio / squared_length; + double clamped_ratio = normed_ratio; + if (clamped_ratio > 1.) + { + clamped_ratio = 1.; + } + else if (clamped_ratio < 0.) + { + clamped_ratio = 0.; + } + + return {clamped_ratio, + { + FloatLongitude(1.0 - clamped_ratio) * source.lon + + target.lon * FloatLongitude(clamped_ratio), + FloatLatitude(1.0 - clamped_ratio) * source.lat + + target.lat * FloatLatitude(clamped_ratio), + }}; +} + +double perpendicularDistance(const Coordinate segment_source, + const Coordinate segment_target, + const Coordinate query_location, + Coordinate &nearest_location, + double &ratio) +{ + using namespace coordinate_calculation; + + BOOST_ASSERT(query_location.IsValid()); + + FloatCoordinate projected_nearest; + std::tie(ratio, projected_nearest) = projectPointOnSegment( + web_mercator::fromWGS84(segment_source), web_mercator::fromWGS84(segment_target), + web_mercator::fromWGS84(query_location)); + nearest_location = web_mercator::toWGS84(projected_nearest); + + const double approximate_distance = greatCircleDistance(query_location, nearest_location); + BOOST_ASSERT(0.0 <= approximate_distance); + return approximate_distance; +} + +double perpendicularDistance(const Coordinate source_coordinate, + const Coordinate target_coordinate, + const Coordinate query_location) +{ + double ratio; + Coordinate nearest_location; + + return perpendicularDistance(source_coordinate, target_coordinate, query_location, + nearest_location, ratio); +} + +Coordinate centroid(const Coordinate lhs, const Coordinate rhs) +{ + Coordinate centroid; + // The coordinates of the midpoints are given by: + // x = (x1 + x2) /2 and y = (y1 + y2) /2. + centroid.lon = (lhs.lon + rhs.lon) / FixedLongitude(2); + centroid.lat = (lhs.lat + rhs.lat) / FixedLatitude(2); + return centroid; +} + +double degToRad(const double degree) +{ + using namespace boost::math::constants; + return degree * (pi() / 180.0); +} + +double radToDeg(const double radian) +{ + using namespace boost::math::constants; + return radian * (180.0 * (1. / pi())); +} + +double bearing(const Coordinate first_coordinate, const Coordinate second_coordinate) +{ + const double lon_diff = + static_cast(toFloating(second_coordinate.lon - first_coordinate.lon)); + const double lon_delta = degToRad(lon_diff); + const double lat1 = degToRad(static_cast(toFloating(first_coordinate.lat))); + const double lat2 = degToRad(static_cast(toFloating(second_coordinate.lat))); + const double y = std::sin(lon_delta) * std::cos(lat2); + const double x = + std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(lon_delta); + double result = radToDeg(std::atan2(y, x)); + while (result < 0.0) + { + result += 360.0; + } + + while (result >= 360.0) + { + result -= 360.0; + } + return result; +} + +double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third) +{ + using namespace boost::math::constants; + using namespace coordinate_calculation; + + if (first == second || second == third) + return 180; + + BOOST_ASSERT(first.IsValid()); + BOOST_ASSERT(second.IsValid()); + BOOST_ASSERT(third.IsValid()); + + const double v1x = static_cast(toFloating(first.lon - second.lon)); + const double v1y = + web_mercator::latToY(toFloating(first.lat)) - web_mercator::latToY(toFloating(second.lat)); + const double v2x = static_cast(toFloating(third.lon - second.lon)); + const double v2y = + web_mercator::latToY(toFloating(third.lat)) - web_mercator::latToY(toFloating(second.lat)); + + double angle = (atan2_lookup(v2y, v2x) - atan2_lookup(v1y, v1x)) * 180. / pi(); + + while (angle < 0.) + { + angle += 360.; + } + + BOOST_ASSERT(angle >= 0); + return angle; +} + +boost::optional +circleCenter(const Coordinate C1, const Coordinate C2, const Coordinate C3) +{ + // free after http://paulbourke.net/geometry/circlesphere/ + // require three distinct points + if (C1 == C2 || C2 == C3 || C1 == C3) + { + return boost::none; + } + + // define line through c1, c2 and c2,c3 + const double C2C1_lat = static_cast(toFloating(C2.lat - C1.lat)); // yDelta_a + const double C2C1_lon = static_cast(toFloating(C2.lon - C1.lon)); // xDelta_a + const double C3C2_lat = static_cast(toFloating(C3.lat - C2.lat)); // yDelta_b + const double C3C2_lon = static_cast(toFloating(C3.lon - C2.lon)); // xDelta_b + + // check for collinear points in X-Direction / Y-Direction + if ((std::abs(C2C1_lon) < std::numeric_limits::epsilon() && + std::abs(C3C2_lon) < std::numeric_limits::epsilon()) || + (std::abs(C2C1_lat) < std::numeric_limits::epsilon() && + std::abs(C3C2_lat) < std::numeric_limits::epsilon())) + { + return boost::none; + } + else if (std::abs(C2C1_lon) < std::numeric_limits::epsilon()) + { + // vertical line C2C1 + // due to c1.lon == c2.lon && c1.lon != c3.lon we can rearrange this way + BOOST_ASSERT(std::abs(static_cast(toFloating(C3.lon - C1.lon))) >= + std::numeric_limits::epsilon() && + std::abs(static_cast(toFloating(C2.lon - C3.lon))) >= + std::numeric_limits::epsilon()); + return circleCenter(C1, C3, C2); + } + else if (std::abs(C3C2_lon) < std::numeric_limits::epsilon()) + { + // vertical line C3C2 + // due to c2.lon == c3.lon && c1.lon != c3.lon we can rearrange this way + // after rearrangement both deltas will be zero + BOOST_ASSERT(std::abs(static_cast(toFloating(C1.lon - C2.lon))) >= + std::numeric_limits::epsilon() && + std::abs(static_cast(toFloating(C3.lon - C1.lon))) >= + std::numeric_limits::epsilon()); + return circleCenter(C2, C1, C3); + } + else + { + const double C2C1_slope = C2C1_lat / C2C1_lon; + const double C3C2_slope = C3C2_lat / C3C2_lon; + + if (std::abs(C2C1_slope) < std::numeric_limits::epsilon()) + { + // Three non-collinear points with C2,C1 on same latitude. + // Due to the x-values correct, we can swap C3 and C1 to obtain the correct slope value + return circleCenter(C3, C2, C1); + } + // valid slope values for both lines, calculate the center as intersection of the lines + + // can this ever happen? + if (std::abs(C2C1_slope - C3C2_slope) < std::numeric_limits::epsilon()) + return boost::none; + + const double C1_y = static_cast(toFloating(C1.lat)); + const double C1_x = static_cast(toFloating(C1.lon)); + const double C2_y = static_cast(toFloating(C2.lat)); + const double C2_x = static_cast(toFloating(C2.lon)); + const double C3_y = static_cast(toFloating(C3.lat)); + const double C3_x = static_cast(toFloating(C3.lon)); + + const double lon = (C2C1_slope * C3C2_slope * (C1_y - C3_y) + C3C2_slope * (C1_x + C2_x) - + C2C1_slope * (C2_x + C3_x)) / + (2 * (C3C2_slope - C2C1_slope)); + const double lat = (0.5 * (C1_x + C2_x) - lon) / C2C1_slope + 0.5 * (C1_y + C2_y); + return Coordinate(FloatLongitude(lon), FloatLatitude(lat)); + } +} + +double circleRadius(const Coordinate C1, const Coordinate C2, const Coordinate C3) +{ + // a circle by three points requires thee distinct points + auto center = circleCenter(C1, C2, C3); + if (center) + return haversineDistance(C1, *center); + else + return std::numeric_limits::infinity(); +} + +Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to) +{ + BOOST_ASSERT(0 <= factor && factor <= 1.0); + + FixedLongitude interpolated_lon(((1. - factor) * static_cast(from.lon)) + + (factor * static_cast(to.lon))); + FixedLatitude interpolated_lat(((1. - factor) * static_cast(from.lat)) + + (factor * static_cast(to.lat))); + + return {std::move(interpolated_lon), std::move(interpolated_lat)}; +} + +} // ns coordinate_calculation +} // ns util +} // ns osrm diff --git a/src/util/exception.cpp b/src/util/exception.cpp new file mode 100644 index 000000000..394d8c07c --- /dev/null +++ b/src/util/exception.cpp @@ -0,0 +1,21 @@ +#include "util/exception.hpp" + +// This function exists to 'anchor' the class, and stop the compiler from +// copying vtable and RTTI info into every object file that includes +// this header. (Caught by -Wweak-vtables under Clang.) + +// More information from the LLVM Coding Standards: +// If a class is defined in a header file and has a vtable (either it has +// virtual methods or it derives from classes with virtual methods), it must +// always have at least one out-of-line virtual method in the class. Without +// this, the compiler will copy the vtable and RTTI into every .o file that +// #includes the header, bloating .o file sizes and increasing link times. + +namespace osrm +{ +namespace util +{ + +void exception::anchor() const {} +} +} diff --git a/src/util/fingerprint.cpp b/src/util/fingerprint.cpp new file mode 100644 index 000000000..2764c7bab --- /dev/null +++ b/src/util/fingerprint.cpp @@ -0,0 +1,2 @@ +#include "util/fingerprint.hpp" +#include "util/fingerprint_impl.hpp" diff --git a/src/util/hilbert_value.cpp b/src/util/hilbert_value.cpp new file mode 100644 index 000000000..a1505ee67 --- /dev/null +++ b/src/util/hilbert_value.cpp @@ -0,0 +1,84 @@ +#include "util/hilbert_value.hpp" + +namespace osrm +{ +namespace util +{ + +namespace +{ + +std::uint64_t bitInterleaving(const std::uint32_t longitude, const std::uint32_t latitude) +{ + std::uint64_t result = 0; + for (std::int8_t index = 31; index >= 0; --index) + { + result |= (latitude >> index) & 1; + result <<= 1; + result |= (longitude >> index) & 1; + if (0 != index) + { + result <<= 1; + } + } + return result; +} + +void transposeCoordinate(std::uint32_t *x) +{ + std::uint32_t M = 1u << (32 - 1), P, Q, t; + int i; + // Inverse undo + for (Q = M; Q > 1; Q >>= 1) + { + P = Q - 1; + for (i = 0; i < 2; ++i) + { + + const bool condition = (x[i] & Q); + if (condition) + { + x[0] ^= P; // invert + } + else + { + t = (x[0] ^ x[i]) & P; + x[0] ^= t; + x[i] ^= t; + } + } // exchange + } + // Gray encode + for (i = 1; i < 2; ++i) + { + x[i] ^= x[i - 1]; + } + t = 0; + for (Q = M; Q > 1; Q >>= 1) + { + const bool condition = (x[2 - 1] & Q); + if (condition) + { + t ^= Q - 1; + } + } // check if this for loop is wrong + for (i = 0; i < 2; ++i) + { + x[i] ^= t; + } +} +} // anonymous ns + +std::uint64_t hilbertCode(const Coordinate coordinate) +{ + std::uint32_t location[2]; + location[0] = static_cast(coordinate.lon) + + static_cast(180 * COORDINATE_PRECISION); + location[1] = static_cast(coordinate.lat) + + static_cast(90 * COORDINATE_PRECISION); + + transposeCoordinate(location); + return bitInterleaving(location[0], location[1]); +} +} +} diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp new file mode 100644 index 000000000..fedc31f58 --- /dev/null +++ b/src/util/name_table.cpp @@ -0,0 +1,65 @@ +#include "util/exception.hpp" +#include "util/name_table.hpp" +#include "util/simple_logger.hpp" + +#include +#include +#include + +#include + +namespace osrm +{ +namespace util +{ + +NameTable::NameTable(const std::string &filename) +{ + boost::filesystem::ifstream name_stream(filename, std::ios::binary); + + if (!name_stream) + throw exception("Failed to open " + filename + " for reading."); + + name_stream >> m_name_table; + + unsigned number_of_chars = 0; + name_stream.read(reinterpret_cast(&number_of_chars), sizeof(number_of_chars)); + if (!name_stream) + throw exception("Encountered invalid file, failed to read number of contained chars"); + + m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element + m_names_char_list.back() = 0; + if (number_of_chars > 0) + { + name_stream.read(reinterpret_cast(&m_names_char_list[0]), + number_of_chars * sizeof(m_names_char_list[0])); + } + else + { + util::SimpleLogger().Write(logWARNING) << "list of street names is empty"; + } + if (!name_stream) + throw exception("Failed to read " + std::to_string(number_of_chars) + + " characters from file."); +} + +std::string NameTable::GetNameForID(const unsigned name_id) const +{ + if (std::numeric_limits::max() == name_id) + { + return ""; + } + auto range = m_name_table.GetRange(name_id); + + std::string result; + result.reserve(range.size()); + if (range.begin() != range.end()) + { + result.resize(range.back() - range.front() + 1); + std::copy(m_names_char_list.begin() + range.front(), + m_names_char_list.begin() + range.back() + 1, result.begin()); + } + return result; +} +} // namespace util +} // namespace osrm diff --git a/util/simple_logger.cpp b/src/util/simple_logger.cpp similarity index 61% rename from util/simple_logger.cpp rename to src/util/simple_logger.cpp index e3f4f8ed0..13ee10391 100644 --- a/util/simple_logger.cpp +++ b/src/util/simple_logger.cpp @@ -1,31 +1,4 @@ -/* - -Copyright (c) 2015, Project OSRM contributors -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "simple_logger.hpp" +#include "util/simple_logger.hpp" #ifdef _MSC_VER #include #define isatty _isatty @@ -38,6 +11,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace osrm +{ +namespace util +{ + namespace { static const char COL_RESET[]{"\x1b[0m"}; @@ -119,3 +97,5 @@ SimpleLogger::~SimpleLogger() } } } +} +} diff --git a/taginfo.json b/taginfo.json index 17a4f8f24..8acc7d764 100644 --- a/taginfo.json +++ b/taginfo.json @@ -6,13 +6,12 @@ "description": "High-performance routing engine for shortest paths in road networks.", "project_url": "http://project-osrm.org", "icon_url": "http://project-osrm.org/images/osrm_icon.png", - "contact_name": "Dennis Luxen", - "contact_email": "info@project-osrm.org" + "contact_name": "Patrick Niklaus", + "contact_email": "patrick@mapbox.com" }, "tags": [ { "key": "highway", - "description": "Type of road.", "object_types": [ "way" ] }, { @@ -22,6 +21,23 @@ }, { "key": "oneway", + "value": "true", + "object_types": [ "way" ] + }, + { + "key": "oneway", + "value": "1", + "object_types": [ "way" ] + }, + { + "key": "oneway", + "value": "-1", + "object_types": [ "way" ] + }, + { + "key": "oneway", + "value": "reversible", + "description": "is marked as non-routable because of time-dependence", "object_types": [ "way" ] }, { @@ -38,10 +54,128 @@ "key": "status", "description": "This is used by HOT." }, - { - "key": "access", - "object_types": [ "way" ] - }, + {"key": "maxspeed:advisory"}, + {"key": "maxspeed:advisory:forward"}, + {"key": "maxspeed:advisory:backward"}, + {"key": "bridge", "value": "movable", "description": "uses capacity and duration"}, + {"key": "capacity:car", "description": "used for movable bridges"}, + {"key": "side_road", "value": "yes", "description": "gets speed penalty"}, + {"key": "side_road", "value": "rotary", "description": "gets speed penalty"}, + {"key": "route", "object_types": ["way"]}, + {"key": "highway", "value": "traffic_signals", "object_types": ["node"]}, + {"key": "access", "value": "yes"}, + {"key": "access", "value": "motorcar"}, + {"key": "access", "value": "motor_vehicle"}, + {"key": "access", "value": "vehicle"}, + {"key": "access", "value": "permissive"}, + {"key": "access", "value": "designated"}, + {"key": "access", "value": "destination"}, + {"key": "access", "value": "no"}, + {"key": "access", "value": "private"}, + {"key": "access", "value": "agricultural"}, + {"key": "access", "value": "forestry"}, + {"key": "access", "value": "emergency"}, + {"key": "access", "value": "psv"}, + {"key": "access", "value": "delivery"}, + {"key": "maxspeed", "value": "none"}, + {"key": "maxspeed", "value": "urban"}, + {"key": "maxspeed", "value": "rural"}, + {"key": "maxspeed", "value": "trunk"}, + {"key": "maxspeed", "value": "motorway"}, + {"key": "maxspeed", "value": "ch:rural"}, + {"key": "maxspeed", "value": "ch:trunk"}, + {"key": "maxspeed", "value": "ch:motorway"}, + {"key": "maxspeed", "value": "de:living_street"}, + {"key": "maxspeed", "value": "ru:living_street"}, + {"key": "maxspeed", "value": "ru:urban"}, + {"key": "maxspeed", "value": "ua:urban"}, + {"key": "maxspeed", "value": "at:rural"}, + {"key": "maxspeed", "value": "de:rural"}, + {"key": "maxspeed", "value": "at:trunk"}, + {"key": "maxspeed", "value": "cz:trunk"}, + {"key": "maxspeed", "value": "ro:trunk"}, + {"key": "maxspeed", "value": "cz:motorway"}, + {"key": "maxspeed", "value": "de:motorway"}, + {"key": "maxspeed", "value": "ru:motorway"}, + {"key": "maxspeed", "value": "gb:nsl_single"}, + {"key": "maxspeed", "value": "gb:nsl_dual"}, + {"key": "maxspeed", "value": "gb:motorway"}, + {"key": "maxspeed", "value": "uk:nsl_single"}, + {"key": "maxspeed", "value": "uk:nsl_dual"}, + {"key": "maxspeed", "value": "uk:motorway"}, + {"key": "smoothness", "value": "intermediate"}, + {"key": "smoothness", "value": "bad"}, + {"key": "smoothness", "value": "very_bad"}, + {"key": "smoothness", "value": "horrible"}, + {"key": "smoothness", "value": "very_horrible"}, + {"key": "smoothness", "value": "impassable"}, + {"key": "tracktype", "value": "grade1"}, + {"key": "tracktype", "value": "grade2"}, + {"key": "tracktype", "value": "grade3"}, + {"key": "tracktype", "value": "grade4"}, + {"key": "tracktype", "value": "grade5"}, + {"key": "bollard", "value": "rising"}, + {"key": "bollard", "description": "Non-rising bollards are barriers"}, + {"key": "barrier", "value": "cattle_grid"}, + {"key": "barrier", "value": "border_control"}, + {"key": "barrier", "value": "checkpoint"}, + {"key": "barrier", "value": "toll_booth"}, + {"key": "barrier", "value": "sally_port"}, + {"key": "barrier", "value": "gate"}, + {"key": "barrier", "value": "lift_gate"}, + {"key": "barrier", "value": "no"}, + {"key": "barrier", "value": "entrance"}, + {"key": "highway", "value": "motorway"}, + {"key": "highway", "value": "motorway_link"}, + {"key": "highway", "value": "trunk"}, + {"key": "highway", "value": "trunk_link"}, + {"key": "highway", "value": "primary"}, + {"key": "highway", "value": "primary_link"}, + {"key": "highway", "value": "secondary"}, + {"key": "highway", "value": "secondary_link"}, + {"key": "highway", "value": "tertiary"}, + {"key": "highway", "value": "tertiary_link"}, + {"key": "highway", "value": "unclassified"}, + {"key": "highway", "value": "residential"}, + {"key": "highway", "value": "living_street"}, + {"key": "highway", "value": "service"}, + {"key": "highway", "value": "ferry"}, + {"key": "highway", "value": "movable"}, + {"key": "highway", "value": "shuttle_train"}, + {"key": "highway", "value": "default"}, + {"key": "width", "description": "Penalties for narrow streets"}, + {"key": "lanes", "description": "Penalties for shared single lane streets"}, + {"key": "surface", "value": "asphalt"}, + {"key": "surface", "value": "concrete"}, + {"key": "surface", "value": "concrete:plates"}, + {"key": "surface", "value": "concrete:lanes"}, + {"key": "surface", "value": "paved"}, + {"key": "surface", "value": "cement"}, + {"key": "surface", "value": "compacted"}, + {"key": "surface", "value": "fine_gravel"}, + {"key": "surface", "value": "paving_stones"}, + {"key": "surface", "value": "metal"}, + {"key": "surface", "value": "bricks"}, + {"key": "surface", "value": "grass"}, + {"key": "surface", "value": "wood"}, + {"key": "surface", "value": "sett"}, + {"key": "surface", "value": "grass_paver"}, + {"key": "surface", "value": "gravel"}, + {"key": "surface", "value": "unpaved"}, + {"key": "surface", "value": "ground"}, + {"key": "surface", "value": "dirt"}, + {"key": "surface", "value": "pebblestone"}, + {"key": "surface", "value": "tartan"}, + {"key": "surface", "value": "cobblestone"}, + {"key": "surface", "value": "clay"}, + {"key": "surface", "value": "earth"}, + {"key": "surface", "value": "stone"}, + {"key": "surface", "value": "rocky"}, + {"key": "surface", "value": "sand"}, + {"key": "surface", "value": "mud"}, + {"key": "motorcar"}, + {"key": "motor_vehicle"}, + {"key": "vehicle"}, { "key": "barrier" }, diff --git a/test/data/Makefile b/test/data/Makefile new file mode 100755 index 000000000..8684bc885 --- /dev/null +++ b/test/data/Makefile @@ -0,0 +1,54 @@ +DATA_NAME:=monaco +DATA_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).osm.pbf +DATA_POLY_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).poly +TOOL_ROOT:=../../build +PROFILE_ROOT:=../../profiles +SCRIPT_ROOT:=../../scripts +OSRM_EXTRACT:=$(TOOL_ROOT)/osrm-extract +OSRM_CONTRACT:=$(TOOL_ROOT)/osrm-contract +OSRM_ROUTED:=$(TOOL_ROOT)/osrm-routed +POLY2REQ:=$(SCRIPT_ROOT)/poly2req.js +TIMER:=$(SCRIPT_ROOT)/timer.sh +PROFILE:=$(PROFILE_ROOT)/car.lua + +all: $(DATA_NAME).osrm.hsgr + +clean: + rm $(DATA_NAME).* + +$(DATA_NAME).osm.pbf: + wget $(DATA_URL) -O $(DATA_NAME).osm.pbf + +$(DATA_NAME).poly: + wget $(DATA_POLY_URL) -O $(DATA_NAME).poly + +$(DATA_NAME).osrm: $(DATA_NAME).osm.pbf $(DATA_NAME).poly $(PROFILE) $(OSRM_EXTRACT) + @echo "Verifiyng data file integrity..." + md5sum -c data.md5sum + @echo "Running osrm-extract..." + $(TIMER) "osrm-extract" $(OSRM_EXTRACT) $(DATA_NAME).osm.pbf -p $(PROFILE) + +$(DATA_NAME).osrm.hsgr: $(DATA_NAME).osrm $(PROFILE) $(OSRM_CONTRACT) + @echo "Running osrm-contract..." + $(TIMER) "osrm-contract" $(OSRM_CONTRACT) $(DATA_NAME).osrm + +$(DATA_NAME).requests: $(DATA_NAME).poly + $(POLY2REQ) $(DATA_NAME).poly > $(DATA_NAME).requests + +osrm-routed.pid: $(DATA_NAME).osrm.hsgr + @/bin/sh -c '$(OSRM_ROUTED) $(DATA_NAME).osrm& echo "$$!" > osrm-routed.pid' + sleep 1 + +benchmark: $(DATA_NAME).requests osrm-routed.pid + @echo "Running benchmark..." + $(TIMER) "queries" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" + @cat osrm-routed.pid | xargs kill + @rm osrm-routed.pid + @echo "**** timings ***" + @cat /tmp/osrm.timings + @echo "****************" + +checksum: + md5sum $(DATA_NAME).osm.pbf $(DATA_NAME).poly > data.md5sum + +.PHONY: clean checksum benchmark diff --git a/test/data/data.md5sum b/test/data/data.md5sum new file mode 100644 index 000000000..9bcfd33c2 --- /dev/null +++ b/test/data/data.md5sum @@ -0,0 +1,2 @@ +2b8dd9343d5e615afc9c67bcc7028a63 monaco.osm.pbf +b0788991ab3791d53c1c20b6281f81ad monaco.poly diff --git a/third_party/libosmium/.travis.yml b/third_party/libosmium/.travis.yml index 6ebdd7167..ac0d270e2 100644 --- a/third_party/libosmium/.travis.yml +++ b/third_party/libosmium/.travis.yml @@ -9,46 +9,151 @@ language: cpp sudo: false matrix: - include: - - os: linux - compiler: clang - env: BUILD_TYPE=Dev - - os: linux - compiler: clang - env: BUILD_TYPE=Release - - os: linux - compiler: gcc - env: BUILD_TYPE=Dev - - os: linux - compiler: gcc - env: BUILD_TYPE=Release - - os: osx - compiler: clang - env: BUILD_TYPE=Dev - - os: osx - compiler: clang - env: BUILD_TYPE=Release + include: + + # 1/ Linux Clang Builds + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.5' BUILD_TYPE='Release' + + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.5' BUILD_TYPE='Dev' + + + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.6' BUILD_TYPE='Release' + + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.6' BUILD_TYPE='Dev' + + + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Release' + + - os: linux + compiler: clang + addons: + apt: + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' + + + # 2/ Linux GCC Builds + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release' + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Dev' + + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-4.9' BUILD_TYPE='Release' + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-4.9' BUILD_TYPE='Dev' + + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-5' BUILD_TYPE='Release' + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-5' BUILD_TYPE='Dev' + + + # 3/ OSX Clang Builds + - os: osx + osx_image: xcode6.4 + compiler: clang + env: COMPILER='clang++' BUILD_TYPE='Dev' + + - os: osx + osx_image: xcode6.4 + compiler: clang + env: COMPILER='clang++' BUILD_TYPE='Release' + + + - os: osx + osx_image: xcode7 + compiler: clang + env: COMPILER='clang++' BUILD_TYPE='Dev' + + - os: osx + osx_image: xcode7 + compiler: clang + env: COMPILER='clang++' BUILD_TYPE='Release' -# http://docs.travis-ci.com/user/apt/ -addons: - apt: - sources: - - boost-latest - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - - gcc-4.8 - - libboost1.55-dev - - libboost-program-options1.55-dev - - libgdal-dev - - libgeos++-dev - - libproj-dev - - libsparsehash-dev - - spatialite-bin install: - - scripts/travis_install.sh + - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - git clone --quiet --depth 1 https://github.com/osmcode/osm-testdata.git + - | + if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then + brew remove gdal + brew install cmake boost google-sparsehash gdal + fi + +before_script: + - cd ${TRAVIS_BUILD_DIR} + - mkdir build && cd build + - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" script: - - scripts/travis_script.sh + - make VERBOSE=1 + - ctest --output-on-failure diff --git a/third_party/libosmium/CHANGELOG.md b/third_party/libosmium/CHANGELOG.md index 22eb06aac..394644890 100644 --- a/third_party/libosmium/CHANGELOG.md +++ b/third_party/libosmium/CHANGELOG.md @@ -12,6 +12,174 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed + +## [2.6.1] - 2016-02-22 + +### Added + +- Add `WITH_PROFILING` option to CMake config. When enabled, this sets the + `-fno-omit-frame-pointer` compiler option. + +### Changed + +- Massive speed improvements when building multipolygons. +- Uses (and includes) new version 1.3.0 of protozero library. +- Removed dependency on Boost Iterator for PBF writer. +- Example program `osmium_area_test` now uses `cerr` instead of `cout` for + debug output. + + +## [2.6.0] - 2016-02-04 + +### Added + +- The new handler osmium::handler::CheckOrder can be used to check that a + file is properly ordered. +- Add new method to build OSM nodes, ways, relations, changesets, and areas + in buffers that wraps the older Builder classes. The new code is much easier + to use and very flexible. There is no documentation yet, but the tests in + `test/t/builder/test_attr.cpp` can give you an idea how it works. +- Add util class to get memory usage of current process on Linux. + +### Changed + +- New Buffer memory management speeds up Buffer use, because it doesn't clear + the memory unnecessarily. + +### Fixed + +- osmium::Box::extend() function now ignores invalid locations. +- Install of external library headers. +- Check way has at least one node before calling `is_closed()` in area + assembler. +- Declaration/definition of some friend functions was in the wrong namespace. + + +## [2.5.4] - 2015-12-03 + +### Changed + +- Included gdalcpp.hpp header was updated to version 1.1.1. +- Included protozero library was updated to version 1.2.3. +- Workarounds for missing constexpr support in Visual Studio removed. All + constexpr features we need are supported now. +- Some code cleanup after running clang-tidy on the code. +- Re-added `Buffer::value_type` typedef. Turns out it is needed when using + `std::back_inserter` on the Buffer. + +### Fixed + +- Bugs with Timestamp code on 32 bit platforms. This necessitated + some changes in Timestamp which might lead to changes in user + code. +- Bug in segment intersection code (which appeared on i686 platform). + + +## [2.5.3] - 2015-11-17 + +### Added + +- `osmium::make_diff_iterator()` helper function. + +### Changed + +- Deprecated `osmium::Buffer::set_full_callback()`. +- Removed DataFile class which was never used anywhere. +- Removed unused and obscure `Buffer::value_type` typedef. + +### Fixed + +- Possible overrun in Buffer when using the full-callback. +- Incorrect swapping of Buffer. + + +## [2.5.2] - 2015-11-06 + +# Fixed + +- Writing data through an OutputIterator was extremly slow due to + lock contention. + + +## [2.5.1] - 2015-11-05 + +### Added + +- Header `osmium/fwd.hpp` with forward declarations of the most commonly + used Osmium classes. + +### Changed + +- Moved `osmium/io/overwrite.hpp` to `osmium/io/writer_options.hpp` + If you still include the old file, you'll get a warning. + + +## [2.5.0] - 2015-11-04 + +### Added + +- Helper functions to make input iterator ranges and output iterators. +- Add support for reading o5m and o5c files. +- Option for osmium::io::Writer to fsync file after writing. +- Lots of internal asserts() and other robustness checks. + +### Changed + +- Updated included protozero library to version 1.2.0. +- Complete overhaul of the I/O system making it much more robust against + wrong data and failures during I/O operations. +- Speed up PBF writing by running parts of it in parallel. +- OutputIterator doesn't hold an internal buffer any more, but it uses + one in Writer. Calling flush() on the OutputIterator isn't needed any + more. +- Reader now throws when trying to read after eof or an error. +- I/O functions that used to throw std::runtime_error now throw + osmium::io_error or derived. +- Optional parameters on osmium::io::Writer now work in any order. + +### Fixed + +- PBF reader now decodes locations of invisible nodes properly. +- Invalid Delta encode iterator dereference. +- Lots of includes fixed to include (only) what's used. +- Dangling reference in area assembly code. + + +## [2.4.1] - 2015-08-29 + +### Fixed + +- CRC calculation of tags and changesets. + + +## [2.4.0] - 2015-08-29 + +### Added + +- Checks that user names, member roles and tag keys and values are not longer + than 256 * 4 bytes. That is the maximum length 256 Unicode characters + can have in UTF-8 encoding. +- Support for GDAL 2. GDAL 1 still works. + +### Changed + +- Improved CMake build scripts. +- Updated internal version of Protozero to 1.1.0. +- Removed `toogr*` examples. They are in their own repository now. + See https://github.com/osmcode/osm-gis-export. +- Files about to be memory-mapped (for instance index files) are now set + to binary mode on Windows so the application doesn't have to do this. + +### Fixed + +- Hanging program when trying to open file with an unknown file format. +- Building problems with old boost versions. +- Initialization errors in PBF writer. +- Bug in byte swap code. +- Output on Windows now always uses binary mode, even when writing to + stdout, so OSM xml and opl files always use LF line endings. + + ## [2.3.0] - 2015-08-18 ### Added @@ -108,8 +276,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.3.0...HEAD -[2.3.0]: https://github.com/osmcode/libosmium/compare/v2.3.0...v2.3.0 +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.6.1...HEAD +[2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1 +[2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0 +[2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4 +[2.5.3]: https://github.com/osmcode/libosmium/compare/v2.5.2...v2.5.3 +[2.5.2]: https://github.com/osmcode/libosmium/compare/v2.5.1...v2.5.2 +[2.5.1]: https://github.com/osmcode/libosmium/compare/v2.5.0...v2.5.1 +[2.5.0]: https://github.com/osmcode/libosmium/compare/v2.4.1...v2.5.0 +[2.4.1]: https://github.com/osmcode/libosmium/compare/v2.4.0...v2.4.1 +[2.4.0]: https://github.com/osmcode/libosmium/compare/v2.3.0...v2.4.0 +[2.3.0]: https://github.com/osmcode/libosmium/compare/v2.2.0...v2.3.0 [2.2.0]: https://github.com/osmcode/libosmium/compare/v2.1.0...v2.2.0 [2.1.0]: https://github.com/osmcode/libosmium/compare/v2.0.0...v2.1.0 diff --git a/third_party/libosmium/CMakeLists.txt b/third_party/libosmium/CMakeLists.txt index fba967a4e..bc9b12ea5 100644 --- a/third_party/libosmium/CMakeLists.txt +++ b/third_party/libosmium/CMakeLists.txt @@ -9,8 +9,6 @@ cmake_minimum_required(VERSION 2.8 FATAL_ERROR) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - #----------------------------------------------------------------------------- # @@ -26,13 +24,13 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 3) -set(LIBOSMIUM_VERSION_PATCH 0) +set(LIBOSMIUM_VERSION_MINOR 6) +set(LIBOSMIUM_VERSION_PATCH 1) set(LIBOSMIUM_VERSION - "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}" - CACHE STRING - "Libosmium version") + "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) #----------------------------------------------------------------------------- @@ -56,6 +54,12 @@ option(BUILD_HEADERS "compile every header file on its own" ${dev_build}) option(BUILD_BENCHMARKS "compile benchmark programs" ${dev_build}) option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${dev_build}) +option(INSTALL_GDALCPP "also install gdalcpp headers" OFF) +option(INSTALL_PROTOZERO "also install protozero headers" OFF) +option(INSTALL_UTFCPP "also install utfcpp headers" OFF) + +option(WITH_PROFILING "add flags needed for profiling" OFF) + #----------------------------------------------------------------------------- # @@ -118,29 +122,39 @@ find_package(Boost 1.38) mark_as_advanced(CLEAR BOOST_ROOT) if(Boost_FOUND) - include_directories(${Boost_INCLUDE_DIRS}) + include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) else() set(BOOST_ROOT "NOT FOUND: please choose" CACHE PATH "") message(FATAL_ERROR "PLEASE, specify the directory where the Boost library is installed in BOOST_ROOT") endif() -set(OSMIUM_INCLUDE_DIR include) +# set OSMIUM_INCLUDE_DIR so FindOsmium will not set anything different +set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") + +include_directories(${OSMIUM_INCLUDE_DIR}) + find_package(Osmium COMPONENTS io gdal geos proj sparsehash) -include_directories(${OSMIUM_INCLUDE_DIRS}) + +# The find_package put the directory where it found the libosmium includes +# into OSMIUM_INCLUDE_DIRS. We remove it again, because we want to make +# sure to use our own include directory already set up above. +list(FIND OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}" _own_index) +list(REMOVE_AT OSMIUM_INCLUDE_DIRS ${_own_index}) +set(_own_index) + +include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) if(MSVC) find_path(GETOPT_INCLUDE_DIR getopt.h) find_library(GETOPT_LIBRARY NAMES wingetopt) if(GETOPT_INCLUDE_DIR AND GETOPT_LIBRARY) - include_directories(${GETOPT_INCLUDE_DIR}) + include_directories(SYSTEM ${GETOPT_INCLUDE_DIR}) list(APPEND OSMIUM_LIBRARIES ${GETOPT_LIBRARY}) else() set(GETOPT_MISSING 1) endif() endif() -include_directories(include) - #----------------------------------------------------------------------------- # @@ -193,18 +207,24 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}" CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds." FORCE) +if(WITH_PROFILING) + add_definitions(-fno-omit-frame-pointer) +endif() + #----------------------------------------------------------------------------- # # Build Type # #----------------------------------------------------------------------------- + # In 'Dev' mode: compile with very strict warnings and turn them into errors. if(CMAKE_BUILD_TYPE STREQUAL "Dev") if(NOT MSVC) add_definitions(-Werror) endif() add_definitions(${OSMIUM_WARNING_OPTIONS}) +# add_definitions(${OSMIUM_WARNING_OPTIONS} ${OSMIUM_DRACONIC_CLANG_OPTIONS} -Wno-documentation -Wno-format-nonliteral -Wno-deprecated -Wno-covered-switch-default -Wno-shadow) endif() # Force RelWithDebInfo build type if none was given @@ -256,19 +276,21 @@ find_program(CPPCHECK cppcheck) if(CPPCHECK) message(STATUS "Looking for cppcheck - found") set(CPPCHECK_OPTIONS - --enable=warning,style,performance,portability,information,missingInclude) + --enable=warning,style,performance,portability,information,missingInclude --force -Uassert) # cpp doesn't find system includes for some reason, suppress that report set(CPPCHECK_OPTIONS ${CPPCHECK_OPTIONS} --suppress=missingIncludeSystem) file(GLOB_RECURSE ALL_INCLUDES include/osmium/*.hpp) file(GLOB ALL_EXAMPLES examples/*.cpp) + file(GLOB ALL_BENCHMARKS benchmarks/*.cpp) file(GLOB ALL_UNIT_TESTS test/t/*/test_*.cpp) file(GLOB ALL_DATA_TESTS test/data-tests/*.cpp) if(Osmium_DEBUG) message(STATUS "Checking includes : ${ALL_INCLUDES}") message(STATUS "Checking example code : ${ALL_EXAMPLES}") + message(STATUS "Checking benchmarks : ${ALL_BENCHMARKS}") message(STATUS "Checking unit test code: ${ALL_UNIT_TESTS}") message(STATUS "Checking data test code: ${ALL_DATA_TESTS}") endif() @@ -276,6 +298,7 @@ if(CPPCHECK) set(CPPCHECK_FILES ${ALL_INCLUDES} ${ALL_EXAMPLES} + ${ALL_BENCHMARKS} ${ALL_UNIT_TESTS} ${ALL_DATA_TESTS}) @@ -288,7 +311,7 @@ if(CPPCHECK) else() message(STATUS "Looking for cppcheck - not found") message(STATUS " Build target 'cppcheck' will not be available.") -endif(CPPCHECK) +endif() #----------------------------------------------------------------------------- @@ -345,11 +368,115 @@ if(BUILD_HEADERS) endforeach() endif() + +#----------------------------------------------------------------------------- +# +# Optional "clang-tidy" target +# +#----------------------------------------------------------------------------- +message(STATUS "Looking for clang-tidy") +find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-3.9 clang-tidy-3.8 clang-tidy-3.7 clang-tidy-3.6 clang-tidy-3.5) + +if(CLANG_TIDY) + message(STATUS "Looking for clang-tidy - found") + + if(BUILD_EXAMPLES) + file(GLOB CT_ALL_EXAMPLES examples/*.cpp) + endif() + + if(BUILD_TESTING) + file(GLOB CT_ALL_UNIT_TESTS test/t/*/test_*.cpp) + endif() + + if(BUILD_HEADERS) + file(GLOB_RECURSE CT_ALL_INCLUDES ${CMAKE_BINARY_DIR}/header_check/osmium__*.cpp) + endif() + + if(BUILD_BENCHMARKS) + file(GLOB CT_ALL_BENCHMARKS benchmarks/*.cpp) + endif() + + if(BUILD_DATA_TESTS) + file(GLOB CT_ALL_DATA_TESTS test/data-tests/*.cpp) + endif() + + if(Osmium_DEBUG) + message(STATUS "Checking example code : ${CT_ALL_EXAMPLES}") + message(STATUS "Checking unit test code: ${CT_ALL_UNIT_TESTS}") + message(STATUS "Checking includes : ${CT_ALL_INCLUDES}") + message(STATUS "Checking benchmarks : ${CT_ALL_BENCHMARKS}") + message(STATUS "Checking data test code: ${CT_ALL_DATA_TESTS}") + endif() + + set(CT_CHECK_FILES + ${CT_ALL_EXAMPLES} + ${CT_ALL_UNIT_TESTS} + ${CT_ALL_INCLUDES} + ${CT_ALL_BENCHMARKS} + ${CT_ALL_DATA_TESTS}) + + # For a list of check options, see: + # http://clang.llvm.org/extra/clang-tidy/checks/list.html + + list(APPEND CT_CHECKS "cert-*" + "-cert-err60-cpp") # even the std lib doesn't do this + + # disabled, because it is slow +# list(APPEND CT_CHECKS "clang-analyzer-*") + + list(APPEND CT_CHECKS "google-*" + "-google-explicit-constructor" + "-google-readability-casting" + "-google-readability-function") + + list(APPEND CT_CHECKS "llvm-*" + "-llvm-include-order") + + list(APPEND CT_CHECKS "misc-*" + "-misc-argument-comment") + + list(APPEND CT_CHECKS "modernize-*") + + list(APPEND CT_CHECKS "readability-*" + "-readability-identifier-naming" + "-readability-named-parameter") + + string(REPLACE ";" "," ALL_CHECKS "${CT_CHECKS}") + + add_custom_target(clang-tidy + ${CLANG_TIDY} + -p ${CMAKE_BINARY_DIR} + -header-filter='include/osmium/.*' + -checks="${ALL_CHECKS}" + ${CT_CHECK_FILES} + ) +else() + message(STATUS "Looking for clang-tidy - not found") + message(STATUS " Build target 'clang-tidy' will not be available.") +endif() + +#----------------------------------------------------------------------------- +# +# Installation +# +# External libraries are only installed if the options are set in case they +# are installed from somewhere else. +# +#----------------------------------------------------------------------------- install(DIRECTORY include/osmium DESTINATION include) -# We only have a copy of this file so we can use older boost versions which -# don't have it. We probably don't want to install it. -#install(FILES include/boost_unicode_iterator.hpp DESTINATION include) +if(INSTALL_GDALCPP) + install(FILES include/gdalcpp.hpp DESTINATION include) +endif() + +if(INSTALL_PROTOZERO) + install(DIRECTORY include/protozero DESTINATION include) +endif() + +if(INSTALL_UTFCPP) + install(FILES include/utf8.h DESTINATION include) + install(DIRECTORY include/utf8 DESTINATION include) +endif() #----------------------------------------------------------------------------- diff --git a/third_party/libosmium/CONTRIBUTING.md b/third_party/libosmium/CONTRIBUTING.md index 323c84744..1064b94de 100644 --- a/third_party/libosmium/CONTRIBUTING.md +++ b/third_party/libosmium/CONTRIBUTING.md @@ -36,22 +36,21 @@ different. * Class names begin with uppercase chars and use CamelCase. Smaller helper classes are usually defined as struct and have lowercase names. * Macros (and only macros) are all uppercase. Use macros sparingly, usually - a constexpr is better. + a simple (maybe constexpr) inline function is better. Undef macros after use + if possible. +* Macros should only be used for controlling which parts of the code should be + included when compiling or to avoid major code repetitions. * Variables, attributes, and function names are lowercase with `underscores_between_words`. * Class attribute names start with `m_` (member). -* Template parameters are single uppercase letters or start with uppercase `T` - and use CamelCase. -* Typedefs have `names_like_this_type` which end in `_type`. -* Macros should only be used for controlling which parts of the code should be - included when compiling. * Use `descriptive_variable_names`, exceptions are well-established conventions like `i` for a loop variable. Iterators are usually called `it`. * Declare variables where they are first used (C++ style), not at the beginning of a function (old C style). * Names from external namespaces (even `std`) are always mentioned explicitly. Do not use `using` (except for `std::swap`). This way we can't even by - accident pollute the namespace of the code including Osmium. + accident pollute the namespace of the code using Osmium. +* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`. * `#include` directives appear in three "blocks" after the copyright notice. The blocks are separated by blank lines. First block contains `#include`s for standard C/C++ includes, second block for any external libs used, third @@ -64,8 +63,20 @@ different. * All files have suffix `.hpp`. * Closing } of all classes and namespaces should have a trailing comment with the name of the class/namespace. -* All constructors with one or more arguments should be declared "explicit" - unless there is a reason for them not to be. Document that reason. +* All constructors with one (or more arguments if they have a default) should + be declared "explicit" unless there is a reason for them not to be. Document + that reason. +* If a class has any of the special methods (copy/move constructor/assigment, + destructor) it should have all of them, possibly marking them as default or + deleted. +* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should + use the new `using foo_type = bar` syntax instead of the old + `typedef bar foo_type`. +* Template parameters are single uppercase letters or start with uppercase `T` + and use CamelCase. +* Always use `typename` in templates, not `class`: `template `. +* The ellipsis in variadic template never has a space to the left of it and + always has a space to the right: `template ` etc. Keep to the indentation and other styles used in the code. Use `make indent` in the toplevel directory to fix indentation and styling. It calls `astyle` @@ -81,15 +92,15 @@ about which compilers support which feature and what operating system versions or distributions have which versions of these compilers installed. GCC 4.6 - too old, not supported (Ubuntu 12.04 LTS) -GCC 4.7.2 - can probably not be supported (Debian wheezy/stable) -GCC 4.7.3 - works -GCC 4.8 - works -clang 3.0 - too old, not supported (Debian wheezy/stable, Ubuntu 12.04 LTS) -clang 3.2 - works +GCC 4.7.2 - can probably not be supported (Debian wheezy) +GCC 4.7.3 - probably works +GCC 4.8 - works and is supported from here on +clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS) +clang 3.2 - probably works +clang 3.5 - works and is supported from here on -C++11 features you should not use: -* Inherited Constructors (works only in GCC 4.8+ and clang 3.3+, not in Visual - Studio) +Use `include/osmium/util/compatibility.hpp` if there are compatibility problems +between compilers due to different C++11 support. ## Checking your code diff --git a/third_party/libosmium/README.md b/third_party/libosmium/README.md index 9676d80b7..68fc2f61b 100644 --- a/third_party/libosmium/README.md +++ b/third_party/libosmium/README.md @@ -4,7 +4,7 @@ http://osmcode.org/libosmium A fast and flexible C++ library for working with OpenStreetMap data. -[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.png)](http://travis-ci.org/osmcode/libosmium) +[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.png)](https://travis-ci.org/osmcode/libosmium) [![Build status](https://ci.appveyor.com/api/projects/status/mkbg6e6stdgq7c1b?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) Libosmium is developed on Linux, but also works on OSX and Windows (with some @@ -27,9 +27,15 @@ you need for your programs. For details see the [list of dependencies](https://github.com/osmcode/libosmium/wiki/Libosmium-dependencies). -The [protozero](https://github.com/mapbox/protozero) and -[utf8-cpp](http://utfcpp.sourceforge.net/) header-only libraries are included -in the libosmium repository. +The following external (header-only) libraries are included in the libosmium +repository: +* [gdalcpp](https://github.com/joto/gdalcpp) +* [protozero](https://github.com/mapbox/protozero) +* [utfcpp](http://utfcpp.sourceforge.net/) + +If you want (some of) those libraries to be installed along with libosmium +itself when calling `make install`, you have to use the CMake options +`INSTALL_GDALCPP`, `INSTALL_PROTOZERO`, and/or `INSTALL_UTFCPP`. ## Directories diff --git a/third_party/libosmium/appveyor.yml b/third_party/libosmium/appveyor.yml index a05c396cc..8244d98e7 100644 --- a/third_party/libosmium/appveyor.yml +++ b/third_party/libosmium/appveyor.yml @@ -9,16 +9,10 @@ environment: - config: Dev - config: RelWithDebInfo -# branches to build -branches: - # whitelist - only: - - master - shallow_clone: true # Operating system (build VM template) -os: Visual Studio 2014 CTP4 +os: Visual Studio 2015 # scripts that are called at very beginning, before repo cloning init: @@ -46,6 +40,8 @@ install: - set PATH=%LODEPSDIR%\expat\lib;%PATH% #libtiff.dll - set PATH=%LODEPSDIR%\libtiff\lib;%PATH% + #jpeg.dll + - set PATH=%LODEPSDIR%\jpeg\lib;%PATH% #zlibwapi.dll - set PATH=%LODEPSDIR%\zlib\lib;%PATH% #convert backslashes in bzip2 path to forward slashes @@ -71,27 +67,16 @@ build_script: # This will produce lots of LNK4099 warnings which can be ignored. # Unfortunately they can't be disabled, see # http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings - - cmake .. -LA -G "Visual Studio 14 Win64" + - cmake -LA -G "Visual Studio 14 Win64" -DOsmium_DEBUG=TRUE -DCMAKE_BUILD_TYPE=%config% -DBUILD_HEADERS=OFF -DBOOST_ROOT=%LODEPSDIR%\boost - -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib + -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_58.lib -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib - -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include - -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib - -DEXPAT_INCLUDE_DIR=%LODEPSDIR%\expat\include - -DBZIP2_LIBRARIES=%LIBBZIP2% - -DBZIP2_INCLUDE_DIR=%LODEPSDIR%\bzip2\include - -DGDAL_LIBRARY=%LODEPSDIR%\gdal\lib\gdal_i.lib - -DGDAL_INCLUDE_DIR=%LODEPSDIR%\gdal\include - -DGEOS_LIBRARY=%LODEPSDIR%\geos\lib\geos.lib - -DGEOS_INCLUDE_DIR=%LODEPSDIR%\geos\include - -DPROJ_LIBRARY=%LODEPSDIR%\proj\lib\proj.lib - -DPROJ_INCLUDE_DIR=%LODEPSDIR%\proj\include - -DSPARSEHASH_INCLUDE_DIR=%LODEPSDIR%\sparsehash\include - -DGETOPT_LIBRARY=%LODEPSDIR%\wingetopt\lib\wingetopt.lib - -DGETOPT_INCLUDE_DIR=%LODEPSDIR%\wingetopt\include + -DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% + -DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt + .. - msbuild libosmium.sln /p:Configuration=%config% /toolsversion:14.0 /p:Platform=x64 /p:PlatformToolset=v140 #- cmake .. -LA -G "NMake Makefiles" # -DOsmium_DEBUG=TRUE diff --git a/third_party/libosmium/cmake/FindOsmium.cmake b/third_party/libosmium/cmake/FindOsmium.cmake index bb140718b..fba8ffb92 100644 --- a/third_party/libosmium/cmake/FindOsmium.cmake +++ b/third_party/libosmium/cmake/FindOsmium.cmake @@ -19,7 +19,7 @@ # Then add the following in your CMakeLists.txt: # # find_package(Osmium REQUIRED COMPONENTS ) -# include_directories(${OSMIUM_INCLUDE_DIRS}) +# include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) # # For the substitute a space separated list of one or more of the # following components: @@ -56,31 +56,13 @@ find_path(OSMIUM_INCLUDE_DIR osmium/osm.hpp PATH_SUFFIXES include PATHS ../libosmium - ../../libosmium - libosmium ~/Library/Frameworks /Library/Frameworks - /usr/local - /usr/ /opt/local # DarwinPorts /opt ) -# Handle the QUIETLY and REQUIRED arguments and set OSMIUM_FOUND to TRUE if -# all listed variables are TRUE. -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OSMIUM REQUIRED_VARS OSMIUM_INCLUDE_DIR) - -# Copy the results to the output variables. -if(OSMIUM_FOUND) - set(OSMIUM_INCLUDE_DIRS ${OSMIUM_INCLUDE_DIR}) -else() - set(OSMIUM_INCLUDE_DIRS "") -endif() - -if(Osmium_FIND_REQUIRED AND NOT OSMIUM_FOUND) - message(FATAL_ERROR "Can not find libosmium headers, please install them or configure the paths") -endif() +set(OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}") #---------------------------------------------------------------------- # @@ -113,6 +95,7 @@ if(Osmium_USE_PBF) find_package(ZLIB) find_package(Threads) + list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND) if(ZLIB_FOUND AND Threads_FOUND) list(APPEND OSMIUM_PBF_LIBRARIES ${ZLIB_LIBRARIES} @@ -125,7 +108,6 @@ if(Osmium_USE_PBF) ${ZLIB_INCLUDE_DIR} ) else() - set(_missing_libraries 1) message(WARNING "Osmium: Can not find some libraries for PBF input/output, please install them or configure the paths.") endif() endif() @@ -138,6 +120,7 @@ if(Osmium_USE_XML) find_package(ZLIB) find_package(Threads) + list(APPEND OSMIUM_EXTRA_FIND_VARS EXPAT_FOUND BZIP2_FOUND ZLIB_FOUND Threads_FOUND) if(EXPAT_FOUND AND BZIP2_FOUND AND ZLIB_FOUND AND Threads_FOUND) list(APPEND OSMIUM_XML_LIBRARIES ${EXPAT_LIBRARIES} @@ -151,7 +134,6 @@ if(Osmium_USE_XML) ${ZLIB_INCLUDE_DIR} ) else() - set(_missing_libraries 1) message(WARNING "Osmium: Can not find some libraries for XML input/output, please install them or configure the paths.") endif() endif() @@ -172,12 +154,12 @@ if(Osmium_USE_GEOS) find_path(GEOS_INCLUDE_DIR geos/geom.h) find_library(GEOS_LIBRARY NAMES geos) + list(APPEND OSMIUM_EXTRA_FIND_VARS GEOS_INCLUDE_DIR GEOS_LIBRARY) if(GEOS_INCLUDE_DIR AND GEOS_LIBRARY) SET(GEOS_FOUND 1) list(APPEND OSMIUM_LIBRARIES ${GEOS_LIBRARY}) list(APPEND OSMIUM_INCLUDE_DIRS ${GEOS_INCLUDE_DIR}) else() - set(_missing_libraries 1) message(WARNING "Osmium: GEOS library is required but not found, please install it or configure the paths.") endif() endif() @@ -187,11 +169,11 @@ endif() if(Osmium_USE_GDAL) find_package(GDAL) + list(APPEND OSMIUM_EXTRA_FIND_VARS GDAL_FOUND) if(GDAL_FOUND) list(APPEND OSMIUM_LIBRARIES ${GDAL_LIBRARIES}) list(APPEND OSMIUM_INCLUDE_DIRS ${GDAL_INCLUDE_DIRS}) else() - set(_missing_libraries 1) message(WARNING "Osmium: GDAL library is required but not found, please install it or configure the paths.") endif() endif() @@ -202,12 +184,12 @@ if(Osmium_USE_PROJ) find_path(PROJ_INCLUDE_DIR proj_api.h) find_library(PROJ_LIBRARY NAMES proj) + list(APPEND OSMIUM_EXTRA_FIND_VARS PROJ_INCLUDE_DIR PROJ_LIBRARY) if(PROJ_INCLUDE_DIR AND PROJ_LIBRARY) set(PROJ_FOUND 1) list(APPEND OSMIUM_LIBRARIES ${PROJ_LIBRARY}) list(APPEND OSMIUM_INCLUDE_DIRS ${PROJ_INCLUDE_DIR}) else() - set(_missing_libraries 1) message(WARNING "Osmium: PROJ.4 library is required but not found, please install it or configure the paths.") endif() endif() @@ -217,21 +199,19 @@ endif() if(Osmium_USE_SPARSEHASH) find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable) + list(APPEND OSMIUM_EXTRA_FIND_VARS SPARSEHASH_INCLUDE_DIR) if(SPARSEHASH_INCLUDE_DIR) # Find size of sparsetable::size_type. This does not work on older # CMake versions because they can do this check only in C, not in C++. - include(CheckTypeSize) - set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR}) - set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable") - check_type_size("google::sparsetable::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX) - set(CMAKE_EXTRA_INCLUDE_FILES) - set(CMAKE_REQUIRED_INCLUDES) - - # Falling back to checking size_t if google::sparsetable::size_type - # could not be checked. - if(SPARSETABLE_SIZE_TYPE STREQUAL "") - check_type_size("void*" VOID_PTR_SIZE) - set(SPARSETABLE_SIZE_TYPE ${VOID_PTR_SIZE}) + if (NOT CMAKE_VERSION VERSION_LESS 3.0) + include(CheckTypeSize) + set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR}) + set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable") + check_type_size("google::sparsetable::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX) + set(CMAKE_EXTRA_INCLUDE_FILES) + set(CMAKE_REQUIRED_INCLUDES) + else() + set(SPARSETABLE_SIZE_TYPE ${CMAKE_SIZEOF_VOID_P}) endif() # Sparsetable::size_type must be at least 8 bytes (64bit), otherwise @@ -244,7 +224,6 @@ if(Osmium_USE_SPARSEHASH) message(WARNING "Osmium: Disabled Google SparseHash library on 32bit system (size_type=${SPARSETABLE_SIZE_TYPE}).") endif() else() - set(_missing_libraries 1) message(WARNING "Osmium: Google SparseHash library is required but not found, please install it or configure the paths.") endif() endif() @@ -274,9 +253,14 @@ endif() # Check that all required libraries are available # #---------------------------------------------------------------------- -if(Osmium_FIND_REQUIRED AND _missing_libraries) - message(FATAL_ERROR "Required library or libraries missing. Aborting.") +if (OSMIUM_EXTRA_FIND_VARS) + list(REMOVE_DUPLICATES OSMIUM_EXTRA_FIND_VARS) endif() +# Handle the QUIETLY and REQUIRED arguments and set OSMIUM_FOUND to TRUE if +# all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Osmium REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS}) +unset(OSMIUM_EXTRA_FIND_VARS) #---------------------------------------------------------------------- # diff --git a/third_party/libosmium/cmake/iwyu.sh b/third_party/libosmium/cmake/iwyu.sh index f7d8a15e8..ceea106c3 100755 --- a/third_party/libosmium/cmake/iwyu.sh +++ b/third_party/libosmium/cmake/iwyu.sh @@ -16,7 +16,7 @@ echo "INCLUDE WHAT YOU USE REPORT:" >$log allok=yes -for file in `find include/osmium -name \*.hpp`; do +for file in `find include/osmium -name \*.hpp | sort`; do mkdir -p `dirname build/check_reports/$file` ifile="build/check_reports/${file%.hpp}.iwyu" $cmdline $file >$ifile 2>&1 diff --git a/third_party/libosmium/examples/CMakeLists.txt b/third_party/libosmium/examples/CMakeLists.txt index c9f59603d..a04a843f2 100644 --- a/third_party/libosmium/examples/CMakeLists.txt +++ b/third_party/libosmium/examples/CMakeLists.txt @@ -14,12 +14,10 @@ set(EXAMPLES count create_node_cache debug + filter_discussions index read serdump - toogr - toogr2 - toogr2_exp use_node_cache CACHE STRING "Example programs" ) @@ -30,7 +28,7 @@ set(EXAMPLES # Examples depending on wingetopt # #----------------------------------------------------------------------------- -set(GETOPT_EXAMPLES area_test convert serdump toogr toogr2 toogr2_exp) +set(GETOPT_EXAMPLES area_test convert serdump) if(NOT GETOPT_MISSING) foreach(example ${GETOPT_EXAMPLES}) list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY}) @@ -74,27 +72,6 @@ else() endif() -#----------------------------------------------------------------------------- -# -# Examples depending on GDAL/PROJ.4/SparseHash -# -#----------------------------------------------------------------------------- -set(OGR_EXAMPLES toogr toogr2 toogr2_exp) - -if(GDAL_FOUND AND PROJ_FOUND AND SPARSEHASH_FOUND) - foreach(example ${OGR_EXAMPLES}) - list(APPEND EXAMPLE_LIBS_${example} ${GDAL_LIBRARIES}) - list(APPEND EXAMPLE_LIBS_${example} ${PROJ_LIBRARIES}) - endforeach() -else() - message(STATUS "Configuring examples - Skipping examples because GDAL and/or Proj.4 and/or SparseHash not found:") - foreach(example ${OGR_EXAMPLES}) - message(STATUS " - osmium_${example}") - list(REMOVE_ITEM EXAMPLES ${example}) - endforeach() -endif() - - #----------------------------------------------------------------------------- # # Configure examples diff --git a/third_party/libosmium/examples/osmium_area_test.cpp b/third_party/libosmium/examples/osmium_area_test.cpp index f072c5e54..e9b7a18ad 100644 --- a/third_party/libosmium/examples/osmium_area_test.cpp +++ b/third_party/libosmium/examples/osmium_area_test.cpp @@ -99,13 +99,13 @@ int main(int argc, char* argv[]) { osmium::area::Assembler::config_type assembler_config; osmium::area::MultipolygonCollector collector(assembler_config); - std::cout << "Pass 1...\n"; + std::cerr << "Pass 1...\n"; osmium::io::Reader reader1(infile, osmium::osm_entity_bits::relation); collector.read_relations(reader1); reader1.close(); - std::cout << "Pass 1 done\n"; + std::cerr << "Pass 1 done\n"; - std::cout << "Memory:\n"; + std::cerr << "Memory:\n"; collector.used_memory(); index_pos_type index_pos; @@ -113,15 +113,15 @@ int main(int argc, char* argv[]) { location_handler_type location_handler(index_pos, index_neg); location_handler.ignore_errors(); // XXX - std::cout << "Pass 2...\n"; + std::cerr << "Pass 2...\n"; osmium::io::Reader reader2(infile); osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) { osmium::apply(buffer, handler); })); reader2.close(); - std::cout << "Pass 2 done\n"; + std::cerr << "Pass 2 done\n"; - std::cout << "Memory:\n"; + std::cerr << "Memory:\n"; collector.used_memory(); std::vector incomplete_relations = collector.get_incomplete_relations(); diff --git a/third_party/libosmium/examples/osmium_filter_discussions.cpp b/third_party/libosmium/examples/osmium_filter_discussions.cpp new file mode 100644 index 000000000..bba25b752 --- /dev/null +++ b/third_party/libosmium/examples/osmium_filter_discussions.cpp @@ -0,0 +1,72 @@ +/* + + Read OSM changesets with discussions from a changeset dump like the one + you get from http://planet.osm.org/planet/discussions-latest.osm.bz2 + and write out only those changesets which have discussions (ie comments). + + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::copy_if +#include // for std::cout, std::cerr + +// we want to read OSM files in XML format +// (other formats don't support full changesets, so only XML is needed here) +#include +#include + +// we want to write OSM files in XML format +#include +#include + +// we want to support any compressioon (.gz2 and .bz2) +#include + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cout << "Usage: " << argv[0] << " INFILE OUTFILE\n"; + exit(1); + } + + // The input file, deduce file format from file suffix + osmium::io::File infile(argv[1]); + + // The output file, force class XML OSM file format + osmium::io::File outfile(argv[2], "osm"); + + // Initialize Reader for the input file. + // Read only changesets (will ignore nodes, ways, and + // relations if there are any). + osmium::io::Reader reader(infile, osmium::osm_entity_bits::changeset); + + // Get the header from the input file + osmium::io::Header header = reader.header(); + + // Initialize writer for the output file. Use the header from the input + // file for the output file. This will copy over some header information. + // The last parameter will tell the writer that it is allowed to overwrite + // an existing file. Without it, it will refuse to do so. + osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow); + + // Create range of input iterators that will iterator over all changesets + // delivered from input file through the "reader". + auto input_range = osmium::io::make_input_iterator_range(reader); + + // Create an output iterator writing through the "writer" object to the + // output file. + auto output_iterator = osmium::io::make_output_iterator(writer); + + // Copy all changesets from input to output that have at least one comment. + std::copy_if(input_range.begin(), input_range.end(), output_iterator, [](const osmium::Changeset& changeset) { + return changeset.num_comments() > 0; + }); + + // Explicitly close the writer and reader. Will throw an exception if + // there is a problem. If you wait for the destructor to close the writer + // and reader, you will not notice the problem, because destructors must + // not throw. + writer.close(); + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_toogr.cpp b/third_party/libosmium/examples/osmium_toogr.cpp deleted file mode 100644 index 7c5a965c5..000000000 --- a/third_party/libosmium/examples/osmium_toogr.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - - This is an example tool that converts OSM data to some output format - like Spatialite or Shapefiles using the OGR library. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include - -#include -#include -#include - -#include -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; -typedef osmium::index::map::Map index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -class MyOGRHandler : public osmium::handler::Handler { - - OGRDataSource* m_data_source; - OGRLayer* m_layer_point; - OGRLayer* m_layer_linestring; - - osmium::geom::OGRFactory<> m_factory; - -public: - - MyOGRHandler(const std::string& driver_name, const std::string& filename) { - - OGRRegisterAll(); - - OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str()); - if (!driver) { - std::cerr << driver_name << " driver not available.\n"; - exit(1); - } - - CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - const char* options[] = { "SPATIALITE=TRUE", nullptr }; - m_data_source = driver->CreateDataSource(filename.c_str(), const_cast(options)); - if (!m_data_source) { - std::cerr << "Creation of output file failed.\n"; - exit(1); - } - - OGRSpatialReference sparef; - sparef.SetWellKnownGeogCS("WGS84"); - m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr); - if (!m_layer_point) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_id("id", OFTReal); - layer_point_field_id.SetWidth(10); - - if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_operator("operator", OFTString); - layer_point_field_operator.SetWidth(30); - - if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) { - std::cerr << "Creating operator field failed.\n"; - exit(1); - } - - /* Transactions might make things faster, then again they might not. - Feel free to experiment and benchmark and report back. */ - m_layer_point->StartTransaction(); - - m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr); - if (!m_layer_linestring) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_id("id", OFTReal); - layer_linestring_field_id.SetWidth(10); - - if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_type("type", OFTString); - layer_linestring_field_type.SetWidth(30); - - if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - m_layer_linestring->StartTransaction(); - } - - ~MyOGRHandler() { - m_layer_linestring->CommitTransaction(); - m_layer_point->CommitTransaction(); - OGRDataSource::DestroyDataSource(m_data_source); - OGRCleanupAll(); - } - - void node(const osmium::Node& node) { - const char* amenity = node.tags().get_value_by_key("amenity"); - if (amenity && !strcmp(amenity, "post_box")) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn()); - std::unique_ptr ogr_point = m_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - feature->SetField("operator", node.tags().get_value_by_key("operator")); - - if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } - } - - void way(const osmium::Way& way) { - const char* highway = way.tags().get_value_by_key("highway"); - if (highway) { - try { - std::unique_ptr ogr_linestring = m_factory.create_linestring(way); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id", static_cast(way.id())); - feature->SetField("type", highway); - - if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } catch (osmium::geometry_error&) { - std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; - } - } - } - -}; - -/* ================================================== */ - -void print_help() { - std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \ - << "If INFILE is not given stdin is assumed.\n" \ - << "If OUTFILE is not given 'ogr_out' is used.\n" \ - << "\nOptions:\n" \ - << " -h, --help This help message\n" \ - << " -l, --location_store=TYPE Set location store\n" \ - << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n" \ - << " -L See available location stores\n"; -} - -int main(int argc, char* argv[]) { - const auto& map_factory = osmium::index::MapFactory::instance(); - - static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"format", required_argument, 0, 'f'}, - {"location_store", required_argument, 0, 'l'}, - {"list_location_stores", no_argument, 0, 'L'}, - {0, 0, 0, 0} - }; - - std::string output_format { "SQLite" }; - std::string location_store { "sparse_mem_array" }; - - while (true) { - int c = getopt_long(argc, argv, "hf:l:L", long_options, 0); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'f': - output_format = optarg; - break; - case 'l': - location_store = optarg; - break; - case 'L': - std::cout << "Available map types:\n"; - for (const auto& map_type : map_factory.map_types()) { - std::cout << " " << map_type << "\n"; - } - exit(0); - default: - exit(1); - } - } - - std::string input_filename; - std::string output_filename("ogr_out"); - int remaining_args = argc - optind; - if (remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl; - exit(1); - } else if (remaining_args == 2) { - input_filename = argv[optind]; - output_filename = argv[optind+1]; - } else if (remaining_args == 1) { - input_filename = argv[optind]; - } else { - input_filename = "-"; - } - - osmium::io::Reader reader(input_filename); - - std::unique_ptr index_pos = map_factory.create_map(location_store); - index_neg_type index_neg; - location_handler_type location_handler(*index_pos, index_neg); - location_handler.ignore_errors(); - - MyOGRHandler ogr_handler(output_format, output_filename); - - osmium::apply(reader, location_handler, ogr_handler); - reader.close(); - - int locations_fd = open("locations.dump", O_WRONLY | O_CREAT, 0644); - if (locations_fd < 0) { - throw std::system_error(errno, std::system_category(), "Open failed"); - } - index_pos->dump_as_list(locations_fd); - close(locations_fd); -} - diff --git a/third_party/libosmium/examples/osmium_toogr2.cpp b/third_party/libosmium/examples/osmium_toogr2.cpp deleted file mode 100644 index e1b505688..000000000 --- a/third_party/libosmium/examples/osmium_toogr2.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - - This is an example tool that converts OSM data to some output format - like Spatialite or Shapefiles using the OGR library. - - This version does multipolygon handling (in contrast to the osmium_toogr - example which doesn't). - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include - -// usually you only need one or two of these -#include -#include - -#include -#include -#include -#include - -#include -//#include -#include -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; - -typedef osmium::index::map::SparseMemArray index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -class MyOGRHandler : public osmium::handler::Handler { - - OGRDataSource* m_data_source; - OGRLayer* m_layer_point; - OGRLayer* m_layer_linestring; - OGRLayer* m_layer_polygon; - - // Choose one of the following: - - // 1. Use WGS84, do not project coordinates. - //osmium::geom::OGRFactory<> m_factory {}; - - // 2. Project coordinates into "Web Mercator". - osmium::geom::OGRFactory m_factory; - - // 3. Use any projection that the proj library can handle. - // (Initialize projection with EPSG code or proj string). - // In addition you need to link with "-lproj" and add - // #include . - //osmium::geom::OGRFactory m_factory {osmium::geom::Projection(3857)}; - -public: - - MyOGRHandler(const std::string& driver_name, const std::string& filename) { - - OGRRegisterAll(); - - OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str()); - if (!driver) { - std::cerr << driver_name << " driver not available.\n"; - exit(1); - } - - CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - const char* options[] = { "SPATIALITE=TRUE", nullptr }; - m_data_source = driver->CreateDataSource(filename.c_str(), const_cast(options)); - if (!m_data_source) { - std::cerr << "Creation of output file failed.\n"; - exit(1); - } - - OGRSpatialReference sparef; - sparef.importFromProj4(m_factory.proj_string().c_str()); - - m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr); - if (!m_layer_point) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_id("id", OFTReal); - layer_point_field_id.SetWidth(10); - - if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_operator("operator", OFTString); - layer_point_field_operator.SetWidth(30); - - if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) { - std::cerr << "Creating operator field failed.\n"; - exit(1); - } - - /* Transactions might make things faster, then again they might not. - Feel free to experiment and benchmark and report back. */ - m_layer_point->StartTransaction(); - - m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr); - if (!m_layer_linestring) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_id("id", OFTReal); - layer_linestring_field_id.SetWidth(10); - - if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_type("type", OFTString); - layer_linestring_field_type.SetWidth(30); - - if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - m_layer_linestring->StartTransaction(); - - m_layer_polygon = m_data_source->CreateLayer("buildings", &sparef, wkbMultiPolygon, nullptr); - if (!m_layer_polygon) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_id("id", OFTInteger); - layer_polygon_field_id.SetWidth(10); - - if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_type("type", OFTString); - layer_polygon_field_type.SetWidth(30); - - if (m_layer_polygon->CreateField(&layer_polygon_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - m_layer_polygon->StartTransaction(); - } - - ~MyOGRHandler() { - m_layer_polygon->CommitTransaction(); - m_layer_linestring->CommitTransaction(); - m_layer_point->CommitTransaction(); - OGRDataSource::DestroyDataSource(m_data_source); - OGRCleanupAll(); - } - - void node(const osmium::Node& node) { - const char* amenity = node.tags()["amenity"]; - if (amenity && !strcmp(amenity, "post_box")) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn()); - std::unique_ptr ogr_point = m_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - feature->SetField("operator", node.tags()["operator"]); - - if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } - } - - void way(const osmium::Way& way) { - const char* highway = way.tags()["highway"]; - if (highway) { - try { - std::unique_ptr ogr_linestring = m_factory.create_linestring(way); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id", static_cast(way.id())); - feature->SetField("type", highway); - - if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } catch (osmium::geometry_error&) { - std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; - } - } - } - - void area(const osmium::Area& area) { - const char* building = area.tags()["building"]; - if (building) { - try { - std::unique_ptr ogr_polygon = m_factory.create_multipolygon(area); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn()); - feature->SetGeometry(ogr_polygon.get()); - feature->SetField("id", static_cast(area.id())); - feature->SetField("type", building); - - std::string type = ""; - if (area.from_way()) { - type += "w"; - } else { - type += "r"; - } - feature->SetField("type", type.c_str()); - - if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } catch (osmium::geometry_error&) { - std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n"; - } - } - } - -}; - -/* ================================================== */ - -void print_help() { - std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \ - << "If INFILE is not given stdin is assumed.\n" \ - << "If OUTFILE is not given 'ogr_out' is used.\n" \ - << "\nOptions:\n" \ - << " -h, --help This help message\n" \ - << " -d, --debug Enable debug output\n" \ - << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n"; -} - -int main(int argc, char* argv[]) { - static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"debug", no_argument, 0, 'd'}, - {"format", required_argument, 0, 'f'}, - {0, 0, 0, 0} - }; - - std::string output_format("SQLite"); - bool debug = false; - - while (true) { - int c = getopt_long(argc, argv, "hdf:", long_options, 0); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'd': - debug = true; - break; - case 'f': - output_format = optarg; - break; - default: - exit(1); - } - } - - std::string input_filename; - std::string output_filename("ogr_out"); - int remaining_args = argc - optind; - if (remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl; - exit(1); - } else if (remaining_args == 2) { - input_filename = argv[optind]; - output_filename = argv[optind+1]; - } else if (remaining_args == 1) { - input_filename = argv[optind]; - } else { - input_filename = "-"; - } - - osmium::area::Assembler::config_type assembler_config; - assembler_config.enable_debug_output(debug); - osmium::area::MultipolygonCollector collector(assembler_config); - - std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1(input_filename); - collector.read_relations(reader1); - reader1.close(); - std::cerr << "Pass 1 done\n"; - - index_pos_type index_pos; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); - - MyOGRHandler ogr_handler(output_format, output_filename); - - std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2(input_filename); - - osmium::apply(reader2, location_handler, ogr_handler, collector.handler([&ogr_handler](const osmium::memory::Buffer& area_buffer) { - osmium::apply(area_buffer, ogr_handler); - })); - - reader2.close(); - std::cerr << "Pass 2 done\n"; - - std::vector incomplete_relations = collector.get_incomplete_relations(); - if (!incomplete_relations.empty()) { - std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; - for (const auto* relation : incomplete_relations) { - std::cerr << " " << relation->id(); - } - std::cerr << "\n"; - } -} - diff --git a/third_party/libosmium/examples/osmium_toogr2_exp.cpp b/third_party/libosmium/examples/osmium_toogr2_exp.cpp deleted file mode 100644 index db8d5cf4e..000000000 --- a/third_party/libosmium/examples/osmium_toogr2_exp.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - - This is an example tool that converts OSM data to some output format - like Spatialite or Shapefiles using the OGR library. - - This version does multipolygon handling (in contrast to the osmium_toogr - example which doesn't). - - This version (..._exp) uses a new experimental unsupported interface. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include - -#include - -#include - -#include -//#include -#include -#include -#include -#include - -typedef osmium::index::map::SparseMemArray index_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -class MyOGRHandler : public osmium::handler::Handler { - - OGRDataSource* m_data_source; - OGRLayer* m_layer_point; - OGRLayer* m_layer_linestring; - OGRLayer* m_layer_polygon; - - // Choose one of the following: - - // 1. Use WGS84, do not project coordinates. - //osmium::geom::OGRFactory<> m_factory {}; - - // 2. Project coordinates into "Web Mercator". - osmium::geom::OGRFactory m_factory; - - // 3. Use any projection that the proj library can handle. - // (Initialize projection with EPSG code or proj string). - // In addition you need to link with "-lproj" and add - // #include . - //osmium::geom::OGRFactory m_factory {osmium::geom::Projection(3857)}; - -public: - - MyOGRHandler(const std::string& driver_name, const std::string& filename) { - - OGRRegisterAll(); - - OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str()); - if (!driver) { - std::cerr << driver_name << " driver not available.\n"; - exit(1); - } - - CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - const char* options[] = { "SPATIALITE=TRUE", nullptr }; - m_data_source = driver->CreateDataSource(filename.c_str(), const_cast(options)); - if (!m_data_source) { - std::cerr << "Creation of output file failed.\n"; - exit(1); - } - - OGRSpatialReference sparef; - sparef.importFromProj4(m_factory.proj_string().c_str()); - - m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr); - if (!m_layer_point) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_id("id", OFTReal); - layer_point_field_id.SetWidth(10); - - if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_operator("operator", OFTString); - layer_point_field_operator.SetWidth(30); - - if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) { - std::cerr << "Creating operator field failed.\n"; - exit(1); - } - - /* Transactions might make things faster, then again they might not. - Feel free to experiment and benchmark and report back. */ - m_layer_point->StartTransaction(); - - m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr); - if (!m_layer_linestring) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_id("id", OFTReal); - layer_linestring_field_id.SetWidth(10); - - if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_type("type", OFTString); - layer_linestring_field_type.SetWidth(30); - - if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - m_layer_linestring->StartTransaction(); - - m_layer_polygon = m_data_source->CreateLayer("buildings", &sparef, wkbMultiPolygon, nullptr); - if (!m_layer_polygon) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_id("id", OFTInteger); - layer_polygon_field_id.SetWidth(10); - - if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_type("type", OFTString); - layer_polygon_field_type.SetWidth(30); - - if (m_layer_polygon->CreateField(&layer_polygon_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - m_layer_polygon->StartTransaction(); - } - - ~MyOGRHandler() { - m_layer_polygon->CommitTransaction(); - m_layer_linestring->CommitTransaction(); - m_layer_point->CommitTransaction(); - OGRDataSource::DestroyDataSource(m_data_source); - OGRCleanupAll(); - } - - void node(const osmium::Node& node) { - const char* amenity = node.tags()["amenity"]; - if (amenity && !strcmp(amenity, "post_box")) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn()); - std::unique_ptr ogr_point = m_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - feature->SetField("operator", node.tags()["operator"]); - - if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } - } - - void way(const osmium::Way& way) { - const char* highway = way.tags()["highway"]; - if (highway) { - try { - std::unique_ptr ogr_linestring = m_factory.create_linestring(way); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id", static_cast(way.id())); - feature->SetField("type", highway); - - if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } catch (osmium::geometry_error&) { - std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; - } - } - } - - void area(const osmium::Area& area) { - const char* building = area.tags()["building"]; - if (building) { - try { - std::unique_ptr ogr_polygon = m_factory.create_multipolygon(area); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn()); - feature->SetGeometry(ogr_polygon.get()); - feature->SetField("id", static_cast(area.id())); - feature->SetField("type", building); - - std::string type = ""; - if (area.from_way()) { - type += "w"; - } else { - type += "r"; - } - feature->SetField("type", type.c_str()); - - if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); - } catch (osmium::geometry_error&) { - std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n"; - } - } - } - -}; - -/* ================================================== */ - -void print_help() { - std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \ - << "If INFILE is not given stdin is assumed.\n" \ - << "If OUTFILE is not given 'ogr_out' is used.\n" \ - << "\nOptions:\n" \ - << " -h, --help This help message\n" \ - << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n"; -} - -int main(int argc, char* argv[]) { - static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"format", required_argument, 0, 'f'}, - {0, 0, 0, 0} - }; - - std::string output_format("SQLite"); - - while (true) { - int c = getopt_long(argc, argv, "hf:", long_options, 0); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'f': - output_format = optarg; - break; - default: - exit(1); - } - } - - std::string input_filename; - std::string output_filename("ogr_out"); - int remaining_args = argc - optind; - if (remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl; - exit(1); - } else if (remaining_args == 2) { - input_filename = argv[optind]; - output_filename = argv[optind+1]; - } else if (remaining_args == 1) { - input_filename = argv[optind]; - } else { - input_filename = "-"; - } - - index_type index_pos; - location_handler_type location_handler(index_pos); - osmium::experimental::FlexReader exr(input_filename, location_handler, osmium::osm_entity_bits::object); - - MyOGRHandler ogr_handler(output_format, output_filename); - - while (auto buffer = exr.read()) { - osmium::apply(buffer, ogr_handler); - } - - exr.close(); - - std::vector incomplete_relations = exr.collector().get_incomplete_relations(); - if (!incomplete_relations.empty()) { - std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; - for (const auto* relation : incomplete_relations) { - std::cerr << " " << relation->id(); - } - std::cerr << "\n"; - } -} - diff --git a/third_party/libosmium/include/gdalcpp.hpp b/third_party/libosmium/include/gdalcpp.hpp new file mode 100644 index 000000000..1502f2f0c --- /dev/null +++ b/third_party/libosmium/include/gdalcpp.hpp @@ -0,0 +1,406 @@ +#ifndef GDALCPP_HPP +#define GDALCPP_HPP + +/* + +C++11 wrapper classes for GDAL/OGR. + +Version 1.1.1 + +https://github.com/joto/gdalcpp + +Copyright 2015 Jochen Topf + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include +#include +#include + +#include +#include +#include +#include + +namespace gdalcpp { + +#if GDAL_VERSION_MAJOR >= 2 + using gdal_driver_type = GDALDriver; + using gdal_dataset_type = GDALDataset; +#else + using gdal_driver_type = OGRSFDriver; + using gdal_dataset_type = OGRDataSource; +#endif + + /** + * Exception thrown for all errors in this class. + */ + class gdal_error : public std::runtime_error { + + std::string m_driver; + std::string m_dataset; + std::string m_layer; + std::string m_field; + OGRErr m_error; + + public: + + gdal_error(const std::string& message, + OGRErr error, + const std::string& driver = "", + const std::string& dataset = "", + const std::string& layer = "", + const std::string& field = "") : + std::runtime_error(message), + m_driver(driver), + m_dataset(dataset), + m_layer(layer), + m_field(field), + m_error(error) { + } + + const std::string& driver() const { + return m_driver; + } + + const std::string& dataset() const { + return m_dataset; + } + + const std::string& layer() const { + return m_layer; + } + + const std::string& field() const { + return m_field; + } + + OGRErr error() const { + return m_error; + } + + }; // class gdal_error + + namespace detail { + + struct init_wrapper { + init_wrapper() { OGRRegisterAll(); } + ~init_wrapper() { OGRCleanupAll(); } + }; + + struct init_library { + init_library() { + static init_wrapper iw; + } + }; + + class Driver : private init_library { + + gdal_driver_type* m_driver; + + public: + + Driver(const std::string& driver_name) : + init_library(), +#if GDAL_VERSION_MAJOR >= 2 + m_driver(GetGDALDriverManager()->GetDriverByName(driver_name.c_str())) { +#else + m_driver(OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str())) { +#endif + if (!m_driver) { + throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name); + } + } + + gdal_driver_type& get() const { + return *m_driver; + } + + }; // struct Driver + + struct Options { + + std::vector m_options; + std::unique_ptr m_ptrs; + + Options(const std::vector& options) : + m_options(options), + m_ptrs(new const char*[options.size()+1]) { + std::transform(m_options.begin(), m_options.end(), m_ptrs.get(), [&](const std::string& s) { + return s.data(); + }); + m_ptrs[options.size()] = nullptr; + } + + char** get() const { + return const_cast(m_ptrs.get()); + } + + }; // struct Options + + } // namespace detail + + class SRS { + + OGRSpatialReference m_spatial_reference; + + public: + + SRS() : + m_spatial_reference() { + auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84"); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result); + } + } + + explicit SRS(int epsg) : + m_spatial_reference() { + auto result = m_spatial_reference.importFromEPSG(epsg); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result); + } + } + + explicit SRS(const char* name) : + m_spatial_reference() { + auto result = m_spatial_reference.importFromProj4(name); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result); + } + } + + explicit SRS(const std::string& name) : + m_spatial_reference() { + auto result = m_spatial_reference.importFromProj4(name.c_str()); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result); + } + } + + explicit SRS(const OGRSpatialReference& spatial_reference) : + m_spatial_reference(spatial_reference) { + } + + OGRSpatialReference& get() { + return m_spatial_reference; + } + + const OGRSpatialReference& get() const { + return m_spatial_reference; + } + + }; // class SRS + + class Dataset { + + struct gdal_dataset_deleter { + + void operator()(gdal_dataset_type* ds) { +#if GDAL_VERSION_MAJOR >= 2 + GDALClose(ds); +#else + OGRDataSource::DestroyDataSource(ds); +#endif + } + + }; // struct gdal_dataset_deleter + + std::string m_driver_name; + std::string m_dataset_name; + detail::Options m_options; + SRS m_srs; + std::unique_ptr m_dataset; + + public: + + Dataset(const std::string& driver_name, const std::string& dataset_name, const SRS& srs = SRS{}, const std::vector& options = {}) : + m_driver_name(driver_name), + m_dataset_name(dataset_name), + m_options(options), + m_srs(srs), +#if GDAL_VERSION_MAJOR >= 2 + m_dataset(detail::Driver(driver_name).get().Create(dataset_name.c_str(), 0, 0, 0, GDT_Unknown, m_options.get())) { +#else + m_dataset(detail::Driver(driver_name).get().CreateDataSource(dataset_name.c_str(), m_options.get())) { +#endif + if (!m_dataset) { + throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name, dataset_name); + } + } + + const std::string& driver_name() const { + return m_driver_name; + } + + const std::string& dataset_name() const { + return m_dataset_name; + } + + gdal_dataset_type& get() const { + return *m_dataset; + } + + SRS& srs() { + return m_srs; + } + + void exec(const char* sql) { + auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr); + if (result) { + m_dataset->ReleaseResultSet(result); + } + } + + void exec(const std::string& sql) { + exec(sql.c_str()); + } + + + Dataset& start_transaction() { +#if GDAL_VERSION_MAJOR >= 2 + m_dataset->StartTransaction(); +#endif + return *this; + } + + Dataset& commit_transaction() { +#if GDAL_VERSION_MAJOR >= 2 + m_dataset->CommitTransaction(); +#endif + return *this; + } + + }; // class Dataset + + class Layer { + + detail::Options m_options; + Dataset& m_dataset; + OGRLayer* m_layer; + + public: + + Layer(Dataset& dataset, const std::string& layer_name, OGRwkbGeometryType type, const std::vector& options = {}) : + m_options(options), + m_dataset(dataset), + m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get())) { + if (!m_layer) { + throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE, + dataset.driver_name(), dataset.dataset_name(), layer_name); + } + } + + OGRLayer& get() { + return *m_layer; + } + + const OGRLayer& get() const { + return *m_layer; + } + + Dataset& dataset() const { + return m_dataset; + } + + const char* name() const { + return m_layer->GetName(); + } + + Layer& add_field(const std::string& field_name, OGRFieldType type, int width, int precision=0) { + OGRFieldDefn field(field_name.c_str(), type); + field.SetWidth(width); + field.SetPrecision(precision); + + if (m_layer->CreateField(&field) != OGRERR_NONE) { + throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'", OGRERR_NONE, + m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name); + } + + return *this; + } + + Layer& start_transaction() { + OGRErr result = m_layer->StartTransaction(); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); + } + return *this; + } + + Layer& commit_transaction() { + OGRErr result = m_layer->CommitTransaction(); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); + } + return *this; + } + + }; // class Layer + + class Feature { + + Layer& m_layer; + OGRFeature m_feature; + + public: + + Feature(Layer& layer, std::unique_ptr&& geometry) : + m_layer(layer), + m_feature(m_layer.get().GetLayerDefn()) { + OGRErr result = m_feature.SetGeometryDirectly(geometry.release()); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name()); + } + } + + void add_to_layer() { + OGRErr result = m_layer.get().CreateFeature(&m_feature); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("creating feature in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name()); + } + } + + template + Feature& set_field(int n, T&& arg) { + m_feature.SetField(n, std::forward(arg)); + return *this; + } + + template + Feature& set_field(const char* name, T&& arg) { + m_feature.SetField(name, std::forward(arg)); + return *this; + } + + }; // class Feature + +} // namespace gdalcpp + +#endif // GDALCPP_HPP diff --git a/third_party/libosmium/include/osmium/area/assembler.hpp b/third_party/libosmium/include/osmium/area/assembler.hpp index f17299109..a2d1c8ebd 100644 --- a/third_party/libosmium/include/osmium/area/assembler.hpp +++ b/third_party/libosmium/include/osmium/area/assembler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,9 +34,13 @@ DEALINGS IN THE SOFTWARE. */ #include +#include +#include #include #include #include +#include +#include #include #include @@ -228,7 +232,7 @@ namespace osmium { if (!ring.closed()) { open_rings = true; if (m_config.problem_reporter) { - m_config.problem_reporter->report_ring_not_closed(ring.get_segment_front().first().location(), ring.get_segment_back().second().location()); + m_config.problem_reporter->report_ring_not_closed(ring.get_node_ref_front().location(), ring.get_node_ref_back().location()); } } } @@ -244,14 +248,14 @@ namespace osmium { * true. */ bool possibly_combine_rings_back(ProtoRing& ring) { - const osmium::NodeRef& nr = ring.get_segment_back().second(); + const osmium::NodeRef& nr = ring.get_node_ref_back(); if (debug()) { std::cerr << " possibly_combine_rings_back()\n"; } for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { if (&*it != &ring && !it->closed()) { - if (has_same_location(nr, it->get_segment_front().first())) { + if (has_same_location(nr, it->get_node_ref_front())) { if (debug()) { std::cerr << " ring.last=it->first\n"; } @@ -259,7 +263,7 @@ namespace osmium { m_rings.erase(it); return true; } - if (has_same_location(nr, it->get_segment_back().second())) { + if (has_same_location(nr, it->get_node_ref_back())) { if (debug()) { std::cerr << " ring.last=it->last\n"; } @@ -280,14 +284,14 @@ namespace osmium { * true. */ bool possibly_combine_rings_front(ProtoRing& ring) { - const osmium::NodeRef& nr = ring.get_segment_front().first(); + const osmium::NodeRef& nr = ring.get_node_ref_front(); if (debug()) { std::cerr << " possibly_combine_rings_front()\n"; } for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { if (&*it != &ring && !it->closed()) { - if (has_same_location(nr, it->get_segment_back().second())) { + if (has_same_location(nr, it->get_node_ref_back())) { if (debug()) { std::cerr << " ring.first=it->last\n"; } @@ -296,7 +300,7 @@ namespace osmium { m_rings.erase(it); return true; } - if (has_same_location(nr, it->get_segment_front().first())) { + if (has_same_location(nr, it->get_node_ref_front())) { if (debug()) { std::cerr << " ring.first=it->first\n"; } @@ -364,7 +368,7 @@ namespace osmium { } osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size()); - std::copy(ring.segments().begin(), ring.segments().end(), segments.begin()); + std::copy(ring.segments().cbegin(), ring.segments().cend(), segments.begin()); std::sort(segments.begin(), segments.end()); const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) { return has_same_location(s1.first(), s2.first()); @@ -429,14 +433,14 @@ namespace osmium { } { osmium::builder::OuterRingBuilder ring_builder(builder.buffer(), &builder); - ring_builder.add_node_ref(ring->get_segment_front().first()); + ring_builder.add_node_ref(ring->get_node_ref_front()); for (const auto& segment : ring->segments()) { ring_builder.add_node_ref(segment.second()); } } for (ProtoRing* inner : ring->inner_rings()) { osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder); - ring_builder.add_node_ref(inner->get_segment_front().first()); + ring_builder.add_node_ref(inner->get_node_ref_front()); for (const auto& segment : inner->segments()) { ring_builder.add_node_ref(segment.second()); } @@ -455,21 +459,21 @@ namespace osmium { std::cerr << " => ring CLOSED\n"; } } else { - if (has_same_location(ring.get_segment_back().second(), segment.first())) { + if (has_same_location(ring.get_node_ref_back(), segment.first())) { combine_rings_back(segment, ring); return true; } - if (has_same_location(ring.get_segment_back().second(), segment.second())) { + if (has_same_location(ring.get_node_ref_back(), segment.second())) { segment.swap_locations(); combine_rings_back(segment, ring); return true; } - if (has_same_location(ring.get_segment_front().first(), segment.first())) { + if (has_same_location(ring.get_node_ref_front(), segment.first())) { segment.swap_locations(); combine_rings_front(segment, ring); return true; } - if (has_same_location(ring.get_segment_front().first(), segment.second())) { + if (has_same_location(ring.get_node_ref_front(), segment.second())) { combine_rings_front(segment, ring); return true; } @@ -692,7 +696,7 @@ namespace osmium { } // Now create the Area object and add the attributes and tags - // from the relation. + // from the way. { osmium::builder::AreaBuilder builder(out_buffer); builder.initialize_from_object(way); @@ -749,7 +753,7 @@ namespace osmium { for (size_t offset : members) { if (!std::strcmp(memit->role(), "inner")) { const osmium::Way& way = in_buffer.get(offset); - if (way.is_closed() && way.tags().size() > 0) { + if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) { auto d = std::count_if(way.tags().begin(), way.tags().end(), filter()); if (d > 0) { osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().begin(), way.tags().end()); diff --git a/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp index 43569a865..7c1555a33 100644 --- a/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp +++ b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -113,7 +113,7 @@ namespace osmium { return m_second; } - bool to_left_of(const osmium::Location location) const { + bool to_left_of(const osmium::Location& location) const { // std::cerr << "segment " << first() << "--" << second() << " to_left_of(" << location << "\n"; if (first().location() == location || second().location() == location) { @@ -195,8 +195,8 @@ namespace osmium { } inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) { - auto m1 = std::minmax(s1.first().location().y(), s1.second().location().y()); - auto m2 = std::minmax(s2.first().location().y(), s2.second().location().y()); + const std::pair m1 = std::minmax(s1.first().location().y(), s1.second().location().y()); + const std::pair m2 = std::minmax(s2.first().location().y(), s2.second().location().y()); if (m1.first > m2.second || m2.first > m1.second) { return false; } @@ -204,19 +204,25 @@ namespace osmium { } /** - * Calculate the intersection between to NodeRefSegments. The result is returned - * as a Location. Note that because the Location uses integers with limited - * precision internally, the result might be slightly different than the - * numerically correct location. + * Calculate the intersection between two NodeRefSegments. The + * result is returned as a Location. Note that because the Location + * uses integers with limited precision internally, the result + * might be slightly different than the numerically correct + * location. * - * If the segments touch in one of their endpoints, it doesn't count as an - * intersection. + * This function uses integer arithmentic as much as possible and + * will not work if the segments are longer than about half the + * planet. This shouldn't happen with real data, so it isn't a big + * problem. * - * If the segments intersect not in a single point but in multiple points, ie - * if they overlap, this is NOT detected. + * If the segments touch in one of their endpoints, it doesn't + * count as an intersection. * - * @returns Undefined osmium::Location if there is no intersection or a defined - * Location if the segments intersect. + * If the segments intersect not in a single point but in multiple + * points, ie if they overlap, this is NOT detected. + * + * @returns Undefined osmium::Location if there is no intersection + * or a defined Location if the segments intersect. */ inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) { if (s1.first().location() == s2.first().location() || @@ -226,26 +232,32 @@ namespace osmium { return osmium::Location(); } - auto d = (static_cast(s2.second().y()) - static_cast(s2.first().y())) * - (static_cast(s1.second().x()) - static_cast(s1.first().x())) - - (static_cast(s2.second().x()) - static_cast(s2.first().x())) * - (static_cast(s1.second().y()) - static_cast(s1.first().y())); + int64_t s1ax = s1.first().x(); + int64_t s1ay = s1.first().y(); + int64_t s1bx = s1.second().x(); + int64_t s1by = s1.second().y(); + int64_t s2ax = s2.first().x(); + int64_t s2ay = s2.first().y(); + int64_t s2bx = s2.second().x(); + int64_t s2by = s2.second().y(); + + int64_t d = (s2by - s2ay) * (s1bx - s1ax) - + (s2bx - s2ax) * (s1by - s1ay); if (d != 0) { - double denom = ((s2.second().lat() - s2.first().lat())*(s1.second().lon() - s1.first().lon())) - - ((s2.second().lon() - s2.first().lon())*(s1.second().lat() - s1.first().lat())); + int64_t na = (s2bx - s2ax) * (s1ay - s2ay) - + (s2by - s2ay) * (s1ax - s2ax); - double nume_a = ((s2.second().lon() - s2.first().lon())*(s1.first().lat() - s2.first().lat())) - - ((s2.second().lat() - s2.first().lat())*(s1.first().lon() - s2.first().lon())); + int64_t nb = (s1bx - s1ax) * (s1ay - s2ay) - + (s1by - s1ay) * (s1ax - s2ax); - double nume_b = ((s1.second().lon() - s1.first().lon())*(s1.first().lat() - s2.first().lat())) - - ((s1.second().lat() - s1.first().lat())*(s1.first().lon() - s2.first().lon())); + if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) || + (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) { + + double ua = double(na) / d; + int32_t ix = int32_t(s1ax + ua*(s1bx - s1ax)); + int32_t iy = int32_t(s1ay + ua*(s1by - s1ay)); - if ((denom > 0 && nume_a >= 0 && nume_a <= denom && nume_b >= 0 && nume_b <= denom) || - (denom < 0 && nume_a <= 0 && nume_a >= denom && nume_b <= 0 && nume_b >= denom)) { - double ua = nume_a / denom; - double ix = s1.first().lon() + ua*(s1.second().lon() - s1.first().lon()); - double iy = s1.first().lat() + ua*(s1.second().lat() - s1.first().lat()); return osmium::Location(ix, iy); } } diff --git a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp index c0f545c98..c4edf4018 100644 --- a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp +++ b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,12 +34,14 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include +#include #include -#include +#include #include #include +#include #include #include @@ -116,6 +118,10 @@ namespace osmium { return m_segments.front(); } + const NodeRef& get_node_ref_front() const { + return get_segment_front().first(); + } + const NodeRefSegment& get_segment_back() const { return m_segments.back(); } @@ -124,6 +130,10 @@ namespace osmium { return m_segments.back(); } + const NodeRef& get_node_ref_back() const { + return get_segment_back().second(); + } + bool closed() const { return m_segments.front().first().location() == m_segments.back().second().location(); } @@ -148,7 +158,8 @@ namespace osmium { } void swap_segments(ProtoRing& other) { - std::swap(m_segments, other.m_segments); + using std::swap; + swap(m_segments, other.m_segments); } void add_inner_ring(ProtoRing* ring) { diff --git a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp index ca6071ede..05e0cd8d4 100644 --- a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp +++ b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -41,6 +41,8 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include #include @@ -56,7 +58,7 @@ namespace osmium { */ class SegmentList { - typedef std::vector slist_type; + using slist_type = std::vector; slist_type m_segments; @@ -65,10 +67,11 @@ namespace osmium { public: explicit SegmentList(bool debug) noexcept : + m_segments(), m_debug(debug) { } - ~SegmentList() = default; + ~SegmentList() noexcept = default; SegmentList(const SegmentList&) = delete; SegmentList(SegmentList&&) = delete; @@ -86,6 +89,15 @@ namespace osmium { } typedef slist_type::const_iterator const_iterator; + typedef slist_type::iterator iterator; + + iterator begin() noexcept { + return m_segments.begin(); + } + + iterator end() noexcept { + return m_segments.end(); + } const_iterator begin() const noexcept { return m_segments.begin(); @@ -96,8 +108,8 @@ namespace osmium { } /** - * Enable or disable debug output to stderr. This is for Osmium - * developers only. + * Enable or disable debug output to stderr. This is used + * for debugging libosmium itself. */ void enable_debug_output(bool debug = true) noexcept { m_debug = debug; @@ -146,9 +158,9 @@ namespace osmium { /** * Find duplicate segments (ie same start and end point) in the - * list and remove them. This will always remove pairs of the same - * segment. So if there are three, for instance, two will be - * removed and one will be left. + * list and remove them. This will always remove pairs of the + * same segment. So if there are three, for instance, two will + * be removed and one will be left. */ void erase_duplicate_segments() { while (true) { @@ -166,7 +178,8 @@ namespace osmium { /** * Find intersection between segments. * - * @param problem_reporter Any intersections found are reported to this object. + * @param problem_reporter Any intersections found are + * reported to this object. * @returns true if there are intersections. */ bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const { diff --git a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp index bf2a4ce86..81a28b24b 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -53,7 +53,7 @@ namespace osmium { namespace relations { class RelationMeta; - } + } // namespace relations /** * @brief Code related to the building of areas (multipolygons) from relations. @@ -71,7 +71,7 @@ namespace osmium { * * @tparam TAssembler Multipolygon Assembler class. */ - template + template class MultipolygonCollector : public osmium::relations::Collector, false, true, false> { typedef typename osmium::relations::Collector, false, true, false> collector_type; @@ -87,7 +87,8 @@ namespace osmium { void flush_output_buffer() { if (this->callback()) { osmium::memory::Buffer buffer(initial_output_buffer_size); - std::swap(buffer, m_output_buffer); + using std::swap; + swap(buffer, m_output_buffer); this->callback()(std::move(buffer)); } } @@ -176,28 +177,6 @@ namespace osmium { } catch (osmium::invalid_location&) { // XXX ignore } - - // clear member metas - for (const auto& member : relation.members()) { - if (member.ref() != 0) { - auto& mmv = this->member_meta(member.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(member.ref())); - assert(range.first != range.second); - - // if this is the last time this object was needed - // then mark it as removed - if (osmium::relations::count_not_removed(range.first, range.second) == 1) { - this->get_member(range.first->buffer_offset()).set_removed(true); - } - - for (auto it = range.first; it != range.second; ++it) { - if (!it->removed() && relation.id() == this->get_relation(it->relation_pos()).id()) { - it->remove(); - break; - } - } - } - } } void flush() { @@ -206,7 +185,10 @@ namespace osmium { osmium::memory::Buffer read() { osmium::memory::Buffer buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes); - std::swap(buffer, m_output_buffer); + + using std::swap; + swap(buffer, m_output_buffer); + return buffer; } diff --git a/third_party/libosmium/include/osmium/area/problem_reporter.hpp b/third_party/libosmium/include/osmium/area/problem_reporter.hpp index 4ae4bb23d..1dde85ba9 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp index 5e743c6d8..abadd9653 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,7 +54,7 @@ namespace osmium { ProblemReporterStream(m_sstream) { } - virtual ~ProblemReporterException() = default; + ~ProblemReporterException() override = default; void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { m_sstream.str(); diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp index c437a3f57..d58fe559c 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -42,34 +42,12 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libgdal`. */ -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4458) -#else -# pragma GCC diagnostic push -# ifdef __clang__ -# pragma GCC diagnostic ignored "-Wdocumentation-unknown-command" -# endif -# pragma GCC diagnostic ignored "-Wfloat-equal" -# pragma GCC diagnostic ignored "-Wold-style-cast" -# pragma GCC diagnostic ignored "-Wpadded" -# pragma GCC diagnostic ignored "-Wredundant-decls" -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -#include -#include - -#ifdef _MSC_VER -# pragma warning(pop) -#else -# pragma GCC diagnostic pop -#endif - #include -#include + +#include #include +#include #include #include #include @@ -86,24 +64,15 @@ namespace osmium { osmium::geom::OGRFactory<> m_ogr_factory; - OGRDataSource* m_data_source; - - OGRLayer* m_layer_perror; - OGRLayer* m_layer_lerror; + gdalcpp::Layer m_layer_perror; + gdalcpp::Layer m_layer_lerror; void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_perror->GetLayerDefn()); - std::unique_ptr ogr_point = m_ogr_factory.create_point(location); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id1", static_cast(id1)); - feature->SetField("id2", static_cast(id2)); - feature->SetField("problem_type", problem_type); - - if (m_layer_perror->CreateFeature(feature) != OGRERR_NONE) { - std::runtime_error("Failed to create feature on layer 'perrors'"); - } - - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_perror, m_ogr_factory.create_point(location)); + feature.set_field("id1", static_cast(id1)); + feature.set_field("id2", static_cast(id2)); + feature.set_field("problem_type", problem_type); + feature.add_to_layer(); } void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) { @@ -112,83 +81,30 @@ namespace osmium { std::unique_ptr ogr_linestring = std::unique_ptr(new OGRLineString()); ogr_linestring->addPoint(ogr_point1.get()); ogr_linestring->addPoint(ogr_point2.get()); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_lerror->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id1", static_cast(id1)); - feature->SetField("id2", static_cast(id2)); - feature->SetField("problem_type", problem_type); - if (m_layer_lerror->CreateFeature(feature) != OGRERR_NONE) { - std::runtime_error("Failed to create feature on layer 'lerrors'"); - } - - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_lerror, std::move(ogr_linestring)); + feature.set_field("id1", static_cast(id1)); + feature.set_field("id2", static_cast(id2)); + feature.set_field("problem_type", problem_type); + feature.add_to_layer(); } public: - explicit ProblemReporterOGR(OGRDataSource* data_source) : - m_data_source(data_source) { + explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) : + m_layer_perror(dataset, "perrors", wkbPoint), + m_layer_lerror(dataset, "lerrors", wkbLineString) { - OGRSpatialReference sparef; - sparef.SetWellKnownGeogCS("WGS84"); + m_layer_perror.add_field("id1", OFTReal, 10); + m_layer_perror.add_field("id2", OFTReal, 10); + m_layer_perror.add_field("problem_type", OFTString, 30); - m_layer_perror = m_data_source->CreateLayer("perrors", &sparef, wkbPoint, nullptr); - if (!m_layer_perror) { - std::runtime_error("Layer creation failed for layer 'perrors'"); - } - - OGRFieldDefn layer_perror_field_id1("id1", OFTReal); - layer_perror_field_id1.SetWidth(10); - - if (m_layer_perror->CreateField(&layer_perror_field_id1) != OGRERR_NONE) { - std::runtime_error("Creating field 'id1' failed for layer 'perrors'"); - } - - OGRFieldDefn layer_perror_field_id2("id2", OFTReal); - layer_perror_field_id2.SetWidth(10); - - if (m_layer_perror->CreateField(&layer_perror_field_id2) != OGRERR_NONE) { - std::runtime_error("Creating field 'id2' failed for layer 'perrors'"); - } - - OGRFieldDefn layer_perror_field_problem_type("problem_type", OFTString); - layer_perror_field_problem_type.SetWidth(30); - - if (m_layer_perror->CreateField(&layer_perror_field_problem_type) != OGRERR_NONE) { - std::runtime_error("Creating field 'problem_type' failed for layer 'perrors'"); - } - - /**************/ - - m_layer_lerror = m_data_source->CreateLayer("lerrors", &sparef, wkbLineString, nullptr); - if (!m_layer_lerror) { - std::runtime_error("Layer creation failed for layer 'lerrors'"); - } - - OGRFieldDefn layer_lerror_field_id1("id1", OFTReal); - layer_lerror_field_id1.SetWidth(10); - - if (m_layer_lerror->CreateField(&layer_lerror_field_id1) != OGRERR_NONE) { - std::runtime_error("Creating field 'id1' failed for layer 'lerrors'"); - } - - OGRFieldDefn layer_lerror_field_id2("id2", OFTReal); - layer_lerror_field_id2.SetWidth(10); - - if (m_layer_lerror->CreateField(&layer_lerror_field_id2) != OGRERR_NONE) { - std::runtime_error("Creating field 'id2' failed for layer 'lerrors'"); - } - - OGRFieldDefn layer_lerror_field_problem_type("problem_type", OFTString); - layer_lerror_field_problem_type.SetWidth(30); - - if (m_layer_lerror->CreateField(&layer_lerror_field_problem_type) != OGRERR_NONE) { - std::runtime_error("Creating field 'problem_type' failed for layer 'lerrors'"); - } + m_layer_lerror.add_field("id1", OFTReal, 10); + m_layer_lerror.add_field("id2", OFTReal, 10); + m_layer_lerror.add_field("problem_type", OFTString, 30); } - virtual ~ProblemReporterOGR() = default; + ~ProblemReporterOGR() override = default; void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { write_point("duplicate_node", node_id1, node_id2, location); diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp index ddcb343e8..ffd94a605 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,7 +54,7 @@ namespace osmium { m_out(&out) { } - virtual ~ProblemReporterStream() = default; + ~ProblemReporterStream() override = default; void header(const char* msg) { *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << ": "; diff --git a/third_party/libosmium/include/osmium/builder/attr.hpp b/third_party/libosmium/include/osmium/builder/attr.hpp new file mode 100644 index 000000000..d9831c28f --- /dev/null +++ b/third_party/libosmium/include/osmium/builder/attr.hpp @@ -0,0 +1,877 @@ +#ifndef OSMIUM_BUILDER_ATTR_HPP +#define OSMIUM_BUILDER_ATTR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace osmium { + + namespace builder { + + namespace detail { + +#ifdef _MSC_VER + // workaround for bug in MSVC + + template + struct is_handled_by; + + template + struct is_handled_by { + static constexpr bool value = false; + }; + + template + struct is_handled_by { + static constexpr bool value = std::is_base_of::value || + is_handled_by::value; + }; + + template + struct are_all_handled_by; + + template + struct are_all_handled_by { + static constexpr bool value = std::is_base_of::value; + }; + + template + struct are_all_handled_by { + static constexpr bool value = std::is_base_of::value && + are_all_handled_by::value; + }; +#else + // True if Predicate matches for none of the types Ts + template class Predicate, typename... Ts> + struct static_none_of : std::is_same::type...>, + std::tuple::type..., std::false_type>> + {}; + + // True if Predicate matches for all of the types Ts + template class Predicate, typename... Ts> + struct static_all_of : std::is_same::type...>, + std::tuple::type..., std::true_type>> + {}; + + // True if THandler is derived from the handler for at least one of the types in TTypes + template + struct is_handled_by { + template + using HasHandler = std::is_base_of; + + static constexpr bool value = !static_none_of::value; + }; + + // True if THandler is derived from the handlers of all the types in TTypes + template + struct are_all_handled_by { + template + using HasHandler = std::is_base_of; + + static constexpr bool value = static_all_of::value; + }; +#endif + + + // Wraps any type, so that we can derive from it + template + struct type_wrapper { + + using type = TType; + + TType value; + + constexpr explicit type_wrapper(const TType& v) : + value(v) { + } + + }; // struct type_wrapper + + // Small wrapper for begin/end iterator + template + struct iterator_wrapper { + + using type = TType; + + TType first; + TType last; + + constexpr iterator_wrapper(TType begin, TType end) : + first(begin), + last(end) {} + + constexpr TType begin() const { + return first; + } + + constexpr TType end() const { + return last; + } + + }; // struct iterator_wrapper + + + struct entity_handler {}; + struct object_handler; + struct node_handler; + struct tags_handler; + struct nodes_handler; + struct members_handler; + struct changeset_handler; + struct discussion_handler; + struct ring_handler; + + } // namespace detail + +#define OSMIUM_ATTRIBUTE(_handler, _name, _type) \ + struct _name : public osmium::builder::detail::type_wrapper<_type> { \ + using handler = osmium::builder::detail::_handler; + +#define OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(_handler, _name, _type) \ + OSMIUM_ATTRIBUTE(_handler, _name, _type) \ + constexpr explicit _name(std::add_const<_type>::type& value) : \ + type_wrapper(value) {} \ + } + +#define OSMIUM_ATTRIBUTE_ITER(_handler, _name) \ + template \ + struct _name : public osmium::builder::detail::iterator_wrapper { \ + using handler = osmium::builder::detail::_handler; \ + constexpr _name(TIterator first, TIterator last) : \ + osmium::builder::detail::iterator_wrapper(first, last) {} \ + } + + namespace attr { + + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _id, osmium::object_id_type); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _version, osmium::object_version_type); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _uid, osmium::user_id_type); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _cid, osmium::changeset_id_type); + + OSMIUM_ATTRIBUTE(object_handler, _deleted, bool) + constexpr explicit _deleted(bool value = true) noexcept : + type_wrapper(value) {} + }; + + OSMIUM_ATTRIBUTE(object_handler, _visible, bool) + constexpr explicit _visible(bool value = true) noexcept : + type_wrapper(value) {} + }; + + OSMIUM_ATTRIBUTE(object_handler, _timestamp, osmium::Timestamp) + constexpr explicit _timestamp(const osmium::Timestamp& value) noexcept : + type_wrapper(value) {} + constexpr explicit _timestamp(time_t value) noexcept : + type_wrapper(osmium::Timestamp{value}) {} + constexpr explicit _timestamp(uint32_t value) noexcept : + type_wrapper(osmium::Timestamp{value}) {} + explicit _timestamp(const char* value) : + type_wrapper(osmium::Timestamp{value}) {} + explicit _timestamp(const std::string& value) : + type_wrapper(osmium::Timestamp{value}) {} + }; + + OSMIUM_ATTRIBUTE(node_handler, _location, osmium::Location) + constexpr explicit _location(const osmium::Location& value) noexcept : + type_wrapper(value) {} + explicit _location(double lat, double lon) : + type_wrapper(osmium::Location{lat, lon}) {} + }; + + OSMIUM_ATTRIBUTE(entity_handler, _user, const char*) + constexpr explicit _user(const char* val) noexcept : + type_wrapper(val) {} + explicit _user(const std::string& val) noexcept : + type_wrapper(val.c_str()) {} + }; + + using pair_of_cstrings = std::pair; + using pair_of_strings = std::pair; + + class member_type { + + osmium::item_type m_type; + osmium::object_id_type m_ref; + const char* m_role; + + public: + + constexpr member_type(osmium::item_type type, osmium::object_id_type ref, const char* role = "") noexcept : + m_type(type), + m_ref(ref), + m_role(role) { + } + + constexpr osmium::item_type type() const noexcept { + return m_type; + } + + constexpr osmium::object_id_type ref() const noexcept { + return m_ref; + } + + constexpr const char* role() const noexcept { + return m_role; + } + + }; // class member_type + + class comment_type { + + osmium::Timestamp m_date; + osmium::user_id_type m_uid; + const char* m_user; + const char* m_text; + + public: + + constexpr comment_type(osmium::Timestamp date, osmium::user_id_type uid, const char* user, const char* text) noexcept : + m_date(date), + m_uid(uid), + m_user(user), + m_text(text) { + } + + constexpr osmium::Timestamp date() const noexcept { + return m_date; + } + + constexpr osmium::user_id_type uid() const noexcept { + return m_uid; + } + + constexpr const char* user() const noexcept { + return m_user; + } + + constexpr const char* text() const noexcept { + return m_text; + } + + }; // class comment_type + + namespace detail { + + OSMIUM_ATTRIBUTE_ITER(tags_handler, tags_from_iterator_pair); + + OSMIUM_ATTRIBUTE_ITER(nodes_handler, nodes_from_iterator_pair); + + OSMIUM_ATTRIBUTE_ITER(members_handler, members_from_iterator_pair); + + OSMIUM_ATTRIBUTE_ITER(discussion_handler, comments_from_iterator_pair); + + OSMIUM_ATTRIBUTE_ITER(ring_handler, outer_ring_from_iterator_pair); + OSMIUM_ATTRIBUTE_ITER(ring_handler, inner_ring_from_iterator_pair); + + } // namespace detail + + OSMIUM_ATTRIBUTE(tags_handler, _tag, pair_of_cstrings) + explicit _tag(const pair_of_cstrings& value) noexcept : + type_wrapper(value) {} + explicit _tag(const std::pair& value) : + type_wrapper(pair_of_cstrings{value.first, value.second}) {} + explicit _tag(const std::pair& value) : + type_wrapper(pair_of_cstrings{value.first, value.second}) {} + explicit _tag(const std::pair& value) : + type_wrapper(pair_of_cstrings{value.first, value.second}) {} + explicit _tag(const pair_of_strings& value) : + type_wrapper(std::make_pair(value.first.c_str(), value.second.c_str())) {} + explicit _tag(const char* key, const char* val) : + type_wrapper(std::make_pair(key, val)) {} + explicit _tag(const std::string& key, const std::string& val) : + type_wrapper(std::make_pair(key.c_str(), val.c_str())) {} + }; + + template + inline constexpr detail::tags_from_iterator_pair _tags(TTagIterator first, TTagIterator last) { + return detail::tags_from_iterator_pair(first, last); + } + + template + inline detail::tags_from_iterator_pair _tags(const TContainer& container) { + return detail::tags_from_iterator_pair(std::begin(container), std::end(container)); + } + + using tag_ilist = std::initializer_list>; + inline detail::tags_from_iterator_pair _tags(const tag_ilist& container) { + return detail::tags_from_iterator_pair(std::begin(container), std::end(container)); + } + + + + OSMIUM_ATTRIBUTE(nodes_handler, _node, osmium::NodeRef) + constexpr explicit _node(osmium::object_id_type value) noexcept : + type_wrapper(NodeRef{value}) {} + constexpr explicit _node(const NodeRef& value) noexcept : + type_wrapper(value) {} + }; + + template + inline constexpr detail::nodes_from_iterator_pair _nodes(TIdIterator first, TIdIterator last) { + return detail::nodes_from_iterator_pair(first, last); + } + + template + inline detail::nodes_from_iterator_pair _nodes(const TContainer& container) { + return detail::nodes_from_iterator_pair(std::begin(container), std::end(container)); + } + + using object_id_ilist = std::initializer_list; + inline detail::nodes_from_iterator_pair _nodes(const object_id_ilist& container) { + return detail::nodes_from_iterator_pair(std::begin(container), std::end(container)); + } + + using node_ref_ilist = std::initializer_list; + inline detail::nodes_from_iterator_pair _nodes(const node_ref_ilist& container) { + return detail::nodes_from_iterator_pair(std::begin(container), std::end(container)); + } + + + OSMIUM_ATTRIBUTE(members_handler, _member, member_type) + constexpr explicit _member(const member_type& value) noexcept : + type_wrapper(value) {} + constexpr explicit _member(osmium::item_type type, osmium::object_id_type id) noexcept : + type_wrapper({type, id}) {} + constexpr explicit _member(osmium::item_type type, osmium::object_id_type id, const char* role) noexcept : + type_wrapper({type, id, role}) {} + explicit _member(osmium::item_type type, osmium::object_id_type id, const std::string& role) noexcept : + type_wrapper({type, id, role.c_str()}) {} + explicit _member(const osmium::RelationMember& member) noexcept : + type_wrapper({member.type(), member.ref(), member.role()}) {} + }; + + template + inline constexpr detail::members_from_iterator_pair _members(TMemberIterator first, TMemberIterator last) { + return detail::members_from_iterator_pair(first, last); + } + + template + inline detail::members_from_iterator_pair _members(const TContainer& container) { + return detail::members_from_iterator_pair(std::begin(container), std::end(container)); + } + + using member_ilist = std::initializer_list; + inline detail::members_from_iterator_pair _members(const member_ilist& container) { + return detail::members_from_iterator_pair(std::begin(container), std::end(container)); + } + + + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_changes, osmium::num_changes_type); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_comments, osmium::num_comments_type); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _created_at, osmium::Timestamp); + OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _closed_at, osmium::Timestamp); + + OSMIUM_ATTRIBUTE(discussion_handler, _comment, comment_type) + constexpr explicit _comment(const comment_type& value) noexcept : + type_wrapper(value) {} + explicit _comment(const osmium::ChangesetComment& comment) noexcept : + type_wrapper({comment.date(), comment.uid(), comment.user(), comment.text()}) {} + }; + + template + inline constexpr detail::comments_from_iterator_pair _comments(TCommentIterator first, TCommentIterator last) { + return detail::comments_from_iterator_pair(first, last); + } + + template + inline detail::comments_from_iterator_pair _comments(const TContainer& container) { + return detail::comments_from_iterator_pair(std::begin(container), std::end(container)); + } + + using comment_ilist = std::initializer_list; + inline detail::comments_from_iterator_pair _comments(const comment_ilist& container) { + return detail::comments_from_iterator_pair(std::begin(container), std::end(container)); + } + + + template + inline constexpr detail::outer_ring_from_iterator_pair _outer_ring(TIdIterator first, TIdIterator last) { + return detail::outer_ring_from_iterator_pair(first, last); + } + + template + inline detail::outer_ring_from_iterator_pair _outer_ring(const TContainer& container) { + return detail::outer_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + using object_id_ilist = std::initializer_list; + inline detail::outer_ring_from_iterator_pair _outer_ring(const object_id_ilist& container) { + return detail::outer_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + using node_ref_ilist = std::initializer_list; + inline detail::outer_ring_from_iterator_pair _outer_ring(const node_ref_ilist& container) { + return detail::outer_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + template + inline constexpr detail::inner_ring_from_iterator_pair _inner_ring(TIdIterator first, TIdIterator last) { + return detail::inner_ring_from_iterator_pair(first, last); + } + + template + inline detail::inner_ring_from_iterator_pair _inner_ring(const TContainer& container) { + return detail::inner_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + using object_id_ilist = std::initializer_list; + inline detail::inner_ring_from_iterator_pair _inner_ring(const object_id_ilist& container) { + return detail::inner_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + using node_ref_ilist = std::initializer_list; + inline detail::inner_ring_from_iterator_pair _inner_ring(const node_ref_ilist& container) { + return detail::inner_ring_from_iterator_pair(std::begin(container), std::end(container)); + } + + + } // namespace attr + +#undef OSMIUM_ATTRIBUTE_ITER +#undef OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR +#undef OSMIUM_ATTRIBUTE + + namespace detail { + + struct changeset_handler : public entity_handler { + + template + static void set_value(osmium::Changeset&, const TDummy&) noexcept { + } + + static void set_value(osmium::Changeset& changeset, attr::_cid id) noexcept { + changeset.set_id(id.value); + } + + static void set_value(osmium::Changeset& changeset, attr::_num_changes num_changes) noexcept { + changeset.set_num_changes(num_changes.value); + } + + static void set_value(osmium::Changeset& changeset, attr::_num_comments num_comments) noexcept { + changeset.set_num_comments(num_comments.value); + } + + static void set_value(osmium::Changeset& changeset, attr::_created_at created_at) noexcept { + changeset.set_created_at(created_at.value); + } + + static void set_value(osmium::Changeset& changeset, attr::_closed_at closed_at) noexcept { + changeset.set_closed_at(closed_at.value); + } + + static void set_value(osmium::Changeset& changeset, attr::_uid uid) noexcept { + changeset.set_uid(uid.value); + } + + }; + + struct object_handler : public entity_handler { + + template + static void set_value(osmium::OSMObject&, const TDummy&) noexcept { + } + + static void set_value(osmium::OSMObject& object, attr::_id id) noexcept { + object.set_id(id.value); + } + + static void set_value(osmium::OSMObject& object, attr::_version version) noexcept { + object.set_version(version.value); + } + + static void set_value(osmium::OSMObject& object, attr::_visible visible) noexcept { + object.set_visible(visible.value); + } + + static void set_value(osmium::OSMObject& object, attr::_deleted deleted) noexcept { + object.set_deleted(deleted.value); + } + + static void set_value(osmium::OSMObject& object, attr::_timestamp timestamp) noexcept { + object.set_timestamp(timestamp.value); + } + + static void set_value(osmium::OSMObject& object, attr::_cid changeset) noexcept { + object.set_changeset(changeset.value); + } + + static void set_value(osmium::OSMObject& object, attr::_uid uid) noexcept { + object.set_uid(uid.value); + } + + }; // object_handler + + struct node_handler : public object_handler { + + using object_handler::set_value; + + static void set_value(osmium::Node& node, attr::_location location) noexcept { + node.set_location(location.value); + } + + }; // node_handler + + template + inline void add_basic(TBuilder& builder, const TArgs&... args) noexcept { + (void)std::initializer_list{ + (THandler::set_value(builder.object(), args), 0)... + }; + } + + // ============================================================== + + template + inline constexpr const char* get_user(const attr::_user& user, const TArgs&...) noexcept { + return user.value; + } + + inline constexpr const char* get_user() noexcept { + return ""; + } + + template + inline constexpr typename std::enable_if::value, const char*>::type + get_user(const TFirst&, const TRest&... args) noexcept { + return get_user(args...); + } + + template + inline void add_user(TBuilder& builder, const TArgs&... args) { + builder.add_user(get_user(args...)); + } + + // ============================================================== + + struct tags_handler { + + template + static void set_value(TagListBuilder&, const TDummy&) noexcept { + } + + static void set_value(TagListBuilder& builder, const attr::_tag& tag) { + builder.add_tag(tag.value); + } + + template + static void set_value(TagListBuilder& builder, const attr::detail::tags_from_iterator_pair& tags) { + for (const auto& tag : tags) { + builder.add_tag(tag); + } + } + + }; // struct tags_handler + + struct nodes_handler { + + template + static void set_value(WayNodeListBuilder&, const TDummy&) noexcept { + } + + static void set_value(WayNodeListBuilder& builder, const attr::_node& node_ref) { + builder.add_node_ref(node_ref.value); + } + + template + static void set_value(WayNodeListBuilder& builder, const attr::detail::nodes_from_iterator_pair& nodes) { + for (const auto& ref : nodes) { + builder.add_node_ref(ref); + } + } + + }; // struct nodes_handler + + struct members_handler { + + template + static void set_value(RelationMemberListBuilder&, const TDummy&) noexcept { + } + + static void set_value(RelationMemberListBuilder& builder, const attr::_member& member) { + builder.add_member(member.value.type(), member.value.ref(), member.value.role()); + } + + template + static void set_value(RelationMemberListBuilder& builder, const attr::detail::members_from_iterator_pair& members) { + for (const auto& member : members) { + builder.add_member(member.type(), member.ref(), member.role()); + } + } + + }; // struct members_handler + + struct discussion_handler { + + template + static void set_value(ChangesetDiscussionBuilder&, const TDummy&) noexcept { + } + + static void set_value(ChangesetDiscussionBuilder& builder, const attr::_comment& comment) { + builder.add_comment(comment.value.date(), comment.value.uid(), comment.value.user()); + builder.add_comment_text(comment.value.text()); + } + + template + static void set_value(ChangesetDiscussionBuilder& builder, const attr::detail::comments_from_iterator_pair& comments) { + for (const auto& comment : comments) { + builder.add_comment(comment.date(), comment.uid(), comment.user()); + builder.add_comment_text(comment.text()); + } + } + + }; // struct discussion_handler + + struct ring_handler { + + template + static void set_value(AreaBuilder&, const TDummy&) noexcept { + } + + template + static void set_value(AreaBuilder& parent, const attr::detail::outer_ring_from_iterator_pair& nodes) { + OuterRingBuilder builder(parent.buffer(), &parent); + for (const auto& ref : nodes) { + builder.add_node_ref(ref); + } + } + + template + static void set_value(AreaBuilder& parent, const attr::detail::inner_ring_from_iterator_pair& nodes) { + InnerRingBuilder builder(parent.buffer(), &parent); + for (const auto& ref : nodes) { + builder.add_node_ref(ref); + } + } + + }; // struct ring_handler + + // ============================================================== + + template + inline typename std::enable_if::value>::type + add_list(osmium::builder::Builder&, const TArgs&...) noexcept { + } + + template + inline typename std::enable_if::value>::type + add_list(osmium::builder::Builder& parent, const TArgs&... args) { + TBuilder builder(parent.buffer(), &parent); + (void)std::initializer_list{ + (THandler::set_value(builder, args), 0)... + }; + } + + struct any_node_handlers : public node_handler, public tags_handler {}; + struct any_way_handlers : public object_handler, public tags_handler, public nodes_handler {}; + struct any_relation_handlers : public object_handler, public tags_handler, public members_handler {}; + struct any_area_handlers : public object_handler, public tags_handler, public ring_handler {}; + struct any_changeset_handlers : public changeset_handler, public tags_handler, public discussion_handler {}; + + } // namespace detail + + + /** + * Create a node using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the node will be added. + * @param args The attributes of the node. + * @returns The position in the buffer where this node was added. + */ + template + inline size_t add_node(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_node() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_node()"); + + NodeBuilder builder(buffer); + + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + + return buffer.commit(); + } + + /** + * Create a way using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the way will be added. + * @param args The attributes of the way. + * @returns The position in the buffer where this way was added. + */ + template + inline size_t add_way(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_way() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_way()"); + + WayBuilder builder(buffer); + + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + + return buffer.commit(); + } + + /** + * Create a relation using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the relation will be added. + * @param args The attributes of the relation. + * @returns The position in the buffer where this relation was added. + */ + template + inline size_t add_relation(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_relation() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_relation()"); + + RelationBuilder builder(buffer); + + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + + return buffer.commit(); + } + + /** + * Create a changeset using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the changeset will be added. + * @param args The attributes of the changeset. + * @returns The position in the buffer where this changeset was added. + */ + template + inline size_t add_changeset(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_changeset() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_changeset()"); + + ChangesetBuilder builder(buffer); + + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + + return buffer.commit(); + } + + /** + * Create a area using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the area will be added. + * @param args The attributes of the area. + * @returns The position in the buffer where this area was added. + */ + template + inline size_t add_area(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_area() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_area()"); + + AreaBuilder builder(buffer); + + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + + (void)std::initializer_list{ + (detail::ring_handler::set_value(builder, args), 0)... + }; + + return buffer.commit(); + } + + /** + * Create a WayNodeList using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the list will be added. + * @param args The contents of the list. + * @returns The position in the buffer where this list was added. + */ + template + inline size_t add_way_node_list(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_way_node_list() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_way_node_list()"); + + { + WayNodeListBuilder builder(buffer); + (void)std::initializer_list{ + (detail::nodes_handler::set_value(builder, args), 0)... + }; + } + + return buffer.commit(); + } + + /** + * Create a TagList using the given arguments and add it to the given buffer. + * + * @param buffer The buffer to which the list will be added. + * @param args The contents of the list. + * @returns The position in the buffer where this list was added. + */ + template + inline size_t add_tag_list(osmium::memory::Buffer& buffer, const TArgs&... args) { + static_assert(sizeof...(args) > 0, "add_tag_list() must have buffer and at least one additional argument"); + static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_tag_list()"); + + { + TagListBuilder builder(buffer); + (void)std::initializer_list{ + (detail::tags_handler::set_value(builder, args), 0)... + }; + } + + return buffer.commit(); + } + + } // namespace builder + +} // namespace osmium + +#endif // OSMIUM_BUILDER_ATTR_HPP diff --git a/third_party/libosmium/include/osmium/builder/builder.hpp b/third_party/libosmium/include/osmium/builder/builder.hpp index 4424d88e1..869fe44e5 100644 --- a/third_party/libosmium/include/osmium/builder/builder.hpp +++ b/third_party/libosmium/include/osmium/builder/builder.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -134,7 +134,7 @@ namespace osmium { * Reserve space for an object of class T in buffer and return * pointer to it. */ - template + template T* reserve_space_for() { assert(m_buffer.is_aligned()); return reinterpret_cast(m_buffer.reserve_space(sizeof(T))); @@ -182,7 +182,7 @@ namespace osmium { }; // class Builder - template + template class ObjectBuilder : public Builder { static_assert(std::is_base_of::value, "ObjectBuilder can only build objects derived from osmium::memory::Item"); diff --git a/third_party/libosmium/include/osmium/builder/builder_helper.hpp b/third_party/libosmium/include/osmium/builder/builder_helper.hpp index eebdf338c..e1b7c52ff 100644 --- a/third_party/libosmium/include/osmium/builder/builder_helper.hpp +++ b/third_party/libosmium/include/osmium/builder/builder_helper.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -41,6 +41,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include namespace osmium { @@ -50,7 +51,11 @@ namespace osmium { namespace builder { - inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list& nodes) { + /** + * @deprecated + * Use osmium::builder::add_way_node_list() instead. + */ + OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list& nodes) { size_t pos = buffer.committed(); { osmium::builder::WayNodeListBuilder wnl_builder(buffer); @@ -62,6 +67,10 @@ namespace osmium { return buffer.get(pos); } + /** + * @deprecated + * Use osmium::builder::add_tag_list() instead. + */ inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { size_t pos = buffer.committed(); { @@ -74,6 +83,10 @@ namespace osmium { return buffer.get(pos); } + /** + * @deprecated + * Use osmium::builder::add_tag_list() instead. + */ inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map& tags) { size_t pos = buffer.committed(); { @@ -86,6 +99,10 @@ namespace osmium { return buffer.get(pos); } + /** + * @deprecated + * Use osmium::builder::add_tag_list() instead. + */ inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function func) { size_t pos = buffer.committed(); { diff --git a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp index 074076ce6..d4b54f8fc 100644 --- a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp +++ b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,9 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include +#include #include +#include #include #include @@ -47,13 +51,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include namespace osmium { namespace memory { class Buffer; - } + } // namespace memory namespace builder { @@ -76,6 +79,12 @@ namespace osmium { * @param value Tag value (0-terminated string). */ void add_tag(const char* key, const char* value) { + if (std::strlen(key) > osmium::max_osm_string_length) { + throw std::length_error("OSM tag key is too long"); + } + if (std::strlen(value) > osmium::max_osm_string_length) { + throw std::length_error("OSM tag value is too long"); + } add_size(append(key) + append(value)); } @@ -87,8 +96,15 @@ namespace osmium { * @param value Pointer to tag value. * @param value_length Length of value (not including the \0 byte). */ - void add_tag(const char* key, const string_size_type key_length, const char* value, const string_size_type value_length) { - add_size(append(key, key_length) + append_zero() + append(value, value_length) + append_zero()); + void add_tag(const char* key, const size_t key_length, const char* value, const size_t value_length) { + if (key_length > osmium::max_osm_string_length) { + throw std::length_error("OSM tag key is too long"); + } + if (value_length > osmium::max_osm_string_length) { + throw std::length_error("OSM tag value is too long"); + } + add_size(append(key, osmium::memory::item_size_type(key_length)) + append_zero() + + append(value, osmium::memory::item_size_type(value_length)) + append_zero()); } /** @@ -98,13 +114,55 @@ namespace osmium { * @param value Tag value. */ void add_tag(const std::string& key, const std::string& value) { - add_size(append(key.data(), static_cast_with_assert(key.size() + 1)) + - append(value.data(), static_cast_with_assert(value.size() + 1))); + if (key.size() > osmium::max_osm_string_length) { + throw std::length_error("OSM tag key is too long"); + } + if (value.size() > osmium::max_osm_string_length) { + throw std::length_error("OSM tag value is too long"); + } + add_size(append(key.data(), osmium::memory::item_size_type(key.size()) + 1) + + append(value.data(), osmium::memory::item_size_type(value.size()) + 1)); + } + + /** + * Add tag to buffer. + * + * @param tag Tag. + */ + void add_tag(const osmium::Tag& tag) { + add_size(append(tag.key()) + append(tag.value())); + } + + /** + * Add tag to buffer. + * + * @param tag Pair of key/value 0-terminated strings. + */ + void add_tag(const std::pair& tag) { + add_tag(tag.first, tag.second); + } + void add_tag(const std::pair& tag) { + add_tag(tag.first, tag.second); + } + void add_tag(const std::pair& tag) { + add_tag(tag.first, tag.second); + } + void add_tag(const std::pair& tag) { + add_tag(tag.first, tag.second); + } + + /** + * Add tag to buffer. + * + * @param tag Pair of std::string references. + */ + void add_tag(const std::pair& tag) { + add_tag(tag.first, tag.second); } }; // class TagListBuilder - template + template class NodeRefListBuilder : public ObjectBuilder { public: @@ -122,7 +180,7 @@ namespace osmium { static_cast(this)->add_size(sizeof(osmium::NodeRef)); } - void add_node_ref(const object_id_type ref, const osmium::Location location = Location()) { + void add_node_ref(const object_id_type ref, const osmium::Location& location = Location{}) { add_node_ref(NodeRef(ref, location)); } @@ -141,35 +199,17 @@ namespace osmium { * will be set. * @param role The role. * @param length Length of role (without \0 termination). + * @throws std:length_error If role is longer than osmium::max_osm_string_length */ - void add_role(osmium::RelationMember& member, const char* role, const string_size_type length) { - member.set_role_size(length + 1); - add_size(append(role, length) + append_zero()); + void add_role(osmium::RelationMember& member, const char* role, const size_t length) { + if (length > osmium::max_osm_string_length) { + throw std::length_error("OSM relation member role is too long"); + } + member.set_role_size(osmium::string_size_type(length) + 1); + add_size(append(role, osmium::memory::item_size_type(length)) + append_zero()); add_padding(true); } - /** - * Add role to buffer. - * - * @param member Relation member object where the length of the role - * will be set. - * @param role \0-terminated role. - */ - void add_role(osmium::RelationMember& member, const char* role) { - add_role(member, role, static_cast_with_assert(std::strlen(role))); - } - - /** - * Add role to buffer. - * - * @param member Relation member object where the length of the role - * will be set. - * @param role Role. - */ - void add_role(osmium::RelationMember& member, const std::string& role) { - add_role(member, role.data(), static_cast_with_assert(role.size())); - } - public: explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : @@ -190,8 +230,10 @@ namespace osmium { * @param full_member Optional pointer to the member object. If it * is available a copy will be added to the * relation. + * @throws std:length_error If role_length is greater than + * osmium::max_osm_string_length */ - void add_member(osmium::item_type type, object_id_type ref, const char* role, const string_size_type role_length, const osmium::OSMObject* full_member = nullptr) { + void add_member(osmium::item_type type, object_id_type ref, const char* role, const size_t role_length, const osmium::OSMObject* full_member = nullptr) { osmium::RelationMember* member = reserve_space_for(); new (member) osmium::RelationMember(ref, type, full_member != nullptr); add_size(sizeof(RelationMember)); @@ -210,9 +252,10 @@ namespace osmium { * @param full_member Optional pointer to the member object. If it * is available a copy will be added to the * relation. + * @throws std:length_error If role is longer than osmium::max_osm_string_length */ void add_member(osmium::item_type type, object_id_type ref, const char* role, const osmium::OSMObject* full_member = nullptr) { - add_member(type, ref, role, strlen(role), full_member); + add_member(type, ref, role, std::strlen(role), full_member); } /** @@ -224,6 +267,7 @@ namespace osmium { * @param full_member Optional pointer to the member object. If it * is available a copy will be added to the * relation. + * @throws std:length_error If role is longer than osmium::max_osm_string_length */ void add_member(osmium::item_type type, object_id_type ref, const std::string& role, const osmium::OSMObject* full_member = nullptr) { add_member(type, ref, role.data(), role.size(), full_member); @@ -231,7 +275,65 @@ namespace osmium { }; // class RelationMemberListBuilder - template + class ChangesetDiscussionBuilder : public ObjectBuilder { + + osmium::ChangesetComment* m_comment = nullptr; + + void add_user(osmium::ChangesetComment& comment, const char* user, const size_t length) { + if (length > osmium::max_osm_string_length) { + throw std::length_error("OSM user name is too long"); + } + comment.set_user_size(osmium::string_size_type(length) + 1); + add_size(append(user, osmium::memory::item_size_type(length)) + append_zero()); + } + + void add_text(osmium::ChangesetComment& comment, const char* text, const size_t length) { + // XXX There is no limit on the length of a comment text. We + // limit it here to 2^16-2 characters, because that's all that + // will fit into our internal data structure. This is not ideal, + // and will have to be discussed and cleared up. + if (length > std::numeric_limits::max() - 1) { + throw std::length_error("OSM changeset comment is too long"); + } + comment.set_text_size(osmium::string_size_type(length) + 1); + add_size(append(text, osmium::memory::item_size_type(length)) + append_zero()); + add_padding(true); + } + + public: + + explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : + ObjectBuilder(buffer, parent) { + } + + ~ChangesetDiscussionBuilder() { + assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + add_padding(); + } + + void add_comment(osmium::Timestamp date, osmium::user_id_type uid, const char* user) { + assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + m_comment = reserve_space_for(); + new (m_comment) osmium::ChangesetComment(date, uid); + add_size(sizeof(ChangesetComment)); + add_user(*m_comment, user, std::strlen(user)); + } + + void add_comment_text(const char* text) { + assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + add_text(*m_comment, text, std::strlen(text)); + m_comment = nullptr; + } + + void add_comment_text(const std::string& text) { + assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + add_text(*m_comment, text.c_str(), text.size()); + m_comment = nullptr; + } + + }; // class ChangesetDiscussionBuilder + + template class OSMObjectBuilder : public ObjectBuilder { public: diff --git a/third_party/libosmium/include/osmium/diff_handler.hpp b/third_party/libosmium/include/osmium/diff_handler.hpp index 4f9b3a1a0..a5fc34fba 100644 --- a/third_party/libosmium/include/osmium/diff_handler.hpp +++ b/third_party/libosmium/include/osmium/diff_handler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,8 +46,7 @@ namespace osmium { public: - DiffHandler() { - } + DiffHandler() = default; void node(const osmium::DiffNode&) const { } diff --git a/third_party/libosmium/include/osmium/diff_iterator.hpp b/third_party/libosmium/include/osmium/diff_iterator.hpp index 0ddf7ff7a..6e0ba4bbf 100644 --- a/third_party/libosmium/include/osmium/diff_iterator.hpp +++ b/third_party/libosmium/include/osmium/diff_iterator.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,7 +43,12 @@ namespace osmium { class OSMObject; - template + /** + * An input iterator wrapping any iterator over OSMObjects. When + * dereferenced it will yield DiffObject objects pointing to the + * underlying OSMObjects. + */ + template class DiffIterator : public std::iterator { static_assert(std::is_base_of::value, "TBasicIterator::value_type must derive from osmium::OSMObject"); @@ -56,37 +61,29 @@ namespace osmium { mutable osmium::DiffObject m_diff; - void set_diff() const { + void set_diff() const noexcept { assert(m_curr != m_end); - TBasicIterator prev = m_prev; - if (prev->type() != m_curr->type() || prev->id() != m_curr->id()) { - prev = m_curr; - } + bool use_curr_for_prev = m_prev->type() != m_curr->type() || m_prev->id() != m_curr->id(); + bool use_curr_for_next = m_next == m_end || m_next->type() != m_curr->type() || m_next->id() != m_curr->id(); - TBasicIterator next = m_next; - if (next == m_end || next->type() != m_curr->type() || next->id() != m_curr->id()) { - next = m_curr; - } - - m_diff = osmium::DiffObject(*prev, *m_curr, *next); + m_diff = std::move(osmium::DiffObject{ + *(use_curr_for_prev ? m_curr : m_prev), + *m_curr, + *(use_curr_for_next ? m_curr : m_next) + }); } public: - explicit DiffIterator(TBasicIterator begin, TBasicIterator end) : + DiffIterator(TBasicIterator begin, TBasicIterator end) : m_prev(begin), m_curr(begin), m_next(begin == end ? begin : ++begin), - m_end(end) { + m_end(std::move(end)), + m_diff() { } - DiffIterator(const DiffIterator&) = default; - DiffIterator& operator=(const DiffIterator&) = default; - - DiffIterator(DiffIterator&&) = default; - DiffIterator& operator=(DiffIterator&&) = default; - DiffIterator& operator++() { m_prev = std::move(m_curr); m_curr = m_next; @@ -104,26 +101,35 @@ namespace osmium { return tmp; } - bool operator==(const DiffIterator& rhs) const { + bool operator==(const DiffIterator& rhs) const noexcept { return m_curr == rhs.m_curr && m_end == rhs.m_end; } - bool operator!=(const DiffIterator& rhs) const { + bool operator!=(const DiffIterator& rhs) const noexcept { return !(*this == rhs); } - reference operator*() const { + reference operator*() const noexcept { set_diff(); return m_diff; } - pointer operator->() const { + pointer operator->() const noexcept { set_diff(); return &m_diff; } }; // class DiffIterator + /** + * Create a DiffIterator based on the given iterators. + */ + template + inline DiffIterator make_diff_iterator(TBasicIterator begin, + TBasicIterator end) { + return DiffIterator{begin, end}; + } + } // namespace osmium #endif // OSMIUM_DIFF_ITERATOR_HPP diff --git a/third_party/libosmium/include/osmium/diff_visitor.hpp b/third_party/libosmium/include/osmium/diff_visitor.hpp index 5e72a7bb7..95660de6b 100644 --- a/third_party/libosmium/include/osmium/diff_visitor.hpp +++ b/third_party/libosmium/include/osmium/diff_visitor.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,7 +43,7 @@ namespace osmium { namespace detail { - template + template inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler) { switch (diff.type()) { case osmium::item_type::node: @@ -60,7 +60,7 @@ namespace osmium { } } - template + template inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler, TRest&... more) { apply_diff_iterator_recurse(diff, handler); apply_diff_iterator_recurse(diff, more...); @@ -68,9 +68,9 @@ namespace osmium { } // namespace detail - template + template inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) { - typedef osmium::DiffIterator diff_iterator; + using diff_iterator = osmium::DiffIterator; diff_iterator dit(it, end); diff_iterator dend(end, end); @@ -82,19 +82,19 @@ namespace osmium { class OSMObject; - template + template inline void apply_diff(TSource& source, THandlers&... handlers) { apply_diff(osmium::io::InputIterator {source}, osmium::io::InputIterator {}, handlers...); } - template + template inline void apply_diff(osmium::memory::Buffer& buffer, THandlers&... handlers) { apply_diff(buffer.begin(), buffer.end(), handlers...); } - template + template inline void apply_diff(const osmium::memory::Buffer& buffer, THandlers&... handlers) { apply_diff(buffer.cbegin(), buffer.cend(), handlers...); } diff --git a/third_party/libosmium/include/osmium/dynamic_handler.hpp b/third_party/libosmium/include/osmium/dynamic_handler.hpp index 9d0bd66f5..e7c9900fa 100644 --- a/third_party/libosmium/include/osmium/dynamic_handler.hpp +++ b/third_party/libosmium/include/osmium/dynamic_handler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -36,16 +36,11 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include namespace osmium { - class Node; - class Way; - class Relation; - class Area; - class Changeset; - namespace handler { namespace detail { @@ -83,11 +78,11 @@ namespace osmium { // to either call handler style functions or visitor style operator(). #define OSMIUM_DYNAMIC_HANDLER_DISPATCH(_name_, _type_) \ -template \ +template \ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, int) -> decltype(handler._name_(object), void()) { \ handler._name_(object); \ } \ -template \ +template \ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> decltype(handler(object), void()) { \ handler(object); \ } @@ -98,47 +93,47 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> OSMIUM_DYNAMIC_HANDLER_DISPATCH(changeset, Changeset) OSMIUM_DYNAMIC_HANDLER_DISPATCH(area, Area) - template + template auto flush_dispatch(THandler& handler, int) -> decltype(handler.flush(), void()) { handler.flush(); } - template + template void flush_dispatch(THandler&, long) {} - template + template class HandlerWrapper : public HandlerWrapperBase { THandler m_handler; public: - template + template HandlerWrapper(TArgs&&... args) : m_handler(std::forward(args)...) { } - void node(const osmium::Node& node) override final { + void node(const osmium::Node& node) final { node_dispatch(m_handler, node, 0); } - void way(const osmium::Way& way) override final { + void way(const osmium::Way& way) final { way_dispatch(m_handler, way, 0); } - void relation(const osmium::Relation& relation) override final { + void relation(const osmium::Relation& relation) final { relation_dispatch(m_handler, relation, 0); } - void area(const osmium::Area& area) override final { + void area(const osmium::Area& area) final { area_dispatch(m_handler, area, 0); } - void changeset(const osmium::Changeset& changeset) override final { + void changeset(const osmium::Changeset& changeset) final { changeset_dispatch(m_handler, changeset, 0); } - void flush() override final { + void flush() final { flush_dispatch(m_handler, 0); } @@ -157,7 +152,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> m_impl(impl_ptr(new osmium::handler::detail::HandlerWrapperBase)) { } - template + template void set(TArgs&&... args) { m_impl = impl_ptr(new osmium::handler::detail::HandlerWrapper(std::forward(args)...)); } @@ -188,7 +183,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> }; // class DynamicHandler - } // namspace handler + } // namespace handler } // namespace osmium diff --git a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp index f00a5ecaf..8059ea32a 100644 --- a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp +++ b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,10 +33,18 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include +#include +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include namespace osmium { @@ -45,7 +53,7 @@ namespace osmium { */ namespace experimental { - template + template class FlexReader { bool m_with_areas; @@ -104,7 +112,7 @@ namespace osmium { return buffer; } - osmium::io::Header header() const { + osmium::io::Header header() { return m_reader.header(); } diff --git a/third_party/libosmium/include/osmium/fwd.hpp b/third_party/libosmium/include/osmium/fwd.hpp new file mode 100644 index 000000000..84def8ef1 --- /dev/null +++ b/third_party/libosmium/include/osmium/fwd.hpp @@ -0,0 +1,70 @@ +#ifndef OSMIUM_FWD_HPP +#define OSMIUM_FWD_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +/** + * + * @file + * + * This file contains forward declarations for commonly used Osmium classes. + * + */ + +namespace osmium { + + class Area; + class Box; + class Changeset; + class ChangesetComment; + class ChangesetDiscussion; + class InnerRing; + class Location; + class Node; + class NodeRef; + class NodeRefList; + class OSMEntity; + class OSMObject; + class OuterRing; + class Relation; + class RelationMemberList; + class Segment; + class Tag; + class TagList; + class Timestamp; + class Way; + class WayNodeList; + +} // namespace osmium + +#endif // OSMIUM_FWD_HPP diff --git a/third_party/libosmium/include/osmium/geom/coordinates.hpp b/third_party/libosmium/include/osmium/geom/coordinates.hpp index 2bad57e0d..0f1387622 100644 --- a/third_party/libosmium/include/osmium/geom/coordinates.hpp +++ b/third_party/libosmium/include/osmium/geom/coordinates.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include diff --git a/third_party/libosmium/include/osmium/geom/factory.hpp b/third_party/libosmium/include/osmium/geom/factory.hpp index 9be050d25..394228eb9 100644 --- a/third_party/libosmium/include/osmium/geom/factory.hpp +++ b/third_party/libosmium/include/osmium/geom/factory.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -61,7 +61,7 @@ namespace osmium { public: - geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) : + explicit geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) : std::runtime_error(message), m_message(message), m_id(id) { @@ -89,7 +89,7 @@ namespace osmium { return m_id; } - virtual const char* what() const noexcept override { + const char* what() const noexcept override { return m_message.c_str(); } @@ -142,7 +142,7 @@ namespace osmium { /** * Geometry factory. */ - template + template class GeometryFactory { /** @@ -166,8 +166,8 @@ namespace osmium { /** * Constructor for default initialized projection. */ - template - GeometryFactory(TArgs&&... args) : + template + explicit GeometryFactory(TArgs&&... args) : m_projection(), m_impl(std::forward(args)...) { } @@ -176,12 +176,13 @@ namespace osmium { * Constructor for explicitly initialized projection. Note that the * projection is moved into the GeometryFactory. */ - template - GeometryFactory(TProjection&& projection, TArgs&&... args) : + template + explicit GeometryFactory(TProjection&& projection, TArgs&&... args) : m_projection(std::move(projection)), m_impl(std::forward(args)...) { } + typedef TProjection projection_type; typedef typename TGeomImpl::point_type point_type; typedef typename TGeomImpl::linestring_type linestring_type; typedef typename TGeomImpl::polygon_type polygon_type; @@ -198,7 +199,7 @@ namespace osmium { /* Point */ - point_type create_point(const osmium::Location location) const { + point_type create_point(const osmium::Location& location) const { return m_impl.make_point(m_projection(location)); } @@ -226,7 +227,7 @@ namespace osmium { m_impl.linestring_start(); } - template + template size_t fill_linestring(TIter it, TIter end) { size_t num_points = 0; for (; it != end; ++it, ++num_points) { @@ -235,7 +236,7 @@ namespace osmium { return num_points; } - template + template size_t fill_linestring_unique(TIter it, TIter end) { size_t num_points = 0; osmium::Location last_location; @@ -300,7 +301,7 @@ namespace osmium { m_impl.polygon_start(); } - template + template size_t fill_polygon(TIter it, TIter end) { size_t num_points = 0; for (; it != end; ++it, ++num_points) { @@ -309,7 +310,7 @@ namespace osmium { return num_points; } - template + template size_t fill_polygon_unique(TIter it, TIter end) { size_t num_points = 0; osmium::Location last_location; diff --git a/third_party/libosmium/include/osmium/geom/geojson.hpp b/third_party/libosmium/include/osmium/geom/geojson.hpp index 7d5953535..044a89f86 100644 --- a/third_party/libosmium/include/osmium/geom/geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/geojson.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -88,7 +88,10 @@ namespace osmium { linestring_type linestring_finish(size_t /* num_points */) { assert(!m_str.empty()); std::string str; - std::swap(str, m_str); + + using std::swap; + swap(str, m_str); + str.back() = ']'; str += "}"; return str; @@ -134,7 +137,10 @@ namespace osmium { multipolygon_type multipolygon_finish() { assert(!m_str.empty()); std::string str; - std::swap(str, m_str); + + using std::swap; + swap(str, m_str); + str.back() = ']'; str += "}"; return str; @@ -144,7 +150,7 @@ namespace osmium { } // namespace detail - template + template using GeoJSONFactory = GeometryFactory; } // namespace geom diff --git a/third_party/libosmium/include/osmium/geom/geos.hpp b/third_party/libosmium/include/osmium/geom/geos.hpp index 771b08736..f9026e5a9 100644 --- a/third_party/libosmium/include/osmium/geom/geos.hpp +++ b/third_party/libosmium/include/osmium/geom/geos.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -228,7 +228,7 @@ namespace osmium { } // namespace detail - template + template using GEOSFactory = GeometryFactory; } // namespace geom diff --git a/third_party/libosmium/include/osmium/geom/haversine.hpp b/third_party/libosmium/include/osmium/geom/haversine.hpp index e62a31b03..632bc1612 100644 --- a/third_party/libosmium/include/osmium/geom/haversine.hpp +++ b/third_party/libosmium/include/osmium/geom/haversine.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/geom/mercator_projection.hpp b/third_party/libosmium/include/osmium/geom/mercator_projection.hpp index 22a0d646b..a0c38088a 100644 --- a/third_party/libosmium/include/osmium/geom/mercator_projection.hpp +++ b/third_party/libosmium/include/osmium/geom/mercator_projection.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/geom/ogr.hpp b/third_party/libosmium/include/osmium/geom/ogr.hpp index f33971c7d..b8e9ef3ab 100644 --- a/third_party/libosmium/include/osmium/geom/ogr.hpp +++ b/third_party/libosmium/include/osmium/geom/ogr.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,35 +47,7 @@ DEALINGS IN THE SOFTWARE. #include #include -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4458) -# pragma warning(disable : 4251) -#else -# pragma GCC diagnostic push -# ifdef __clang__ -# pragma GCC diagnostic ignored "-Wdocumentation-unknown-command" -# endif -# pragma GCC diagnostic ignored "-Wfloat-equal" -# pragma GCC diagnostic ignored "-Wold-style-cast" -# pragma GCC diagnostic ignored "-Wpadded" -# pragma GCC diagnostic ignored "-Wredundant-decls" -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -/* Strictly speaking the following include would be enough here, - but everybody using this file will very likely need the other includes, - so we are adding them here, so that not everybody will need all those - pragmas to disable warnings. */ -//#include -#include -#include - -#ifdef _MSC_VER -# pragma warning(pop) -#else -# pragma GCC diagnostic pop -#endif +#include #include #include @@ -196,7 +168,7 @@ namespace osmium { } // namespace detail - template + template using OGRFactory = GeometryFactory; } // namespace geom diff --git a/third_party/libosmium/include/osmium/geom/projection.hpp b/third_party/libosmium/include/osmium/geom/projection.hpp index 6419101f8..42d95b27e 100644 --- a/third_party/libosmium/include/osmium/geom/projection.hpp +++ b/third_party/libosmium/include/osmium/geom/projection.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -68,10 +68,6 @@ namespace osmium { std::unique_ptr m_crs; - projPJ get() const { - return m_crs.get(); - } - public: CRS(const std::string& crs) : @@ -85,6 +81,13 @@ namespace osmium { CRS(std::string("+init=epsg:") + std::to_string(epsg)) { } + /** + * Get underlying projPJ handle from proj library. + */ + projPJ get() const { + return m_crs.get(); + } + bool is_latlong() const { return pj_is_latlong(m_crs.get()) != 0; } @@ -93,24 +96,24 @@ namespace osmium { return pj_is_geocent(m_crs.get()) != 0; } - /** - * Transform coordinates from one CRS into another. Wraps the same function - * of the proj library. - * - * Coordinates have to be in radians and are produced in radians. - * - * @throws osmmium::projection_error if the projection fails - */ - friend Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) { - int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr); - if (result != 0) { - throw osmium::projection_error(std::string("projection failed: ") + pj_strerrno(result)); - } - return c; - } - }; // class CRS + /** + * Transform coordinates from one CRS into another. Wraps the same + * function of the proj library. + * + * Coordinates have to be in radians and are produced in radians. + * + * @throws osmmium::projection_error if the projection fails + */ + inline Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) { + int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr); + if (result != 0) { + throw osmium::projection_error(std::string("projection failed: ") + pj_strerrno(result)); + } + return c; + } + /** * Functor that does projection from WGS84 (EPSG:4326) to the given * CRS. diff --git a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp index a3d46870c..87e13110f 100644 --- a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,7 +46,7 @@ namespace osmium { * A geometry factory implementation that can be used with the * RapidJSON (https://github.com/miloyip/rapidjson) JSON writer. */ - template + template class RapidGeoJSONFactoryImpl { TWriter* m_writer; @@ -180,7 +180,7 @@ namespace osmium { } // namespace detail - template + template using RapidGeoJSONFactory = GeometryFactory, TProjection>; } // namespace geom diff --git a/third_party/libosmium/include/osmium/geom/relations.hpp b/third_party/libosmium/include/osmium/geom/relations.hpp index e9e2aa420..76d452e4e 100644 --- a/third_party/libosmium/include/osmium/geom/relations.hpp +++ b/third_party/libosmium/include/osmium/geom/relations.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/geom/tile.hpp b/third_party/libosmium/include/osmium/geom/tile.hpp index 9cd0b282b..e35c2ee59 100644 --- a/third_party/libosmium/include/osmium/geom/tile.hpp +++ b/third_party/libosmium/include/osmium/geom/tile.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -67,10 +67,10 @@ namespace osmium { explicit Tile(uint32_t zoom, const osmium::Location& location) : z(zoom) { osmium::geom::Coordinates c = lonlat_to_mercator(location); - const int32_t n = 1LL << zoom; + const int32_t n = 1 << zoom; const double scale = detail::max_coordinate_epsg3857 * 2 / n; - x = detail::restrict_to_range((c.x + detail::max_coordinate_epsg3857) / scale, 0, n-1); - y = detail::restrict_to_range((detail::max_coordinate_epsg3857 - c.y) / scale, 0, n-1); + x = uint32_t(detail::restrict_to_range(int32_t((c.x + detail::max_coordinate_epsg3857) / scale), 0, n-1)); + y = uint32_t(detail::restrict_to_range(int32_t((detail::max_coordinate_epsg3857 - c.y) / scale), 0, n-1)); } }; // struct Tile diff --git a/third_party/libosmium/include/osmium/geom/util.hpp b/third_party/libosmium/include/osmium/geom/util.hpp index 5e9f8228c..fa9c8bc36 100644 --- a/third_party/libosmium/include/osmium/geom/util.hpp +++ b/third_party/libosmium/include/osmium/geom/util.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/geom/wkb.hpp b/third_party/libosmium/include/osmium/geom/wkb.hpp index a290c02d0..19c12165e 100644 --- a/third_party/libosmium/include/osmium/geom/wkb.hpp +++ b/third_party/libosmium/include/osmium/geom/wkb.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -188,7 +188,9 @@ namespace osmium { linestring_type linestring_finish(size_t num_points) { set_size(m_linestring_size_offset, num_points); std::string data; - std::swap(data, m_data); + + using std::swap; + swap(data, m_data); if (m_out_type == out_type::hex) { return convert_to_hex(data); @@ -246,7 +248,9 @@ namespace osmium { multipolygon_type multipolygon_finish() { set_size(m_multipolygon_size_offset, m_polygons); std::string data; - std::swap(data, m_data); + + using std::swap; + swap(data, m_data); if (m_out_type == out_type::hex) { return convert_to_hex(data); @@ -259,7 +263,7 @@ namespace osmium { } // namespace detail - template + template using WKBFactory = GeometryFactory; } // namespace geom diff --git a/third_party/libosmium/include/osmium/geom/wkt.hpp b/third_party/libosmium/include/osmium/geom/wkt.hpp index 4fea96baa..ae73b9604 100644 --- a/third_party/libosmium/include/osmium/geom/wkt.hpp +++ b/third_party/libosmium/include/osmium/geom/wkt.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -86,7 +86,10 @@ namespace osmium { linestring_type linestring_finish(size_t /* num_points */) { assert(!m_str.empty()); std::string str; - std::swap(str, m_str); + + using std::swap; + swap(str, m_str); + str.back() = ')'; return str; } @@ -131,7 +134,10 @@ namespace osmium { multipolygon_type multipolygon_finish() { assert(!m_str.empty()); std::string str; - std::swap(str, m_str); + + using std::swap; + swap(str, m_str); + str.back() = ')'; return str; } @@ -140,7 +146,7 @@ namespace osmium { } // namespace detail - template + template using WKTFactory = GeometryFactory; } // namespace geom diff --git a/third_party/libosmium/include/osmium/handler.hpp b/third_party/libosmium/include/osmium/handler.hpp index 34d878511..3620d6546 100644 --- a/third_party/libosmium/include/osmium/handler.hpp +++ b/third_party/libosmium/include/osmium/handler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,19 +33,9 @@ DEALINGS IN THE SOFTWARE. */ -namespace osmium { +#include - class OSMObject; - class Node; - class Way; - class Relation; - class Area; - class Changeset; - class TagList; - class WayNodeList; - class RelationMemberList; - class OuterRing; - class InnerRing; +namespace osmium { /** * @brief Osmium handlers provide callbacks for OSM objects @@ -89,12 +79,15 @@ namespace osmium { void inner_ring(const osmium::InnerRing&) const { } + void changeset_discussion(const osmium::ChangesetDiscussion&) const { + } + void flush() const { } }; // class Handler - } // namspace handler + } // namespace handler } // namespace osmium diff --git a/third_party/libosmium/include/osmium/handler/chain.hpp b/third_party/libosmium/include/osmium/handler/chain.hpp index 1af3962fd..e59eb05b9 100644 --- a/third_party/libosmium/include/osmium/handler/chain.hpp +++ b/third_party/libosmium/include/osmium/handler/chain.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -38,14 +38,14 @@ DEALINGS IN THE SOFTWARE. #include #define OSMIUM_CHAIN_HANDLER_CALL(_func_, _type_) \ - template \ + template \ struct call_ ## _func_ { \ void operator()(THandlers& handlers, osmium::_type_& object) { \ std::get(handlers)._func_(object); \ call_ ## _func_()(handlers, object); \ } \ }; \ - template \ + template \ struct call_ ## _func_ { \ void operator()(THandlers&, osmium::_type_&) {} \ }; @@ -64,13 +64,13 @@ namespace osmium { * This handler allows chaining of any number of handlers into a single * handler. */ - template + template class ChainHandler : public osmium::handler::Handler { typedef std::tuple handlers_type; handlers_type m_handlers; - template + template struct call_flush { void operator()(THandlers& handlers) { std::get(handlers).flush(); @@ -78,7 +78,7 @@ namespace osmium { } }; // struct call_flush - template + template struct call_flush { void operator()(THandlers&) {} }; // struct call_flush diff --git a/third_party/libosmium/include/osmium/handler/check_order.hpp b/third_party/libosmium/include/osmium/handler/check_order.hpp new file mode 100644 index 000000000..143794bd5 --- /dev/null +++ b/third_party/libosmium/include/osmium/handler/check_order.hpp @@ -0,0 +1,136 @@ +#ifndef OSMIUM_HANDLER_CHECK_ORDER_HPP +#define OSMIUM_HANDLER_CHECK_ORDER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include + +#include +#include +#include +#include +#include + +namespace osmium { + + /** + * Exception thrown when a method in the CheckOrder class detects + * that the input is out of order. + */ + struct out_of_order_error : public std::runtime_error { + + out_of_order_error(const std::string& what) : + std::runtime_error(what) { + } + + out_of_order_error(const char* what) : + std::runtime_error(what) { + } + + }; // struct out_of_order_error + + namespace handler { + + /** + * Handler that can be used to check that an OSM file is ordered + * correctly. Ordered in this case refers to the usual order in OSM + * files: First nodes in the order of their IDs, then ways in the order + * of their IDs, then relations in the order or their IDs. IDs have to + * be unique for each type. + * + * To use this add a CheckOrder member variable to your handler and + * call the node(), way(), and relation() methods from your node(), + * way(), and relations() handlers, respectively. An out_of_order_error + * exception will be thrown when the input is not in order. + */ + class CheckOrder : public osmium::handler::Handler { + + osmium::object_id_type m_max_node_id = std::numeric_limits::min(); + osmium::object_id_type m_max_way_id = std::numeric_limits::min(); + osmium::object_id_type m_max_relation_id = std::numeric_limits::min(); + + public: + + void node(const osmium::Node& node) { + if (m_max_way_id > 0) { + throw out_of_order_error("Found a node after a way."); + } + if (m_max_relation_id > 0) { + throw out_of_order_error("Found a node after a relation."); + } + + if (m_max_node_id >= node.id()) { + throw out_of_order_error("Node IDs out of order."); + } + m_max_node_id = node.id(); + } + + void way(const osmium::Way& way) { + if (m_max_relation_id > 0) { + throw out_of_order_error("Found a way after a relation."); + } + + if (m_max_way_id >= way.id()) { + throw out_of_order_error("Way IDs out of order."); + } + m_max_way_id = way.id(); + } + + void relation(const osmium::Relation& relation) { + if (m_max_relation_id >= relation.id()) { + throw out_of_order_error("Relation IDs out of order."); + } + m_max_relation_id = relation.id(); + } + + osmium::object_id_type max_node_id() const noexcept { + return m_max_node_id; + } + + osmium::object_id_type max_way_id() const noexcept { + return m_max_way_id; + } + + osmium::object_id_type max_relation_id() const noexcept { + return m_max_relation_id; + } + + }; // class CheckOrder + + } // namespace handler + +} // namespace osmium + +#endif // OSMIUM_HANDLER_CHECK_ORDER_HPP diff --git a/third_party/libosmium/include/osmium/handler/disk_store.hpp b/third_party/libosmium/include/osmium/handler/disk_store.hpp index ccae5962a..b8ab229a3 100644 --- a/third_party/libosmium/include/osmium/handler/disk_store.hpp +++ b/third_party/libosmium/include/osmium/handler/disk_store.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/handler/dump.hpp b/third_party/libosmium/include/osmium/handler/dump.hpp index a23236e10..3863b77ec 100644 --- a/third_party/libosmium/include/osmium/handler/dump.hpp +++ b/third_party/libosmium/include/osmium/handler/dump.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp b/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp index 9b9fcbf3f..f62a4db7c 100644 --- a/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp +++ b/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -60,7 +60,7 @@ namespace osmium { * get(id) methods. * @tparam TStorageNegIDs Same but for negative IDs. */ - template + template class NodeLocationsForWays : public osmium::handler::Handler { static_assert(std::is_base_of, TStoragePosIDs>::value, "Index class must be derived from osmium::index::map::Map"); diff --git a/third_party/libosmium/include/osmium/handler/object_relations.hpp b/third_party/libosmium/include/osmium/handler/object_relations.hpp index dc4aa45d8..4afcf6a6d 100644 --- a/third_party/libosmium/include/osmium/handler/object_relations.hpp +++ b/third_party/libosmium/include/osmium/handler/object_relations.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/bool_vector.hpp b/third_party/libosmium/include/osmium/index/bool_vector.hpp index 94e1f7268..e6e190e27 100644 --- a/third_party/libosmium/include/osmium/index/bool_vector.hpp +++ b/third_party/libosmium/include/osmium/index/bool_vector.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -56,11 +56,13 @@ namespace osmium { public: BoolVector() = default; + BoolVector(const BoolVector&) = default; BoolVector(BoolVector&&) = default; BoolVector& operator=(const BoolVector&) = default; BoolVector& operator=(BoolVector&&) = default; - ~BoolVector() = default; + + ~BoolVector() noexcept = default; void set(T id, bool value = true) { if (m_bits.size() <= id) { diff --git a/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp b/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp index 5ccbfc809..640b3c693 100644 --- a/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp +++ b/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,19 +47,18 @@ namespace osmium { namespace detail { - template + template inline T* create_map_with_fd(const std::vector& config) { if (config.size() == 1) { return new T(); - } else { - assert(config.size() > 1); - const std::string& filename = config[1]; - int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); - if (fd == -1) { - throw std::runtime_error(std::string("can't open file '") + filename + "': " + strerror(errno)); - } - return new T(fd); } + assert(config.size() > 1); + const std::string& filename = config[1]; + int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); + if (fd == -1) { + throw std::runtime_error(std::string("can't open file '") + filename + "': " + strerror(errno)); + } + return new T(fd); } } // namespace detail diff --git a/third_party/libosmium/include/osmium/index/detail/mmap_vector_anon.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_anon.hpp index fc016261e..cd6942fbf 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_anon.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_anon.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,6 +54,8 @@ namespace osmium { mmap_vector_base() { } + ~mmap_vector_anon() noexcept = default; + }; // class mmap_vector_anon } // namespace detail diff --git a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp index 9b6476812..aadeef0b5 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -60,7 +60,7 @@ namespace osmium { public: - explicit mmap_vector_base(int fd, size_t capacity, size_t size = 0) : + mmap_vector_base(int fd, size_t capacity, size_t size = 0) : m_size(size), m_mapping(capacity, osmium::util::MemoryMapping::mapping_mode::write_shared, fd) { } @@ -70,6 +70,8 @@ namespace osmium { m_mapping(capacity) { } + ~mmap_vector_base() noexcept = default; + typedef T value_type; typedef T& reference; typedef const T& const_reference; @@ -78,8 +80,6 @@ namespace osmium { typedef T* iterator; typedef const T* const_iterator; - ~mmap_vector_base() = default; - void close() { m_mapping.unmap(); } diff --git a/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp index 1dadbcb45..666a409d4 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -50,17 +50,21 @@ namespace osmium { public: - explicit mmap_vector_file() : mmap_vector_base( + mmap_vector_file() : + mmap_vector_base( osmium::detail::create_tmp_file(), osmium::detail::mmap_vector_size_increment) { } - explicit mmap_vector_file(int fd) : mmap_vector_base( + explicit mmap_vector_file(int fd) : + mmap_vector_base( fd, osmium::util::file_size(fd) / sizeof(T), osmium::util::file_size(fd) / sizeof(T)) { } + ~mmap_vector_file() noexcept = default; + }; // class mmap_vector_file } // namespace detail diff --git a/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp b/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp index 06cab6544..0f3d1205b 100644 --- a/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp +++ b/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/detail/vector_map.hpp b/third_party/libosmium/include/osmium/index/detail/vector_map.hpp index 9c1cf4ed8..0fa59dd48 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_map.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_map.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -48,7 +48,7 @@ namespace osmium { namespace map { - template + template class VectorBasedDenseMap : public Map { TVector m_vector; @@ -68,20 +68,20 @@ namespace osmium { m_vector(fd) { } - ~VectorBasedDenseMap() = default; + ~VectorBasedDenseMap() noexcept final = default; - void reserve(const size_t size) override final { + void reserve(const size_t size) final { m_vector.reserve(size); } - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { if (size() <= id) { m_vector.resize(id+1); } m_vector[id] = value; } - const TValue get(const TId id) const override final { + const TValue get(const TId id) const final { try { const TValue& value = m_vector.at(id); if (value == osmium::index::empty_value()) { @@ -93,7 +93,7 @@ namespace osmium { } } - size_t size() const override final { + size_t size() const final { return m_vector.size(); } @@ -101,16 +101,16 @@ namespace osmium { return m_vector.size() * sizeof(element_type); } - size_t used_memory() const override final { + size_t used_memory() const final { return sizeof(TValue) * size(); } - void clear() override final { + void clear() final { m_vector.clear(); m_vector.shrink_to_fit(); } - void dump_as_array(const int fd) override final { + void dump_as_array(const int fd) final { osmium::io::detail::reliable_write(fd, reinterpret_cast(m_vector.data()), byte_size()); } @@ -161,17 +161,17 @@ namespace osmium { m_vector() { } - VectorBasedSparseMap(int fd) : + explicit VectorBasedSparseMap(int fd) : m_vector(fd) { } - ~VectorBasedSparseMap() override final = default; + ~VectorBasedSparseMap() final = default; - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { m_vector.push_back(element_type(id, value)); } - const TValue get(const TId id) const override final { + const TValue get(const TId id) const final { const element_type element { id, osmium::index::empty_value() @@ -186,7 +186,7 @@ namespace osmium { } } - size_t size() const override final { + size_t size() const final { return m_vector.size(); } @@ -194,20 +194,20 @@ namespace osmium { return m_vector.size() * sizeof(element_type); } - size_t used_memory() const override final { + size_t used_memory() const final { return sizeof(element_type) * size(); } - void clear() override final { + void clear() final { m_vector.clear(); m_vector.shrink_to_fit(); } - void sort() override final { + void sort() final { std::sort(m_vector.begin(), m_vector.end()); } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { osmium::io::detail::reliable_write(fd, reinterpret_cast(m_vector.data()), byte_size()); } diff --git a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp index 1d875fcba..35a4cc182 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -75,9 +75,9 @@ namespace osmium { m_vector(fd) { } - ~VectorBasedSparseMultimap() = default; + ~VectorBasedSparseMultimap() noexcept final = default; - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { m_vector.push_back(element_type(id, value)); } @@ -105,7 +105,7 @@ namespace osmium { }); } - size_t size() const override final { + size_t size() const final { return m_vector.size(); } @@ -113,16 +113,16 @@ namespace osmium { return m_vector.size() * sizeof(element_type); } - size_t used_memory() const override final { + size_t used_memory() const final { return sizeof(element_type) * size(); } - void clear() override final { + void clear() final { m_vector.clear(); m_vector.shrink_to_fit(); } - void sort() override final { + void sort() final { std::sort(m_vector.begin(), m_vector.end()); } @@ -147,7 +147,7 @@ namespace osmium { ); } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { osmium::io::detail::reliable_write(fd, reinterpret_cast(m_vector.data()), byte_size()); } diff --git a/third_party/libosmium/include/osmium/index/index.hpp b/third_party/libosmium/include/osmium/index/index.hpp index f415192d2..37d022d1b 100644 --- a/third_party/libosmium/include/osmium/index/index.hpp +++ b/third_party/libosmium/include/osmium/index/index.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -89,7 +89,7 @@ namespace osmium { * the full range, so the max value is a good "empty" value. */ template <> - inline OSMIUM_CONSTEXPR size_t empty_value() { + inline constexpr size_t empty_value() { return std::numeric_limits::max(); } diff --git a/third_party/libosmium/include/osmium/index/map.hpp b/third_party/libosmium/include/osmium/index/map.hpp index 61af672cd..f90a895d1 100644 --- a/third_party/libosmium/include/osmium/index/map.hpp +++ b/third_party/libosmium/include/osmium/index/map.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -84,7 +84,8 @@ namespace osmium { template class Map { - static_assert(std::is_integral::value && std::is_unsigned::value, "TId template parameter for class Map must be unsigned integral type"); + static_assert(std::is_integral::value && std::is_unsigned::value, + "TId template parameter for class Map must be unsigned integral type"); Map(const Map&) = delete; Map& operator=(const Map&) = delete; @@ -104,7 +105,7 @@ namespace osmium { Map() = default; - virtual ~Map() = default; + virtual ~Map() noexcept = default; virtual void reserve(const size_t) { // default implementation is empty @@ -147,10 +148,16 @@ namespace osmium { // default implementation is empty } + // This function could usually be const in derived classes, + // but not always. It could, for instance, sort internal data. + // This is why it is not declared const here. virtual void dump_as_list(const int /*fd*/) { throw std::runtime_error("can't dump as list"); } + // This function could usually be const in derived classes, + // but not always. It could, for instance, sort internal data. + // This is why it is not declared const here. virtual void dump_as_array(const int /*fd*/) { throw std::runtime_error("can't dump as array"); } @@ -252,12 +259,14 @@ namespace osmium { #define OSMIUM_CONCATENATE_DETAIL_(x, y) x##y #define OSMIUM_CONCATENATE_(x, y) OSMIUM_CONCATENATE_DETAIL_(x, y) -#define OSMIUM_MAKE_UNIQUE_(x) OSMIUM_CONCATENATE_(x, __COUNTER__) #define REGISTER_MAP(id, value, klass, name) \ -namespace { \ - const bool OSMIUM_MAKE_UNIQUE_(registered_index_map_##name) = osmium::index::register_map(#name); \ -} +namespace osmium { namespace index { namespace detail { \ + const bool OSMIUM_CONCATENATE_(registered_, name) = osmium::index::register_map(#name); \ + inline bool OSMIUM_CONCATENATE_(get_registered_, name)() noexcept { \ + return OSMIUM_CONCATENATE_(registered_, name); \ + } \ +} } } } // namespace index diff --git a/third_party/libosmium/include/osmium/index/map/all.hpp b/third_party/libosmium/include/osmium/index/map/all.hpp index 9ffadc0de..348d77135 100644 --- a/third_party/libosmium/include/osmium/index/map/all.hpp +++ b/third_party/libosmium/include/osmium/index/map/all.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/dense_file_array.hpp b/third_party/libosmium/include/osmium/index/map/dense_file_array.hpp index d209a875c..6694d11f3 100644 --- a/third_party/libosmium/include/osmium/index/map/dense_file_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/dense_file_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/dense_mem_array.hpp b/third_party/libosmium/include/osmium/index/map/dense_mem_array.hpp index b45eec458..6c33f7dda 100644 --- a/third_party/libosmium/include/osmium/index/map/dense_mem_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/dense_mem_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/dense_mmap_array.hpp b/third_party/libosmium/include/osmium/index/map/dense_mmap_array.hpp index a912aebd1..e63170a77 100644 --- a/third_party/libosmium/include/osmium/index/map/dense_mmap_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/dense_mmap_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/dummy.hpp b/third_party/libosmium/include/osmium/index/map/dummy.hpp index de05d1d69..98d082b82 100644 --- a/third_party/libosmium/include/osmium/index/map/dummy.hpp +++ b/third_party/libosmium/include/osmium/index/map/dummy.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -56,25 +56,25 @@ namespace osmium { Dummy() = default; - ~Dummy() override final = default; + ~Dummy() noexcept final = default; - void set(const TId, const TValue) override final { + void set(const TId, const TValue) final { // intentionally left blank } - const TValue get(const TId id) const override final { + const TValue get(const TId id) const final { not_found_error(id); } - size_t size() const override final { + size_t size() const final { return 0; } - size_t used_memory() const override final { + size_t used_memory() const final { return 0; } - void clear() override final { + void clear() final { } }; // class Dummy diff --git a/third_party/libosmium/include/osmium/index/map/sparse_file_array.hpp b/third_party/libosmium/include/osmium/index/map/sparse_file_array.hpp index 2ba9315dd..495833c72 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_file_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_file_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/sparse_mem_array.hpp b/third_party/libosmium/include/osmium/index/map/sparse_mem_array.hpp index 9adf41f0a..29fadf653 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_mem_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_mem_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp b/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp index 2b9048b3b..41351c865 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -71,36 +71,37 @@ namespace osmium { SparseMemMap() = default; - ~SparseMemMap() override final = default; + ~SparseMemMap() noexcept final = default; - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { m_elements[id] = value; } - const TValue get(const TId id) const override final { - try { - return m_elements.at(id); - } catch (std::out_of_range&) { + const TValue get(const TId id) const final { + auto it = m_elements.find(id); + if (it == m_elements.end()) { not_found_error(id); } + return it->second; } - size_t size() const override final { + size_t size() const noexcept final { return m_elements.size(); } - size_t used_memory() const override final { + size_t used_memory() const noexcept final { return element_size * m_elements.size(); } - void clear() override final { + void clear() final { m_elements.clear(); } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { typedef typename std::map::value_type t; std::vector v; - std::copy(m_elements.begin(), m_elements.end(), std::back_inserter(v)); + v.reserve(m_elements.size()); + std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v)); osmium::io::detail::reliable_write(fd, reinterpret_cast(v.data()), sizeof(t) * v.size()); } diff --git a/third_party/libosmium/include/osmium/index/map/sparse_mem_table.hpp b/third_party/libosmium/include/osmium/index/map/sparse_mem_table.hpp index 09ee81bbc..241a64f97 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_mem_table.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_mem_table.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -88,16 +88,16 @@ namespace osmium { m_elements(grow_size) { } - ~SparseMemTable() override final = default; + ~SparseMemTable() noexcept final = default; - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { if (id >= m_elements.size()) { m_elements.resize(id + m_grow_size); } m_elements[id] = value; } - const TValue get(const TId id) const override final { + const TValue get(const TId id) const final { if (id >= m_elements.size()) { not_found_error(id); } @@ -107,22 +107,23 @@ namespace osmium { return m_elements[id]; } - size_t size() const override final { + size_t size() const final { return m_elements.size(); } - size_t used_memory() const override final { + size_t used_memory() const final { // unused elements use 1 bit, used elements sizeof(TValue) bytes // http://google-sparsehash.googlecode.com/svn/trunk/doc/sparsetable.html return (m_elements.size() / 8) + (m_elements.num_nonempty() * sizeof(TValue)); } - void clear() override final { + void clear() final { m_elements.clear(); } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { std::vector> v; + v.reserve(m_elements.size()); int n = 0; for (const TValue value : m_elements) { if (value != osmium::index::empty_value()) { diff --git a/third_party/libosmium/include/osmium/index/map/sparse_mmap_array.hpp b/third_party/libosmium/include/osmium/index/map/sparse_mmap_array.hpp index c85e2c938..939c389e1 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_mmap_array.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_mmap_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/multimap.hpp b/third_party/libosmium/include/osmium/index/multimap.hpp index c817b6fb6..de6aa1eb4 100644 --- a/third_party/libosmium/include/osmium/index/multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -118,7 +118,7 @@ namespace osmium { }; // class Multimap - } // namespace map + } // namespace multimap } // namespace index diff --git a/third_party/libosmium/include/osmium/index/multimap/all.hpp b/third_party/libosmium/include/osmium/index/multimap/all.hpp index 8b0ae99f9..3fc99d4c9 100644 --- a/third_party/libosmium/include/osmium/index/multimap/all.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/all.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp index ac2d96452..ba9430232 100644 --- a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -62,16 +62,18 @@ namespace osmium { public: - explicit HybridIterator(typename main_map_type::iterator begin_main, - typename main_map_type::iterator end_main, - typename extra_map_type::iterator begin_extra, - typename extra_map_type::iterator end_extra) : + HybridIterator(typename main_map_type::iterator begin_main, + typename main_map_type::iterator end_main, + typename extra_map_type::iterator begin_extra, + typename extra_map_type::iterator end_extra) : m_begin_main(begin_main), m_end_main(end_main), m_begin_extra(begin_extra), m_end_extra(end_extra) { } + ~HybridIterator() noexcept = default; + HybridIterator& operator++() { if (m_begin_main == m_end_main) { ++m_begin_extra; @@ -134,11 +136,13 @@ namespace osmium { m_extra() { } - size_t size() const override final { + ~Hybrid() noexcept = default; + + size_t size() const final { return m_main.size() + m_extra.size(); } - size_t used_memory() const override final { + size_t used_memory() const final { return m_main.used_memory() + m_extra.used_memory(); } @@ -150,7 +154,7 @@ namespace osmium { m_main.set(id, value); } - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { m_extra.set(id, value); } @@ -175,17 +179,17 @@ namespace osmium { m_main.sort(); } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { consolidate(); m_main.dump_as_list(fd); } - void clear() override final { + void clear() final { m_main.clear(); m_extra.clear(); } - void sort() override final { + void sort() final { m_main.sort(); } diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_file_array.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_file_array.hpp index 0b9ae92c5..c36200162 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_file_array.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_file_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_array.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_array.hpp index c4140cba0..f718edd91 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_array.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp index 5b4715279..0859fca21 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -78,13 +78,13 @@ namespace osmium { SparseMemMultimap() = default; - ~SparseMemMultimap() noexcept override final = default; + ~SparseMemMultimap() noexcept final = default; void unsorted_set(const TId id, const TValue value) { m_elements.emplace(id, value); } - void set(const TId id, const TValue value) override final { + void set(const TId id, const TValue value) final { m_elements.emplace(id, value); } @@ -114,15 +114,15 @@ namespace osmium { return m_elements.end(); } - size_t size() const override final { + size_t size() const final { return m_elements.size(); } - size_t used_memory() const override final { + size_t used_memory() const final { return element_size * m_elements.size(); } - void clear() override final { + void clear() final { m_elements.clear(); } @@ -130,12 +130,12 @@ namespace osmium { // intentionally left blank } - void dump_as_list(const int fd) override final { + void dump_as_list(const int fd) final { std::vector v; + v.reserve(m_elements.size()); for (const auto& element : m_elements) { v.emplace_back(element.first, element.second); } -// std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v)); std::sort(v.begin(), v.end()); osmium::io::detail::reliable_write(fd, reinterpret_cast(v.data()), sizeof(element_type) * v.size()); } diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_mmap_array.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_mmap_array.hpp index 9f92555f6..6319c1754 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_mmap_array.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_mmap_array.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/index/node_locations_map.hpp b/third_party/libosmium/include/osmium/index/node_locations_map.hpp index ca4b1361e..c6ce37dbb 100644 --- a/third_party/libosmium/include/osmium/index/node_locations_map.hpp +++ b/third_party/libosmium/include/osmium/index/node_locations_map.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/any_compression.hpp b/third_party/libosmium/include/osmium/io/any_compression.hpp index 00e8ee2be..7f9f3d562 100644 --- a/third_party/libosmium/include/osmium/io/any_compression.hpp +++ b/third_party/libosmium/include/osmium/io/any_compression.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/any_input.hpp b/third_party/libosmium/include/osmium/io/any_input.hpp index d16d069a5..dd8445132 100644 --- a/third_party/libosmium/include/osmium/io/any_input.hpp +++ b/third_party/libosmium/include/osmium/io/any_input.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,5 +47,6 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #endif // OSMIUM_IO_ANY_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/any_output.hpp b/third_party/libosmium/include/osmium/io/any_output.hpp index 990a27bac..53cc5bbd2 100644 --- a/third_party/libosmium/include/osmium/io/any_output.hpp +++ b/third_party/libosmium/include/osmium/io/any_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp index e961a87ab..e5cad0be3 100644 --- a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp +++ b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -55,7 +55,9 @@ DEALINGS IN THE SOFTWARE. #endif #include +#include #include +#include #include #include @@ -65,13 +67,13 @@ namespace osmium { * Exception thrown when there are problems compressing or * decompressing bzip2 files. */ - struct bzip2_error : public std::runtime_error { + struct bzip2_error : public io_error { int bzip2_error_code; int system_errno; bzip2_error(const std::string& what, int error_code) : - std::runtime_error(what), + io_error(what), bzip2_error_code(error_code), system_errno(error_code == BZ_IO_ERROR ? errno : 0) { } @@ -105,8 +107,8 @@ namespace osmium { public: - explicit Bzip2Compressor(int fd) : - Compressor(), + explicit Bzip2Compressor(int fd, fsync sync) : + Compressor(sync), m_file(fdopen(dup(fd), "wb")), m_bzerror(BZ_OK), m_bzfile(::BZ2_bzWriteOpen(&m_bzerror, m_file, 6, 0, 0)) { @@ -115,11 +117,15 @@ namespace osmium { } } - ~Bzip2Compressor() override final { - close(); + ~Bzip2Compressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - void write(const std::string& data) override final { + void write(const std::string& data) final { int error; ::BZ2_bzWrite(&error, m_bzfile, const_cast(data.data()), static_cast_with_assert(data.size())); if (error != BZ_OK && error != BZ_STREAM_END) { @@ -127,13 +133,18 @@ namespace osmium { } } - void close() override final { + void close() final { if (m_bzfile) { int error; ::BZ2_bzWriteClose(&error, m_bzfile, 0, nullptr, nullptr); m_bzfile = nullptr; if (m_file) { - fclose(m_file); + if (do_fsync()) { + osmium::io::detail::reliable_fsync(::fileno(m_file)); + } + if (fclose(m_file) != 0) { + throw std::system_error(errno, std::system_category(), "Close failed"); + } } if (error != BZ_OK) { detail::throw_bzip2_error(m_bzfile, "write close failed", error); @@ -152,7 +163,7 @@ namespace osmium { public: - Bzip2Decompressor(int fd) : + explicit Bzip2Decompressor(int fd) : Decompressor(), m_file(fdopen(dup(fd), "rb")), m_bzerror(BZ_OK), @@ -162,11 +173,15 @@ namespace osmium { } } - ~Bzip2Decompressor() override final { - close(); + ~Bzip2Decompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - std::string read() override final { + std::string read() final { std::string buffer; if (!m_stream_end) { @@ -203,13 +218,15 @@ namespace osmium { return buffer; } - void close() override final { + void close() final { if (m_bzfile) { int error; ::BZ2_bzReadClose(&error, m_bzfile); m_bzfile = nullptr; if (m_file) { - fclose(m_file); + if (fclose(m_file) != 0) { + throw std::system_error(errno, std::system_category(), "Close failed"); + } } if (error != BZ_OK) { detail::throw_bzip2_error(m_bzfile, "read close failed", error); @@ -240,11 +257,15 @@ namespace osmium { } } - ~Bzip2BufferDecompressor() override final { - BZ2_bzDecompressEnd(&m_bzstream); + ~Bzip2BufferDecompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - std::string read() override final { + std::string read() final { std::string output; if (m_buffer) { @@ -270,22 +291,28 @@ namespace osmium { return output; } + void close() final { + BZ2_bzDecompressEnd(&m_bzstream); + } + }; // class Bzip2BufferDecompressor - namespace { + namespace detail { -// we want the register_compression() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" + // we want the register_compression() function to run, setting + // the variable is only a side-effect, it will never be used const bool registered_bzip2_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::bzip2, - [](int fd) { return new osmium::io::Bzip2Compressor(fd); }, + [](int fd, fsync sync) { return new osmium::io::Bzip2Compressor(fd, sync); }, [](int fd) { return new osmium::io::Bzip2Decompressor(fd); }, [](const char* buffer, size_t size) { return new osmium::io::Bzip2BufferDecompressor(buffer, size); } ); -#pragma GCC diagnostic pop - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_bzip2_compression() noexcept { + return registered_bzip2_compression; + } + + } // namespace detail } // namespace io diff --git a/third_party/libosmium/include/osmium/io/compression.hpp b/third_party/libosmium/include/osmium/io/compression.hpp index 252976181..7d95661bd 100644 --- a/third_party/libosmium/include/osmium/io/compression.hpp +++ b/third_party/libosmium/include/osmium/io/compression.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -40,6 +40,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #ifndef _MSC_VER @@ -49,7 +50,9 @@ DEALINGS IN THE SOFTWARE. #endif #include +#include #include +#include #include namespace osmium { @@ -58,11 +61,21 @@ namespace osmium { class Compressor { + fsync m_fsync; + + protected: + + bool do_fsync() const { + return m_fsync == fsync::yes; + } + public: - Compressor() = default; + explicit Compressor(fsync sync) : + m_fsync(sync) { + } - virtual ~Compressor() { + virtual ~Compressor() noexcept { } virtual void write(const std::string& data) = 0; @@ -85,13 +98,12 @@ namespace osmium { Decompressor(Decompressor&&) = delete; Decompressor& operator=(Decompressor&&) = delete; - virtual ~Decompressor() { + virtual ~Decompressor() noexcept { } virtual std::string read() = 0; - virtual void close() { - } + virtual void close() = 0; }; // class Decompressor @@ -106,13 +118,16 @@ namespace osmium { public: - typedef std::function create_compressor_type; + typedef std::function create_compressor_type; typedef std::function create_decompressor_type_fd; typedef std::function create_decompressor_type_buffer; private: - typedef std::map> compression_map_type; + typedef std::map> compression_map_type; compression_map_type m_callbacks; @@ -128,7 +143,7 @@ namespace osmium { std::string error_message {"Support for compression '"}; error_message += as_string(compression); error_message += "' not compiled into this binary."; - throw std::runtime_error(error_message); + throw unsupported_file_format_error(error_message); } public: @@ -144,15 +159,20 @@ namespace osmium { create_decompressor_type_fd create_decompressor_fd, create_decompressor_type_buffer create_decompressor_buffer) { - compression_map_type::value_type cc(compression, std::make_tuple(create_compressor, create_decompressor_fd, create_decompressor_buffer)); + compression_map_type::value_type cc(compression, + std::make_tuple(create_compressor, + create_decompressor_fd, + create_decompressor_buffer)); + return m_callbacks.insert(cc).second; } - std::unique_ptr create_compressor(osmium::io::file_compression compression, int fd) { + template + std::unique_ptr create_compressor(osmium::io::file_compression compression, TArgs&&... args) { auto it = m_callbacks.find(compression); if (it != m_callbacks.end()) { - return std::unique_ptr(std::get<0>(it->second)(fd)); + return std::unique_ptr(std::get<0>(it->second)(std::forward(args)...)); } error(compression); @@ -186,23 +206,31 @@ namespace osmium { public: - NoCompressor(int fd) : - Compressor(), + NoCompressor(int fd, fsync sync) : + Compressor(sync), m_fd(fd) { } - ~NoCompressor() override final { - close(); + ~NoCompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - void write(const std::string& data) override final { + void write(const std::string& data) final { osmium::io::detail::reliable_write(m_fd, data.data(), data.size()); } - void close() override final { + void close() final { if (m_fd >= 0) { - ::close(m_fd); + int fd = m_fd; m_fd = -1; + if (do_fsync()) { + osmium::io::detail::reliable_fsync(fd); + } + osmium::io::detail::reliable_close(fd); } } @@ -216,7 +244,7 @@ namespace osmium { public: - NoDecompressor(int fd) : + explicit NoDecompressor(int fd) : Decompressor(), m_fd(fd), m_buffer(nullptr), @@ -230,11 +258,15 @@ namespace osmium { m_buffer_size(size) { } - ~NoDecompressor() override final { - close(); + ~NoDecompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - std::string read() override final { + std::string read() final { std::string buffer; if (m_buffer) { @@ -249,35 +281,38 @@ namespace osmium { if (nread < 0) { throw std::system_error(errno, std::system_category(), "Read failed"); } - buffer.resize(nread); + buffer.resize(std::string::size_type(nread)); } return buffer; } - void close() override final { + void close() final { if (m_fd >= 0) { - ::close(m_fd); + int fd = m_fd; m_fd = -1; + osmium::io::detail::reliable_close(fd); } } }; // class NoDecompressor - namespace { + namespace detail { -// we want the register_compression() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" + // we want the register_compression() function to run, setting + // the variable is only a side-effect, it will never be used const bool registered_no_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::none, - [](int fd) { return new osmium::io::NoCompressor(fd); }, + [](int fd, fsync sync) { return new osmium::io::NoCompressor(fd, sync); }, [](int fd) { return new osmium::io::NoDecompressor(fd); }, [](const char* buffer, size_t size) { return new osmium::io::NoDecompressor(buffer, size); } ); -#pragma GCC diagnostic pop - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_no_compression() noexcept { + return registered_no_compression; + } + + } // namespace detail } // namespace io diff --git a/third_party/libosmium/include/osmium/io/debug_output.hpp b/third_party/libosmium/include/osmium/io/debug_output.hpp index 2836f7987..84db9cdb8 100644 --- a/third_party/libosmium/include/osmium/io/debug_output.hpp +++ b/third_party/libosmium/include/osmium/io/debug_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp index efecc5833..3f7537ea6 100644 --- a/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include @@ -41,14 +40,10 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include -#include - -#include #include #include #include @@ -87,65 +82,32 @@ namespace osmium { constexpr const char* color_white = "\x1b[37m"; constexpr const char* color_reset = "\x1b[0m"; + struct debug_output_options { + + /// Should metadata of objects be added? + bool add_metadata; + + /// Output with ANSI colors? + bool use_color; + + }; + /** * Writes out one buffer with OSM data in Debug format. */ - class DebugOutputBlock : public osmium::handler::Handler { + class DebugOutputBlock : public OutputBlock { - static constexpr size_t tmp_buffer_size = 50; + debug_output_options m_options; - std::shared_ptr m_input_buffer; - - std::shared_ptr m_out; - - char m_tmp_buffer[tmp_buffer_size+1]; - - bool m_add_metadata; - bool m_use_color; - - template - void output_formatted(const char* format, TArgs&&... args) { -#ifndef NDEBUG - int len = -#endif -#ifndef _MSC_VER - snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward(args)...); -#else - _snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward(args)...); -#endif - assert(len > 0 && static_cast(len) < tmp_buffer_size); - *m_out += m_tmp_buffer; - } + const char* m_utf8_prefix = ""; + const char* m_utf8_suffix = ""; void append_encoded_string(const char* data) { - const char* end = data + std::strlen(data); - - while (data != end) { - const char* last = data; - uint32_t c = utf8::next(data, end); - - // This is a list of Unicode code points that we let - // through instead of escaping them. It is incomplete - // and can be extended later. - // Generally we don't want to let through any - // non-printing characters. - if ((0x0020 <= c && c <= 0x0021) || - (0x0023 <= c && c <= 0x003b) || - (0x003d == c) || - (0x003f <= c && c <= 0x007e) || - (0x00a1 <= c && c <= 0x00ac) || - (0x00ae <= c && c <= 0x05ff)) { - m_out->append(last, data); - } else { - write_color(color_red); - output_formatted("", c); - write_color(color_blue); - } - } + append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix); } void write_color(const char* color) { - if (m_use_color) { + if (m_options.use_color) { *m_out += color; } } @@ -177,15 +139,38 @@ namespace osmium { *m_out += ": "; } + void write_comment_field(const char* name) { + write_color(color_cyan); + *m_out += name; + write_color(color_reset); + *m_out += ": "; + } + + void write_counter(int width, int n) { + write_color(color_white); + output_formatted(" %0*d: ", width, n++); + write_color(color_reset); + } + void write_error(const char* msg) { write_color(color_red); *m_out += msg; write_color(color_reset); } + void write_timestamp(const osmium::Timestamp& timestamp) { + if (timestamp.valid()) { + *m_out += timestamp.to_iso(); + output_formatted(" (%d)", timestamp.seconds_since_epoch()); + } else { + write_error("NOT SET"); + } + *m_out += '\n'; + } + void write_meta(const osmium::OSMObject& object) { output_formatted("%" PRId64 "\n", object.id()); - if (m_add_metadata) { + if (m_options.add_metadata) { write_fieldname("version"); output_formatted(" %d", object.version()); if (object.visible()) { @@ -196,8 +181,7 @@ namespace osmium { write_fieldname("changeset"); output_formatted("%d\n", object.changeset()); write_fieldname("timestamp"); - *m_out += object.timestamp().to_iso(); - output_formatted(" (%d)\n", object.timestamp()); + write_timestamp(object.timestamp()); write_fieldname("user"); output_formatted(" %d ", object.uid()); write_string(object.user()); @@ -211,14 +195,14 @@ namespace osmium { *m_out += padding; output_formatted(" %d\n", tags.size()); - osmium::max_op max; + osmium::max_op max; for (const auto& tag : tags) { max.update(std::strlen(tag.key())); } for (const auto& tag : tags) { *m_out += " "; write_string(tag.key()); - int spacing = max() - std::strlen(tag.key()); + auto spacing = max() - std::strlen(tag.key()); while (spacing--) { *m_out += " "; } @@ -255,12 +239,11 @@ namespace osmium { public: - explicit DebugOutputBlock(osmium::memory::Buffer&& buffer, bool add_metadata, bool use_color) : - m_input_buffer(std::make_shared(std::move(buffer))), - m_out(std::make_shared()), - m_tmp_buffer(), - m_add_metadata(add_metadata), - m_use_color(use_color) { + DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) : + OutputBlock(std::move(buffer)), + m_options(options), + m_utf8_prefix(options.use_color ? color_red : ""), + m_utf8_suffix(options.use_color ? color_blue : "") { } DebugOutputBlock(const DebugOutputBlock&) = default; @@ -269,13 +252,15 @@ namespace osmium { DebugOutputBlock(DebugOutputBlock&&) = default; DebugOutputBlock& operator=(DebugOutputBlock&&) = default; - ~DebugOutputBlock() = default; + ~DebugOutputBlock() noexcept = default; std::string operator()() { osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this); std::string out; - std::swap(out, *m_out); + using std::swap; + swap(out, *m_out); + return out; } @@ -313,7 +298,8 @@ namespace osmium { int width = int(log10(way.nodes().size())) + 1; int n = 0; for (const auto& node_ref : way.nodes()) { - output_formatted(" %0*d: %10" PRId64, width, n++, node_ref.ref()); + write_counter(width, n++); + output_formatted("%10" PRId64, node_ref.ref()); if (node_ref.location().valid()) { output_formatted(" (%.7f,%.7f)", node_ref.location().lon_without_check(), node_ref.location().lat_without_check()); } @@ -335,7 +321,7 @@ namespace osmium { int width = int(log10(relation.members().size())) + 1; int n = 0; for (const auto& member : relation.members()) { - output_formatted(" %0*d: ", width, n++); + write_counter(width, n++); *m_out += short_typename[item_type_to_nwr_index(member.type())]; output_formatted(" %10" PRId64 " ", member.ref()); write_string(member.role()); @@ -348,24 +334,26 @@ namespace osmium { void changeset(const osmium::Changeset& changeset) { write_object_type("changeset"); output_formatted("%d\n", changeset.id()); + write_fieldname("num changes"); output_formatted("%d", changeset.num_changes()); if (changeset.num_changes() == 0) { write_error(" NO CHANGES!"); } *m_out += '\n'; + write_fieldname("created at"); *m_out += ' '; - *m_out += changeset.created_at().to_iso(); - output_formatted(" (%d)\n", changeset.created_at()); + write_timestamp(changeset.created_at()); + write_fieldname("closed at"); *m_out += " "; if (changeset.closed()) { - *m_out += changeset.closed_at().to_iso(); - output_formatted(" (%d)\n", changeset.closed_at()); + write_timestamp(changeset.closed_at()); } else { write_error("OPEN!\n"); } + write_fieldname("user"); output_formatted(" %d ", changeset.uid()); write_string(changeset.user()); @@ -374,51 +362,73 @@ namespace osmium { write_box(changeset.bounds()); write_tags(changeset.tags(), " "); + if (changeset.num_comments() > 0) { + write_fieldname("comments"); + output_formatted(" %d\n", changeset.num_comments()); + + int width = int(log10(changeset.num_comments())) + 1; + int n = 0; + for (const auto& comment : changeset.discussion()) { + write_counter(width, n++); + + write_comment_field("date"); + write_timestamp(comment.date()); + output_formatted(" %*s", width, ""); + + write_comment_field("user"); + output_formatted("%d ", comment.uid()); + write_string(comment.user()); + output_formatted("\n %*s", width, ""); + + write_comment_field("text"); + write_string(comment.text()); + *m_out += '\n'; + } + } + *m_out += '\n'; } - }; // DebugOutputBlock + }; // class DebugOutputBlock class DebugOutputFormat : public osmium::io::detail::OutputFormat { - bool m_add_metadata; - bool m_use_color; - - public: - - DebugOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) : - OutputFormat(file, output_queue), - m_add_metadata(file.get("add_metadata") != "false"), - m_use_color(file.get("color") == "true") { - } - - DebugOutputFormat(const DebugOutputFormat&) = delete; - DebugOutputFormat& operator=(const DebugOutputFormat&) = delete; - - void write_buffer(osmium::memory::Buffer&& buffer) override final { - m_output_queue.push(osmium::thread::Pool::instance().submit(DebugOutputBlock{std::move(buffer), m_add_metadata, m_use_color})); - } + debug_output_options m_options; void write_fieldname(std::string& out, const char* name) { out += " "; - if (m_use_color) { + if (m_options.use_color) { out += color_cyan; } out += name; - if (m_use_color) { + if (m_options.use_color) { out += color_reset; } out += ": "; } - void write_header(const osmium::io::Header& header) override final { + public: + + DebugOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(output_queue), + m_options() { + m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.use_color = file.is_true("color"); + } + + DebugOutputFormat(const DebugOutputFormat&) = delete; + DebugOutputFormat& operator=(const DebugOutputFormat&) = delete; + + ~DebugOutputFormat() noexcept final = default; + + void write_header(const osmium::io::Header& header) final { std::string out; - if (m_use_color) { + if (m_options.use_color) { out += color_bold; } out += "header\n"; - if (m_use_color) { + if (m_options.use_color) { out += color_reset; } @@ -445,33 +455,26 @@ namespace osmium { } out += "\n=============================================\n\n"; - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(std::move(out)); + send_to_output_queue(std::move(out)); } - void close() override final { - std::string out; - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(out); + void write_buffer(osmium::memory::Buffer&& buffer) final { + m_output_queue.push(osmium::thread::Pool::instance().submit(DebugOutputBlock{std::move(buffer), m_options})); } }; // class DebugOutputFormat - namespace { + // we want the register_output_format() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_debug_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::debug, + [](const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::DebugOutputFormat(file, output_queue); + }); -// we want the register_output_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_debug_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::debug, - [](const osmium::io::File& file, data_queue_type& output_queue) { - return new osmium::io::detail::DebugOutputFormat(file, output_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_debug_output() noexcept { + return registered_debug_output; + } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/input_format.hpp b/third_party/libosmium/include/osmium/io/detail/input_format.hpp index 03e1190c3..0d7885131 100644 --- a/third_party/libosmium/include/osmium/io/detail/input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/input_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,13 +33,16 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -48,106 +51,156 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - namespace thread { - template class Queue; - } // namespace thread - namespace io { namespace detail { - /** - * Virtual base class for all classes reading OSM files in different - * formats. - * - * Do not use this class or derived classes directly. Use the - * osmium::io::Reader class instead. - */ - class InputFormat { + class Parser { + + future_buffer_queue_type& m_output_queue; + std::promise& m_header_promise; + queue_wrapper m_input_queue; + osmium::osm_entity_bits::type m_read_types; + bool m_header_is_done; protected: - osmium::io::File m_file; - osmium::osm_entity_bits::type m_read_which_entities; - osmium::io::Header m_header; - - explicit InputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities) : - m_file(file), - m_read_which_entities(read_which_entities) { - m_header.set_has_multiple_object_versions(m_file.has_multiple_object_versions()); + std::string get_input() { + return m_input_queue.pop(); } - InputFormat(const InputFormat&) = delete; - InputFormat(InputFormat&&) = delete; + bool input_done() const { + return m_input_queue.has_reached_end_of_data(); + } - InputFormat& operator=(const InputFormat&) = delete; - InputFormat& operator=(InputFormat&&) = delete; + osmium::osm_entity_bits::type read_types() const { + return m_read_types; + } + + bool header_is_done() const { + return m_header_is_done; + } + + void set_header_value(const osmium::io::Header& header) { + if (!m_header_is_done) { + m_header_is_done = true; + m_header_promise.set_value(header); + } + } + + void set_header_exception(const std::exception_ptr& exception) { + if (!m_header_is_done) { + m_header_is_done = true; + m_header_promise.set_exception(exception); + } + } + + /** + * Wrap the buffer into a future and add it to the output queue. + */ + void send_to_output_queue(osmium::memory::Buffer&& buffer) { + add_to_queue(m_output_queue, std::move(buffer)); + } + + void send_to_output_queue(std::future&& future) { + m_output_queue.push(std::move(future)); + } public: - virtual ~InputFormat() { + Parser(future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types) : + m_output_queue(output_queue), + m_header_promise(header_promise), + m_input_queue(input_queue), + m_read_types(read_types), + m_header_is_done(false) { } - virtual osmium::memory::Buffer read() = 0; + Parser(const Parser&) = delete; + Parser& operator=(const Parser&) = delete; - virtual void close() { + Parser(Parser&&) = delete; + Parser& operator=(Parser&&) = delete; + + virtual ~Parser() noexcept = default; + + virtual void run() = 0; + + void parse() { + try { + run(); + } catch (...) { + std::exception_ptr exception = std::current_exception(); + set_header_exception(exception); + add_to_queue(m_output_queue, std::move(exception)); + } + + add_end_of_data_to_queue(m_output_queue); } - virtual osmium::io::Header header() { - return m_header; - } - - }; // class InputFormat + }; // class Parser /** - * This factory class is used to create objects that read OSM data - * written in a specified format. + * This factory class is used to create objects that decode OSM + * data written in a specified format. * - * Do not use this class directly. Instead use the osmium::io::Reader - * class. + * Do not use this class directly. Use the osmium::io::Reader + * class instead. */ - class InputFormatFactory { + class ParserFactory { public: - typedef std::function&)> create_input_type; + typedef std::function< + std::unique_ptr( + future_string_queue_type&, + future_buffer_queue_type&, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities + ) + > create_parser_type; private: - typedef std::map map_type; + typedef std::map map_type; map_type m_callbacks; - InputFormatFactory() : + ParserFactory() : m_callbacks() { } public: - static InputFormatFactory& instance() { - static InputFormatFactory factory; + static ParserFactory& instance() { + static ParserFactory factory; return factory; } - bool register_input_format(osmium::io::file_format format, create_input_type create_function) { + bool register_parser(osmium::io::file_format format, create_parser_type create_function) { if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) { return false; } return true; } - std::unique_ptr create_input(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue& input_queue) { - file.check(); - + create_parser_type get_creator_function(const osmium::io::File& file) { auto it = m_callbacks.find(file.format()); - if (it != m_callbacks.end()) { - return std::unique_ptr((it->second)(file, read_which_entities, input_queue)); + if (it == m_callbacks.end()) { + throw unsupported_file_format_error( + std::string("Can not open file '") + + file.filename() + + "' with type '" + + as_string(file.format()) + + "'. No support for reading this format in this program."); } - - throw std::runtime_error(std::string("Support for input format '") + as_string(file.format()) + "' not compiled into this binary."); + return it->second; } - }; // class InputFormatFactory + }; // class ParserFactory } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp new file mode 100644 index 000000000..7293cfa73 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp @@ -0,0 +1,636 @@ +#ifndef OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP +#define OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osmium { + + /** + * Exception thrown when the o5m deocder failed. The exception contains + * (if available) information about the place where the error happened + * and the type of error. + */ + struct o5m_error : public io_error { + + explicit o5m_error(const char* what) : + io_error(std::string("o5m format error: ") + what) { + } + + }; // struct o5m_error + + namespace io { + + namespace detail { + + // Implementation of the o5m/o5c file formats according to the + // description at http://wiki.openstreetmap.org/wiki/O5m . + + class ReferenceTable { + + // The following settings are from the o5m description: + + // The maximum number of entries in this table. + const uint64_t number_of_entries = 15000; + + // The size of one entry in the table. + const unsigned int entry_size = 256; + + // The maximum length of a string in the table including + // two \0 bytes. + const unsigned int max_length = 250 + 2; + + // The data is stored in this string. It is default constructed + // and then resized on demand the first time something is added. + // This is done because the ReferenceTable is in a O5mParser + // object which will be copied from one thread to another. This + // way the string is still small when it is copied. + std::string m_table; + + unsigned int current_entry = 0; + + public: + + void clear() { + current_entry = 0; + } + + void add(const char* string, size_t size) { + if (m_table.empty()) { + m_table.resize(entry_size * number_of_entries); + } + if (size <= max_length) { + std::copy_n(string, size, &m_table[current_entry * entry_size]); + if (++current_entry == number_of_entries) { + current_entry = 0; + } + } + } + + const char* get(uint64_t index) const { + if (m_table.empty() || index == 0 || index > number_of_entries) { + throw o5m_error("reference to non-existing string in table"); + } + auto entry = (current_entry + number_of_entries - index) % number_of_entries; + return &m_table[entry * entry_size]; + } + + }; // class ReferenceTable + + class O5mParser : public Parser { + + static constexpr int buffer_size = 2 * 1000 * 1000; + + osmium::io::Header m_header; + + osmium::memory::Buffer m_buffer; + + std::string m_input; + + const char* m_data; + const char* m_end; + + ReferenceTable m_reference_table; + + static int64_t zvarint(const char** data, const char* end) { + return protozero::decode_zigzag64(protozero::decode_varint(data, end)); + } + + bool ensure_bytes_available(size_t need_bytes) { + if ((m_end - m_data) >= long(need_bytes)) { + return true; + } + + if (input_done() && (m_input.size() < need_bytes)) { + return false; + } + + m_input.erase(0, m_data - m_input.data()); + + while (m_input.size() < need_bytes) { + std::string data = get_input(); + if (input_done()) { + return false; + } + m_input.append(data); + } + + m_data = m_input.data(); + m_end = m_input.data() + m_input.size(); + + return true; + } + + void check_header_magic() { + static const unsigned char header_magic[] = { 0xff, 0xe0, 0x04, 'o', '5' }; + + if (std::strncmp(reinterpret_cast(header_magic), m_data, sizeof(header_magic))) { + throw o5m_error("wrong header magic"); + } + + m_data += sizeof(header_magic); + } + + void check_file_type() { + if (*m_data == 'm') { // o5m data file + m_header.set_has_multiple_object_versions(false); + } else if (*m_data == 'c') { // o5c change file + m_header.set_has_multiple_object_versions(true); + } else { + throw o5m_error("wrong header magic"); + } + + m_data++; + } + + void check_file_format_version() { + if (*m_data != '2') { + throw o5m_error("wrong header magic"); + } + + m_data++; + } + + void decode_header() { + if (! ensure_bytes_available(7)) { // overall length of header + throw o5m_error("file too short (incomplete header info)"); + } + + check_header_magic(); + check_file_type(); + check_file_format_version(); + } + + void mark_header_as_done() { + set_header_value(m_header); + } + + osmium::util::DeltaDecode m_delta_id; + + osmium::util::DeltaDecode m_delta_timestamp; + osmium::util::DeltaDecode m_delta_changeset; + osmium::util::DeltaDecode m_delta_lon; + osmium::util::DeltaDecode m_delta_lat; + + osmium::util::DeltaDecode m_delta_way_node_id; + osmium::util::DeltaDecode m_delta_member_ids[3]; + + void reset() { + m_reference_table.clear(); + + m_delta_id.clear(); + m_delta_timestamp.clear(); + m_delta_changeset.clear(); + m_delta_lon.clear(); + m_delta_lat.clear(); + + m_delta_way_node_id.clear(); + m_delta_member_ids[0].clear(); + m_delta_member_ids[1].clear(); + m_delta_member_ids[2].clear(); + } + + const char* decode_string(const char** dataptr, const char* const end) { + if (**dataptr == 0x00) { // get inline string + (*dataptr)++; + if (*dataptr == end) { + throw o5m_error("string format error"); + } + return *dataptr; + } else { // get from reference table + auto index = protozero::decode_varint(dataptr, end); + return m_reference_table.get(index); + } + } + + std::pair decode_user(const char** dataptr, const char* const end) { + bool update_pointer = (**dataptr == 0x00); + const char* data = decode_string(dataptr, end); + const char* start = data; + + auto uid = protozero::decode_varint(&data, end); + + if (data == end) { + throw o5m_error("missing user name"); + } + + const char* user = ++data; + + if (uid == 0 && update_pointer) { + m_reference_table.add("\0\0", 2); + *dataptr = data; + return std::make_pair(0, ""); + } + + while (*data++) { + if (data == end) { + throw o5m_error("no null byte in user name"); + } + } + + if (update_pointer) { + m_reference_table.add(start, data - start); + *dataptr = data; + } + + return std::make_pair(static_cast_with_assert(uid), user); + } + + void decode_tags(osmium::builder::Builder* builder, const char** dataptr, const char* const end) { + osmium::builder::TagListBuilder tl_builder(m_buffer, builder); + + while(*dataptr != end) { + bool update_pointer = (**dataptr == 0x00); + const char* data = decode_string(dataptr, end); + const char* start = data; + + while (*data++) { + if (data == end) { + throw o5m_error("no null byte in tag key"); + } + } + + const char* value = data; + while (*data++) { + if (data == end) { + throw o5m_error("no null byte in tag value"); + } + } + + if (update_pointer) { + m_reference_table.add(start, data - start); + *dataptr = data; + } + + tl_builder.add_tag(start, value); + } + } + + const char* decode_info(osmium::OSMObject& object, const char** dataptr, const char* const end) { + const char* user = ""; + + if (**dataptr == 0x00) { // no info section + ++*dataptr; + } else { // has info section + object.set_version(static_cast_with_assert(protozero::decode_varint(dataptr, end))); + auto timestamp = m_delta_timestamp.update(zvarint(dataptr, end)); + if (timestamp != 0) { // has timestamp + object.set_timestamp(timestamp); + object.set_changeset(m_delta_changeset.update(zvarint(dataptr, end))); + if (*dataptr != end) { + auto uid_user = decode_user(dataptr, end); + object.set_uid(uid_user.first); + user = uid_user.second; + } else { + object.set_uid(user_id_type(0)); + } + } + } + + return user; + } + + void decode_node(const char* data, const char* const end) { + osmium::builder::NodeBuilder builder(m_buffer); + osmium::Node& node = builder.object(); + + node.set_id(m_delta_id.update(zvarint(&data, end))); + + builder.add_user(decode_info(node, &data, end)); + + if (data == end) { + // no location, object is deleted + builder.object().set_visible(false); + builder.object().set_location(osmium::Location{}); + } else { + auto lon = m_delta_lon.update(zvarint(&data, end)); + auto lat = m_delta_lat.update(zvarint(&data, end)); + builder.object().set_location(osmium::Location{lon, lat}); + + if (data != end) { + decode_tags(&builder, &data, end); + } + } + + m_buffer.commit(); + } + + void decode_way(const char* data, const char* const end) { + osmium::builder::WayBuilder builder(m_buffer); + osmium::Way& way = builder.object(); + + way.set_id(m_delta_id.update(zvarint(&data, end))); + + builder.add_user(decode_info(way, &data, end)); + + if (data == end) { + // no reference section, object is deleted + builder.object().set_visible(false); + } else { + auto reference_section_length = protozero::decode_varint(&data, end); + if (reference_section_length > 0) { + const char* const end_refs = data + reference_section_length; + if (end_refs > end) { + throw o5m_error("way nodes ref section too long"); + } + + osmium::builder::WayNodeListBuilder wn_builder(m_buffer, &builder); + + while (data < end_refs) { + wn_builder.add_node_ref(m_delta_way_node_id.update(zvarint(&data, end))); + } + } + + if (data != end) { + decode_tags(&builder, &data, end); + } + } + + m_buffer.commit(); + } + + osmium::item_type decode_member_type(char c) { + if (c < '0' || c > '2') { + throw o5m_error("unknown member type"); + } + return osmium::nwr_index_to_item_type(c - '0'); + } + + std::pair decode_role(const char** dataptr, const char* const end) { + bool update_pointer = (**dataptr == 0x00); + const char* data = decode_string(dataptr, end); + const char* start = data; + + auto member_type = decode_member_type(*data++); + if (data == end) { + throw o5m_error("missing role"); + } + const char* role = data; + + while (*data++) { + if (data == end) { + throw o5m_error("no null byte in role"); + } + } + + if (update_pointer) { + m_reference_table.add(start, data - start); + *dataptr = data; + } + + return std::make_pair(member_type, role); + } + + void decode_relation(const char* data, const char* const end) { + osmium::builder::RelationBuilder builder(m_buffer); + osmium::Relation& relation = builder.object(); + + relation.set_id(m_delta_id.update(zvarint(&data, end))); + + builder.add_user(decode_info(relation, &data, end)); + + if (data == end) { + // no reference section, object is deleted + builder.object().set_visible(false); + } else { + auto reference_section_length = protozero::decode_varint(&data, end); + if (reference_section_length > 0) { + const char* const end_refs = data + reference_section_length; + if (end_refs > end) { + throw o5m_error("relation format error"); + } + + osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder); + + while (data < end_refs) { + auto delta_id = zvarint(&data, end); + if (data == end) { + throw o5m_error("relation member format error"); + } + auto type_role = decode_role(&data, end); + auto i = osmium::item_type_to_nwr_index(type_role.first); + auto ref = m_delta_member_ids[i].update(delta_id); + rml_builder.add_member(type_role.first, ref, type_role.second); + } + } + + if (data != end) { + decode_tags(&builder, &data, end); + } + } + + m_buffer.commit(); + } + + void decode_bbox(const char* data, const char* const end) { + auto sw_lon = zvarint(&data, end); + auto sw_lat = zvarint(&data, end); + auto ne_lon = zvarint(&data, end); + auto ne_lat = zvarint(&data, end); + + m_header.add_box(osmium::Box{osmium::Location{sw_lon, sw_lat}, + osmium::Location{ne_lon, ne_lat}}); + } + + void decode_timestamp(const char* data, const char* const end) { + auto timestamp = osmium::Timestamp(zvarint(&data, end)).to_iso(); + m_header.set("o5m_timestamp", timestamp); + m_header.set("timestamp", timestamp); + } + + void flush() { + osmium::memory::Buffer buffer(buffer_size); + using std::swap; + swap(m_buffer, buffer); + send_to_output_queue(std::move(buffer)); + } + + enum class dataset_type : unsigned char { + node = 0x10, + way = 0x11, + relation = 0x12, + bounding_box = 0xdb, + timestamp = 0xdc, + header = 0xe0, + sync = 0xee, + jump = 0xef, + reset = 0xff + }; + + void decode_data() { + while (ensure_bytes_available(1)) { + dataset_type ds_type = dataset_type(*m_data++); + if (ds_type > dataset_type::jump) { + if (ds_type == dataset_type::reset) { + reset(); + } + } else { + ensure_bytes_available(protozero::max_varint_length); + + uint64_t length = 0; + try { + length = protozero::decode_varint(&m_data, m_end); + } catch (protozero::end_of_buffer_exception&) { + throw o5m_error("premature end of file"); + } + + if (! ensure_bytes_available(length)) { + throw o5m_error("premature end of file"); + } + + switch (ds_type) { + case dataset_type::node: + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::node) { + decode_node(m_data, m_data + length); + } + break; + case dataset_type::way: + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::way) { + decode_way(m_data, m_data + length); + } + break; + case dataset_type::relation: + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::relation) { + decode_relation(m_data, m_data + length); + } + break; + case dataset_type::bounding_box: + decode_bbox(m_data, m_data + length); + break; + case dataset_type::timestamp: + decode_timestamp(m_data, m_data + length); + break; + default: + // ignore unknown datasets + break; + } + + if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) { + break; + } + + m_data += length; + + if (m_buffer.committed() > buffer_size / 10 * 9) { + flush(); + } + } + } + + if (m_buffer.committed()) { + flush(); + } + + mark_header_as_done(); + } + + public: + + O5mParser(future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types) : + Parser(input_queue, output_queue, header_promise, read_types), + m_header(), + m_buffer(buffer_size), + m_input(), + m_data(m_input.data()), + m_end(m_data) { + } + + ~O5mParser() noexcept final = default; + + void run() final { + osmium::thread::set_thread_name("_osmium_o5m_in"); + + decode_header(); + decode_data(); + } + + }; // class O5mParser + + // we want the register_parser() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_o5m_parser = ParserFactory::instance().register_parser( + file_format::o5m, + [](future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new O5mParser(input_queue, output_queue, header_promise, read_which_entities)); + }); + + // dummy function to silence the unused variable warning from above + inline bool get_registered_o5m_parser() noexcept { + return registered_o5m_parser; + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + +#endif // OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp index a3103d9bf..3fedf7e9c 100644 --- a/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include @@ -41,14 +40,10 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include -#include - -#include #include #include #include @@ -74,71 +69,27 @@ namespace osmium { namespace detail { + struct opl_output_options { + + /// Should metadata of objects be added? + bool add_metadata; + + }; + /** * Writes out one buffer with OSM data in OPL format. */ - class OPLOutputBlock : public osmium::handler::Handler { + class OPLOutputBlock : public OutputBlock { - static constexpr size_t tmp_buffer_size = 100; - - std::shared_ptr m_input_buffer; - - std::shared_ptr m_out; - - char m_tmp_buffer[tmp_buffer_size+1]; - - bool m_add_metadata; - - template - void output_formatted(const char* format, TArgs&&... args) { -#ifndef NDEBUG - int len = -#endif -#ifndef _MSC_VER - snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward(args)...); -#else - _snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward(args)...); -#endif - assert(len > 0 && static_cast(len) < tmp_buffer_size); - *m_out += m_tmp_buffer; - } + opl_output_options m_options; void append_encoded_string(const char* data) { - const char* end = data + std::strlen(data); - - while (data != end) { - const char* last = data; - uint32_t c = utf8::next(data, end); - - // This is a list of Unicode code points that we let - // through instead of escaping them. It is incomplete - // and can be extended later. - // Generally we don't want to let through any character - // that has special meaning in the OPL format such as - // space, comma, @, etc. and any non-printing characters. - if ((0x0021 <= c && c <= 0x0024) || - (0x0026 <= c && c <= 0x002b) || - (0x002d <= c && c <= 0x003c) || - (0x003e <= c && c <= 0x003f) || - (0x0041 <= c && c <= 0x007e) || - (0x00a1 <= c && c <= 0x00ac) || - (0x00ae <= c && c <= 0x05ff)) { - m_out->append(last, data); - } else { - *m_out += '%'; - if (c <= 0xff) { - output_formatted("%02x", c); - } else { - output_formatted("%04x", c); - } - *m_out += '%'; - } - } + osmium::io::detail::append_utf8_encoded_string(*m_out, data); } void write_meta(const osmium::OSMObject& object) { output_formatted("%" PRId64, object.id()); - if (m_add_metadata) { + if (m_options.add_metadata) { output_formatted(" v%d d", object.version()); *m_out += (object.visible() ? 'V' : 'D'); output_formatted(" c%d t", object.changeset()); @@ -160,7 +111,7 @@ namespace osmium { } } - void write_location(const osmium::Location location, const char x, const char y) { + void write_location(const osmium::Location& location, const char x, const char y) { if (location) { output_formatted(" %c%.7f %c%.7f", x, location.lon_without_check(), y, location.lat_without_check()); } else { @@ -173,11 +124,9 @@ namespace osmium { public: - explicit OPLOutputBlock(osmium::memory::Buffer&& buffer, bool add_metadata) : - m_input_buffer(std::make_shared(std::move(buffer))), - m_out(std::make_shared()), - m_tmp_buffer(), - m_add_metadata(add_metadata) { + OPLOutputBlock(osmium::memory::Buffer&& buffer, const opl_output_options& options) : + OutputBlock(std::move(buffer)), + m_options(options) { } OPLOutputBlock(const OPLOutputBlock&) = default; @@ -186,13 +135,15 @@ namespace osmium { OPLOutputBlock(OPLOutputBlock&&) = default; OPLOutputBlock& operator=(OPLOutputBlock&&) = default; - ~OPLOutputBlock() = default; + ~OPLOutputBlock() noexcept = default; std::string operator()() { osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this); std::string out; - std::swap(out, *m_out); + using std::swap; + swap(out, *m_out); + return out; } @@ -244,7 +195,7 @@ namespace osmium { *m_out += changeset.created_at().to_iso(); *m_out += " e"; *m_out += changeset.closed_at().to_iso(); - output_formatted(" i%d u", changeset.uid()); + output_formatted(" d%d i%d u", changeset.num_comments(), changeset.uid()); append_encoded_string(changeset.user()); write_location(changeset.bounds().bottom_left(), 'x', 'y'); write_location(changeset.bounds().top_right(), 'X', 'Y'); @@ -264,48 +215,42 @@ namespace osmium { *m_out += '\n'; } - }; // OPLOutputBlock + }; // class OPLOutputBlock class OPLOutputFormat : public osmium::io::detail::OutputFormat { - bool m_add_metadata; + opl_output_options m_options; public: - OPLOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) : - OutputFormat(file, output_queue), - m_add_metadata(file.get("add_metadata") != "false") { + OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(output_queue), + m_options() { + m_options.add_metadata = file.is_not_false("add_metadata"); } OPLOutputFormat(const OPLOutputFormat&) = delete; OPLOutputFormat& operator=(const OPLOutputFormat&) = delete; - void write_buffer(osmium::memory::Buffer&& buffer) override final { - m_output_queue.push(osmium::thread::Pool::instance().submit(OPLOutputBlock{std::move(buffer), m_add_metadata})); - } + ~OPLOutputFormat() noexcept final = default; - void close() override final { - std::string out; - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(out); + void write_buffer(osmium::memory::Buffer&& buffer) final { + m_output_queue.push(osmium::thread::Pool::instance().submit(OPLOutputBlock{std::move(buffer), m_options})); } }; // class OPLOutputFormat - namespace { + // we want the register_output_format() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_opl_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::opl, + [](const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::OPLOutputFormat(file, output_queue); + }); -// we want the register_output_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_opl_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::opl, - [](const osmium::io::File& file, data_queue_type& output_queue) { - return new osmium::io::detail::OPLOutputFormat(file, output_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_opl_output() noexcept { + return registered_opl_output; + } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/output_format.hpp b/third_party/libosmium/include/osmium/io/detail/output_format.hpp index 529a1890f..34ca82f24 100644 --- a/third_party/libosmium/include/osmium/io/detail/output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/output_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,29 +34,48 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include #include #include +#include +#include +#include #include #include -#include -#include +#include namespace osmium { - namespace memory { - class Buffer; - } + namespace io { + class Header; + } // namespace io namespace io { namespace detail { - typedef osmium::thread::Queue> data_queue_type; + class OutputBlock : public osmium::handler::Handler { + + protected: + + std::shared_ptr m_input_buffer; + + std::shared_ptr m_out; + + explicit OutputBlock(osmium::memory::Buffer&& buffer) : + m_input_buffer(std::make_shared(std::move(buffer))), + m_out(std::make_shared()) { + } + + template + void output_formatted(const char* format, TArgs&&... args) { + append_printf_formatted_string(*m_out, format, std::forward(args)...); + } + + }; // class OutputBlock; /** * Virtual base class for all classes writing OSM files in different @@ -69,13 +88,19 @@ namespace osmium { protected: - osmium::io::File m_file; - data_queue_type& m_output_queue; + future_string_queue_type& m_output_queue; + + /** + * Wrap the string into a future and add it to the output + * queue. + */ + void send_to_output_queue(std::string&& data) { + add_to_queue(m_output_queue, std::move(data)); + } public: - explicit OutputFormat(const osmium::io::File& file, data_queue_type& output_queue) : - m_file(file), + explicit OutputFormat(future_string_queue_type& output_queue) : m_output_queue(output_queue) { } @@ -85,15 +110,15 @@ namespace osmium { OutputFormat& operator=(const OutputFormat&) = delete; OutputFormat& operator=(OutputFormat&&) = delete; - virtual ~OutputFormat() { - } + virtual ~OutputFormat() noexcept = default; virtual void write_header(const osmium::io::Header&) { } virtual void write_buffer(osmium::memory::Buffer&&) = 0; - virtual void close() = 0; + virtual void write_end() { + } }; // class OutputFormat @@ -108,7 +133,7 @@ namespace osmium { public: - typedef std::function create_output_type; + typedef std::function create_output_type; private: @@ -134,15 +159,18 @@ namespace osmium { return true; } - std::unique_ptr create_output(const osmium::io::File& file, data_queue_type& output_queue) { - file.check(); - + std::unique_ptr create_output(const osmium::io::File& file, future_string_queue_type& output_queue) { auto it = m_callbacks.find(file.format()); if (it != m_callbacks.end()) { return std::unique_ptr((it->second)(file, output_queue)); } - throw std::runtime_error(std::string("Support for output format '") + as_string(file.format()) + "' not compiled into this binary."); + throw unsupported_file_format_error( + std::string("Can not open file '") + + file.filename() + + "' with type '" + + as_string(file.format()) + + "'. No support for writing this format in this program."); } }; // class OutputFormatFactory diff --git a/third_party/libosmium/include/osmium/io/detail/pbf.hpp b/third_party/libosmium/include/osmium/io/detail/pbf.hpp index 15e457a12..88c499326 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include // needed for htonl and ntohl @@ -53,11 +54,11 @@ namespace osmium { */ struct pbf_error : public io_error { - pbf_error(const std::string& what) : + explicit pbf_error(const std::string& what) : io_error(std::string("PBF error: ") + what) { } - pbf_error(const char* what) : + explicit pbf_error(const char* what) : io_error(std::string("PBF error: ") + what) { } @@ -79,9 +80,9 @@ namespace osmium { const int64_t resolution_convert = lonlat_resolution / osmium::Location::coordinate_precision; - } + } // namespace detail - } + } // namespace io } // namespace osmium diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp index 79e899ff8..43de1c18e 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -37,8 +37,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include +#include +#include +#include +#include +#include #include @@ -62,13 +66,14 @@ namespace osmium { namespace detail { using ptr_len_type = std::pair; + using osm_string_len_type = std::pair; class PBFPrimitiveBlockDecoder { static constexpr size_t initial_buffer_size = 2 * 1024 * 1024; ptr_len_type m_data; - std::vector m_stringtable; + std::vector m_stringtable; int64_t m_lon_offset = 0; int64_t m_lat_offset = 0; @@ -86,7 +91,11 @@ namespace osmium { protozero::pbf_message pbf_string_table(data); while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) { - m_stringtable.push_back(pbf_string_table.get_data()); + auto str_len = pbf_string_table.get_data(); + if (str_len.second > osmium::max_osm_string_length) { + throw osmium::pbf_error("overlong string in string table"); + } + m_stringtable.emplace_back(str_len.first, osmium::string_size_type(str_len.second)); } } @@ -156,8 +165,8 @@ namespace osmium { } } - ptr_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) { - ptr_len_type user = std::make_pair("", 0); + osm_string_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) { + osm_string_len_type user = std::make_pair("", 0); protozero::pbf_message pbf_info(data); while (pbf_info.next()) { @@ -220,7 +229,7 @@ namespace osmium { } int32_t convert_pbf_coordinate(int64_t c) const { - return (c * m_granularity + m_lon_offset) / resolution_convert; + return int32_t((c * m_granularity + m_lon_offset) / resolution_convert); } void decode_node(const ptr_len_type& data) { @@ -232,7 +241,7 @@ namespace osmium { int64_t lon = std::numeric_limits::max(); int64_t lat = std::numeric_limits::max(); - ptr_len_type user = { "", 0 }; + osm_string_len_type user = { "", 0 }; protozero::pbf_message pbf_node(data); while (pbf_node.next()) { @@ -285,7 +294,7 @@ namespace osmium { kv_type vals; std::pair refs; - ptr_len_type user = { "", 0 }; + osm_string_len_type user = { "", 0 }; protozero::pbf_message pbf_way(data); while (pbf_way.next()) { @@ -334,7 +343,7 @@ namespace osmium { std::pair refs; std::pair types; - ptr_len_type user = { "", 0 }; + osm_string_len_type user = { "", 0 }; protozero::pbf_message pbf_relation(data); while (pbf_relation.next()) { @@ -512,7 +521,7 @@ namespace osmium { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } - visible = *visibles.first++; + visible = (*visibles.first++) != 0; } node.set_visible(visible); @@ -522,10 +531,14 @@ namespace osmium { builder.add_user(""); } + // even if the node isn't visible, there's still a record + // of its lat/lon in the dense arrays. + const auto lon = dense_longitude.update(*lons.first++); + const auto lat = dense_latitude.update(*lats.first++); if (visible) { builder.object().set_location(osmium::Location( - convert_pbf_coordinate(dense_longitude.update(*lons.first++)), - convert_pbf_coordinate(dense_latitude.update(*lats.first++)) + convert_pbf_coordinate(lon), + convert_pbf_coordinate(lat) )); } @@ -552,7 +565,7 @@ namespace osmium { public: - explicit PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) : + PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) : m_data(data), m_read_types(read_types) { } @@ -563,7 +576,7 @@ namespace osmium { PBFPrimitiveBlockDecoder(PBFPrimitiveBlockDecoder&&) = delete; PBFPrimitiveBlockDecoder& operator=(PBFPrimitiveBlockDecoder&&) = delete; - ~PBFPrimitiveBlockDecoder() = default; + ~PBFPrimitiveBlockDecoder() noexcept = default; osmium::memory::Buffer operator()() { try { @@ -579,8 +592,8 @@ namespace osmium { }; // class PBFPrimitiveBlockDecoder inline ptr_len_type decode_blob(const std::string& blob_data, std::string& output) { - int32_t raw_size; - std::pair zlib_data; + int32_t raw_size = 0; + std::pair zlib_data = {nullptr, 0}; protozero::pbf_message pbf_blob(blob_data); while (pbf_blob.next()) { @@ -609,7 +622,7 @@ namespace osmium { } } - if (zlib_data.second != 0) { + if (zlib_data.second != 0 && raw_size != 0) { return osmium::io::detail::zlib_uncompress_string( zlib_data.first, static_cast(zlib_data.second), @@ -694,7 +707,11 @@ namespace osmium { header.set("generator", pbf_header_block.get_string()); break; case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp: - header.set("osmosis_replication_timestamp", osmium::Timestamp(pbf_header_block.get_int64()).to_iso()); + { + auto timestamp = osmium::Timestamp(pbf_header_block.get_int64()).to_iso(); + header.set("osmosis_replication_timestamp", timestamp); + header.set("timestamp", timestamp); + } break; case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number: header.set("osmosis_replication_sequence_number", std::to_string(pbf_header_block.get_int64())); @@ -741,7 +758,7 @@ namespace osmium { PBFDataBlobDecoder(PBFDataBlobDecoder&&) = default; PBFDataBlobDecoder& operator=(PBFDataBlobDecoder&&) = default; - ~PBFDataBlobDecoder() = default; + ~PBFDataBlobDecoder() noexcept = default; osmium::memory::Buffer operator()() { std::string output; diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp index 7817d27d1..0c6a9ef54 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,17 +34,12 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include -#include #include #include #include -#include #include -#include #include -#include #include #include #include @@ -58,40 +53,22 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include -#include #include -#include #include #include #include -#include #include -#include #include namespace osmium { namespace io { - class File; - namespace detail { - /** - * Class for parsing PBF files. - */ - class PBFInputFormat : public osmium::io::detail::InputFormat { + class PBFParser : public Parser { - typedef osmium::thread::Queue> queue_type; - - bool m_use_thread_pool; - bool m_eof { false }; - queue_type m_queue; - std::atomic m_quit_input_thread; - std::thread m_reader; - osmium::thread::Queue& m_input_queue; std::string m_input_buffer; /** @@ -103,9 +80,8 @@ namespace osmium { */ std::string read_from_input_queue(size_t size) { while (m_input_buffer.size() < size) { - std::string new_data; - m_input_queue.wait_and_pop(new_data); - if (new_data.empty()) { + std::string new_data = get_input(); + if (input_done()) { throw osmium::pbf_error("truncated data (EOF encountered)"); } m_input_buffer += new_data; @@ -113,7 +89,10 @@ namespace osmium { std::string output { m_input_buffer.substr(size) }; m_input_buffer.resize(size); - std::swap(output, m_input_buffer); + + using std::swap; + swap(output, m_input_buffer); + return output; } @@ -125,7 +104,7 @@ namespace osmium { uint32_t size_in_network_byte_order; try { - std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order)); + const std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order)); size_in_network_byte_order = *reinterpret_cast(input_data.data()); } catch (osmium::pbf_error&) { return 0; // EOF @@ -174,125 +153,85 @@ namespace osmium { size_t check_type_and_get_blob_size(const char* expected_type) { assert(expected_type); - auto size = read_blob_header_size_from_file(); + const auto size = read_blob_header_size_from_file(); if (size == 0) { // EOF return 0; } - std::string blob_header = read_from_input_queue(size); + const std::string blob_header = read_from_input_queue(size); return decode_blob_header(protozero::pbf_message(blob_header), expected_type); } - void parse_osm_data(osmium::osm_entity_bits::type read_types) { - osmium::thread::set_thread_name("_osmium_pbf_in"); - - while (auto size = check_type_and_get_blob_size("OSMData")) { - std::string input_buffer = read_from_input_queue(size); - if (input_buffer.size() > max_uncompressed_blob_size) { - throw osmium::pbf_error(std::string("invalid blob size: " + std::to_string(input_buffer.size()))); - } - - if (m_use_thread_pool) { - m_queue.push(osmium::thread::Pool::instance().submit(PBFDataBlobDecoder{ std::move(input_buffer), read_types })); - } else { - std::promise promise; - m_queue.push(promise.get_future()); - PBFDataBlobDecoder data_blob_parser{ std::move(input_buffer), read_types }; - promise.set_value(data_blob_parser()); - } - - if (m_quit_input_thread) { - return; - } + std::string read_from_input_queue_with_check(size_t size) { + if (size > max_uncompressed_blob_size) { + throw osmium::pbf_error(std::string("invalid blob size: " + + std::to_string(size))); } - - // Send an empty buffer to signal the reader that we are - // done. - std::promise promise; - m_queue.push(promise.get_future()); - promise.set_value(osmium::memory::Buffer{}); + return read_from_input_queue(size); } - void signal_input_thread_to_quit() { - m_quit_input_thread = true; + // Parse the header in the PBF OSMHeader blob. + void parse_header_blob() { + osmium::io::Header header; + const auto size = check_type_and_get_blob_size("OSMHeader"); + header = decode_header(read_from_input_queue_with_check(size)); + set_header_value(header); + } + + void parse_data_blobs() { + while (const auto size = check_type_and_get_blob_size("OSMData")) { + std::string input_buffer = read_from_input_queue_with_check(size); + + PBFDataBlobDecoder data_blob_parser{ std::move(input_buffer), read_types() }; + + if (osmium::config::use_pool_threads_for_pbf_parsing()) { + send_to_output_queue(osmium::thread::Pool::instance().submit(std::move(data_blob_parser))); + } else { + send_to_output_queue(data_blob_parser()); + } + } } public: - /** - * Instantiate PBF Parser - * - * @param file osmium::io::File instance describing file to be read from. - * @param read_which_entities Which types of OSM entities (nodes, ways, relations, changesets) should be parsed? - * @param input_queue String queue where data is read from. - */ - PBFInputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue& input_queue) : - osmium::io::detail::InputFormat(file, read_which_entities), - m_use_thread_pool(osmium::config::use_pool_threads_for_pbf_parsing()), - m_queue(20, "pbf_parser_results"), // XXX - m_quit_input_thread(false), - m_input_queue(input_queue), + PBFParser(future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types) : + Parser(input_queue, output_queue, header_promise, read_types), m_input_buffer() { + } - // handle OSMHeader - const auto size = check_type_and_get_blob_size("OSMHeader"); - m_header = decode_header(read_from_input_queue(size)); + ~PBFParser() noexcept final = default; - if (m_read_which_entities != osmium::osm_entity_bits::nothing) { - m_reader = std::thread(&PBFInputFormat::parse_osm_data, this, m_read_which_entities); + void run() final { + osmium::thread::set_thread_name("_osmium_pbf_in"); + + parse_header_blob(); + + if (read_types() != osmium::osm_entity_bits::nothing) { + parse_data_blobs(); } } - ~PBFInputFormat() { - signal_input_thread_to_quit(); - if (m_reader.joinable()) { - m_reader.join(); - } - } + }; // class PBFParser - /** - * Returns the next buffer with OSM data read from the PBF - * file. Blocks if data is not available yet. - * Returns an empty buffer at end of input. - */ - osmium::memory::Buffer read() override { - osmium::memory::Buffer buffer; - if (m_eof) { - return buffer; - } + // we want the register_parser() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_pbf_parser = ParserFactory::instance().register_parser( + file_format::pbf, + [](future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new PBFParser(input_queue, output_queue, header_promise, read_which_entities)); + }); - std::future buffer_future; - m_queue.wait_and_pop(buffer_future); - - try { - buffer = std::move(buffer_future.get()); - if (!buffer) { - m_eof = true; - } - return buffer; - } catch (...) { - m_eof = true; - signal_input_thread_to_quit(); - throw; - } - } - - }; // class PBFInputFormat - - namespace { - -// we want the register_input_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_pbf_input = osmium::io::detail::InputFormatFactory::instance().register_input_format(osmium::io::file_format::pbf, - [](const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue& input_queue) { - return new osmium::io::detail::PBFInputFormat(file, read_which_entities, input_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_pbf_parser() noexcept { + return registered_pbf_parser; + } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp index 8d8a079b4..878d7b4ec 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,21 +34,16 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include #include #include #include -#include #include #include -#include #include -#include #include #include -#include - #include #include @@ -71,6 +66,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -81,6 +77,32 @@ namespace osmium { namespace detail { + struct pbf_output_options { + + /// Should nodes be encoded in DenseNodes? + bool use_dense_nodes; + + /** + * Should the PBF blobs contain zlib compressed data? + * + * The zlib compression is optional, it's possible to store the + * blobs in raw format. Disabling the compression can improve + * the writing speed a little but the output will be 2x to 3x + * bigger. + */ + bool use_compression; + + /// Should metadata of objects be written? + bool add_metadata; + + /// Add the "HistoricalInformation" header flag. + bool add_historical_information_flag; + + /// Should the visible flag be added to all OSM objects? + bool add_visible_flag; + + }; + /** * Maximum number of items in a primitive block. * @@ -104,43 +126,81 @@ namespace osmium { return static_cast(std::round(lonlat * lonlat_resolution / location_granularity)); } - /** - * Serialize a protobuf message into a Blob, optionally apply compression - * and return it together with a BlobHeader ready to be written to a file. - * - * @param type Type-string used in the BlobHeader. - * @param msg Protobuf-message. - * @param use_compression Should the output be compressed using zlib? - */ - inline std::string serialize_blob(const std::string& type, const std::string& msg, bool use_compression) { - std::string blob_data; - protozero::pbf_builder pbf_blob(blob_data); + enum class pbf_blob_type { + header = 0, + data = 1 + }; - if (use_compression) { - pbf_blob.add_int32(FileFormat::Blob::optional_int32_raw_size, msg.size()); - pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_zlib_data, osmium::io::detail::zlib_compress(msg)); - } else { - pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_raw, msg); + class SerializeBlob { + + std::string m_msg; + + pbf_blob_type m_blob_type; + + bool m_use_compression; + + public: + + /** + * Initialize a blob serializer. + * + * @param msg Protobuf-message containing the blob data + * @param type Type of blob. + * @param use_compression Should the output be compressed using + * zlib? + */ + SerializeBlob(std::string&& msg, pbf_blob_type type, bool use_compression) : + m_msg(std::move(msg)), + m_blob_type(type), + m_use_compression(use_compression) { } - std::string blob_header_data; - protozero::pbf_builder pbf_blob_header(blob_header_data); + /** + * Serialize a protobuf message into a Blob, optionally apply + * compression and return it together with a BlobHeader ready + * to be written to a file. + */ + std::string operator()() { + assert(m_msg.size() <= max_uncompressed_blob_size); - pbf_blob_header.add_string(FileFormat::BlobHeader::required_string_type, type); - pbf_blob_header.add_int32(FileFormat::BlobHeader::required_int32_datasize, blob_data.size()); + std::string blob_data; + protozero::pbf_builder pbf_blob(blob_data); - uint32_t sz = htonl(static_cast_with_assert(blob_header_data.size())); + if (m_use_compression) { + pbf_blob.add_int32(FileFormat::Blob::optional_int32_raw_size, int32_t(m_msg.size())); + pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_zlib_data, osmium::io::detail::zlib_compress(m_msg)); + } else { + pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_raw, m_msg); + } - // write to output: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob - std::string output; - output.reserve(sizeof(sz) + blob_header_data.size() + blob_data.size()); - output.append(reinterpret_cast(&sz), sizeof(sz)); - output.append(blob_header_data); - output.append(blob_data); + std::string blob_header_data; + protozero::pbf_builder pbf_blob_header(blob_header_data); - return output; - } + pbf_blob_header.add_string(FileFormat::BlobHeader::required_string_type, m_blob_type == pbf_blob_type::data ? "OSMData" : "OSMHeader"); + pbf_blob_header.add_int32(FileFormat::BlobHeader::required_int32_datasize, static_cast_with_assert(blob_data.size())); + uint32_t sz = htonl(static_cast_with_assert(blob_header_data.size())); + + // write to output: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob + std::string output; + output.reserve(sizeof(sz) + blob_header_data.size() + blob_data.size()); + output.append(reinterpret_cast(&sz), sizeof(sz)); + output.append(blob_header_data); + output.append(blob_data); + + return output; + } + + }; // class SerializeBlob + + /** + * Contains the code to pack any number of nodes into a DenseNode + * structure. + * + * Because this needs to allocate a lot of memory on the heap, + * only one object of this class will be created and then re-used + * after calling clear() on it. + */ class DenseNodes { StringTable& m_stringtable; @@ -158,27 +218,26 @@ namespace osmium { std::vector m_lons; std::vector m_tags; - osmium::util::DeltaEncode m_delta_id; + osmium::util::DeltaEncode m_delta_id; - osmium::util::DeltaEncode m_delta_timestamp; - osmium::util::DeltaEncode m_delta_changeset; - osmium::util::DeltaEncode m_delta_uid; - osmium::util::DeltaEncode m_delta_user_sid; + osmium::util::DeltaEncode m_delta_timestamp; + osmium::util::DeltaEncode m_delta_changeset; + osmium::util::DeltaEncode m_delta_uid; + osmium::util::DeltaEncode m_delta_user_sid; - osmium::util::DeltaEncode m_delta_lat; - osmium::util::DeltaEncode m_delta_lon; + osmium::util::DeltaEncode m_delta_lat; + osmium::util::DeltaEncode m_delta_lon; - bool m_add_metadata; - bool m_add_visible; + const pbf_output_options& m_options; public: - DenseNodes(StringTable& stringtable, bool add_metadata, bool add_visible) : + DenseNodes(StringTable& stringtable, const pbf_output_options& options) : m_stringtable(stringtable), - m_add_metadata(add_metadata), - m_add_visible(add_visible) { + m_options(options) { } + /// Clear object for re-use. Keep the allocated memory. void clear() { m_ids.clear(); @@ -211,13 +270,13 @@ namespace osmium { void add_node(const osmium::Node& node) { m_ids.push_back(m_delta_id.update(node.id())); - if (m_add_metadata) { - m_versions.push_back(node.version()); - m_timestamps.push_back(m_delta_timestamp.update(node.timestamp())); + if (m_options.add_metadata) { + m_versions.push_back(static_cast_with_assert(node.version())); + m_timestamps.push_back(m_delta_timestamp.update(uint32_t(node.timestamp()))); m_changesets.push_back(m_delta_changeset.update(node.changeset())); m_uids.push_back(m_delta_uid.update(node.uid())); m_user_sids.push_back(m_delta_user_sid.update(m_stringtable.add(node.user()))); - if (m_add_visible) { + if (m_options.add_visible_flag) { m_visibles.push_back(node.visible()); } } @@ -226,8 +285,8 @@ namespace osmium { m_lons.push_back(m_delta_lon.update(lonlat2int(node.location().lon_without_check()))); for (const auto& tag : node.tags()) { - m_tags.push_back(m_stringtable.add(tag.key())); - m_tags.push_back(m_stringtable.add(tag.value())); + m_tags.push_back(static_cast_with_assert(m_stringtable.add(tag.key()))); + m_tags.push_back(static_cast_with_assert(m_stringtable.add(tag.value()))); } m_tags.push_back(0); } @@ -238,7 +297,7 @@ namespace osmium { pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_id, m_ids.cbegin(), m_ids.cend()); - if (m_add_metadata) { + if (m_options.add_metadata) { protozero::pbf_builder pbf_dense_info(pbf_dense_nodes, OSMFormat::DenseNodes::optional_DenseInfo_denseinfo); pbf_dense_info.add_packed_int32(OSMFormat::DenseInfo::packed_int32_version, m_versions.cbegin(), m_versions.cend()); pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_timestamp, m_timestamps.cbegin(), m_timestamps.cend()); @@ -246,7 +305,7 @@ namespace osmium { pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_uid, m_uids.cbegin(), m_uids.cend()); pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_user_sid, m_user_sids.cbegin(), m_user_sids.cend()); - if (m_add_visible) { + if (m_options.add_visible_flag) { pbf_dense_info.add_packed_bool(OSMFormat::DenseInfo::packed_bool_visible, m_visibles.cbegin(), m_visibles.cend()); } } @@ -272,11 +331,11 @@ namespace osmium { public: - PrimitiveBlock(bool add_metadata, bool add_visible) : + explicit PrimitiveBlock(const pbf_output_options& options) : m_pbf_primitive_group_data(), m_pbf_primitive_group(m_pbf_primitive_group_data), m_stringtable(), - m_dense_nodes(m_stringtable, add_metadata, add_visible), + m_dense_nodes(m_stringtable, options), m_type(OSMFormat::PrimitiveGroup::unknown), m_count(0) { } @@ -312,7 +371,7 @@ namespace osmium { ++m_count; } - size_t add_string(const char* s) { + uint32_t store_in_stringtable(const char* s) { return m_stringtable.add(s); } @@ -350,24 +409,7 @@ namespace osmium { class PBFOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler { - /// Should nodes be encoded in DenseNodes? - bool m_use_dense_nodes; - - /** - * Should the PBF blobs contain zlib compressed data? - * - * The zlib compression is optional, it's possible to store the - * blobs in raw format. Disabling the compression can improve - * the writing speed a little but the output will be 2x to 3x - * bigger. - */ - bool m_use_compression; - - /// Should metadata of objects be written? - bool m_add_metadata; - - /// Should the visible flag be added to objects? - bool m_add_visible; + pbf_output_options m_options; PrimitiveBlock m_primitive_block; @@ -386,63 +428,69 @@ namespace osmium { primitive_block.add_message(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup, m_primitive_block.group_data()); - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(serialize_blob("OSMData", primitive_block_data, m_use_compression)); + m_output_queue.push(osmium::thread::Pool::instance().submit( + SerializeBlob{std::move(primitive_block_data), + pbf_blob_type::data, + m_options.use_compression} + )); } template void add_meta(const osmium::OSMObject& object, T& pbf_object) { - const osmium::TagList& tags = object.tags(); + { + protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_keys)}; + for (const auto& tag : object.tags()) { + field.add_element(m_primitive_block.store_in_stringtable(tag.key())); + } + } - auto map_tag_key = [this](const osmium::Tag& tag) -> size_t { - return m_primitive_block.add_string(tag.key()); - }; - auto map_tag_value = [this](const osmium::Tag& tag) -> size_t { - return m_primitive_block.add_string(tag.value()); - }; + { + protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_vals)}; + for (const auto& tag : object.tags()) { + field.add_element(m_primitive_block.store_in_stringtable(tag.value())); + } + } - pbf_object.add_packed_uint32(T::enum_type::packed_uint32_keys, - boost::make_transform_iterator(tags.begin(), map_tag_key), - boost::make_transform_iterator(tags.end(), map_tag_key)); - - pbf_object.add_packed_uint32(T::enum_type::packed_uint32_vals, - boost::make_transform_iterator(tags.begin(), map_tag_value), - boost::make_transform_iterator(tags.end(), map_tag_value)); - - if (m_add_metadata) { + if (m_options.add_metadata) { protozero::pbf_builder pbf_info(pbf_object, T::enum_type::optional_Info_info); - pbf_info.add_int32(OSMFormat::Info::optional_int32_version, object.version()); - pbf_info.add_int64(OSMFormat::Info::optional_int64_timestamp, object.timestamp()); + pbf_info.add_int32(OSMFormat::Info::optional_int32_version, static_cast_with_assert(object.version())); + pbf_info.add_int64(OSMFormat::Info::optional_int64_timestamp, uint32_t(object.timestamp())); pbf_info.add_int64(OSMFormat::Info::optional_int64_changeset, object.changeset()); - pbf_info.add_int32(OSMFormat::Info::optional_int32_uid, object.uid()); - pbf_info.add_uint32(OSMFormat::Info::optional_uint32_user_sid, m_primitive_block.add_string(object.user())); - if (m_add_visible) { + pbf_info.add_int32(OSMFormat::Info::optional_int32_uid, static_cast_with_assert(object.uid())); + pbf_info.add_uint32(OSMFormat::Info::optional_uint32_user_sid, m_primitive_block.store_in_stringtable(object.user())); + if (m_options.add_visible_flag) { pbf_info.add_bool(OSMFormat::Info::optional_bool_visible, object.visible()); } } } - PBFOutputFormat(const PBFOutputFormat&) = delete; - PBFOutputFormat& operator=(const PBFOutputFormat&) = delete; + void switch_primitive_block_type(OSMFormat::PrimitiveGroup type) { + if (!m_primitive_block.can_add(type)) { + store_primitive_block(); + m_primitive_block.reset(type); + } + } public: - explicit PBFOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) : - OutputFormat(file, output_queue), - m_use_dense_nodes(file.get("pbf_dense_nodes") != "false"), - m_use_compression(file.get("pbf_compression") != "none" && file.get("pbf_compression") != "false"), - m_add_metadata(file.get("pbf_add_metadata") != "false" && file.get("add_metadata") != "false"), - m_add_visible(file.has_multiple_object_versions()), - m_primitive_block(m_add_metadata, m_add_visible) { + PBFOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(output_queue), + m_options(), + m_primitive_block(m_options) { + m_options.use_dense_nodes = file.is_not_false("pbf_dense_nodes"); + m_options.use_compression = file.get("pbf_compression") != "none" && file.is_not_false("pbf_compression"); + m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata"); + m_options.add_historical_information_flag = file.has_multiple_object_versions(); + m_options.add_visible_flag = file.has_multiple_object_versions(); } - void write_buffer(osmium::memory::Buffer&& buffer) override final { - osmium::apply(buffer.cbegin(), buffer.cend(), *this); - } + PBFOutputFormat(const PBFOutputFormat&) = delete; + PBFOutputFormat& operator=(const PBFOutputFormat&) = delete; - void write_header(const osmium::io::Header& header) override final { + ~PBFOutputFormat() noexcept final = default; + + void write_header(const osmium::io::Header& header) final { std::string data; protozero::pbf_builder pbf_header_block(data); @@ -450,19 +498,19 @@ namespace osmium { protozero::pbf_builder pbf_header_bbox(pbf_header_block, OSMFormat::HeaderBlock::optional_HeaderBBox_bbox); osmium::Box box = header.joined_boxes(); - pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_left, box.bottom_left().lon() * lonlat_resolution); - pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_right, box.top_right().lon() * lonlat_resolution); - pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_top, box.top_right().lat() * lonlat_resolution); - pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_bottom, box.bottom_left().lat() * lonlat_resolution); + pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_left, int64_t(box.bottom_left().lon() * lonlat_resolution)); + pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_right, int64_t(box.top_right().lon() * lonlat_resolution)); + pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_top, int64_t(box.top_right().lat() * lonlat_resolution)); + pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_bottom, int64_t(box.bottom_left().lat() * lonlat_resolution)); } pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "OsmSchema-V0.6"); - if (m_use_dense_nodes) { + if (m_options.use_dense_nodes) { pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "DenseNodes"); } - if (m_file.has_multiple_object_versions()) { + if (m_options.add_historical_information_flag) { pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation"); } @@ -471,7 +519,7 @@ namespace osmium { std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp"); if (!osmosis_replication_timestamp.empty()) { osmium::Timestamp ts(osmosis_replication_timestamp.c_str()); - pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, ts); + pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts)); } std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number"); @@ -484,20 +532,23 @@ namespace osmium { pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url, osmosis_replication_base_url); } - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(serialize_blob("OSMHeader", data, m_use_compression)); + m_output_queue.push(osmium::thread::Pool::instance().submit( + SerializeBlob{std::move(data), + pbf_blob_type::header, + m_options.use_compression} + )); } - void switch_primitive_block_type(OSMFormat::PrimitiveGroup type) { - if (!m_primitive_block.can_add(type)) { - store_primitive_block(); - m_primitive_block.reset(type); - } + void write_buffer(osmium::memory::Buffer&& buffer) final { + osmium::apply(buffer.cbegin(), buffer.cend(), *this); + } + + void write_end() final { + store_primitive_block(); } void node(const osmium::Node& node) { - if (m_use_dense_nodes) { + if (m_options.use_dense_nodes) { switch_primitive_block_type(OSMFormat::PrimitiveGroup::optional_DenseNodes_dense); m_primitive_block.add_dense_node(node); return; @@ -538,12 +589,12 @@ namespace osmium { pbf_relation.add_int64(OSMFormat::Relation::required_int64_id, relation.id()); add_meta(relation, pbf_relation); - auto map_member_role = [this](const osmium::RelationMember& member) -> size_t { - return m_primitive_block.add_string(member.role()); - }; - pbf_relation.add_packed_int32(OSMFormat::Relation::packed_int32_roles_sid, - boost::make_transform_iterator(relation.members().begin(), map_member_role), - boost::make_transform_iterator(relation.members().end(), map_member_role)); + { + protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_int32_roles_sid)}; + for (const auto& member : relation.members()) { + field.add_element(m_primitive_block.store_in_stringtable(member.role())); + } + } static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type { return member->ref(); @@ -554,41 +605,27 @@ namespace osmium { it_type last { members.cend(), members.cend(), map_member_ref }; pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last); - static auto map_member_type = [](const osmium::RelationMember& member) noexcept -> int { - return osmium::item_type_to_nwr_index(member.type()); - }; - pbf_relation.add_packed_int32(OSMFormat::Relation::packed_MemberType_types, - boost::make_transform_iterator(relation.members().begin(), map_member_type), - boost::make_transform_iterator(relation.members().end(), map_member_type)); - } - - /** - * Finalize the writing process, flush any open primitive - * blocks to the file and close the file. - */ - void close() override final { - store_primitive_block(); - - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(std::string()); + { + protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)}; + for (const auto& member : relation.members()) { + field.add_element(int32_t(osmium::item_type_to_nwr_index(member.type()))); + } + } } }; // class PBFOutputFormat - namespace { + // we want the register_output_format() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_pbf_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::pbf, + [](const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::PBFOutputFormat(file, output_queue); + }); -// we want the register_output_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_pbf_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::pbf, - [](const osmium::io::File& file, data_queue_type& output_queue) { - return new osmium::io::detail::PBFOutputFormat(file, output_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_pbf_output() noexcept { + return registered_pbf_output; + } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp b/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp index 3f2308766..bdaabbaec 100644 --- a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp +++ b/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,7 +33,7 @@ DEALINGS IN THE SOFTWARE. */ -#include +#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/io/detail/queue_util.hpp b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp new file mode 100644 index 000000000..af8454411 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp @@ -0,0 +1,157 @@ +#ifndef OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP +#define OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include +#include + +#include +#include + +namespace osmium { + + namespace io { + + namespace detail { + + /** + * This type of queue contains buffers with OSM data in them. + * The "end of file" is marked by an invalid Buffer. + * The buffers are wrapped in a std::future so that they can also + * transport exceptions. The future also helps with keeping the + * data in order. + */ + using future_buffer_queue_type = osmium::thread::Queue>; + + /** + * This type of queue contains OSM file data in the form it is + * stored on disk, ie encoded as XML, PBF, etc. + * The "end of file" is marked by an empty string. + */ + using string_queue_type = osmium::thread::Queue; + + /** + * This type of queue contains OSM file data in the form it is + * stored on disk, ie encoded as XML, PBF, etc. + * The "end of file" is marked by an empty string. + * The strings are wrapped in a std::future so that they can also + * transport exceptions. The future also helps with keeping the + * data in order. + */ + using future_string_queue_type = osmium::thread::Queue>; + + template + inline void add_to_queue(osmium::thread::Queue>& queue, T&& data) { + std::promise promise; + queue.push(promise.get_future()); + promise.set_value(std::forward(data)); + } + + template + inline void add_to_queue(osmium::thread::Queue>& queue, std::exception_ptr&& exception) { + std::promise promise; + queue.push(promise.get_future()); + promise.set_exception(std::move(exception)); + } + + template + inline void add_end_of_data_to_queue(osmium::thread::Queue>& queue) { + add_to_queue(queue, T{}); + } + + inline bool at_end_of_data(const std::string& data) { + return data.empty(); + } + + inline bool at_end_of_data(osmium::memory::Buffer& buffer) { + return !buffer; + } + + template + class queue_wrapper { + + using queue_type = osmium::thread::Queue>; + + queue_type& m_queue; + bool m_has_reached_end_of_data; + + public: + + explicit queue_wrapper(queue_type& queue) : + m_queue(queue), + m_has_reached_end_of_data(false) { + } + + ~queue_wrapper() noexcept { + drain(); + } + + void drain() { + while (!m_has_reached_end_of_data) { + try { + pop(); + } catch (...) { + // Ignore any exceptions. + } + } + } + + bool has_reached_end_of_data() const noexcept { + return m_has_reached_end_of_data; + } + + T pop() { + T data; + if (!m_has_reached_end_of_data) { + std::future data_future; + m_queue.wait_and_pop(data_future); + data = std::move(data_future.get()); + if (at_end_of_data(data)) { + m_has_reached_end_of_data = true; + } + } + return data; + } + + }; // class queue_wrapper + + } // namespace detail + + } // namespace io + +} // namespace osmium + +#endif // OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/read_thread.hpp b/third_party/libosmium/include/osmium/io/detail/read_thread.hpp index bce4f5507..a933787e3 100644 --- a/third_party/libosmium/include/osmium/io/detail/read_thread.hpp +++ b/third_party/libosmium/include/osmium/io/detail/read_thread.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,14 +34,13 @@ DEALINGS IN THE SOFTWARE. */ #include -#include -#include +#include #include #include #include #include -#include +#include #include namespace osmium { @@ -50,52 +49,80 @@ namespace osmium { namespace detail { - class ReadThread { + /** + * This code uses an internally managed thread to read data from + * the input file and (optionally) decompress it. The result is + * sent to the given queue. Any exceptions will also be send to + * the queue. + */ + class ReadThreadManager { - osmium::thread::Queue& m_queue; - osmium::io::Decompressor* m_decompressor; + // only used in the sub-thread + osmium::io::Decompressor& m_decompressor; + future_string_queue_type& m_queue; - // If this is set in the main thread, we have to wrap up at the - // next possible moment. - std::atomic& m_done; + // used in both threads + std::atomic m_done; - public: + // only used in the main thread + std::thread m_thread; - explicit ReadThread(osmium::thread::Queue& queue, osmium::io::Decompressor* decompressor, std::atomic& done) : - m_queue(queue), - m_decompressor(decompressor), - m_done(done) { - } - - bool operator()() { - osmium::thread::set_thread_name("_osmium_input"); + void run_in_thread() { + osmium::thread::set_thread_name("_osmium_read"); try { while (!m_done) { - std::string data {m_decompressor->read()}; - if (data.empty()) { - m_queue.push(std::move(data)); + std::string data {m_decompressor.read()}; + if (at_end_of_data(data)) { break; } - m_queue.push(std::move(data)); - while (m_queue.size() > 10 && !m_done) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } + add_to_queue(m_queue, std::move(data)); } - m_decompressor->close(); + m_decompressor.close(); } catch (...) { - // If there is an exception in this thread, we make sure - // to push an empty string onto the queue to signal the - // end-of-data to the reading thread so that it will not - // hang. Then we re-throw the exception. - m_queue.push(std::string()); - throw; + add_to_queue(m_queue, std::current_exception()); } - return true; + + add_end_of_data_to_queue(m_queue); } - }; // class ReadThread + public: + + ReadThreadManager(osmium::io::Decompressor& decompressor, + future_string_queue_type& queue) : + m_decompressor(decompressor), + m_queue(queue), + m_done(false), + m_thread(std::thread(&ReadThreadManager::run_in_thread, this)) { + } + + ReadThreadManager(const ReadThreadManager&) = delete; + ReadThreadManager& operator=(const ReadThreadManager&) = delete; + + ReadThreadManager(ReadThreadManager&&) = delete; + ReadThreadManager& operator=(ReadThreadManager&&) = delete; + + ~ReadThreadManager() noexcept { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } + } + + void stop() noexcept { + m_done = true; + } + + void close() { + stop(); + if (m_thread.joinable()) { + m_thread.join(); + } + } + + }; // class ReadThreadManager } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/read_write.hpp b/third_party/libosmium/include/osmium/io/detail/read_write.hpp index 9863bd719..769e2b39a 100644 --- a/third_party/libosmium/include/osmium/io/detail/read_write.hpp +++ b/third_party/libosmium/include/osmium/io/detail/read_write.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -45,7 +46,7 @@ DEALINGS IN THE SOFTWARE. # include #endif -#include +#include namespace osmium { @@ -68,23 +69,26 @@ namespace osmium { */ inline int open_for_writing(const std::string& filename, osmium::io::overwrite allow_overwrite = osmium::io::overwrite::no) { if (filename == "" || filename == "-") { - return 1; // stdout - } else { - int flags = O_WRONLY | O_CREAT; - if (allow_overwrite == osmium::io::overwrite::allow) { - flags |= O_TRUNC; - } else { - flags |= O_EXCL; - } #ifdef _WIN32 - flags |= O_BINARY; + _setmode(1, _O_BINARY); #endif - int fd = ::open(filename.c_str(), flags, 0666); - if (fd < 0) { - throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); - } - return fd; + return 1; // stdout } + + int flags = O_WRONLY | O_CREAT; + if (allow_overwrite == osmium::io::overwrite::allow) { + flags |= O_TRUNC; + } else { + flags |= O_EXCL; + } +#ifdef _WIN32 + flags |= O_BINARY; +#endif + int fd = ::open(filename.c_str(), flags, 0666); + if (fd < 0) { + throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); + } + return fd; } /** @@ -98,17 +102,17 @@ namespace osmium { inline int open_for_reading(const std::string& filename) { if (filename == "" || filename == "-") { return 0; // stdin - } else { - int flags = O_RDONLY; -#ifdef _WIN32 - flags |= O_BINARY; -#endif - int fd = ::open(filename.c_str(), flags); - if (fd < 0) { - throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); - } - return fd; } + + int flags = O_RDONLY; +#ifdef _WIN32 + flags |= O_BINARY; +#endif + int fd = ::open(filename.c_str(), flags); + if (fd < 0) { + throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); + } + return fd; } /** @@ -151,6 +155,22 @@ namespace osmium { reliable_write(fd, reinterpret_cast(output_buffer), size); } + inline void reliable_fsync(const int fd) { +#ifdef _WIN32 + if (_commit(fd) != 0) { +#else + if (::fsync(fd) != 0) { +#endif + throw std::system_error(errno, std::system_category(), "Fsync failed"); + } + } + + inline void reliable_close(const int fd) { + if (::close(fd) != 0) { + throw std::system_error(errno, std::system_category(), "Close failed"); + } + } + } // namespace detail } // namespace io diff --git a/third_party/libosmium/include/osmium/io/detail/string_table.hpp b/third_party/libosmium/include/osmium/io/detail/string_table.hpp index ae9d5f0ce..55c622681 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_table.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_table.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include @@ -41,6 +42,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include + namespace osmium { namespace io { @@ -72,7 +75,7 @@ namespace osmium { public: - StringStore(size_t chunk_size) : + explicit StringStore(size_t chunk_size) : m_chunk_size(chunk_size), m_chunks() { add_chunk(); @@ -172,15 +175,15 @@ namespace osmium { // These functions get you some idea how much memory was // used. - int get_chunk_size() const noexcept { + size_t get_chunk_size() const noexcept { return m_chunk_size; } - int get_chunk_count() const noexcept { + size_t get_chunk_count() const noexcept { return m_chunks.size(); } - int get_used_bytes_in_last_chunk() const noexcept { + size_t get_used_bytes_in_last_chunk() const noexcept { return m_chunks.front().size(); } @@ -196,9 +199,16 @@ namespace osmium { class StringTable { + // This is the maximum number of entries in a string table. + // This should never be reached in practice but we better + // make sure it doesn't. If we had max_uncompressed_blob_size + // many entries, we are sure they would never fit into a PBF + // Blob. + static constexpr const uint32_t max_entries = max_uncompressed_blob_size; + StringStore m_strings; std::map m_index; - size_t m_size; + uint32_t m_size; public: @@ -216,18 +226,23 @@ namespace osmium { m_strings.add(""); } - size_t size() const noexcept { + uint32_t size() const noexcept { return m_size + 1; } - size_t add(const char* s) { + uint32_t add(const char* s) { auto f = m_index.find(s); if (f != m_index.end()) { - return f->second; + return uint32_t(f->second); } const char* cs = m_strings.add(s); m_index[cs] = ++m_size; + + if (m_size > max_entries) { + throw osmium::pbf_error("string table has too many entries"); + } + return m_size; } diff --git a/third_party/libosmium/include/osmium/io/detail/string_util.hpp b/third_party/libosmium/include/osmium/io/detail/string_util.hpp new file mode 100644 index 000000000..f80088e63 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/string_util.hpp @@ -0,0 +1,209 @@ +#ifndef OSMIUM_IO_DETAIL_STRING_UTIL_HPP +#define OSMIUM_IO_DETAIL_STRING_UTIL_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include +#include +#include + +#include + +namespace osmium { + + namespace io { + + namespace detail { + +#ifndef _MSC_VER +# define SNPRINTF std::snprintf +#else +# define SNPRINTF _snprintf +#endif + + template + inline int string_snprintf(std::string& out, + size_t old_size, + size_t max_size, + const char* format, + TArgs&&... args) { + out.resize(old_size + max_size); + + return SNPRINTF(max_size ? const_cast(out.c_str()) + old_size : nullptr, + max_size, + format, + std::forward(args)...); + } + +#undef SNPRINTF + + /** + * This is a helper function for writing printf-like formatted + * data into a std::string. + * + * @param out The data will be appended to this string. + * @param format A string with formatting instructions a la printf. + * @param args Any further arguments like in printf. + * @throws std::bad_alloc If the string needed to grow and there + * wasn't enough memory. + */ + template + inline void append_printf_formatted_string(std::string& out, + const char* format, + TArgs&&... args) { + + // First try to write string with the max_size, if that doesn't + // work snprintf will tell us how much space it needs. We + // reserve that much space and try again. So this will always + // work, even if the output is larger than the given max_size. + // + // Unfortunately this trick doesn't work on Windows, because + // the _snprintf() function there only returns the length it + // needs if max_size==0 and the buffer pointer is the null + // pointer. So we have to take this into account. + +#ifndef _MSC_VER + static const size_t max_size = 100; +#else + static const size_t max_size = 0; +#endif + + size_t old_size = out.size(); + + int len = string_snprintf(out, + old_size, + max_size, + format, + std::forward(args)...); + assert(len > 0); + + if (size_t(len) >= max_size) { +#ifndef NDEBUG + int len2 = +#endif + string_snprintf(out, + old_size, + size_t(len) + 1, + format, + std::forward(args)...); + assert(len2 == len); + } + + out.resize(old_size + size_t(len)); + } + + inline void append_utf8_encoded_string(std::string& out, const char* data) { + const char* end = data + std::strlen(data); + + while (data != end) { + const char* last = data; + uint32_t c = utf8::next(data, end); + + // This is a list of Unicode code points that we let + // through instead of escaping them. It is incomplete + // and can be extended later. + // Generally we don't want to let through any character + // that has special meaning in the OPL format such as + // space, comma, @, etc. and any non-printing characters. + if ((0x0021 <= c && c <= 0x0024) || + (0x0026 <= c && c <= 0x002b) || + (0x002d <= c && c <= 0x003c) || + (0x003e <= c && c <= 0x003f) || + (0x0041 <= c && c <= 0x007e) || + (0x00a1 <= c && c <= 0x00ac) || + (0x00ae <= c && c <= 0x05ff)) { + out.append(last, data); + } else { + out += '%'; + if (c <= 0xff) { + append_printf_formatted_string(out, "%02x", c); + } else { + append_printf_formatted_string(out, "%04x", c); + } + out += '%'; + } + } + } + + inline void append_xml_encoded_string(std::string& out, const char* data) { + for (; *data != '\0'; ++data) { + switch(*data) { + case '&': out += "&"; break; + case '\"': out += """; break; + case '\'': out += "'"; break; + case '<': out += "<"; break; + case '>': out += ">"; break; + case '\n': out += " "; break; + case '\r': out += " "; break; + case '\t': out += " "; break; + default: out += *data; break; + } + } + } + + inline void append_debug_encoded_string(std::string& out, const char* data, const char* prefix, const char* suffix) { + const char* end = data + std::strlen(data); + + while (data != end) { + const char* last = data; + uint32_t c = utf8::next(data, end); + + // This is a list of Unicode code points that we let + // through instead of escaping them. It is incomplete + // and can be extended later. + // Generally we don't want to let through any + // non-printing characters. + if ((0x0020 <= c && c <= 0x0021) || + (0x0023 <= c && c <= 0x003b) || + (0x003d == c) || + (0x003f <= c && c <= 0x007e) || + (0x00a1 <= c && c <= 0x00ac) || + (0x00ae <= c && c <= 0x05ff)) { + out.append(last, data); + } else { + out.append(prefix); + append_printf_formatted_string(out, "", c); + out.append(suffix); + } + } + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + +#endif // OSMIUM_IO_DETAIL_STRING_UTIL_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp index fad22ed69..796048638 100644 --- a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp +++ b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,11 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include #include -#include +#include #include namespace osmium { @@ -46,33 +48,52 @@ namespace osmium { namespace detail { + /** + * This codes runs in its own thread, getting data from the given + * queue, (optionally) compressing it, and writing it to the output + * file. + */ class WriteThread { - typedef osmium::io::detail::data_queue_type data_queue_type; - - data_queue_type& m_input_queue; - osmium::io::Compressor* m_compressor; + queue_wrapper m_queue; + std::unique_ptr m_compressor; + std::promise m_promise; public: - explicit WriteThread(data_queue_type& input_queue, osmium::io::Compressor* compressor) : - m_input_queue(input_queue), - m_compressor(compressor) { + WriteThread(future_string_queue_type& input_queue, + std::unique_ptr&& compressor, + std::promise&& promise) : + m_queue(input_queue), + m_compressor(std::move(compressor)), + m_promise(std::move(promise)) { } - bool operator()() { - osmium::thread::set_thread_name("_osmium_output"); + WriteThread(const WriteThread&) = delete; + WriteThread& operator=(const WriteThread&) = delete; - std::future data_future; - std::string data; - do { - m_input_queue.wait_and_pop(data_future); - data = data_future.get(); - m_compressor->write(data); - } while (!data.empty()); + WriteThread(WriteThread&&) = delete; + WriteThread& operator=(WriteThread&&) = delete; - m_compressor->close(); - return true; + ~WriteThread() noexcept = default; + + void operator()() { + osmium::thread::set_thread_name("_osmium_write"); + + try { + while (true) { + std::string data = m_queue.pop(); + if (at_end_of_data(data)) { + break; + } + m_compressor->write(data); + } + m_compressor->close(); + m_promise.set_value(true); + } catch (...) { + m_promise.set_exception(std::current_exception()); + m_queue.drain(); + } } }; // class WriteThread diff --git a/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp index b0f3da339..8cb5efe8a 100644 --- a/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,21 +33,14 @@ DEALINGS IN THE SOFTWARE. */ -#include #include -#include #include #include #include #include #include -#include #include -#include -#include -#include #include -#include #include #include @@ -55,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -130,23 +124,11 @@ namespace osmium { namespace io { - class File; - namespace detail { - /** - * Once the header is fully parsed this exception will be thrown if - * the caller is not interested in anything else except the header. - * It will break off the parsing at this point. - * - * This exception is never seen by user code, it is caught internally. - */ - class ParserIsDone : std::exception { - }; + class XMLParser : public Parser { - class XMLParser { - - static constexpr int buffer_size = 10 * 1000 * 1000; + static constexpr int buffer_size = 2 * 1000 * 1000; enum class context { root, @@ -155,6 +137,9 @@ namespace osmium { way, relation, changeset, + discussion, + comment, + comment_text, ignored_node, ignored_way, ignored_relation, @@ -175,29 +160,22 @@ namespace osmium { osmium::memory::Buffer m_buffer; - std::unique_ptr m_node_builder; - std::unique_ptr m_way_builder; - std::unique_ptr m_relation_builder; - std::unique_ptr m_changeset_builder; + std::unique_ptr m_node_builder; + std::unique_ptr m_way_builder; + std::unique_ptr m_relation_builder; + std::unique_ptr m_changeset_builder; + std::unique_ptr m_changeset_discussion_builder; - std::unique_ptr m_tl_builder; - std::unique_ptr m_wnl_builder; - std::unique_ptr m_rml_builder; + std::unique_ptr m_tl_builder; + std::unique_ptr m_wnl_builder; + std::unique_ptr m_rml_builder; - osmium::thread::Queue& m_input_queue; - osmium::thread::Queue& m_queue; - std::promise& m_header_promise; - - osmium::osm_entity_bits::type m_read_types; - - std::atomic& m_done; - - bool m_header_is_done; + std::string m_comment_text; /** * A C++ wrapper for the Expat parser that makes sure no memory is leaked. */ - template + template class ExpatXMLParser { XML_Parser m_parser; @@ -210,15 +188,20 @@ namespace osmium { static_cast(data)->end_element(element); } + static void XMLCALL character_data_wrapper(void* data, const XML_Char* text, int len) { + static_cast(data)->characters(text, len); + } + public: - ExpatXMLParser(T* callback_object) : + explicit ExpatXMLParser(T* callback_object) : m_parser(XML_ParserCreate(nullptr)) { if (!m_parser) { throw osmium::io_error("Internal error: Can not create parser"); } XML_SetUserData(m_parser, callback_object); XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper); + XML_SetCharacterDataHandler(m_parser, character_data_wrapper); } ExpatXMLParser(const ExpatXMLParser&) = delete; @@ -227,7 +210,7 @@ namespace osmium { ExpatXMLParser& operator=(const ExpatXMLParser&) = delete; ExpatXMLParser& operator=(ExpatXMLParser&&) = delete; - ~ExpatXMLParser() { + ~ExpatXMLParser() noexcept { XML_ParserFree(m_parser); } @@ -239,126 +222,14 @@ namespace osmium { }; // class ExpatXMLParser - /** - * A helper class that makes sure a promise is kept. It stores - * a reference to some piece of data and to a promise and, on - * destruction, sets the value of the promise from the data. - */ - template - class PromiseKeeper { - - T& m_data; - std::promise& m_promise; - bool m_done; - - public: - - PromiseKeeper(T& data, std::promise& promise) : - m_data(data), - m_promise(promise), - m_done(false) { + template + static void check_attributes(const XML_Char** attrs, T check) { + while (*attrs) { + check(attrs[0], attrs[1]); + attrs += 2; } - - void fullfill_promise() { - if (!m_done) { - m_promise.set_value(m_data); - m_done = true; - } - } - - ~PromiseKeeper() { - fullfill_promise(); - } - - }; // class PromiseKeeper - - public: - - explicit XMLParser(osmium::thread::Queue& input_queue, osmium::thread::Queue& queue, std::promise& header_promise, osmium::osm_entity_bits::type read_types, std::atomic& done) : - m_context(context::root), - m_last_context(context::root), - m_in_delete_section(false), - m_header(), - m_buffer(buffer_size), - m_node_builder(), - m_way_builder(), - m_relation_builder(), - m_changeset_builder(), - m_tl_builder(), - m_wnl_builder(), - m_rml_builder(), - m_input_queue(input_queue), - m_queue(queue), - m_header_promise(header_promise), - m_read_types(read_types), - m_done(done), - m_header_is_done(false) { } - /** - * The copy constructor is needed for storing XMLParser in a std::function. - * The copy will look the same as if it has been initialized with the - * same parameters as the original. Any state changes in the original will - * not be reflected in the copy. - */ - XMLParser(const XMLParser& other) : - m_context(context::root), - m_last_context(context::root), - m_in_delete_section(false), - m_header(), - m_buffer(buffer_size), - m_node_builder(), - m_way_builder(), - m_relation_builder(), - m_changeset_builder(), - m_tl_builder(), - m_wnl_builder(), - m_rml_builder(), - m_input_queue(other.m_input_queue), - m_queue(other.m_queue), - m_header_promise(other.m_header_promise), - m_read_types(other.m_read_types), - m_done(other.m_done), - m_header_is_done(other.m_header_is_done) { - } - - XMLParser(XMLParser&&) = default; - - XMLParser& operator=(const XMLParser&) = delete; - - XMLParser& operator=(XMLParser&&) = default; - - ~XMLParser() = default; - - bool operator()() { - ExpatXMLParser parser(this); - PromiseKeeper promise_keeper(m_header, m_header_promise); - bool last; - do { - std::string data; - m_input_queue.wait_and_pop(data); - last = data.empty(); - try { - parser(data, last); - if (m_header_is_done) { - promise_keeper.fullfill_promise(); - } - } catch (ParserIsDone&) { - return true; - } catch (...) { - m_queue.push(osmium::memory::Buffer()); // empty buffer to signify eof - throw; - } - } while (!last && !m_done); - if (m_buffer.committed() > 0) { - m_queue.push(std::move(m_buffer)); - } - m_queue.push(osmium::memory::Buffer()); // empty buffer to signify eof - return true; - } - - private: - const char* init_object(osmium::OSMObject& object, const XML_Char** attrs) { const char* user = ""; @@ -367,17 +238,18 @@ namespace osmium { } osmium::Location location; - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "lon")) { - location.set_lon(std::atof(attrs[count+1])); // XXX doesn't detect garbage after the number - } else if (!strcmp(attrs[count], "lat")) { - location.set_lat(std::atof(attrs[count+1])); // XXX doesn't detect garbage after the number - } else if (!strcmp(attrs[count], "user")) { - user = attrs[count+1]; + + check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "lon")) { + location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number + } else if (!strcmp(name, "lat")) { + location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number + } else if (!strcmp(name, "user")) { + user = value; } else { - object.set_attribute(attrs[count], attrs[count+1]); + object.set_attribute(name, value); } - } + }); if (location && object.type() == osmium::item_type::node) { static_cast(object).set_location(location); @@ -392,21 +264,21 @@ namespace osmium { osmium::Location min; osmium::Location max; - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "min_lon")) { - min.set_lon(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "min_lat")) { - min.set_lat(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "max_lon")) { - max.set_lon(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "max_lat")) { - max.set_lat(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "user")) { - user = attrs[count+1]; + check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "min_lon")) { + min.set_lon(atof(value)); + } else if (!strcmp(name, "min_lat")) { + min.set_lat(atof(value)); + } else if (!strcmp(name, "max_lon")) { + max.set_lon(atof(value)); + } else if (!strcmp(name, "max_lat")) { + max.set_lat(atof(value)); + } else if (!strcmp(name, "user")) { + user = value; } else { - new_changeset.set_attribute(attrs[count], attrs[count+1]); + new_changeset.set_attribute(name, value); } - } + }); new_changeset.bounds().extend(min); new_changeset.bounds().extend(max); @@ -414,32 +286,24 @@ namespace osmium { builder->add_user(user); } - void check_tag(osmium::builder::Builder* builder, const XML_Char* element, const XML_Char** attrs) { - if (!strcmp(element, "tag")) { - m_wnl_builder.reset(); - m_rml_builder.reset(); - - const char* key = ""; - const char* value = ""; - for (int count = 0; attrs[count]; count += 2) { - if (attrs[count][0] == 'k' && attrs[count][1] == 0) { - key = attrs[count+1]; - } else if (attrs[count][0] == 'v' && attrs[count][1] == 0) { - value = attrs[count+1]; - } + void get_tag(osmium::builder::Builder* builder, const XML_Char** attrs) { + const char* k = ""; + const char* v = ""; + check_attributes(attrs, [&k, &v](const XML_Char* name, const XML_Char* value) { + if (name[0] == 'k' && name[1] == 0) { + k = value; + } else if (name[0] == 'v' && name[1] == 0) { + v = value; } - if (!m_tl_builder) { - m_tl_builder = std::unique_ptr(new osmium::builder::TagListBuilder(m_buffer, builder)); - } - m_tl_builder->add_tag(key, value); + }); + if (!m_tl_builder) { + m_tl_builder = std::unique_ptr(new osmium::builder::TagListBuilder(m_buffer, builder)); } + m_tl_builder->add_tag(k, v); } - void header_is_done() { - m_header_is_done = true; - if (m_read_types == osmium::osm_entity_bits::nothing) { - throw ParserIsDone(); - } + void mark_header_as_done() { + set_header_value(m_header); } void start_element(const XML_Char* element, const XML_Char** attrs) { @@ -449,16 +313,16 @@ namespace osmium { if (!strcmp(element, "osmChange")) { m_header.set_has_multiple_object_versions(true); } - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "version")) { - m_header.set("version", attrs[count+1]); - if (strcmp(attrs[count+1], "0.6")) { - throw osmium::format_version_error(attrs[count+1]); + check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "version")) { + m_header.set("version", value); + if (strcmp(value, "0.6")) { + throw osmium::format_version_error(value); } - } else if (!strcmp(attrs[count], "generator")) { - m_header.set("generator", attrs[count+1]); + } else if (!strcmp(name, "generator")) { + m_header.set("generator", value); } - } + }); if (m_header.get("version") == "") { throw osmium::format_version_error(); } @@ -470,8 +334,8 @@ namespace osmium { case context::top: assert(!m_tl_builder); if (!strcmp(element, "node")) { - header_is_done(); - if (m_read_types & osmium::osm_entity_bits::node) { + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::node) { m_node_builder = std::unique_ptr(new osmium::builder::NodeBuilder(m_buffer)); m_node_builder->add_user(init_object(m_node_builder->object(), attrs)); m_context = context::node; @@ -479,8 +343,8 @@ namespace osmium { m_context = context::ignored_node; } } else if (!strcmp(element, "way")) { - header_is_done(); - if (m_read_types & osmium::osm_entity_bits::way) { + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::way) { m_way_builder = std::unique_ptr(new osmium::builder::WayBuilder(m_buffer)); m_way_builder->add_user(init_object(m_way_builder->object(), attrs)); m_context = context::way; @@ -488,8 +352,8 @@ namespace osmium { m_context = context::ignored_way; } } else if (!strcmp(element, "relation")) { - header_is_done(); - if (m_read_types & osmium::osm_entity_bits::relation) { + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::relation) { m_relation_builder = std::unique_ptr(new osmium::builder::RelationBuilder(m_buffer)); m_relation_builder->add_user(init_object(m_relation_builder->object(), attrs)); m_context = context::relation; @@ -497,8 +361,8 @@ namespace osmium { m_context = context::ignored_relation; } } else if (!strcmp(element, "changeset")) { - header_is_done(); - if (m_read_types & osmium::osm_entity_bits::changeset) { + mark_header_as_done(); + if (read_types() & osmium::osm_entity_bits::changeset) { m_changeset_builder = std::unique_ptr(new osmium::builder::ChangesetBuilder(m_buffer)); init_changeset(m_changeset_builder.get(), attrs); m_context = context::changeset; @@ -508,17 +372,17 @@ namespace osmium { } else if (!strcmp(element, "bounds")) { osmium::Location min; osmium::Location max; - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "minlon")) { - min.set_lon(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "minlat")) { - min.set_lat(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "maxlon")) { - max.set_lon(atof(attrs[count+1])); - } else if (!strcmp(attrs[count], "maxlat")) { - max.set_lat(atof(attrs[count+1])); + check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "minlon")) { + min.set_lon(atof(value)); + } else if (!strcmp(name, "minlat")) { + min.set_lat(atof(value)); + } else if (!strcmp(name, "maxlon")) { + max.set_lon(atof(value)); + } else if (!strcmp(name, "maxlat")) { + max.set_lat(atof(value)); } - } + }); osmium::Box box; box.extend(min).extend(max); m_header.add_box(box); @@ -529,7 +393,9 @@ namespace osmium { case context::node: m_last_context = context::node; m_context = context::in_object; - check_tag(m_node_builder.get(), element, attrs); + if (!strcmp(element, "tag")) { + get_tag(m_node_builder.get(), attrs); + } break; case context::way: m_last_context = context::way; @@ -541,13 +407,14 @@ namespace osmium { m_wnl_builder = std::unique_ptr(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get())); } - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "ref")) { - m_wnl_builder->add_node_ref(osmium::string_to_object_id(attrs[count+1])); + check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "ref")) { + m_wnl_builder->add_node_ref(osmium::string_to_object_id(value)); } - } - } else { - check_tag(m_way_builder.get(), element, attrs); + }); + } else if (!strcmp(element, "tag")) { + m_wnl_builder.reset(); + get_tag(m_way_builder.get(), attrs); } break; case context::relation: @@ -560,28 +427,68 @@ namespace osmium { m_rml_builder = std::unique_ptr(new osmium::builder::RelationMemberListBuilder(m_buffer, m_relation_builder.get())); } - char type = 'x'; - object_id_type ref = 0; + item_type type = item_type::undefined; + object_id_type ref = 0; const char* role = ""; - for (int count = 0; attrs[count]; count += 2) { - if (!strcmp(attrs[count], "type")) { - type = static_cast(attrs[count+1][0]); - } else if (!strcmp(attrs[count], "ref")) { - ref = osmium::string_to_object_id(attrs[count+1]); - } else if (!strcmp(attrs[count], "role")) { - role = static_cast(attrs[count+1]); + check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "type")) { + type = char_to_item_type(value[0]); + } else if (!strcmp(name, "ref")) { + ref = osmium::string_to_object_id(value); + } else if (!strcmp(name, "role")) { + role = static_cast(value); } + }); + if (type != item_type::node && type != item_type::way && type != item_type::relation) { + throw osmium::xml_error("Unknown type on relation member"); } - // XXX assert type, ref, role are set - m_rml_builder->add_member(char_to_item_type(type), ref, role); - } else { - check_tag(m_relation_builder.get(), element, attrs); + if (ref == 0) { + throw osmium::xml_error("Missing ref on relation member"); + } + m_rml_builder->add_member(type, ref, role); + } else if (!strcmp(element, "tag")) { + m_rml_builder.reset(); + get_tag(m_relation_builder.get(), attrs); } break; case context::changeset: m_last_context = context::changeset; - m_context = context::in_object; - check_tag(m_changeset_builder.get(), element, attrs); + if (!strcmp(element, "discussion")) { + m_context = context::discussion; + m_tl_builder.reset(); + if (!m_changeset_discussion_builder) { + m_changeset_discussion_builder = std::unique_ptr(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get())); + } + } else if (!strcmp(element, "tag")) { + m_context = context::in_object; + m_changeset_discussion_builder.reset(); + get_tag(m_changeset_builder.get(), attrs); + } + break; + case context::discussion: + if (!strcmp(element, "comment")) { + m_context = context::comment; + osmium::Timestamp date; + osmium::user_id_type uid = 0; + const char* user = ""; + check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) { + if (!strcmp(name, "date")) { + date = osmium::Timestamp(value); + } else if (!strcmp(name, "uid")) { + uid = osmium::string_to_user_id(value); + } else if (!strcmp(name, "user")) { + user = static_cast(value); + } + }); + m_changeset_discussion_builder->add_comment(date, uid, user); + } + break; + case context::comment: + if (!strcmp(element, "text")) { + m_context = context::comment_text; + } + break; + case context::comment_text: break; case context::ignored_node: break; @@ -604,7 +511,7 @@ namespace osmium { break; case context::top: if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) { - header_is_done(); + mark_header_as_done(); m_context = context::root; } else if (!strcmp(element, "delete")) { m_in_delete_section = false; @@ -639,11 +546,25 @@ namespace osmium { case context::changeset: assert(!strcmp(element, "changeset")); m_tl_builder.reset(); + m_changeset_discussion_builder.reset(); m_changeset_builder.reset(); m_buffer.commit(); m_context = context::top; flush_buffer(); break; + case context::discussion: + assert(!strcmp(element, "discussion")); + m_context = context::changeset; + break; + case context::comment: + assert(!strcmp(element, "comment")); + m_context = context::discussion; + break; + case context::comment_text: + assert(!strcmp(element, "text")); + m_context = context::comment; + m_changeset_discussion_builder->add_comment_text(m_comment_text); + break; case context::in_object: m_context = m_last_context; break; @@ -670,85 +591,84 @@ namespace osmium { } } + void characters(const XML_Char* text, int len) { + if (m_context == context::comment_text) { + m_comment_text.append(text, len); + } else { + m_comment_text.resize(0); + } + } + void flush_buffer() { - if (m_buffer.capacity() - m_buffer.committed() < 1000 * 1000) { - m_queue.push(std::move(m_buffer)); + if (m_buffer.committed() > buffer_size / 10 * 9) { + send_to_output_queue(std::move(m_buffer)); osmium::memory::Buffer buffer(buffer_size); - std::swap(m_buffer, buffer); + using std::swap; + swap(m_buffer, buffer); + } + } + + public: + + XMLParser(future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types) : + Parser(input_queue, output_queue, header_promise, read_types), + m_context(context::root), + m_last_context(context::root), + m_in_delete_section(false), + m_header(), + m_buffer(buffer_size), + m_node_builder(), + m_way_builder(), + m_relation_builder(), + m_changeset_builder(), + m_changeset_discussion_builder(), + m_tl_builder(), + m_wnl_builder(), + m_rml_builder() { + } + + ~XMLParser() noexcept final = default; + + void run() final { + osmium::thread::set_thread_name("_osmium_xml_in"); + + ExpatXMLParser parser(this); + + while (!input_done()) { + std::string data = get_input(); + parser(data, input_done()); + if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) { + break; + } + } + + mark_header_as_done(); + + if (m_buffer.committed() > 0) { + send_to_output_queue(std::move(m_buffer)); } } }; // class XMLParser - class XMLInputFormat : public osmium::io::detail::InputFormat { + // we want the register_parser() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_xml_parser = ParserFactory::instance().register_parser( + file_format::xml, + [](future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new XMLParser(input_queue, output_queue, header_promise, read_which_entities)); + }); - static constexpr size_t max_queue_size = 100; - - osmium::thread::Queue m_queue; - std::atomic m_done; - std::promise m_header_promise; - std::future m_parser_future; - - public: - - /** - * Instantiate XML Parser - * - * @param file osmium::io::File instance describing file to be read from. - * @param read_which_entities Which types of OSM entities (nodes, ways, relations, changesets) should be parsed? - * @param input_queue String queue where data is read from. - */ - explicit XMLInputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue& input_queue) : - osmium::io::detail::InputFormat(file, read_which_entities), - m_queue(max_queue_size, "xml_parser_results"), - m_done(false), - m_header_promise(), - m_parser_future(std::async(std::launch::async, XMLParser(input_queue, m_queue, m_header_promise, read_which_entities, m_done))) { - } - - ~XMLInputFormat() { - try { - close(); - } catch (...) { - // ignore any exceptions at this point because destructor should not throw - } - } - - virtual osmium::io::Header header() override final { - osmium::thread::check_for_exception(m_parser_future); - return m_header_promise.get_future().get(); - } - - osmium::memory::Buffer read() override { - osmium::memory::Buffer buffer; - if (!m_done || !m_queue.empty()) { - m_queue.wait_and_pop(buffer); - } - - osmium::thread::check_for_exception(m_parser_future); - return buffer; - } - - void close() override { - m_done = true; - osmium::thread::wait_until_done(m_parser_future); - } - - }; // class XMLInputFormat - - namespace { - -// we want the register_input_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_xml_input = osmium::io::detail::InputFormatFactory::instance().register_input_format(osmium::io::file_format::xml, - [](const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue& input_queue) { - return new osmium::io::detail::XMLInputFormat(file, read_which_entities, input_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_xml_parser() noexcept { + return registered_xml_parser; + } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp index 2a381d5ab..09bd6b3ce 100644 --- a/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,20 +33,17 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include +#include #include #include #include #include #include #include -#include #include #include #include -#include #include #include #include @@ -75,45 +72,23 @@ namespace osmium { struct XMLWriteError {}; - namespace { + struct xml_output_options { - void xml_string(std::string& out, const char* in) { - for (; *in != '\0'; ++in) { - switch(*in) { - case '&': out += "&"; break; - case '\"': out += """; break; - case '\'': out += "'"; break; - case '<': out += "<"; break; - case '>': out += ">"; break; - case '\n': out += " "; break; - case '\r': out += " "; break; - case '\t': out += " "; break; - default: out += *in; break; - } - } - } + /// Should metadata of objects be added? + bool add_metadata; - const size_t tmp_buffer_size = 100; + /// Should the visible flag be added to all OSM objects? + bool add_visible_flag; - template - void oprintf(std::string& out, const char* format, T value) { - char buffer[tmp_buffer_size+1]; - size_t max_size = sizeof(buffer)/sizeof(char); -#ifndef NDEBUG - int len = -#endif -#ifndef _MSC_VER - snprintf(buffer, max_size, format, value); -#else - _snprintf(buffer, max_size, format, value); -#endif - assert(len > 0 && static_cast(len) < max_size); - out += buffer; - } + /** + * Should , , "operations" be added? + * (This is used for .osc files.) + */ + bool use_change_ops; - } // anonymous namespace + }; - class XMLOutputBlock : public osmium::handler::Handler { + class XMLOutputBlock : public OutputBlock { // operation (create, modify, delete) for osc files enum class operation { @@ -123,15 +98,9 @@ namespace osmium { op_delete = 3 }; // enum class operation - std::shared_ptr m_input_buffer; - - std::shared_ptr m_out; - operation m_last_op {operation::op_none}; - const bool m_add_metadata; - const bool m_write_visible_flag; - const bool m_write_change_ops; + xml_output_options m_options; void write_spaces(int num) { for (; num != 0; --num) { @@ -139,20 +108,20 @@ namespace osmium { } } + int prefix_spaces() { + return m_options.use_change_ops ? 4 : 2; + } + void write_prefix() { - if (m_write_change_ops) { - write_spaces(4); - } else { - write_spaces(2); - } + write_spaces(prefix_spaces()); } void write_meta(const osmium::OSMObject& object) { - oprintf(*m_out, " id=\"%" PRId64 "\"", object.id()); + output_formatted(" id=\"%" PRId64 "\"", object.id()); - if (m_add_metadata) { + if (m_options.add_metadata) { if (object.version()) { - oprintf(*m_out, " version=\"%d\"", object.version()); + output_formatted(" version=\"%d\"", object.version()); } if (object.timestamp()) { @@ -162,16 +131,16 @@ namespace osmium { } if (!object.user_is_anonymous()) { - oprintf(*m_out, " uid=\"%d\" user=\"", object.uid()); - xml_string(*m_out, object.user()); + output_formatted(" uid=\"%d\" user=\"", object.uid()); + append_xml_encoded_string(*m_out, object.user()); *m_out += "\""; } if (object.changeset()) { - oprintf(*m_out, " changeset=\"%d\"", object.changeset()); + output_formatted(" changeset=\"%d\"", object.changeset()); } - if (m_write_visible_flag) { + if (m_options.add_visible_flag) { if (object.visible()) { *m_out += " visible=\"true\""; } else { @@ -181,17 +150,31 @@ namespace osmium { } } - void write_tags(const osmium::TagList& tags) { + void write_tags(const osmium::TagList& tags, int spaces) { for (const auto& tag : tags) { - write_prefix(); + write_spaces(spaces); *m_out += " \n"; } } + void write_discussion(const osmium::ChangesetDiscussion& comments) { + for (const auto& comment : comments) { + output_formatted(" \n"; + *m_out += " "; + append_xml_encoded_string(*m_out, comment.text()); + *m_out += "\n \n"; + } + *m_out += " \n"; + } + void open_close_op_tag(const operation op = operation::op_none) { if (op == m_last_op) { return; @@ -230,12 +213,9 @@ namespace osmium { public: - explicit XMLOutputBlock(osmium::memory::Buffer&& buffer, bool add_metadata, bool write_visible_flag, bool write_change_ops) : - m_input_buffer(std::make_shared(std::move(buffer))), - m_out(std::make_shared()), - m_add_metadata(add_metadata), - m_write_visible_flag(write_visible_flag && !write_change_ops), - m_write_change_ops(write_change_ops) { + XMLOutputBlock(osmium::memory::Buffer&& buffer, const xml_output_options& options) : + OutputBlock(std::move(buffer)), + m_options(options) { } XMLOutputBlock(const XMLOutputBlock&) = default; @@ -244,22 +224,24 @@ namespace osmium { XMLOutputBlock(XMLOutputBlock&&) = default; XMLOutputBlock& operator=(XMLOutputBlock&&) = default; - ~XMLOutputBlock() = default; + ~XMLOutputBlock() noexcept = default; std::string operator()() { osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this); - if (m_write_change_ops) { + if (m_options.use_change_ops) { open_close_op_tag(); } std::string out; - std::swap(out, *m_out); + using std::swap; + swap(out, *m_out); + return out; } void node(const osmium::Node& node) { - if (m_write_change_ops) { + if (m_options.use_change_ops) { open_close_op_tag(node.visible() ? (node.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete); } @@ -283,14 +265,14 @@ namespace osmium { *m_out += ">\n"; - write_tags(node.tags()); + write_tags(node.tags(), prefix_spaces()); write_prefix(); *m_out += "\n"; } void way(const osmium::Way& way) { - if (m_write_change_ops) { + if (m_options.use_change_ops) { open_close_op_tag(way.visible() ? (way.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete); } @@ -307,17 +289,17 @@ namespace osmium { for (const auto& node_ref : way.nodes()) { write_prefix(); - oprintf(*m_out, " \n", node_ref.ref()); + output_formatted(" \n", node_ref.ref()); } - write_tags(way.tags()); + write_tags(way.tags(), prefix_spaces()); write_prefix(); *m_out += "\n"; } void relation(const osmium::Relation& relation) { - if (m_write_change_ops) { + if (m_options.use_change_ops) { open_close_op_tag(relation.visible() ? (relation.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete); } @@ -336,22 +318,21 @@ namespace osmium { write_prefix(); *m_out += " \n"; } - write_tags(relation.tags()); + write_tags(relation.tags(), prefix_spaces()); write_prefix(); *m_out += "\n"; } void changeset(const osmium::Changeset& changeset) { - write_prefix(); - *m_out += " 0) { + *m_out += " \n"; + write_discussion(changeset.discussion()); + } + + *m_out += " \n"; } }; // class XMLOutputBlock class XMLOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler { - bool m_add_metadata; - bool m_write_visible_flag; + xml_output_options m_options; public: - XMLOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) : - OutputFormat(file, output_queue), - m_add_metadata(file.get("add_metadata") != "false"), - m_write_visible_flag(file.has_multiple_object_versions() || m_file.is_true("force_visible_flag")) { + XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(output_queue), + m_options() { + m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.use_change_ops = file.is_true("xml_change_format"); + m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops; } XMLOutputFormat(const XMLOutputFormat&) = delete; XMLOutputFormat& operator=(const XMLOutputFormat&) = delete; - ~XMLOutputFormat() override final { - } + ~XMLOutputFormat() noexcept final = default; - void write_buffer(osmium::memory::Buffer&& buffer) override final { - m_output_queue.push(osmium::thread::Pool::instance().submit(XMLOutputBlock{std::move(buffer), m_add_metadata, m_write_visible_flag, m_file.is_true("xml_change_format")})); - } - - void write_header(const osmium::io::Header& header) override final { + void write_header(const osmium::io::Header& header) final { std::string out = "\n"; - if (m_file.is_true("xml_change_format")) { + if (m_options.use_change_ops) { out += "\n"; } else { out += "\n"; } + append_xml_encoded_string(out, header.get("generator").c_str()); + out += "\">\n"; for (const auto& box : header.boxes()) { out += " \n", box.top_right().lat()); + append_printf_formatted_string(out, " minlon=\"%.7f\"", box.bottom_left().lon()); + append_printf_formatted_string(out, " minlat=\"%.7f\"", box.bottom_left().lat()); + append_printf_formatted_string(out, " maxlon=\"%.7f\"", box.top_right().lon()); + append_printf_formatted_string(out, " maxlat=\"%.7f\"/>\n", box.top_right().lat()); } - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(std::move(out)); + send_to_output_queue(std::move(out)); } - void close() override final { - { - std::string out; - if (m_file.is_true("xml_change_format")) { - out += "\n"; - } else { - out += "\n"; - } + void write_buffer(osmium::memory::Buffer&& buffer) final { + m_output_queue.push(osmium::thread::Pool::instance().submit(XMLOutputBlock{std::move(buffer), m_options})); + } - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(std::move(out)); + void write_end() final { + std::string out; + + if (m_options.use_change_ops) { + out += "\n"; + } else { + out += "\n"; } - std::promise promise; - m_output_queue.push(promise.get_future()); - promise.set_value(std::string()); + send_to_output_queue(std::move(out)); } }; // class XMLOutputFormat - namespace { + // we want the register_output_format() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_xml_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::xml, + [](const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::XMLOutputFormat(file, output_queue); + }); -// we want the register_output_format() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - const bool registered_xml_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::xml, - [](const osmium::io::File& file, data_queue_type& output_queue) { - return new osmium::io::detail::XMLOutputFormat(file, output_queue); - }); -#pragma GCC diagnostic pop - - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_xml_output() noexcept { + return registered_xml_output; + } } // namespace detail - } // namespace output + } // namespace io } // namespace osmium diff --git a/third_party/libosmium/include/osmium/io/detail/zlib.hpp b/third_party/libosmium/include/osmium/io/detail/zlib.hpp index fc0baf344..2b6dddb94 100644 --- a/third_party/libosmium/include/osmium/io/detail/zlib.hpp +++ b/third_party/libosmium/include/osmium/io/detail/zlib.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. #include +#include #include namespace osmium { @@ -69,7 +70,7 @@ namespace osmium { ); if (result != Z_OK) { - throw std::runtime_error(std::string("failed to compress data: ") + zError(result)); + throw io_error(std::string("failed to compress data: ") + zError(result)); } output.resize(output_size); @@ -99,7 +100,7 @@ namespace osmium { ); if (result != Z_OK) { - throw std::runtime_error(std::string("failed to uncompress data: ") + zError(result)); + throw io_error(std::string("failed to uncompress data: ") + zError(result)); } return std::make_pair(output.data(), output.size()); diff --git a/third_party/libosmium/include/osmium/io/error.hpp b/third_party/libosmium/include/osmium/io/error.hpp index 07652bc59..fb6ccf738 100644 --- a/third_party/libosmium/include/osmium/io/error.hpp +++ b/third_party/libosmium/include/osmium/io/error.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,16 +43,28 @@ namespace osmium { */ struct io_error : public std::runtime_error { - io_error(const std::string& what) : + explicit io_error(const std::string& what) : std::runtime_error(what) { } - io_error(const char* what) : + explicit io_error(const char* what) : std::runtime_error(what) { } }; // struct io_error + struct unsupported_file_format_error : public io_error { + + explicit unsupported_file_format_error(const std::string& what) : + io_error(what) { + } + + explicit unsupported_file_format_error(const char* what) : + io_error(what) { + } + + }; // struct unsupported_file_format_error + } // namespace osmium #endif // OSMIUM_IO_ERROR_HPP diff --git a/third_party/libosmium/include/osmium/io/file.hpp b/third_party/libosmium/include/osmium/io/file.hpp index 3bbfacc6d..56fc8d513 100644 --- a/third_party/libosmium/include/osmium/io/file.hpp +++ b/third_party/libosmium/include/osmium/io/file.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -255,9 +256,9 @@ namespace osmium { * Check file format etc. for consistency and throw exception if * there is a problem. * - * @throws std::runtime_error + * @throws osmium::io_error */ - void check() const { + const File& check() const { if (m_file_format == file_format::unknown) { std::string msg = "Could not detect file format"; if (!m_format_string.empty()) { @@ -273,8 +274,9 @@ namespace osmium { msg += "'"; } msg += "."; - throw std::runtime_error(msg); + throw io_error(msg); } + return *this; } file_format format() const noexcept { diff --git a/third_party/libosmium/include/osmium/io/file_compression.hpp b/third_party/libosmium/include/osmium/io/file_compression.hpp index 292ddcfca..1cfb0f0c2 100644 --- a/third_party/libosmium/include/osmium/io/file_compression.hpp +++ b/third_party/libosmium/include/osmium/io/file_compression.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/file_format.hpp b/third_party/libosmium/include/osmium/io/file_format.hpp index edfb1ff9f..c447cb478 100644 --- a/third_party/libosmium/include/osmium/io/file_format.hpp +++ b/third_party/libosmium/include/osmium/io/file_format.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/gzip_compression.hpp b/third_party/libosmium/include/osmium/io/gzip_compression.hpp index 204bfd521..e6ca010de 100644 --- a/third_party/libosmium/include/osmium/io/gzip_compression.hpp +++ b/third_party/libosmium/include/osmium/io/gzip_compression.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,7 +49,9 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include @@ -59,13 +61,13 @@ namespace osmium { * Exception thrown when there are problems compressing or * decompressing gzip files. */ - struct gzip_error : public std::runtime_error { + struct gzip_error : public io_error { int gzip_error_code; int system_errno; gzip_error(const std::string& what, int error_code) : - std::runtime_error(what), + io_error(what), gzip_error_code(error_code), system_errno(error_code == Z_ERRNO ? errno : 0) { } @@ -93,23 +95,29 @@ namespace osmium { class GzipCompressor : public Compressor { + int m_fd; gzFile m_gzfile; public: - explicit GzipCompressor(int fd) : - Compressor(), + explicit GzipCompressor(int fd, fsync sync) : + Compressor(sync), + m_fd(dup(fd)), m_gzfile(::gzdopen(fd, "w")) { if (!m_gzfile) { detail::throw_gzip_error(m_gzfile, "write initialization failed"); } } - ~GzipCompressor() override final { - close(); + ~GzipCompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - void write(const std::string& data) override final { + void write(const std::string& data) final { if (!data.empty()) { int nwrite = ::gzwrite(m_gzfile, data.data(), static_cast_with_assert(data.size())); if (nwrite == 0) { @@ -118,13 +126,17 @@ namespace osmium { } } - void close() override final { + void close() final { if (m_gzfile) { int result = ::gzclose(m_gzfile); m_gzfile = nullptr; if (result != Z_OK) { detail::throw_gzip_error(m_gzfile, "write close failed", result); } + if (do_fsync()) { + osmium::io::detail::reliable_fsync(m_fd); + } + osmium::io::detail::reliable_close(m_fd); } } @@ -144,11 +156,15 @@ namespace osmium { } } - ~GzipDecompressor() override final { - close(); + ~GzipDecompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - std::string read() override final { + std::string read() final { std::string buffer(osmium::io::Decompressor::input_buffer_size, '\0'); int nread = ::gzread(m_gzfile, const_cast(buffer.data()), static_cast_with_assert(buffer.size())); if (nread < 0) { @@ -158,7 +174,7 @@ namespace osmium { return buffer; } - void close() override final { + void close() final { if (m_gzfile) { int result = ::gzclose(m_gzfile); m_gzfile = nullptr; @@ -194,11 +210,15 @@ namespace osmium { } } - ~GzipBufferDecompressor() override final { - inflateEnd(&m_zstream); + ~GzipBufferDecompressor() noexcept final { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. + } } - std::string read() override final { + std::string read() final { std::string output; if (m_buffer) { @@ -227,22 +247,28 @@ namespace osmium { return output; } + void close() final { + inflateEnd(&m_zstream); + } + }; // class GzipBufferDecompressor - namespace { + namespace detail { -// we want the register_compression() function to run, setting the variable -// is only a side-effect, it will never be used -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" + // we want the register_compression() function to run, setting + // the variable is only a side-effect, it will never be used const bool registered_gzip_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::gzip, - [](int fd) { return new osmium::io::GzipCompressor(fd); }, + [](int fd, fsync sync) { return new osmium::io::GzipCompressor(fd, sync); }, [](int fd) { return new osmium::io::GzipDecompressor(fd); }, [](const char* buffer, size_t size) { return new osmium::io::GzipBufferDecompressor(buffer, size); } ); -#pragma GCC diagnostic pop - } // anonymous namespace + // dummy function to silence the unused variable warning from above + inline bool get_registered_gzip_compression() noexcept { + return registered_gzip_compression; + } + + } // namespace detail } // namespace io diff --git a/third_party/libosmium/include/osmium/io/header.hpp b/third_party/libosmium/include/osmium/io/header.hpp index 4b0830a62..55ff5c6de 100644 --- a/third_party/libosmium/include/osmium/io/header.hpp +++ b/third_party/libosmium/include/osmium/io/header.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/input_iterator.hpp b/third_party/libosmium/include/osmium/io/input_iterator.hpp index f6197299d..8be975930 100644 --- a/third_party/libosmium/include/osmium/io/input_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/input_iterator.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -52,7 +52,7 @@ namespace osmium { * source. It hides all the buffer handling and makes the contents of a * source accessible as a normal STL input iterator. */ - template + template class InputIterator { static_assert(std::is_base_of::value, "TItem must derive from osmium::buffer::Item"); @@ -133,6 +133,44 @@ namespace osmium { }; // class InputIterator + template + class InputIteratorRange { + + InputIterator m_begin; + InputIterator m_end; + + public: + + InputIteratorRange(InputIterator&& begin, + InputIterator&& end) : + m_begin(std::move(begin)), + m_end(std::move(end)) { + } + + InputIterator begin() const noexcept { + return m_begin; + } + + InputIterator end() const noexcept { + return m_end; + } + + const InputIterator cbegin() const noexcept { + return m_begin; + } + + const InputIterator cend() const noexcept { + return m_end; + } + + }; // class InputIteratorRange + + template + InputIteratorRange make_input_iterator_range(TSource& source) { + using it_type = InputIterator; + return InputIteratorRange(it_type{source}, it_type{}); + } + } // namespace io } // namespace osmium diff --git a/third_party/libosmium/include/osmium/io/o5m_input.hpp b/third_party/libosmium/include/osmium/io/o5m_input.hpp new file mode 100644 index 000000000..c59e3a6b6 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/o5m_input.hpp @@ -0,0 +1,45 @@ +#ifndef OSMIUM_IO_O5M_INPUT_HPP +#define OSMIUM_IO_O5M_INPUT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +/** + * @file + * + * Include this file if you want to read OSM o5m and o5c files. + */ + +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif // OSMIUM_IO_O5M_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/opl_output.hpp b/third_party/libosmium/include/osmium/io/opl_output.hpp index 04385d968..c79e260b6 100644 --- a/third_party/libosmium/include/osmium/io/opl_output.hpp +++ b/third_party/libosmium/include/osmium/io/opl_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/output_iterator.hpp b/third_party/libosmium/include/osmium/io/output_iterator.hpp index 608852fa9..ce050ebab 100644 --- a/third_party/libosmium/include/osmium/io/output_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/output_iterator.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -40,6 +40,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include namespace osmium { @@ -49,30 +50,26 @@ namespace osmium { namespace io { - template + template class OutputIterator : public std::iterator { - struct buffer_wrapper { - - osmium::memory::Buffer buffer; - - buffer_wrapper(size_t buffer_size) : - buffer(buffer_size, osmium::memory::Buffer::auto_grow::no) { - } - - }; // struct buffer_wrapper - - static constexpr size_t default_buffer_size = 10 * 1024 * 1024; - TDest* m_destination; - std::shared_ptr m_buffer_wrapper; - public: - explicit OutputIterator(TDest& destination, const size_t buffer_size = default_buffer_size) : - m_destination(&destination), - m_buffer_wrapper(std::make_shared(buffer_size)) { + explicit OutputIterator(TDest& destination) : + m_destination(&destination) { + } + + /** + * @deprecated + * Use of buffer size argument on OutputIterator + * constructor is deprecated. Call Writer::set_buffer_size() + * instead if you want to change the default. + */ + OSMIUM_DEPRECATED OutputIterator(TDest& destination, const size_t buffer_size) : + m_destination(&destination) { + destination.set_buffer_size(buffer_size); } OutputIterator(const OutputIterator&) = default; @@ -83,19 +80,17 @@ namespace osmium { ~OutputIterator() = default; - void flush() { - osmium::memory::Buffer buffer(m_buffer_wrapper->buffer.capacity(), osmium::memory::Buffer::auto_grow::no); - std::swap(m_buffer_wrapper->buffer, buffer); - (*m_destination)(std::move(buffer)); + /** + * @deprecated + * Calling OutputIterator::flush() is usually not + * needed any more. Call flush() on the Writer instead if needed. + */ + OSMIUM_DEPRECATED void flush() { + m_destination->flush(); } OutputIterator& operator=(const osmium::memory::Item& item) { - try { - m_buffer_wrapper->buffer.push_back(item); - } catch (osmium::buffer_is_full&) { - flush(); - m_buffer_wrapper->buffer.push_back(item); - } + (*m_destination)(item); return *this; } @@ -117,6 +112,23 @@ namespace osmium { }; // class OutputIterator + template + OutputIterator make_output_iterator(TDest& destination) { + return OutputIterator{destination}; + } + + /** + * @deprecated + * Use of buffer size argument on make_output_iterator is deprecated. + * Call Writer::set_buffer_size() instead if you want to change the + * default. + */ + template + OSMIUM_DEPRECATED OutputIterator make_output_iterator(TDest& destination, const size_t buffer_size) { + destination.set_buffer_size(buffer_size); + return OutputIterator{destination}; + } + } // namespace io } // namespace osmium diff --git a/third_party/libosmium/include/osmium/io/overwrite.hpp b/third_party/libosmium/include/osmium/io/overwrite.hpp index e33894bd8..5361698a5 100644 --- a/third_party/libosmium/include/osmium/io/overwrite.hpp +++ b/third_party/libosmium/include/osmium/io/overwrite.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,20 +33,7 @@ DEALINGS IN THE SOFTWARE. */ -namespace osmium { - - namespace io { - - /** - * Allow overwriting of existing file. - */ - enum class overwrite : bool { - no = false, - allow = true - }; - - } // namespace io - -} // namespace osmium +#pragma message("Including overwrite.hpp is deprecated, #include instead.") +#include #endif // OSMIUM_IO_OVERWRITE_HPP diff --git a/third_party/libosmium/include/osmium/io/pbf_input.hpp b/third_party/libosmium/include/osmium/io/pbf_input.hpp index d7f3787ae..ab1f324d7 100644 --- a/third_party/libosmium/include/osmium/io/pbf_input.hpp +++ b/third_party/libosmium/include/osmium/io/pbf_input.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/pbf_output.hpp b/third_party/libosmium/include/osmium/io/pbf_output.hpp index dad1013d7..0a50f2cc9 100644 --- a/third_party/libosmium/include/osmium/io/pbf_output.hpp +++ b/third_party/libosmium/include/osmium/io/pbf_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/reader.hpp b/third_party/libosmium/include/osmium/io/reader.hpp index c68a8e180..7c60511af 100644 --- a/third_party/libosmium/include/osmium/io/reader.hpp +++ b/third_party/libosmium/include/osmium/io/reader.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,10 +33,10 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include +#include #include #include #include @@ -55,12 +55,13 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include #include #include #include #include -#include namespace osmium { @@ -74,17 +75,46 @@ namespace osmium { */ class Reader { + static constexpr size_t max_input_queue_size = 20; // XXX + static constexpr size_t max_osmdata_queue_size = 20; // XXX + osmium::io::File m_file; osmium::osm_entity_bits::type m_read_which_entities; - std::atomic m_input_done; + + enum class status { + okay = 0, // normal reading + error = 1, // some error occurred while reading + closed = 2, // close() called successfully after eof + eof = 3 // eof of file was reached without error + } m_status; + int m_childpid; - osmium::thread::Queue m_input_queue; + detail::future_string_queue_type m_input_queue; std::unique_ptr m_decompressor; - std::future m_read_future; - std::unique_ptr m_input; + osmium::io::detail::ReadThreadManager m_read_thread_manager; + + detail::future_buffer_queue_type m_osmdata_queue; + detail::queue_wrapper m_osmdata_queue_wrapper; + + std::future m_header_future; + osmium::io::Header m_header; + + osmium::thread::thread_handler m_thread; + + // This function will run in a separate thread. + static void parser_thread(const osmium::io::File& file, + detail::future_string_queue_type& input_queue, + detail::future_buffer_queue_type& osmdata_queue, + std::promise&& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + std::promise promise = std::move(header_promise); + auto creator = detail::ParserFactory::instance().get_creator_function(file); + auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); + parser->parse(); + } #ifndef _WIN32 /** @@ -149,7 +179,7 @@ namespace osmium { #ifndef _WIN32 return execute("curl", filename, childpid); #else - throw std::runtime_error("Reading OSM files from the network currently not supported on Windows."); + throw io_error("Reading OSM files from the network currently not supported on Windows."); #endif } else { return osmium::io::detail::open_for_reading(filename); @@ -168,16 +198,23 @@ namespace osmium { * parsed. */ explicit Reader(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities = osmium::osm_entity_bits::all) : - m_file(file), + m_file(file.check()), m_read_which_entities(read_which_entities), - m_input_done(false), + m_status(status::okay), m_childpid(0), - m_input_queue(20, "raw_input"), // XXX + m_input_queue(max_input_queue_size, "raw_input"), m_decompressor(m_file.buffer() ? osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), m_file.buffer(), m_file.buffer_size()) : osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), open_input_file_or_url(m_file.filename(), &m_childpid))), - m_read_future(std::async(std::launch::async, detail::ReadThread(m_input_queue, m_decompressor.get(), m_input_done))), - m_input(osmium::io::detail::InputFormatFactory::instance().create_input(m_file, m_read_which_entities, m_input_queue)) { + m_read_thread_manager(*m_decompressor, m_input_queue), + m_osmdata_queue(max_osmdata_queue_size, "parser_results"), + m_osmdata_queue_wrapper(m_osmdata_queue), + m_header_future(), + m_header(), + m_thread() { + std::promise header_promise; + m_header_future = header_promise.get_future(); + m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), read_which_entities}; } explicit Reader(const std::string& filename, osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) : @@ -191,27 +228,37 @@ namespace osmium { Reader(const Reader&) = delete; Reader& operator=(const Reader&) = delete; - ~Reader() { + Reader(Reader&&) = default; + Reader& operator=(Reader&&) = default; + + ~Reader() noexcept { try { close(); - } - catch (...) { + } catch (...) { + // Ignore any exceptions because destructor must not throw. } } /** * Close down the Reader. A call to this is optional, because the * destructor of Reader will also call this. But if you don't call - * this function first, the destructor might throw an exception - * which is not good. + * this function first, you might miss an exception, because the + * destructor is not allowed to throw. * - * @throws Some form of std::runtime_error when there is a problem. + * @throws Some form of osmium::io_error when there is a problem. */ void close() { - // Signal to input child process that it should wrap up. - m_input_done = true; + m_status = status::closed; - m_input->close(); + m_read_thread_manager.stop(); + + m_osmdata_queue_wrapper.drain(); + + try { + m_read_thread_manager.close(); + } catch (...) { + // Ignore any exceptions. + } #ifndef _WIN32 if (m_childpid) { @@ -226,15 +273,32 @@ namespace osmium { m_childpid = 0; } #endif - - osmium::thread::wait_until_done(m_read_future); } /** * Get the header data from the file. + * + * @returns Header. + * @throws Some form of osmium::io_error if there is an error. */ - osmium::io::Header header() const { - return m_input->header(); + osmium::io::Header header() { + if (m_status == status::error) { + throw io_error("Can not get header from reader when in status 'error'"); + } + + try { + if (m_header_future.valid()) { + m_header = m_header_future.get(); + if (m_read_which_entities == osmium::osm_entity_bits::nothing) { + m_status = status::eof; + } + } + } catch (...) { + close(); + m_status = status::error; + throw; + } + return m_header; } /** @@ -245,32 +309,36 @@ namespace osmium { * constructed. * * @returns Buffer. - * @throws Some form of std::runtime_error if there is an error. + * @throws Some form of osmium::io_error if there is an error. */ osmium::memory::Buffer read() { - // If an exception happened in the input thread, re-throw - // it in this (the main) thread. - osmium::thread::check_for_exception(m_read_future); + osmium::memory::Buffer buffer; - if (m_read_which_entities == osmium::osm_entity_bits::nothing || m_input_done) { - // If the caller didn't want anything but the header, it will - // always get an empty buffer here. - return osmium::memory::Buffer(); + if (m_status != status::okay || + m_read_which_entities == osmium::osm_entity_bits::nothing) { + throw io_error("Can not read from reader when in status 'closed', 'eof', or 'error'"); } - // m_input->read() can return an invalid buffer to signal EOF, - // or a valid buffer with or without data. A valid buffer - // without data is not an error, it just means we have to - // keep getting the next buffer until there is one with data. - while (true) { - osmium::memory::Buffer buffer = m_input->read(); - if (!buffer) { - m_input_done = true; - return buffer; - } - if (buffer.committed() > 0) { - return buffer; + try { + // m_input_format.read() can return an invalid buffer to signal EOF, + // or a valid buffer with or without data. A valid buffer + // without data is not an error, it just means we have to + // keep getting the next buffer until there is one with data. + while (true) { + buffer = m_osmdata_queue_wrapper.pop(); + if (detail::at_end_of_data(buffer)) { + m_status = status::eof; + m_read_thread_manager.close(); + return buffer; + } + if (buffer.committed() > 0) { + return buffer; + } } + } catch (...) { + close(); + m_status = status::error; + throw; } } @@ -279,7 +347,7 @@ namespace osmium { * data has been read. It is also set by calling close(). */ bool eof() const { - return m_input_done; + return m_status == status::eof || m_status == status::closed; } }; // class Reader @@ -292,7 +360,7 @@ namespace osmium { * unless you are working with small OSM files and/or have lots of * RAM. */ - template + template osmium::memory::Buffer read_file(TArgs&&... args) { osmium::memory::Buffer buffer(1024*1024, osmium::memory::Buffer::auto_grow::yes); diff --git a/third_party/libosmium/include/osmium/io/reader_iterator.hpp b/third_party/libosmium/include/osmium/io/reader_iterator.hpp index 862078935..116442fd1 100644 --- a/third_party/libosmium/include/osmium/io/reader_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/reader_iterator.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/writer.hpp b/third_party/libosmium/include/osmium/io/writer.hpp index 64afe2066..b389698fc 100644 --- a/third_party/libosmium/include/osmium/io/writer.hpp +++ b/third_party/libosmium/include/osmium/io/writer.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,17 +33,23 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include #include +#include #include #include #include +#include #include #include +#include #include #include -#include +#include #include #include @@ -54,21 +60,112 @@ namespace osmium { /** * This is the user-facing interface for writing OSM files. Instantiate * an object of this class with a file name or osmium::io::File object - * and optionally the data for the header and then call operator() on it - * to write Buffers. Call close() to finish up. + * and optionally the data for the header and then call operator() on + * it to write Buffers or Items. + * + * The writer uses multithreading internally to do the actual encoding + * of the data into the intended format, possible compress the data and + * then write it out. But this is intentionally hidden from the user + * of this class who can use it without knowing those details. + * + * If you are done call the close() method to finish up. Only if you + * don't get an exception from the close() method, you can be sure + * the data is written correctly (modulo operating system buffering). + * The destructor of this class will also do the right thing if you + * forget to call close(), but because the destructor can't throw you + * will not get informed about any problems. + * + * The writer is usually used to write complete blocks of data stored + * in osmium::memory::Buffers. But you can also write single + * osmium::memory::Items. In this case the Writer uses an internal + * Buffer. */ class Writer { + static constexpr size_t default_buffer_size = 10 * 1024 * 1024; + osmium::io::File m_file; - osmium::io::detail::data_queue_type m_output_queue; + detail::future_string_queue_type m_output_queue; std::unique_ptr m_output; - std::unique_ptr m_compressor; + osmium::memory::Buffer m_buffer; + + size_t m_buffer_size; std::future m_write_future; + osmium::thread::thread_handler m_thread; + + enum class status { + okay = 0, // normal writing + error = 1, // some error occurred while writing + closed = 2 // close() called successfully + } m_status; + + // This function will run in a separate thread. + static void write_thread(detail::future_string_queue_type& output_queue, + std::unique_ptr&& compressor, + std::promise&& write_promise) { + detail::WriteThread write_thread{output_queue, + std::move(compressor), + std::move(write_promise)}; + write_thread(); + } + + void do_write(osmium::memory::Buffer&& buffer) { + if (buffer && buffer.committed() > 0) { + m_output->write_buffer(std::move(buffer)); + } + } + + void do_flush() { + osmium::thread::check_for_exception(m_write_future); + if (m_buffer && m_buffer.committed() > 0) { + osmium::memory::Buffer buffer{m_buffer_size, + osmium::memory::Buffer::auto_grow::no}; + using std::swap; + swap(m_buffer, buffer); + + m_output->write_buffer(std::move(buffer)); + } + } + + template + void ensure_cleanup(TFunction func, TArgs&&... args) { + if (m_status != status::okay) { + throw io_error("Can not write to writer when in status 'closed' or 'error'"); + } + + try { + func(std::forward(args)...); + } catch (...) { + m_status = status::error; + detail::add_to_queue(m_output_queue, std::current_exception()); + detail::add_end_of_data_to_queue(m_output_queue); + throw; + } + } + + struct options_type { + osmium::io::Header header; + overwrite allow_overwrite = overwrite::no; + fsync sync = fsync::no; + }; + + static void set_option(options_type& options, const osmium::io::Header& header) { + options.header = header; + } + + static void set_option(options_type& options, overwrite value) { + options.allow_overwrite = value; + } + + static void set_option(options_type& options, fsync value) { + options.sync = value; + } + public: /** @@ -76,64 +173,166 @@ namespace osmium { * header to it. * * @param file File (contains name and format info) to open. - * @param header Optional header data. If this is not given sensible - * defaults will be used. See the default constructor - * of osmium::io::Header for details. - * @param allow_overwrite Allow overwriting of existing file? Can be - * osmium::io::overwrite::allow or osmium::io::overwrite::no - * (default). + * @param args All further arguments are optional and can appear + * in any order: * - * @throws std::runtime_error If the file could not be opened. + * * osmium::io::Header: Optional header data. If this is + * not given, a default constructed osmium::io::Header + * object will be used. + * + * * osmium::io::overwrite: Allow overwriting of existing file? + * Can be osmium::io::overwrite::allow or + * osmium::io::overwrite::no (default). + * + * * osmium::io::fsync: Should fsync be called on the file + * before closing it? Can be osmium::io::fsync::yes or + * osmium::io::fsync::no (default). + * + * @throws osmium::io_error If there was an error. * @throws std::system_error If the file could not be opened. */ - explicit Writer(const osmium::io::File& file, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) : - m_file(file), + template + explicit Writer(const osmium::io::File& file, TArgs&&... args) : + m_file(file.check()), m_output_queue(20, "raw_output"), // XXX m_output(osmium::io::detail::OutputFormatFactory::instance().create_output(m_file, m_output_queue)), - m_compressor(osmium::io::CompressionFactory::instance().create_compressor(file.compression(), osmium::io::detail::open_for_writing(m_file.filename(), allow_overwrite))), - m_write_future(std::async(std::launch::async, detail::WriteThread(m_output_queue, m_compressor.get()))) { - assert(!m_file.buffer()); - m_output->write_header(header); + m_buffer(), + m_buffer_size(default_buffer_size), + m_write_future(), + m_thread(), + m_status(status::okay) { + assert(!m_file.buffer()); // XXX can't handle pseudo-files + + options_type options; + (void)std::initializer_list{ + (set_option(options, args), 0)... + }; + + std::unique_ptr compressor = + CompressionFactory::instance().create_compressor(file.compression(), + osmium::io::detail::open_for_writing(m_file.filename(), options.allow_overwrite), + options.sync); + + std::promise write_promise; + m_write_future = write_promise.get_future(); + m_thread = osmium::thread::thread_handler{write_thread, std::ref(m_output_queue), std::move(compressor), std::move(write_promise)}; + + ensure_cleanup([&](){ + m_output->write_header(options.header); + }); } - explicit Writer(const std::string& filename, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) : - Writer(osmium::io::File(filename), header, allow_overwrite) { + template + explicit Writer(const std::string& filename, TArgs&&... args) : + Writer(osmium::io::File(filename), std::forward(args)...) { } - explicit Writer(const char* filename, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) : - Writer(osmium::io::File(filename), header, allow_overwrite) { + template + explicit Writer(const char* filename, TArgs&&... args) : + Writer(osmium::io::File(filename), std::forward(args)...) { } Writer(const Writer&) = delete; Writer& operator=(const Writer&) = delete; - ~Writer() { - close(); - } + Writer(Writer&&) = default; + Writer& operator=(Writer&&) = default; - /** - * Write contents of a buffer to the output file. - * - * @throws Some form of std::runtime_error when there is a problem. - */ - void operator()(osmium::memory::Buffer&& buffer) { - osmium::thread::check_for_exception(m_write_future); - if (buffer.committed() > 0) { - m_output->write_buffer(std::move(buffer)); + ~Writer() noexcept { + try { + close(); + } catch (...) { + // Ignore any exceptions because destructor must not throw. } } /** - * Flush writes to output file and closes it. If you do not - * call this, the destructor of Writer will also do the same - * thing. But because this call might thrown an exception, - * it is better to call close() explicitly. + * Get the currently configured size of the internal buffer. + */ + size_t buffer_size() const noexcept { + return m_buffer_size; + } + + /** + * Set the size of the internal buffer. This will only take effect + * if you have not yet written anything or after the next flush(). + */ + void set_buffer_size(size_t size) noexcept { + m_buffer_size = size; + } + + /** + * Flush the internal buffer if it contains any data. This is + * usually not needed as the buffer gets flushed on close() + * automatically. * - * @throws Some form of std::runtime_error when there is a problem. + * @throws Some form of osmium::io_error when there is a problem. + */ + void flush() { + ensure_cleanup([&](){ + do_flush(); + }); + } + + /** + * Write contents of a buffer to the output file. The buffer is + * moved into this function and will be in an undefined moved-from + * state afterwards. + * + * @param buffer Buffer that is being written out. + * @throws Some form of osmium::io_error when there is a problem. + */ + void operator()(osmium::memory::Buffer&& buffer) { + ensure_cleanup([&](){ + do_flush(); + do_write(std::move(buffer)); + }); + } + + /** + * Add item to the internal buffer for eventual writing to the + * output file. + * + * @param item Item to write (usually an OSM object). + * @throws Some form of osmium::io_error when there is a problem. + */ + void operator()(const osmium::memory::Item& item) { + ensure_cleanup([&](){ + if (!m_buffer) { + m_buffer = osmium::memory::Buffer{m_buffer_size, + osmium::memory::Buffer::auto_grow::no}; + } + try { + m_buffer.push_back(item); + } catch (osmium::buffer_is_full&) { + do_flush(); + m_buffer.push_back(item); + } + }); + } + + /** + * Flushes internal buffer and closes output file. If you do not + * call this, the destructor of Writer will also do the same + * thing. But because this call might throw an exception, which + * the destructor will ignore, it is better to call close() + * explicitly. + * + * @throws Some form of osmium::io_error when there is a problem. */ void close() { - m_output->close(); - osmium::thread::wait_until_done(m_write_future); + if (m_status == status::okay) { + ensure_cleanup([&](){ + do_write(std::move(m_buffer)); + m_output->write_end(); + m_status = status::closed; + detail::add_end_of_data_to_queue(m_output_queue); + }); + } + + if (m_write_future.valid()) { + m_write_future.get(); + } } }; // class Writer diff --git a/third_party/libosmium/include/osmium/io/writer_options.hpp b/third_party/libosmium/include/osmium/io/writer_options.hpp new file mode 100644 index 000000000..9f6416424 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/writer_options.hpp @@ -0,0 +1,60 @@ +#ifndef OSMIUM_IO_WRITER_OPTIONS_HPP +#define OSMIUM_IO_WRITER_OPTIONS_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +namespace osmium { + + namespace io { + + /** + * Allow overwriting of existing file? + */ + enum class overwrite : bool { + no = false, + allow = true + }; + + /** + * Should writer do an fsync before closing the file? + */ + enum class fsync : bool { + no = false, + yes = true + }; + + } // namespace io + +} // namespace osmium + +#endif // OSMIUM_IO_WRITER_OPTIONS_HPP diff --git a/third_party/libosmium/include/osmium/io/xml_input.hpp b/third_party/libosmium/include/osmium/io/xml_input.hpp index dfcd0a9fc..87f4ecc81 100644 --- a/third_party/libosmium/include/osmium/io/xml_input.hpp +++ b/third_party/libosmium/include/osmium/io/xml_input.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/io/xml_output.hpp b/third_party/libosmium/include/osmium/io/xml_output.hpp index 18a13869d..344487d5d 100644 --- a/third_party/libosmium/include/osmium/io/xml_output.hpp +++ b/third_party/libosmium/include/osmium/io/xml_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/memory/buffer.hpp b/third_party/libosmium/include/osmium/memory/buffer.hpp index d800c685c..07a31fed9 100644 --- a/third_party/libosmium/include/osmium/memory/buffer.hpp +++ b/third_party/libosmium/include/osmium/memory/buffer.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,13 +39,14 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include -#include #include #include #include +#include namespace osmium { @@ -89,12 +90,17 @@ namespace osmium { * * By default, if a buffer gets full it will throw a buffer_is_full exception. * You can use the set_full_callback() method to set a callback functor - * which will be called instead of throwing an exception. + * which will be called instead of throwing an exception. The full + * callback functionality is deprecated and will be removed in the + * future. See the documentation for set_full_callback() for alternatives. */ class Buffer { public: + // This is needed so we can call std::back_inserter() on a Buffer. + using value_type = Item; + enum class auto_grow : bool { yes = true, no = false @@ -102,7 +108,7 @@ namespace osmium { private: - std::vector m_memory; + std::unique_ptr m_memory; unsigned char* m_data; size_t m_capacity; size_t m_written; @@ -112,12 +118,13 @@ namespace osmium { public: - typedef Item value_type; - /** - * The constructor without any parameters creates a non-initialized + * The constructor without any parameters creates an invalid, * buffer, ie an empty hull of a buffer that has no actual memory - * associated with it. It can be used to signify end-of-input. + * associated with it. It can be used to signify end-of-data. + * + * Most methods of the Buffer class will not work with an invalid + * buffer. */ Buffer() noexcept : m_memory(), @@ -128,12 +135,14 @@ namespace osmium { } /** - * Constructs an externally memory-managed buffer using the given - * memory and size. + * Constructs a valid externally memory-managed buffer using the + * given memory and size. * * @param data A pointer to some already initialized data. * @param size The size of the initialized data. - * @throws std::invalid_argument When the size isn't a multiple of the alignment. + * + * @throws std::invalid_argument if the size isn't a multiple of + * the alignment. */ explicit Buffer(unsigned char* data, size_t size) : m_memory(), @@ -147,13 +156,15 @@ namespace osmium { } /** - * Constructs an externally memory-managed buffer with the given - * capacity that already contains 'committed' bytes of data. + * Constructs a valid externally memory-managed buffer with the + * given capacity that already contains 'committed' bytes of data. * * @param data A pointer to some (possibly initialized) data. * @param capacity The size of the memory for this buffer. * @param committed The size of the initialized data. If this is 0, the buffer startes out empty. - * @throws std::invalid_argument When the capacity or committed isn't a multiple of the alignment. + * + * @throws std::invalid_argument if the capacity or committed isn't + * a multiple of the alignment. */ explicit Buffer(unsigned char* data, size_t capacity, size_t committed) : m_memory(), @@ -170,14 +181,22 @@ namespace osmium { } /** - * Create an internally memory-managed buffer with the given capacity. - * different in that it internally gets dynamic memory of the - * required size. The dynamic memory will be automatically - * freed when the Buffer is destroyed. + * Constructs a valid internally memory-managed buffer with the + * given capacity. + * Will internally get dynamic memory of the required size. + * The dynamic memory will be automatically freed when the Buffer + * is destroyed. + * + * @param capacity The (initial) size of the memory for this buffer. + * @param auto_grow Should this buffer automatically grow when it + * becomes to small? + * + * @throws std::invalid_argument if the capacity isn't a multiple + * of the alignment. */ explicit Buffer(size_t capacity, auto_grow auto_grow = auto_grow::yes) : - m_memory(capacity), - m_data(m_memory.data()), + m_memory(new unsigned char[capacity]), + m_data(m_memory.get()), m_capacity(capacity), m_written(0), m_committed(0), @@ -199,13 +218,17 @@ namespace osmium { /** * Return a pointer to data inside the buffer. + * + * @pre The buffer must be valid. */ unsigned char* data() const noexcept { + assert(m_data); return m_data; } /** - * Returns the capacity of the buffer, ie how many bytes it can contain. + * Returns the capacity of the buffer, ie how many bytes it can + * contain. Always returns 0 on invalid buffers. */ size_t capacity() const noexcept { return m_capacity; @@ -213,6 +236,7 @@ namespace osmium { /** * Returns the number of bytes already filled in this buffer. + * Always returns 0 on invalid buffers. */ size_t committed() const noexcept { return m_committed; @@ -221,6 +245,7 @@ namespace osmium { /** * Returns the number of bytes currently filled in this buffer that * are not yet committed. + * Always returns 0 on invalid buffers. */ size_t written() const noexcept { return m_written; @@ -229,37 +254,70 @@ namespace osmium { /** * This tests if the current state of the buffer is aligned * properly. Can be used for asserts. + * + * @pre The buffer must be valid. */ bool is_aligned() const noexcept { + assert(m_data); return (m_written % align_bytes == 0) && (m_committed % align_bytes == 0); } /** * Set functor to be called whenever the buffer is full * instead of throwing buffer_is_full. + * + * The behaviour is undefined if you call this on an invalid + * buffer. + * + * @pre The buffer must be valid. + * + * @deprecated + * Callback functionality will be removed in the future. Either + * detect the buffer_is_full exception or use a buffer with + * auto_grow::yes. If you want to avoid growing buffers, check + * that the used size of the buffer (committed()) is small enough + * compared to the capacity (for instance small than 90% of the + * capacity) before adding anything to the Buffer. If the buffer + * is initialized with auto_grow::yes, it will still grow in the + * rare case that a very large object will be added taking more + * than the difference between committed() and capacity(). */ - void set_full_callback(std::function full) { + OSMIUM_DEPRECATED void set_full_callback(std::function full) { + assert(m_data); m_full = full; } /** * Grow capacity of this buffer to the given size. * This works only with internally memory-managed buffers. - * If the given size is not larger than the current capacity, nothing is done. + * If the given size is not larger than the current capacity, + * nothing is done. * Already written but not committed data is discarded. * + * @pre The buffer must be valid. + * * @param size New capacity. + * + * @throws std::logic_error if the buffer doesn't use internal + * memory management. + * @throws std::invalid_argument if the size isn't a multiple + * of the alignment. + * @throws std::bad_alloc if there isn't enough memory available. */ void grow(size_t size) { - if (m_memory.empty()) { + assert(m_data); + if (!m_memory) { throw std::logic_error("Can't grow Buffer if it doesn't use internal memory management."); } if (m_capacity < size) { if (size % align_bytes != 0) { throw std::invalid_argument("buffer capacity needs to be multiple of alignment"); } - m_memory.resize(size); - m_data = m_memory.data(); + std::unique_ptr memory(new unsigned char[size]); + std::copy_n(m_memory.get(), m_capacity, memory.get()); + using std::swap; + swap(m_memory, memory); + m_data = m_memory.get(); m_capacity = size; } } @@ -267,9 +325,15 @@ namespace osmium { /** * Mark currently written bytes in the buffer as committed. * - * @returns Last number of committed bytes before this commit. + * @pre The buffer must be valid and aligned properly (as indicated + * by is_aligned(). + * + * @returns Number of committed bytes before this commit. Can be + * used as an offset into the buffer to get to the + * object being committed by this call. */ size_t commit() { + assert(m_data); assert(is_aligned()); const size_t offset = m_committed; @@ -279,14 +343,19 @@ namespace osmium { /** * Roll back changes in buffer to last committed state. + * + * @pre The buffer must be valid. */ void rollback() { + assert(m_data); m_written = m_committed; } /** * Clear the buffer. * + * No-op on an invalid buffer. + * * @returns Number of bytes in the buffer before it was cleared. */ size_t clear() { @@ -299,11 +368,16 @@ namespace osmium { /** * Get the data in the buffer at the given offset. * + * @pre The buffer must be valid. + * * @tparam T Type we want to the data to be interpreted as. - * @returns Reference of given type pointing to the data in the buffer. + * + * @returns Reference of given type pointing to the data in the + * buffer. */ - template + template T& get(const size_t offset) const { + assert(m_data); return *reinterpret_cast(&m_data[offset]); } @@ -320,23 +394,35 @@ namespace osmium { * * * If you have set a callback with set_full_callback(), it is * called. After the call returns, you must have either grown - * the buffer or cleared it by calling buffer.clear(). + * the buffer or cleared it by calling buffer.clear(). (Usage + * of the full callback is deprecated and this functionality + * will be removed in the future. See the documentation for + * set_full_callback() for alternatives. * * If no callback is defined and this buffer uses internal * memory management, the buffers capacity is grown, so that * the new data will fit. * * Else the buffer_is_full exception is thrown. * + * @pre The buffer must be valid. + * * @param size Number of bytes to reserve. + * * @returns Pointer to reserved space. Note that this pointer is - * only guaranteed to be valid until the next call to - * reserve_space(). - * @throws osmium::buffer_is_full Might be thrown if the buffer is full. + * only guaranteed to be valid until the next call to + * reserve_space(). + * + * @throws osmium::buffer_is_full if the buffer is full there is + * no callback defined and the buffer isn't auto-growing. */ unsigned char* reserve_space(const size_t size) { + assert(m_data); + // try to flush the buffer empty first. + if (m_written + size > m_capacity && m_full) { + m_full(*this); + } + // if there's still not enough space, then try growing the buffer. if (m_written + size > m_capacity) { - if (m_full) { - m_full(*this); - } else if (!m_memory.empty() && (m_auto_grow == auto_grow::yes)) { + if (m_memory && (m_auto_grow == auto_grow::yes)) { // double buffer size until there is enough space size_t new_capacity = m_capacity * 2; while (m_written + size > new_capacity) { @@ -359,12 +445,17 @@ namespace osmium { * Note that you have to eventually call commit() to actually * commit this data. * + * @pre The buffer must be valid. + * * @tparam T Class of the item to be copied. + * * @param item Reference to the item to be copied. + * * @returns Reference to newly copied data in the buffer. */ - template + template T& add_item(const T& item) { + assert(m_data); unsigned char* target = reserve_space(item.padded_size()); std::copy_n(reinterpret_cast(&item), item.padded_size(), target); return *reinterpret_cast(target); @@ -373,91 +464,176 @@ namespace osmium { /** * Add committed contents of the given buffer to this buffer. * + * @pre The buffer must be valid. + * * Note that you have to eventually call commit() to actually * commit this data. + * + * @param buffer The source of the copy. Must be valid. */ void add_buffer(const Buffer& buffer) { + assert(m_data && buffer); unsigned char* target = reserve_space(buffer.committed()); - std::copy_n(reinterpret_cast(buffer.data()), buffer.committed(), target); + std::copy_n(buffer.data(), buffer.committed(), target); } /** * Add an item to the buffer. This function is provided so that * you can use std::back_inserter. + * + * @pre The buffer must be valid. + * + * @param item The item to be added. */ void push_back(const osmium::memory::Item& item) { + assert(m_data); add_item(item); commit(); } /** - * These iterators can be used to iterate over all items in - * a buffer. + * An iterator that can be used to iterate over all items of + * type T in a buffer. */ - template + template using t_iterator = osmium::memory::ItemIterator; - template + /** + * A const iterator that can be used to iterate over all items of + * type T in a buffer. + */ + template using t_const_iterator = osmium::memory::ItemIterator; - typedef t_iterator iterator; - typedef t_const_iterator const_iterator; + /** + * An iterator that can be used to iterate over all OSMEntity + * objects in a buffer. + */ + using iterator = t_iterator; - template + /** + * A const iterator that can be used to iterate over all OSMEntity + * objects in a buffer. + */ + using const_iterator = t_const_iterator; + + /** + * Get iterator for iterating over all items of type T in the + * buffer. + * + * @pre The buffer must be valid. + * + * @returns Iterator to first item of type T in the buffer. + */ + template t_iterator begin() { + assert(m_data); return t_iterator(m_data, m_data + m_committed); } + /** + * Get iterator for iterating over all objects of class OSMEntity + * in the buffer. + * + * @pre The buffer must be valid. + * + * @returns Iterator to first OSMEntity in the buffer. + */ iterator begin() { + assert(m_data); return iterator(m_data, m_data + m_committed); } - template + /** + * Get iterator for iterating over all items of type T in the + * buffer. + * + * @pre The buffer must be valid. + * + * @returns Iterator to first item of type T after given offset + * in the buffer. + */ + template t_iterator get_iterator(size_t offset) { + assert(m_data); return t_iterator(m_data + offset, m_data + m_committed); } + /** + * Get iterator for iterating over all objects of class OSMEntity + * in the buffer. + * + * @pre The buffer must be valid. + * + * @returns Iterator to first OSMEntity after given offset in the + * buffer. + */ iterator get_iterator(size_t offset) { + assert(m_data); return iterator(m_data + offset, m_data + m_committed); } - template + /** + * Get iterator for iterating over all items of type T in the + * buffer. + * + * @pre The buffer must be valid. + * + * @returns End iterator. + */ + template t_iterator end() { + assert(m_data); return t_iterator(m_data + m_committed, m_data + m_committed); } + /** + * Get iterator for iterating over all objects of class OSMEntity + * in the buffer. + * + * @pre The buffer must be valid. + * + * @returns End iterator. + */ iterator end() { + assert(m_data); return iterator(m_data + m_committed, m_data + m_committed); } - template + template t_const_iterator cbegin() const { + assert(m_data); return t_const_iterator(m_data, m_data + m_committed); } const_iterator cbegin() const { + assert(m_data); return const_iterator(m_data, m_data + m_committed); } - template + template t_const_iterator get_iterator(size_t offset) const { + assert(m_data); return t_const_iterator(m_data + offset, m_data + m_committed); } const_iterator get_iterator(size_t offset) const { + assert(m_data); return const_iterator(m_data + offset, m_data + m_committed); } - template + template t_const_iterator cend() const { + assert(m_data); return t_const_iterator(m_data + m_committed, m_data + m_committed); } const_iterator cend() const { + assert(m_data); return const_iterator(m_data + m_committed, m_data + m_committed); } - template + template t_const_iterator begin() const { return cbegin(); } @@ -466,7 +642,7 @@ namespace osmium { return cbegin(); } - template + template t_const_iterator end() const { return cend(); } @@ -476,20 +652,22 @@ namespace osmium { } /** - * In a bool context any initialized buffer is true. + * In a bool context any valid buffer is true. */ - explicit operator bool() const { + explicit operator bool() const noexcept { return m_data != nullptr; } - friend void swap(Buffer& lhs, Buffer& rhs) { + void swap(Buffer& other) { using std::swap; - swap(lhs.m_memory, rhs.m_memory); - swap(lhs.m_data, rhs.m_data); - swap(lhs.m_capacity, rhs.m_capacity); - swap(lhs.m_written, rhs.m_written); - swap(lhs.m_committed, rhs.m_committed); + swap(m_memory, other.m_memory); + swap(m_data, other.m_data); + swap(m_capacity, other.m_capacity); + swap(m_written, other.m_written); + swap(m_committed, other.m_committed); + swap(m_auto_grow, other.m_auto_grow); + swap(m_full, other.m_full); } /** @@ -497,17 +675,20 @@ namespace osmium { * non-removed items forward in the buffer overwriting removed * items and then correcting the m_written and m_committed numbers. * - * Note that calling this function invalidates all iterators on this - * buffer and all offsets in this buffer. + * Note that calling this function invalidates all iterators on + * this buffer and all offsets in this buffer. * * For every non-removed item that moves its position, the function * 'moving_in_buffer' is called on the given callback object with * the old and new offsets in the buffer where the object used to * be and is now, respectively. This call can be used to update any * indexes. + * + * @pre The buffer must be valid. */ - template + template void purge_removed(TCallbackClass* callback) { + assert(m_data); if (begin() == end()) { return; } @@ -537,7 +718,21 @@ namespace osmium { }; // class Buffer + inline void swap(Buffer& lhs, Buffer& rhs) { + lhs.swap(rhs); + } + + /** + * Compare two buffers for equality. + * + * Buffers are equal if they are both invalid or if they are both + * valid and have the same data pointer, capacity and committed + * data. + */ inline bool operator==(const Buffer& lhs, const Buffer& rhs) noexcept { + if (!lhs || !rhs) { + return !lhs && !rhs; + } return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed(); } diff --git a/third_party/libosmium/include/osmium/memory/collection.hpp b/third_party/libosmium/include/osmium/memory/collection.hpp index 5cf3cc6e1..17ace7025 100644 --- a/third_party/libosmium/include/osmium/memory/collection.hpp +++ b/third_party/libosmium/include/osmium/memory/collection.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,7 +43,7 @@ namespace osmium { namespace memory { - template + template class CollectionIterator : public std::iterator { // This data_type is either 'unsigned char*' or 'const unsigned char*' depending @@ -59,7 +59,7 @@ namespace osmium { m_data(nullptr) { } - CollectionIterator(data_type data) noexcept : + explicit CollectionIterator(data_type data) noexcept : m_data(data) { } @@ -95,13 +95,19 @@ namespace osmium { } template - friend std::basic_ostream& operator<<(std::basic_ostream& out, const CollectionIterator& iter) { - return out << static_cast(iter.m_data); + void print(std::basic_ostream& out) const { + out << static_cast(m_data); } }; // class CollectionIterator - template + template + inline std::basic_ostream& operator<<(std::basic_ostream& out, const CollectionIterator& iter) { + iter.print(out); + return out; + } + + template class Collection : public Item { public: diff --git a/third_party/libosmium/include/osmium/memory/item.hpp b/third_party/libosmium/include/osmium/memory/item.hpp index dc544049a..fd404ce93 100644 --- a/third_party/libosmium/include/osmium/memory/item.hpp +++ b/third_party/libosmium/include/osmium/memory/item.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,7 +43,7 @@ namespace osmium { namespace builder { class Builder; - } + } // namespace builder namespace memory { @@ -102,10 +102,10 @@ namespace osmium { uint16_t m_removed : 1; uint16_t m_padding : 15; - template + template friend class CollectionIterator; - template + template friend class ItemIterator; friend class osmium::builder::Builder; diff --git a/third_party/libosmium/include/osmium/memory/item_iterator.hpp b/third_party/libosmium/include/osmium/memory/item_iterator.hpp index 3e5b5fa8b..3886c9870 100644 --- a/third_party/libosmium/include/osmium/memory/item_iterator.hpp +++ b/third_party/libosmium/include/osmium/memory/item_iterator.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -38,29 +38,17 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include namespace osmium { - class Node; - class Way; - class Relation; - class Area; - class Changeset; - class OSMObject; - class OSMEntity; - class TagList; - class WayNodeList; - class RelationMemberList; - class InnerRing; - class OuterRing; - namespace memory { namespace detail { - template + template inline bool type_is_compatible(osmium::item_type) noexcept { return true; } @@ -127,7 +115,7 @@ namespace osmium { } // namespace detail - template + template class ItemIterator : public std::iterator { static_assert(std::is_base_of::value, "TMember must derive from osmium::memory::Item"); @@ -160,7 +148,7 @@ namespace osmium { advance_to_next_item_of_right_type(); } - template + template ItemIterator cast() const { return ItemIterator(m_data, m_end); } @@ -217,16 +205,22 @@ namespace osmium { } explicit operator bool() const { - return m_data != nullptr; + return (m_data != nullptr) && (m_data != m_end); } template - friend std::basic_ostream& operator<<(std::basic_ostream& out, const ItemIterator& iter) { - return out << static_cast(iter.m_data); + void print(std::basic_ostream& out) const { + out << static_cast(m_data); } }; // class ItemIterator + template + inline std::basic_ostream& operator<<(std::basic_ostream& out, const ItemIterator& iter) { + iter.print(out); + return out; + } + } // namespace memory } // namespace osmium diff --git a/third_party/libosmium/include/osmium/object_pointer_collection.hpp b/third_party/libosmium/include/osmium/object_pointer_collection.hpp index 752470305..09a52934e 100644 --- a/third_party/libosmium/include/osmium/object_pointer_collection.hpp +++ b/third_party/libosmium/include/osmium/object_pointer_collection.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -84,7 +84,7 @@ namespace osmium { /** * Sort objects according to the given order functor. */ - template + template void sort(TCompare&& compare) { std::sort(m_objects.begin(), m_objects.end(), std::forward(compare)); } diff --git a/third_party/libosmium/include/osmium/osm.hpp b/third_party/libosmium/include/osmium/osm.hpp index e92d9b8ef..594db7572 100644 --- a/third_party/libosmium/include/osmium/osm.hpp +++ b/third_party/libosmium/include/osmium/osm.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/osm/area.hpp b/third_party/libosmium/include/osmium/osm/area.hpp index 3e129d0fb..ee232f0bd 100644 --- a/third_party/libosmium/include/osmium/osm/area.hpp +++ b/third_party/libosmium/include/osmium/osm/area.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -48,7 +48,7 @@ namespace osmium { namespace builder { template class ObjectBuilder; - } + } // namespace builder /** * An outer ring of an Area. @@ -167,6 +167,7 @@ namespace osmium { case osmium::item_type::way_node_list: case osmium::item_type::relation_member_list: case osmium::item_type::relation_member_list_with_full_members: + case osmium::item_type::changeset_discussion: assert(false && "Children of Area can only be outer/inner_ring and tag_list."); break; } diff --git a/third_party/libosmium/include/osmium/osm/box.hpp b/third_party/libosmium/include/osmium/osm/box.hpp index 631f91911..6fcf48d6c 100644 --- a/third_party/libosmium/include/osmium/osm/box.hpp +++ b/third_party/libosmium/include/osmium/osm/box.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -100,15 +100,15 @@ namespace osmium { /** * Extend this bounding box by the specified location. If the - * location is undefined, the bounding box is unchanged. If - * the box is undefined it will only contain the location after + * location is invalid, the bounding box is unchanged. If the + * box is undefined it will only contain the new location after * this call. * * @param location The location we want to extend the box by. * @returns A reference to this box. */ Box& extend(const Location& location) noexcept { - if (location) { + if (location.valid()) { if (m_bottom_left) { if (location.x() < m_bottom_left.x()) { m_bottom_left.set_x(location.x()); @@ -147,21 +147,21 @@ namespace osmium { * Box is defined, ie. contains defined locations. */ explicit constexpr operator bool() const noexcept { - return static_cast(m_bottom_left); + return bool(m_bottom_left) && bool(m_top_right); } /** * Box is valid, ie. defined and inside usual bounds * (-180<=lon<=180, -90<=lat<=90). */ - OSMIUM_CONSTEXPR bool valid() const noexcept { + constexpr bool valid() const noexcept { return bottom_left().valid() && top_right().valid(); } /** * Access bottom-left location. */ - OSMIUM_CONSTEXPR Location bottom_left() const noexcept { + constexpr Location bottom_left() const noexcept { return m_bottom_left; } @@ -175,7 +175,7 @@ namespace osmium { /** * Access top-right location. */ - OSMIUM_CONSTEXPR Location top_right() const noexcept { + constexpr Location top_right() const noexcept { return m_top_right; } @@ -203,6 +203,9 @@ namespace osmium { /** * Calculate size of the box in square degrees. * + * Note that this measure isn't very useful if you want to know the + * real-world size of the bounding box! + * * @throws osmium::invalid_location unless all coordinates are valid. */ double size() const { @@ -216,7 +219,7 @@ namespace osmium { * Boxes are equal if both locations are equal. Undefined boxes will * compare equal. */ - inline OSMIUM_CONSTEXPR bool operator==(const Box& lhs, const Box& rhs) noexcept { + inline constexpr bool operator==(const Box& lhs, const Box& rhs) noexcept { return lhs.bottom_left() == rhs.bottom_left() && lhs.top_right() == rhs.top_right(); } diff --git a/third_party/libosmium/include/osmium/osm/changeset.hpp b/third_party/libosmium/include/osmium/osm/changeset.hpp index 07bc0dd95..f59db4808 100644 --- a/third_party/libosmium/include/osmium/osm/changeset.hpp +++ b/third_party/libosmium/include/osmium/osm/changeset.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -48,8 +48,102 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; - } + class ChangesetDiscussionBuilder; + template class ObjectBuilder; + } // namespace builder + + class Changeset; + + class ChangesetComment : public osmium::memory::detail::ItemHelper { + + friend class osmium::builder::ChangesetDiscussionBuilder; + + osmium::Timestamp m_date; + osmium::user_id_type m_uid {0}; + string_size_type m_user_size; + string_size_type m_text_size; + + ChangesetComment(const ChangesetComment&) = delete; + ChangesetComment(ChangesetComment&&) = delete; + + ChangesetComment& operator=(const ChangesetComment&) = delete; + ChangesetComment& operator=(ChangesetComment&&) = delete; + + unsigned char* endpos() { + return data() + osmium::memory::padded_length(sizeof(ChangesetComment) + m_user_size + m_text_size); + } + + const unsigned char* endpos() const { + return data() + osmium::memory::padded_length(sizeof(ChangesetComment) + m_user_size + m_text_size); + } + + template + friend class osmium::memory::CollectionIterator; + + unsigned char* next() { + return endpos(); + } + + unsigned const char* next() const { + return endpos(); + } + + void set_user_size(string_size_type size) noexcept { + m_user_size = size; + } + + void set_text_size(string_size_type size) noexcept { + m_text_size = size; + } + + public: + + static constexpr item_type collection_type = item_type::changeset_discussion; + + ChangesetComment(osmium::Timestamp date, osmium::user_id_type uid) noexcept : + m_date(date), + m_uid(uid), + m_user_size(0), + m_text_size(0) { + } + + osmium::Timestamp date() const noexcept { + return m_date; + } + + osmium::user_id_type uid() const noexcept { + return m_uid; + } + + const char* user() const noexcept { + return reinterpret_cast(data() + sizeof(ChangesetComment)); + } + + const char* text() const noexcept { + return reinterpret_cast(data() + sizeof(ChangesetComment) + m_user_size); + } + + }; // class ChangesetComment + + class ChangesetDiscussion : public osmium::memory::Collection { + + friend class osmium::builder::ObjectBuilder; + + public: + + typedef size_t size_type; + + ChangesetDiscussion() : + osmium::memory::Collection() { + } + + size_type size() const noexcept { + return static_cast(std::distance(begin(), end())); + } + + }; // class ChangesetDiscussion + + static_assert(sizeof(ChangesetDiscussion) % osmium::memory::align_bytes == 0, "Class osmium::ChangesetDiscussion has wrong size to be aligned properly!"); /** * \brief An OSM Changeset, a group of changes made by a single user over @@ -62,13 +156,16 @@ namespace osmium { friend class osmium::builder::ObjectBuilder; + osmium::Box m_bounds; osmium::Timestamp m_created_at; osmium::Timestamp m_closed_at; - osmium::Box m_bounds; changeset_id_type m_id {0}; num_changes_type m_num_changes {0}; + num_comments_type m_num_comments {0}; user_id_type m_uid {0}; string_size_type m_user_size; + int16_t m_padding1 {0}; + int32_t m_padding2 {0}; Changeset() : OSMEntity(sizeof(Changeset), osmium::item_type::changeset) { @@ -188,7 +285,7 @@ namespace osmium { * @param timestamp Timestamp * @returns Reference to changeset to make calls chainable. */ - Changeset& set_created_at(const osmium::Timestamp timestamp) { + Changeset& set_created_at(const osmium::Timestamp& timestamp) { m_created_at = timestamp; return *this; } @@ -199,7 +296,7 @@ namespace osmium { * @param timestamp Timestamp * @returns Reference to changeset to make calls chainable. */ - Changeset& set_closed_at(const osmium::Timestamp timestamp) { + Changeset& set_closed_at(const osmium::Timestamp& timestamp) { m_closed_at = timestamp; return *this; } @@ -216,10 +313,26 @@ namespace osmium { } /// Set the number of changes in this changeset - Changeset& set_num_changes(const char* num_changes) noexcept { + Changeset& set_num_changes(const char* num_changes) { return set_num_changes(osmium::string_to_num_changes(num_changes)); } + /// Get the number of comments in this changeset + num_comments_type num_comments() const noexcept { + return m_num_comments; + } + + /// Set the number of comments in this changeset + Changeset& set_num_comments(num_comments_type num_comments) noexcept { + m_num_comments = num_comments; + return *this; + } + + /// Set the number of comments in this changeset + Changeset& set_num_comments(const char* num_comments) { + return set_num_comments(osmium::string_to_num_comments(num_comments)); + } + /** * Get the bounding box of this changeset. * @@ -260,6 +373,8 @@ namespace osmium { set_id(value); } else if (!strcmp(attr, "num_changes")) { set_num_changes(value); + } else if (!strcmp(attr, "comments_count")) { + set_num_comments(value); } else if (!strcmp(attr, "created_at")) { set_created_at(osmium::Timestamp(value)); } else if (!strcmp(attr, "closed_at")) { @@ -296,6 +411,14 @@ namespace osmium { return cend(); } + ChangesetDiscussion& discussion() { + return osmium::detail::subitem_of_type(begin(), end()); + } + + const ChangesetDiscussion& discussion() const { + return osmium::detail::subitem_of_type(cbegin(), cend()); + } + }; // class Changeset static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0, "Class osmium::Changeset has wrong size to be aligned properly!"); diff --git a/third_party/libosmium/include/osmium/osm/crc.hpp b/third_party/libosmium/include/osmium/osm/crc.hpp index eefa4a13e..ff39996f5 100644 --- a/third_party/libosmium/include/osmium/osm/crc.hpp +++ b/third_party/libosmium/include/osmium/osm/crc.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,10 +46,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - template - class CRC { + namespace util { - static inline uint16_t byte_swap_16(uint16_t value) noexcept { + inline uint16_t byte_swap_16(uint16_t value) noexcept { # if defined(__GNUC__) || defined(__clang__) return __builtin_bswap16(value); # else @@ -57,27 +56,32 @@ namespace osmium { # endif } - static inline uint32_t byte_swap_32(uint32_t value) noexcept { + inline uint32_t byte_swap_32(uint32_t value) noexcept { # if defined(__GNUC__) || defined(__clang__) return __builtin_bswap32(value); # else return (value >> 24) | - ((value >> 8) & 0x0000FF00) | - ((value << 8) & 0x00FF0000) | + ((value >> 8) & 0x0000FF00) | + ((value << 8) & 0x00FF0000) | (value << 24); # endif } - static inline uint64_t byte_swap_64(uint64_t value) noexcept { + inline uint64_t byte_swap_64(uint64_t value) noexcept { # if defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(value); # else uint64_t val1 = byte_swap_32(value & 0xFFFFFFFF); uint64_t val2 = byte_swap_32(value >> 32); - return (val1 << 32) & val2; + return (val1 << 32) | val2; # endif } + } // namespace util + + template + class CRC { + TCRC m_crc; public: @@ -90,37 +94,37 @@ namespace osmium { return m_crc; } - void update_bool(bool value) { + void update_bool(const bool value) { m_crc.process_byte(value); } - void update_int8(uint8_t value) { + void update_int8(const uint8_t value) { m_crc.process_byte(value); } - void update_int16(uint16_t value) { + void update_int16(const uint16_t value) { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint16_t)); #else - uint16_t v = byte_swap_16(value); + uint16_t v = osmium::util::byte_swap_16(value); m_crc.process_bytes(&v, sizeof(uint16_t)); #endif } - void update_int32(uint32_t value) { + void update_int32(const uint32_t value) { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint32_t)); #else - uint32_t v = byte_swap_32(value); + uint32_t v = osmium::util::byte_swap_32(value); m_crc.process_bytes(&v, sizeof(uint32_t)); #endif } - void update_int64(uint64_t value) { + void update_int64(const uint64_t value) { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint64_t)); #else - uint64_t v = byte_swap_64(value); + uint64_t v = osmium::util::byte_swap_64(value); m_crc.process_bytes(&v, sizeof(uint64_t)); #endif } @@ -156,7 +160,10 @@ namespace osmium { } void update(const TagList& tags) { - m_crc.process_bytes(tags.data(), tags.byte_size()); + for (const Tag& tag : tags) { + update_string(tag.key()); + update_string(tag.value()); + } } void update(const osmium::RelationMember& member) { @@ -206,14 +213,26 @@ namespace osmium { } } + void update(const osmium::ChangesetDiscussion& discussion) { + for (const auto& comment : discussion) { + update(comment.date()); + update_int32(comment.uid()); + update_string(comment.user()); + update_string(comment.text()); + } + } + void update(const osmium::Changeset& changeset) { update_int64(changeset.id()); update(changeset.created_at()); update(changeset.closed_at()); update(changeset.bounds()); update_int32(changeset.num_changes()); + update_int32(changeset.num_comments()); update_int32(changeset.uid()); update_string(changeset.user()); + update(changeset.tags()); + update(changeset.discussion()); } }; // class CRC diff --git a/third_party/libosmium/include/osmium/osm/diff_object.hpp b/third_party/libosmium/include/osmium/osm/diff_object.hpp index 1e053fdda..609ab7453 100644 --- a/third_party/libosmium/include/osmium/osm/diff_object.hpp +++ b/third_party/libosmium/include/osmium/osm/diff_object.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,9 @@ DEALINGS IN THE SOFTWARE. */ +#include + +#include #include #include #include @@ -40,75 +43,158 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - class Node; - class Way; - class Relation; - + /** + * A DiffObject holds pointers to three OSMObjects, the current object, + * the previous, and the next. They always have the same type (Node, Way, + * or Relation) and the same ID, but may have different versions. + * + * It is used when iterating over OSM files with history data to make + * working with versioned OSM objects easier. Because you have access to + * the previous and next objects as well as the current one, comparisons + * between object versions is easy. + * + * If the current object is the first version available, the previous + * pointer must be the same as the current one. If the current object is + * the last version available, the next pointer must be the same as the + * current one. + * + * DiffObjects are immutable. + */ class DiffObject { - protected: - - osmium::OSMObject* m_prev; - osmium::OSMObject* m_curr; - osmium::OSMObject* m_next; + const osmium::OSMObject* m_prev; + const osmium::OSMObject* m_curr; + const osmium::OSMObject* m_next; public: + /** + * Default construct an empty DiffObject. Most methods of this class + * can not be called on empty DiffObjects. + */ DiffObject() noexcept : m_prev(nullptr), m_curr(nullptr), m_next(nullptr) { } - explicit DiffObject(osmium::OSMObject& prev, osmium::OSMObject& curr, osmium::OSMObject& next) noexcept : + /** + * Construct a non-empty DiffObject from the given OSMObjects. All + * OSMObjects must be of the same type (Node, Way, or Relation) and + * have the same ID. + */ + DiffObject(const osmium::OSMObject& prev, const osmium::OSMObject& curr, const osmium::OSMObject& next) noexcept : m_prev(&prev), m_curr(&curr), m_next(&next) { + assert(prev.type() == curr.type() && curr.type() == next.type()); + assert(prev.id() == curr.id() && curr.id() == next.id()); } - DiffObject(const DiffObject&) = default; - DiffObject& operator=(const DiffObject&) = default; - - DiffObject(DiffObject&&) = default; - DiffObject& operator=(DiffObject&&) = default; + /** + * Check whether the DiffObject was created empty. + */ + bool empty() const noexcept { + return m_prev == nullptr; + } + /** + * Get the previous object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& prev() const noexcept { + assert(m_prev && m_curr && m_next); return *m_prev; } + /** + * Get the current object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& curr() const noexcept { + assert(m_prev && m_curr && m_next); return *m_curr; } + /** + * Get the next object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& next() const noexcept { + assert(m_prev && m_curr && m_next); return *m_next; } + /** + * Is the current object version the first (with this type and ID)? + * + * @pre DiffObject must not be empty. + */ bool first() const noexcept { + assert(m_prev && m_curr && m_next); return m_prev == m_curr; } + /** + * Is the current object version the last (with this type and ID)? + * + * @pre DiffObject must not be empty. + */ bool last() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr == m_next; } + /** + * Return the type of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::item_type type() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->type(); } + /** + * Return the ID of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::object_id_type id() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->id(); } + /** + * Return the version of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::object_version_type version() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->version(); } + /** + * Return the changeset ID of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::changeset_id_type changeset() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->changeset(); } + /** + * Return the timestamp when the current object version was created. + * + * @pre DiffObject must not be empty. + */ const osmium::Timestamp start_time() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->timestamp(); } @@ -118,8 +204,11 @@ namespace osmium { * is valid. If this is the last version of the object, this will * return a special "end of time" timestamp that is guaranteed to * be larger than any normal timestamp. + * + * @pre DiffObject must not be empty. */ const osmium::Timestamp end_time() const noexcept { + assert(m_prev && m_curr && m_next); return last() ? osmium::end_of_time() : m_next->timestamp(); } @@ -129,8 +218,11 @@ namespace osmium { * * This is a bit more complex than you'd think, because we have to * handle the case properly where the start_time() == end_time(). + * + * @pre DiffObject must not be empty. */ bool is_between(const osmium::Timestamp& from, const osmium::Timestamp& to) const noexcept { + assert(m_prev && m_curr && m_next); return start_time() < to && ((start_time() != end_time() && end_time() > from) || (start_time() == end_time() && end_time() >= from)); @@ -138,45 +230,42 @@ namespace osmium { /** * Current object version is visible at the given timestamp. + * + * @pre DiffObject must not be empty. */ bool is_visible_at(const osmium::Timestamp& timestamp) const noexcept { + assert(m_prev && m_curr && m_next); return start_time() <= timestamp && end_time() > timestamp && m_curr->visible(); } }; // class DiffObject - template + template class DiffObjectDerived : public DiffObject { public: - DiffObjectDerived(T& prev, T& curr, T& next) noexcept : + DiffObjectDerived(const T& prev, const T& curr, const T& next) noexcept : DiffObject(prev, curr, next) { } - DiffObjectDerived(const DiffObjectDerived&) = default; - DiffObjectDerived& operator=(const DiffObjectDerived&) = default; - - DiffObjectDerived(DiffObjectDerived&&) = default; - DiffObjectDerived& operator=(DiffObjectDerived&&) = default; - const T& prev() const noexcept { - return *static_cast(m_prev); + return static_cast(DiffObject::prev()); } const T& curr() const noexcept { - return *static_cast(m_curr); + return static_cast(DiffObject::curr()); } const T& next() const noexcept { - return *static_cast(m_next); + return static_cast(DiffObject::next()); } }; // class DiffObjectDerived - typedef DiffObjectDerived DiffNode; - typedef DiffObjectDerived DiffWay; - typedef DiffObjectDerived DiffRelation; + using DiffNode = DiffObjectDerived; + using DiffWay = DiffObjectDerived; + using DiffRelation = DiffObjectDerived; } // namespace osmium diff --git a/third_party/libosmium/include/osmium/osm/entity.hpp b/third_party/libosmium/include/osmium/osm/entity.hpp index ce292c8d6..140a559bc 100644 --- a/third_party/libosmium/include/osmium/osm/entity.hpp +++ b/third_party/libosmium/include/osmium/osm/entity.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -41,7 +41,7 @@ namespace osmium { namespace detail { - template + template inline TSubitem& subitem_of_type(TIter it, TIter end) { for (; it != end; ++it) { if (it->type() == TSubitem::itemtype) { diff --git a/third_party/libosmium/include/osmium/osm/entity_bits.hpp b/third_party/libosmium/include/osmium/osm/entity_bits.hpp index 1c1cb8027..50b3e4cb0 100644 --- a/third_party/libosmium/include/osmium/osm/entity_bits.hpp +++ b/third_party/libosmium/include/osmium/osm/entity_bits.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/osm/item_type.hpp b/third_party/libosmium/include/osmium/osm/item_type.hpp index 54975e326..272b2dd4d 100644 --- a/third_party/libosmium/include/osmium/osm/item_type.hpp +++ b/third_party/libosmium/include/osmium/osm/item_type.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -53,13 +53,18 @@ namespace osmium { relation_member_list = 0x13, relation_member_list_with_full_members = 0x23, outer_ring = 0x40, - inner_ring = 0x41 + inner_ring = 0x41, + changeset_discussion = 0x80 }; // enum class item_type /** * Return item_type for index: * 0 -> node, 1 -> way, 2 -> relation + * + * @param i Index. Must be between 0 and 2. + * + * @returns Item type. */ inline item_type nwr_index_to_item_type(unsigned int i) noexcept { assert(i <= 2); @@ -69,6 +74,10 @@ namespace osmium { /** * Return index for item_type: * node -> 0, way -> 1, relation -> 2 + * + * @param type Item type. Must be node, way, or relation. + * + * @returns Index. */ inline unsigned int item_type_to_nwr_index(item_type type) noexcept { unsigned int i = static_cast(type); @@ -102,6 +111,8 @@ namespace osmium { return item_type::outer_ring; case 'I': return item_type::inner_ring; + case 'D': + return item_type::changeset_discussion; default: return item_type::undefined; } @@ -136,6 +147,8 @@ namespace osmium { return 'O'; case item_type::inner_ring: return 'I'; + case item_type::changeset_discussion: + return 'D'; } } @@ -165,6 +178,8 @@ namespace osmium { return "outer_ring"; case item_type::inner_ring: return "inner_ring"; + case item_type::changeset_discussion: + return "changeset_discussion"; } } #pragma GCC diagnostic pop diff --git a/third_party/libosmium/include/osmium/osm/location.hpp b/third_party/libosmium/include/osmium/osm/location.hpp index 0d4fdc13d..85f4b162a 100644 --- a/third_party/libosmium/include/osmium/osm/location.hpp +++ b/third_party/libosmium/include/osmium/osm/location.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -52,11 +52,11 @@ namespace osmium { */ struct invalid_location : public std::range_error { - invalid_location(const std::string& what) : + explicit invalid_location(const std::string& what) : std::range_error(what) { } - invalid_location(const char* what) : + explicit invalid_location(const char* what) : std::range_error(what) { } @@ -95,7 +95,7 @@ namespace osmium { return static_cast(std::round(c * coordinate_precision)); } - static OSMIUM_CONSTEXPR double fix_to_double(const int32_t c) noexcept { + static constexpr double fix_to_double(const int32_t c) noexcept { return static_cast(c) / coordinate_precision; } @@ -238,11 +238,11 @@ namespace osmium { /** * Locations are equal if both coordinates are equal. */ - inline OSMIUM_CONSTEXPR bool operator==(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator==(const Location& lhs, const Location& rhs) noexcept { return lhs.x() == rhs.x() && lhs.y() == rhs.y(); } - inline OSMIUM_CONSTEXPR bool operator!=(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator!=(const Location& lhs, const Location& rhs) noexcept { return ! (lhs == rhs); } @@ -251,19 +251,19 @@ namespace osmium { * the y coordinate. If either of the locations is * undefined the result is undefined. */ - inline OSMIUM_CONSTEXPR bool operator<(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator<(const Location& lhs, const Location& rhs) noexcept { return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x(); } - inline OSMIUM_CONSTEXPR bool operator>(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator>(const Location& lhs, const Location& rhs) noexcept { return rhs < lhs; } - inline OSMIUM_CONSTEXPR bool operator<=(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator<=(const Location& lhs, const Location& rhs) noexcept { return ! (rhs < lhs); } - inline OSMIUM_CONSTEXPR bool operator>=(const Location& lhs, const Location& rhs) noexcept { + inline constexpr bool operator>=(const Location& lhs, const Location& rhs) noexcept { return ! (lhs < rhs); } diff --git a/third_party/libosmium/include/osmium/osm/node.hpp b/third_party/libosmium/include/osmium/osm/node.hpp index 123bfc4fa..677ffc784 100644 --- a/third_party/libosmium/include/osmium/osm/node.hpp +++ b/third_party/libosmium/include/osmium/osm/node.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -41,8 +41,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; - } + template class ObjectBuilder; + } // namespace builder class Node : public OSMObject { diff --git a/third_party/libosmium/include/osmium/osm/node_ref.hpp b/third_party/libosmium/include/osmium/osm/node_ref.hpp index 72359cd0f..05b9ca711 100644 --- a/third_party/libosmium/include/osmium/osm/node_ref.hpp +++ b/third_party/libosmium/include/osmium/osm/node_ref.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,15 +54,21 @@ namespace osmium { public: - NodeRef(const osmium::object_id_type ref = 0, const osmium::Location& location = Location()) noexcept : + constexpr NodeRef(const osmium::object_id_type ref = 0, const osmium::Location& location = Location()) noexcept : m_ref(ref), m_location(location) { } - osmium::object_id_type ref() const noexcept { + /** + * Get reference ID of this NodeRef. + */ + constexpr osmium::object_id_type ref() const noexcept { return m_ref; } + /** + * Get absolute value of the reference ID of this NodeRef. + */ osmium::unsigned_object_id_type positive_ref() const noexcept { return static_cast(std::abs(m_ref)); } @@ -74,31 +80,60 @@ namespace osmium { return m_location; } - osmium::Location location() const noexcept { + /** + * Get location of this NodeRef. + */ + constexpr osmium::Location location() const noexcept { return m_location; } + /** + * Get longitude of the location in this NodeRef. + * + * @throws osmium::invalid_location if the location is not set. + */ double lon() const { return m_location.lon(); } + /** + * Get latitude of the location in this NodeRef. + * + * @throws osmium::invalid_location if the location is not set. + */ double lat() const { return m_location.lat(); } - int32_t x() const noexcept { + /** + * Get internal x value of the location in this NodeRef. + */ + constexpr int32_t x() const noexcept { return m_location.x(); } - int32_t y() const noexcept { + /** + * Get internal y value of the location in this NodeRef. + */ + constexpr int32_t y() const noexcept { return m_location.y(); } + /** + * Set the referenced ID. + * + * @returns Reference to this NodeRef for chaining calls. + */ NodeRef& set_ref(const osmium::object_id_type ref) noexcept { m_ref = ref; return *this; } + /** + * Set the location. + * + * @returns Reference to this NodeRef for chaining calls. + */ NodeRef& set_location(const osmium::Location& location) noexcept { m_location = location; return *this; @@ -106,27 +141,50 @@ namespace osmium { }; // class NodeRef - inline bool operator==(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. They are equal if they reference the same Node ID. + */ + inline constexpr bool operator==(const NodeRef& lhs, const NodeRef& rhs) noexcept { return lhs.ref() == rhs.ref(); } - inline bool operator!=(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. They are not equal if they reference different + * Node IDs. + */ + inline constexpr bool operator!=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (lhs == rhs); } - inline bool operator<(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ + inline constexpr bool operator<(const NodeRef& lhs, const NodeRef& rhs) noexcept { return lhs.ref() < rhs.ref(); } - inline bool operator>(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ + inline constexpr bool operator>(const NodeRef& lhs, const NodeRef& rhs) noexcept { return rhs < lhs; } - inline bool operator<=(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ + inline constexpr bool operator<=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (rhs < lhs); } - inline bool operator>=(const NodeRef& lhs, const NodeRef& rhs) noexcept { + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ + inline constexpr bool operator>=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (lhs < rhs); } @@ -139,32 +197,32 @@ namespace osmium { } /** - * Functor to compare NodeRefs by Location instead of id. + * Functor to compare NodeRefs by Location instead of ID. */ struct location_equal { - bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept { + constexpr bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept { return lhs.location() == rhs.location(); } - typedef NodeRef first_argument_type; - typedef NodeRef second_argument_type; - typedef bool result_type; + using first_argument_type = NodeRef; + using second_argument_type = NodeRef; + using result_type = bool; }; // struct location_equal /** - * Functor to compare NodeRefs by Location instead of id. + * Functor to compare NodeRefs by Location instead of ID. */ struct location_less { - bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept { + constexpr bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept { return lhs.location() < rhs.location(); } - typedef NodeRef first_argument_type; - typedef NodeRef second_argument_type; - typedef bool result_type; + using first_argument_type = NodeRef; + using second_argument_type = NodeRef; + using result_type = bool; }; // struct location_less diff --git a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp index f0dfedbc1..84edc0760 100644 --- a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp +++ b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -44,29 +44,29 @@ DEALINGS IN THE SOFTWARE. namespace osmium { /** - * A vector of NodeRef objects. Usually this is not instantiated directly, - * but one of its subclasses are used. + * An ordered collection of NodeRef objects. Usually this is not + * instantiated directly, but one of its subclasses are used. */ class NodeRefList : public osmium::memory::Item { public: - NodeRefList(osmium::item_type itemtype) noexcept : + explicit NodeRefList(osmium::item_type itemtype) noexcept : osmium::memory::Item(sizeof(NodeRefList), itemtype) { } /** - * Checks whether the node list is empty. + * Checks whether the collection is empty. */ bool empty() const noexcept { return sizeof(NodeRefList) == byte_size(); } /** - * Returns the number of nodes in the list. + * Returns the number of NodeRefs in the collection. */ size_t size() const noexcept { - auto size_node_refs = osmium::memory::Item::byte_size() - sizeof(NodeRefList); + auto size_node_refs = byte_size() - sizeof(NodeRefList); assert(size_node_refs % sizeof(NodeRef) == 0); return size_node_refs / sizeof(NodeRef); } @@ -74,8 +74,9 @@ namespace osmium { /** * Access specified element. * - * @param n Get this element of the list. * @pre @code n < size() @endcode + * + * @param n Get the n-th element of the collection. */ const NodeRef& operator[](size_t n) const noexcept { assert(n < size()); @@ -104,16 +105,18 @@ namespace osmium { } /** - * Checks whether the first and last node in the list have the same ID. + * Checks whether the first and last node in the collection have the + * same ID. The locations are not checked. * * @pre @code !empty() @endcode */ bool is_closed() const noexcept { - return front().ref() == back().ref(); + return ends_have_same_id(); } /** - * Checks whether the first and last node in the list have the same ID. + * Checks whether the first and last node in the collection have the + * same ID. The locations are not checked. * * @pre @code !empty() @endcode */ @@ -122,8 +125,8 @@ namespace osmium { } /** - * Checks whether the first and last node in the list have the same - * location. The ID is not checked. + * Checks whether the first and last node in the collection have the + * same location. The IDs are not checked. * * @pre @code !empty() @endcode * @pre @code front().location() && back().location() @endcode @@ -133,9 +136,9 @@ namespace osmium { return front().location() == back().location(); } - typedef NodeRef* iterator; - typedef const NodeRef* const_iterator; - typedef std::reverse_iterator const_reverse_iterator; + using iterator = NodeRef*; + using const_iterator = const NodeRef*; + using const_reverse_iterator = std::reverse_iterator; /// Returns an iterator to the beginning. iterator begin() noexcept { diff --git a/third_party/libosmium/include/osmium/osm/object.hpp b/third_party/libosmium/include/osmium/osm/object.hpp index 8c745ce9b..6d1de6f67 100644 --- a/third_party/libosmium/include/osmium/osm/object.hpp +++ b/third_party/libosmium/include/osmium/osm/object.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -281,7 +281,7 @@ namespace osmium { * @param timestamp Timestamp * @returns Reference to object to make calls chainable. */ - OSMObject& set_timestamp(const osmium::Timestamp timestamp) noexcept { + OSMObject& set_timestamp(const osmium::Timestamp& timestamp) noexcept { m_timestamp = timestamp; return *this; } @@ -355,38 +355,38 @@ namespace osmium { return cend(); } - template + template using t_iterator = osmium::memory::ItemIterator; - template + template using t_const_iterator = osmium::memory::ItemIterator; - template + template t_iterator begin() { return t_iterator(subitems_position(), next()); } - template + template t_iterator end() { return t_iterator(next(), next()); } - template + template t_const_iterator cbegin() const { return t_const_iterator(subitems_position(), next()); } - template + template t_const_iterator cend() const { return t_const_iterator(next(), next()); } - template + template t_const_iterator begin() const { return cbegin(); } - template + template t_const_iterator end() const { return cend(); } diff --git a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp index bdf99e11c..fe3529b4a 100644 --- a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp +++ b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/osm/relation.hpp b/third_party/libosmium/include/osmium/osm/relation.hpp index 99a4f4cd1..9c4e69cc7 100644 --- a/third_party/libosmium/include/osmium/osm/relation.hpp +++ b/third_party/libosmium/include/osmium/osm/relation.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,9 +47,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; + template class ObjectBuilder; class RelationMemberListBuilder; - } + } // namespace builder class RelationMember : public osmium::memory::detail::ItemHelper { @@ -74,23 +74,21 @@ namespace osmium { return data() + osmium::memory::padded_length(sizeof(RelationMember) + m_role_size); } - template + template friend class osmium::memory::CollectionIterator; unsigned char* next() { if (full_member()) { return endpos() + reinterpret_cast(endpos())->byte_size(); - } else { - return endpos(); } + return endpos(); } unsigned const char* next() const { if (full_member()) { return endpos() + reinterpret_cast(endpos())->byte_size(); - } else { - return endpos(); } + return endpos(); } void set_role_size(string_size_type size) noexcept { diff --git a/third_party/libosmium/include/osmium/osm/segment.hpp b/third_party/libosmium/include/osmium/osm/segment.hpp index f3a82c97f..d35f97066 100644 --- a/third_party/libosmium/include/osmium/osm/segment.hpp +++ b/third_party/libosmium/include/osmium/osm/segment.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -65,12 +65,12 @@ namespace osmium { ~Segment() = default; /// Return first Location of Segment. - OSMIUM_CONSTEXPR osmium::Location first() const noexcept { + constexpr osmium::Location first() const noexcept { return m_first; } /// Return second Location of Segment. - OSMIUM_CONSTEXPR osmium::Location second() const noexcept { + constexpr osmium::Location second() const noexcept { return m_second; } @@ -84,11 +84,11 @@ namespace osmium { }; // class Segment /// Segments are equal if both their locations are equal - inline OSMIUM_CONSTEXPR bool operator==(const Segment& lhs, const Segment& rhs) noexcept { + inline constexpr bool operator==(const Segment& lhs, const Segment& rhs) noexcept { return lhs.first() == rhs.first() && lhs.second() == rhs.second(); } - inline OSMIUM_CONSTEXPR bool operator!=(const Segment& lhs, const Segment& rhs) noexcept { + inline constexpr bool operator!=(const Segment& lhs, const Segment& rhs) noexcept { return ! (lhs == rhs); } diff --git a/third_party/libosmium/include/osmium/osm/tag.hpp b/third_party/libosmium/include/osmium/osm/tag.hpp index 2e93ede24..3f1a29826 100644 --- a/third_party/libosmium/include/osmium/osm/tag.hpp +++ b/third_party/libosmium/include/osmium/osm/tag.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -53,7 +53,7 @@ namespace osmium { Tag& operator=(const Tag&) = delete; Tag& operator=(Tag&&) = delete; - template + template friend class osmium::memory::CollectionIterator; static unsigned char* after_null(unsigned char* ptr) { @@ -122,9 +122,8 @@ namespace osmium { }); if (result == cend()) { return default_value; - } else { - return result->value(); } + return result->value(); } const char* operator[](const char* key) const noexcept { diff --git a/third_party/libosmium/include/osmium/osm/timestamp.hpp b/third_party/libosmium/include/osmium/osm/timestamp.hpp index 390f0e745..613752e68 100644 --- a/third_party/libosmium/include/osmium/osm/timestamp.hpp +++ b/third_party/libosmium/include/osmium/osm/timestamp.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -47,17 +48,18 @@ namespace osmium { /** * A timestamp. Internal representation is an unsigned 32bit integer - * holding seconds since epoch, so this will overflow in 2038. + * holding seconds since epoch (1970-01-01T00:00:00Z), so this will + * overflow in 2106. We can use an unsigned integer here, because the + * OpenStreetMap project was started long after 1970, so there will + * never be dates before that. */ class Timestamp { // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0 static constexpr int timestamp_length = 20 + 1; - /** - * The timestamp format for OSM timestamps in strftime(3) format. - * This is the ISO-Format yyyy-mm-ddThh:mm:ssZ - */ + // The timestamp format for OSM timestamps in strftime(3) format. + // This is the ISO-Format "yyyy-mm-ddThh:mm:ssZ". static const char* timestamp_format() { static const char f[timestamp_length] = "%Y-%m-%dT%H:%M:%SZ"; return f; @@ -67,19 +69,32 @@ namespace osmium { public: + /** + * Default construct an invalid Timestamp. + */ constexpr Timestamp() noexcept : m_timestamp(0) { } - // Not "explicit" so that conversions from time_t work - // like in node.timestamp(123); - constexpr Timestamp(time_t timestamp) noexcept : - m_timestamp(static_cast(timestamp)) { + /** + * Construct a Timestamp from any integer type containing the seconds + * since the epoch. This will not check for overruns, you have to + * make sure the value fits into a uint32_t which is used internally + * in the Timestamp. + * + * The constructor is not declared "explicit" so that conversions + * like @code node.set_timestamp(123); @endcode work. + */ + template ::value, int>::type = 0> + constexpr Timestamp(T timestamp) noexcept : + m_timestamp(uint32_t(timestamp)) { } /** - * Construct timestamp from ISO date/time string. - * Throws std::invalid_argument, if the timestamp can not be parsed. + * Construct timestamp from ISO date/time string in the format + * "yyyy-mm-ddThh:mm:ssZ". + * + * @throws std::invalid_argument if the timestamp can not be parsed. */ explicit Timestamp(const char* timestamp) { #ifndef _WIN32 @@ -105,16 +120,51 @@ namespace osmium { #endif } + /** + * Construct timestamp from ISO date/time string in the format + * "yyyy-mm-ddThh:mm:ssZ". + * + * @throws std::invalid_argument if the timestamp can not be parsed. + */ + explicit Timestamp(const std::string& timestamp) : + Timestamp(timestamp.c_str()) { + } + + /** + * Returns true if this timestamp is valid (ie set to something other + * than 0). + */ + bool valid() const noexcept { + return m_timestamp != 0; + } + + /// Explicit conversion into bool. + explicit constexpr operator bool() const noexcept { + return m_timestamp != 0; + } + + /// Explicit conversion into time_t. constexpr time_t seconds_since_epoch() const noexcept { - return static_cast(m_timestamp); - } - - constexpr operator time_t() const noexcept { - return static_cast(m_timestamp); + return time_t(m_timestamp); } + /// Explicit conversion into uint32_t. explicit constexpr operator uint32_t() const noexcept { - return m_timestamp; + return uint32_t(m_timestamp); + } + + /// Explicit conversion into uint64_t. + explicit constexpr operator uint64_t() const noexcept { + return uint64_t(m_timestamp); + } + + /** + * Implicit conversion into time_t. + * + * @deprecated You should call seconds_since_epoch() explicitly instead. + */ + OSMIUM_DEPRECATED constexpr operator time_t() const noexcept { + return static_cast(m_timestamp); } template @@ -128,7 +178,9 @@ namespace osmium { } /** - * Return UTC Unix time as string in ISO date/time format. + * Return the timestamp as string in ISO date/time + * ("yyyy-mm-ddThh:mm:ssZ") format. If the timestamp is invalid, an + * empty string will be returned. */ std::string to_iso() const { std::string s; @@ -136,10 +188,15 @@ namespace osmium { if (m_timestamp != 0) { struct tm tm; time_t sse = seconds_since_epoch(); +#ifndef NDEBUG + auto result = +#endif #ifndef _MSC_VER - gmtime_r(&sse, &tm); + gmtime_r(&sse, &tm); + assert(result != nullptr); #else - gmtime_s(&tm, &sse); + gmtime_s(&tm, &sse); + assert(result == 0); #endif s.resize(timestamp_length); @@ -156,12 +213,20 @@ namespace osmium { }; // class Timestamp - inline OSMIUM_CONSTEXPR Timestamp start_of_time() noexcept { + /** + * A special Timestamp guaranteed to be ordered before any other valid + * Timestamp. + */ + inline constexpr Timestamp start_of_time() noexcept { return Timestamp(1); } - inline OSMIUM_CONSTEXPR Timestamp end_of_time() noexcept { - return Timestamp(std::numeric_limits::max()); + /** + * A special Timestamp guaranteed to be ordered after any other valid + * Timestamp. + */ + inline constexpr Timestamp end_of_time() noexcept { + return Timestamp(std::numeric_limits::max()); } template @@ -170,6 +235,30 @@ namespace osmium { return out; } + inline bool operator==(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return uint32_t(lhs) == uint32_t(rhs); + } + + inline bool operator!=(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return !(lhs == rhs); + } + + inline bool operator<(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return uint32_t(lhs) < uint32_t(rhs); + } + + inline bool operator>(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return rhs < lhs; + } + + inline bool operator<=(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return ! (rhs < lhs); + } + + inline bool operator>=(const Timestamp& lhs, const Timestamp& rhs) noexcept { + return ! (lhs < rhs); + } + template <> inline osmium::Timestamp min_op_start_value() { return end_of_time(); diff --git a/third_party/libosmium/include/osmium/osm/types.hpp b/third_party/libosmium/include/osmium/osm/types.hpp index b3414e594..984dd135c 100644 --- a/third_party/libosmium/include/osmium/osm/types.hpp +++ b/third_party/libosmium/include/osmium/osm/types.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,6 +49,7 @@ namespace osmium { typedef uint32_t user_id_type; ///< Type for OSM user IDs. typedef int32_t signed_user_id_type; ///< Type for signed OSM user IDs. typedef uint32_t num_changes_type; ///< Type for changeset num_changes. + typedef uint32_t num_comments_type; ///< Type for changeset num_comments. /** * Size for strings in OSM data such as user names, tag keys, roles, etc. @@ -57,6 +58,9 @@ namespace osmium { */ typedef uint16_t string_size_type; + // maximum of 256 characters of max 4 bytes each (in UTF-8 encoding) + constexpr const int max_osm_string_length = 256 * 4; + } // namespace osmium #endif // OSMIUM_OSM_TYPES_HPP diff --git a/third_party/libosmium/include/osmium/osm/types_from_string.hpp b/third_party/libosmium/include/osmium/osm/types_from_string.hpp index b8de14c90..aed064898 100644 --- a/third_party/libosmium/include/osmium/osm/types_from_string.hpp +++ b/third_party/libosmium/include/osmium/osm/types_from_string.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,9 +43,19 @@ DEALINGS IN THE SOFTWARE. #include #include +#include namespace osmium { + /** + * Convert string with object id to object_id_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline object_id_type string_to_object_id(const char* input) { assert(input); if (*input != '\0' && !std::isspace(*input)) { @@ -58,6 +68,19 @@ namespace osmium { throw std::range_error(std::string("illegal id: '") + input + "'"); } + /** + * Parse string with object type identifier followed by object id. This + * reads strings like "n1234" and "w10". + * + * @pre input must not be nullptr. + * + * @param input Input string. + * @param types Allowed types. Must not be osmium::osm_entity_bits::nothing. + * + * @returns std::pair of type and id. + * + * @throws std::range_error if the value is out of range. + */ inline std::pair string_to_object_id(const char* input, osmium::osm_entity_bits::type types) { assert(input); assert(types != osmium::osm_entity_bits::nothing); @@ -75,7 +98,7 @@ namespace osmium { namespace detail { - inline long string_to_ulong(const char* input, const char *name) { + inline unsigned long string_to_ulong(const char* input, const char *name) { if (*input != '\0' && *input != '-' && !std::isspace(*input)) { char* end; auto value = std::strtoul(input, &end, 10); @@ -88,27 +111,77 @@ namespace osmium { } // namespace detail + /** + * Convert string with object version to object_version_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline object_version_type string_to_object_version(const char* input) { assert(input); - return static_cast(detail::string_to_ulong(input, "version")); + return static_cast_with_assert(detail::string_to_ulong(input, "version")); } + /** + * Convert string with object version to object_version_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline changeset_id_type string_to_changeset_id(const char* input) { assert(input); - return static_cast(detail::string_to_ulong(input, "changeset")); + return static_cast_with_assert(detail::string_to_ulong(input, "changeset")); } + /** + * Convert string with user id to signed_user_id_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline signed_user_id_type string_to_user_id(const char* input) { assert(input); if (input[0] == '-' && input[1] == '1' && input[2] == '\0') { return -1; } - return static_cast(detail::string_to_ulong(input, "user id")); + return static_cast_with_assert(detail::string_to_ulong(input, "user id")); } + /** + * Convert string with number of changes to num_changes_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline num_changes_type string_to_num_changes(const char* input) { assert(input); - return static_cast(detail::string_to_ulong(input, "value for num changes")); + return static_cast_with_assert(detail::string_to_ulong(input, "value for num changes")); + } + + /** + * Convert string with number of comments to num_comments_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ + inline num_comments_type string_to_num_comments(const char* input) { + assert(input); + return static_cast_with_assert(detail::string_to_ulong(input, "value for num comments")); } } // namespace osmium diff --git a/third_party/libosmium/include/osmium/osm/undirected_segment.hpp b/third_party/libosmium/include/osmium/osm/undirected_segment.hpp index 654ef7d9a..c2442ae3c 100644 --- a/third_party/libosmium/include/osmium/osm/undirected_segment.hpp +++ b/third_party/libosmium/include/osmium/osm/undirected_segment.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/osm/way.hpp b/third_party/libosmium/include/osmium/osm/way.hpp index 3c5f1f6dc..3bc30b0c6 100644 --- a/third_party/libosmium/include/osmium/osm/way.hpp +++ b/third_party/libosmium/include/osmium/osm/way.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,8 +43,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; - } + template class ObjectBuilder; + } // namespace builder /** * List of node references (id and location) in a way. diff --git a/third_party/libosmium/include/osmium/relations/collector.hpp b/third_party/libosmium/include/osmium/relations/collector.hpp index 40e377393..7d7d14d0b 100644 --- a/third_party/libosmium/include/osmium/relations/collector.hpp +++ b/third_party/libosmium/include/osmium/relations/collector.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,15 +39,17 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include +//#include #include +#include #include #include #include // IWYU pragma: keep #include #include #include +#include #include #include @@ -55,14 +57,22 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - class Node; - class Way; - /** * @brief Code related to the assembly of OSM relations */ namespace relations { + namespace detail { + + template + inline typename std::iterator_traits::difference_type count_not_removed(const R& range) { + return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) { + return !mm.removed(); + }); + } + + } // namespace detail + /** * The Collector class collects members of a relation. This is a generic * base class that can be used to assemble all kinds of relations. It has numerous @@ -91,7 +101,7 @@ namespace osmium { * * @tparam TRelations Are we interested in member relations? */ - template + template class Collector { /** @@ -124,82 +134,15 @@ namespace osmium { TCollector& m_collector; - /** - * This variable is initialized with the number of different - * kinds of OSM objects we are interested in. If we only need - * way members (for instance for the multipolygon collector) - * it is intialized with 1 for instance. If node and way - * members are needed, it is initialized with 2. - * - * In the after_* methods of this handler, it is decremented - * and once it reaches 0, we know we have all members available - * that we are ever going to get. - */ - int m_want_types; - - /** - * Find this object in the member vectors and add it to all - * relations that need it. - * - * @returns true if the member was added to at least one - * relation and false otherwise - */ - bool find_and_add_object(const osmium::OSMObject& object) { - auto& mmv = m_collector.member_meta(object.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id())); - - if (osmium::relations::count_not_removed(range.first, range.second) == 0) { - // nothing found - return false; - } - - { - m_collector.members_buffer().add_item(object); - const size_t member_offset = m_collector.members_buffer().commit(); - - for (auto it = range.first; it != range.second; ++it) { - it->set_buffer_offset(member_offset); - } - } - - for (auto it = range.first; it != range.second; ++it) { - MemberMeta& member_meta = *it; - if (member_meta.removed()) { - break; - } - assert(member_meta.member_id() == object.id()); - assert(member_meta.relation_pos() < m_collector.m_relations.size()); - RelationMeta& relation_meta = m_collector.m_relations[member_meta.relation_pos()]; -// std::cerr << " => " << member_meta.member_pos() << " < " << m_collector.get_relation(relation_meta).members().size() << " (id=" << m_collector.get_relation(relation_meta).id() << ")\n"; - assert(member_meta.member_pos() < m_collector.get_relation(relation_meta).members().size()); -// std::cerr << " add way " << member_meta.member_id() << " to rel " << m_collector.get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; - relation_meta.got_one_member(); - if (relation_meta.has_all_members()) { - const size_t relation_offset = member_meta.relation_pos(); - m_collector.complete_relation(relation_meta); - m_collector.m_relations[relation_offset] = RelationMeta(); - m_collector.possibly_purge_removed_members(); - } - } - - // Remove MemberMetas that were marked as removed. - mmv.erase(std::remove_if(mmv.begin(), mmv.end(), [](MemberMeta& mm) { - return mm.removed(); - }), mmv.end()); - - return true; - } - public: HandlerPass2(TCollector& collector) noexcept : - m_collector(collector), - m_want_types((TNodes?1:0) + (TWays?1:0) + (TRelations?1:0)) { + m_collector(collector) { } void node(const osmium::Node& node) { if (TNodes) { - if (! find_and_add_object(node)) { + if (! m_collector.find_and_add_object(node)) { m_collector.node_not_in_any_relation(node); } } @@ -207,7 +150,7 @@ namespace osmium { void way(const osmium::Way& way) { if (TWays) { - if (! find_and_add_object(way)) { + if (! m_collector.find_and_add_object(way)) { m_collector.way_not_in_any_relation(way); } } @@ -215,7 +158,7 @@ namespace osmium { void relation(const osmium::Relation& relation) { if (TRelations) { - if (! find_and_add_object(relation)) { + if (! m_collector.find_and_add_object(relation)) { m_collector.relation_not_in_any_relation(relation); } } @@ -227,6 +170,8 @@ namespace osmium { }; // class HandlerPass2 + private: + HandlerPass2 m_handler_pass2; // All relations we are interested in will be kept in this buffer @@ -242,7 +187,9 @@ namespace osmium { * One vector each for nodes, ways, and relations containing all * mappings from member ids to their relations. */ - std::vector m_member_meta[3]; + using mm_vector_type = std::vector; + using mm_iterator = mm_vector_type::iterator; + mm_vector_type m_member_meta[3]; int m_count_complete = 0; @@ -251,6 +198,11 @@ namespace osmium { static constexpr size_t initial_buffer_size = 1024 * 1024; + iterator_range find_member_meta(osmium::item_type type, osmium::object_id_type id) { + auto& mmv = member_meta(type); + return iterator_range{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))}; + } + public: /** @@ -375,6 +327,8 @@ namespace osmium { return m_members_buffer.get(offset); } + private: + /** * Tell the Collector that you are interested in this relation * and want it kept until all members have been assembled and @@ -424,6 +378,76 @@ namespace osmium { std::sort(m_member_meta[2].begin(), m_member_meta[2].end()); } + /** + * Find this object in the member vectors and add it to all + * relations that need it. + * + * @returns true if the member was added to at least one + * relation and false otherwise + */ + bool find_and_add_object(const osmium::OSMObject& object) { + auto range = find_member_meta(object.type(), object.id()); + + if (detail::count_not_removed(range) == 0) { + // nothing found + return false; + } + + { + members_buffer().add_item(object); + const size_t member_offset = members_buffer().commit(); + + for (auto& member_meta : range) { + member_meta.set_buffer_offset(member_offset); + } + } + + for (auto& member_meta : range) { + if (member_meta.removed()) { + break; + } + assert(member_meta.member_id() == object.id()); + assert(member_meta.relation_pos() < m_relations.size()); + RelationMeta& relation_meta = m_relations[member_meta.relation_pos()]; +// std::cerr << " => " << member_meta.member_pos() << " < " << get_relation(relation_meta).members().size() << " (id=" << get_relation(relation_meta).id() << ")\n"; + assert(member_meta.member_pos() < get_relation(relation_meta).members().size()); +// std::cerr << " add way " << member_meta.member_id() << " to rel " << get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; + relation_meta.got_one_member(); + if (relation_meta.has_all_members()) { + const size_t relation_offset = member_meta.relation_pos(); + static_cast(this)->complete_relation(relation_meta); + clear_member_metas(relation_meta); + m_relations[relation_offset] = RelationMeta(); + possibly_purge_removed_members(); + } + } + + return true; + } + + void clear_member_metas(const osmium::relations::RelationMeta& relation_meta) { + const osmium::Relation& relation = get_relation(relation_meta); + for (const auto& member : relation.members()) { + if (member.ref() != 0) { + auto range = find_member_meta(member.type(), member.ref()); + assert(!range.empty()); + + // if this is the last time this object was needed + // then mark it as removed + if (detail::count_not_removed(range) == 1) { + get_member(range.begin()->buffer_offset()).set_removed(true); + } + + for (auto& member_meta : range) { + if (!member_meta.removed() && relation.id() == get_relation(member_meta.relation_pos()).id()) { + member_meta.remove(); + break; + } + } + } + } + } + public: uint64_t used_memory() const { @@ -433,24 +457,24 @@ namespace osmium { const uint64_t relations_buffer_capacity = m_relations_buffer.capacity(); const uint64_t members_buffer_capacity = m_members_buffer.capacity(); - std::cout << " nR = m_relations.capacity() ........... = " << std::setw(12) << m_relations.capacity() << "\n"; - std::cout << " nMN = m_member_meta[NODE].capacity() ... = " << std::setw(12) << m_member_meta[0].capacity() << "\n"; - std::cout << " nMW = m_member_meta[WAY].capacity() .... = " << std::setw(12) << m_member_meta[1].capacity() << "\n"; - std::cout << " nMR = m_member_meta[RELATION].capacity() = " << std::setw(12) << m_member_meta[2].capacity() << "\n"; - std::cout << " nM = m_member_meta[*].capacity() ...... = " << std::setw(12) << nmembers << "\n"; + std::cerr << " nR = m_relations.capacity() ........... = " << std::setw(12) << m_relations.capacity() << "\n"; + std::cerr << " nMN = m_member_meta[NODE].capacity() ... = " << std::setw(12) << m_member_meta[0].capacity() << "\n"; + std::cerr << " nMW = m_member_meta[WAY].capacity() .... = " << std::setw(12) << m_member_meta[1].capacity() << "\n"; + std::cerr << " nMR = m_member_meta[RELATION].capacity() = " << std::setw(12) << m_member_meta[2].capacity() << "\n"; + std::cerr << " nM = m_member_meta[*].capacity() ...... = " << std::setw(12) << nmembers << "\n"; - std::cout << " sRM = sizeof(RelationMeta) ............. = " << std::setw(12) << sizeof(RelationMeta) << "\n"; - std::cout << " sMM = sizeof(MemberMeta) ............... = " << std::setw(12) << sizeof(MemberMeta) << "\n\n"; + std::cerr << " sRM = sizeof(RelationMeta) ............. = " << std::setw(12) << sizeof(RelationMeta) << "\n"; + std::cerr << " sMM = sizeof(MemberMeta) ............... = " << std::setw(12) << sizeof(MemberMeta) << "\n\n"; - std::cout << " nR * sRM ............................... = " << std::setw(12) << relations << "\n"; - std::cout << " nM * sMM ............................... = " << std::setw(12) << members << "\n"; - std::cout << " relations_buffer_capacity .............. = " << std::setw(12) << relations_buffer_capacity << "\n"; - std::cout << " members_buffer_capacity ................ = " << std::setw(12) << members_buffer_capacity << "\n"; + std::cerr << " nR * sRM ............................... = " << std::setw(12) << relations << "\n"; + std::cerr << " nM * sMM ............................... = " << std::setw(12) << members << "\n"; + std::cerr << " relations_buffer_capacity .............. = " << std::setw(12) << relations_buffer_capacity << "\n"; + std::cerr << " members_buffer_capacity ................ = " << std::setw(12) << members_buffer_capacity << "\n"; const uint64_t total = relations + members + relations_buffer_capacity + members_buffer_capacity; - std::cout << " total .................................. = " << std::setw(12) << total << "\n"; - std::cout << " =======================================================\n"; + std::cerr << " total .................................. = " << std::setw(12) << total << "\n"; + std::cerr << " =======================================================\n"; return relations_buffer_capacity + members_buffer_capacity + relations + members; } @@ -468,20 +492,19 @@ namespace osmium { } size_t get_offset(osmium::item_type type, osmium::object_id_type id) { - const auto& mmv = member_meta(type); - const auto range = std::equal_range(mmv.cbegin(), mmv.cend(), MemberMeta(id)); - assert(range.first != range.second); - return range.first->buffer_offset(); + const auto range = find_member_meta(type, id); + assert(!range.empty()); + return range.begin()->buffer_offset(); } - template + template void read_relations(TIter begin, TIter end) { HandlerPass1 handler(*static_cast(this)); osmium::apply(begin, end, handler); sort_member_meta(); } - template + template void read_relations(TSource& source) { read_relations(std::begin(source), std::end(source)); source.close(); @@ -489,11 +512,10 @@ namespace osmium { void moving_in_buffer(size_t old_offset, size_t new_offset) { const osmium::OSMObject& object = m_members_buffer.get(old_offset); - auto& mmv = member_meta(object.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(object.id())); - for (auto it = range.first; it != range.second; ++it) { - assert(it->buffer_offset() == old_offset); - it->set_buffer_offset(new_offset); + auto range = find_member_meta(object.type(), object.id()); + for (auto& member_meta : range) { + assert(member_meta.buffer_offset() == old_offset); + member_meta.set_buffer_offset(new_offset); } } @@ -506,13 +528,15 @@ namespace osmium { void possibly_purge_removed_members() { ++m_count_complete; if (m_count_complete > 10000) { // XXX - const size_t size_before = m_members_buffer.committed(); +// const size_t size_before = m_members_buffer.committed(); m_members_buffer.purge_removed(this); +/* const size_t size_after = m_members_buffer.committed(); double percent = static_cast(size_before - size_after); percent /= size_before; percent *= 100; -// std::cerr << "PURGE (size before=" << size_before << " after=" << size_after << " purged=" << (size_before - size_after) << " / " << static_cast(percent) << "%)\n"; + std::cerr << "PURGE (size before=" << size_before << " after=" << size_after << " purged=" << (size_before - size_after) << " / " << static_cast(percent) << "%)\n"; +*/ m_count_complete = 0; } } diff --git a/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp index a45088eab..f0e9c3625 100644 --- a/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp +++ b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -136,21 +136,6 @@ namespace osmium { return out; } - /** - * Count the number of MemberMeta objects in the iterator range - * that are not marked as removed. - * - * @tparam TIter Iterator that dereferences to a MemberMeta - * @param begin Begin of iterator range - * @param end End of iterator range - */ - template - inline typename std::iterator_traits::difference_type count_not_removed(TIter begin, TIter end) { - return std::count_if(begin, end, [](MemberMeta& mm) { - return !mm.removed(); - }); - } - } // namespace relations } // namespace osmium diff --git a/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp index a48c50acc..93aa41cf2 100644 --- a/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp +++ b/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/tags/filter.hpp b/third_party/libosmium/include/osmium/tags/filter.hpp index 3c1946c54..407992e6d 100644 --- a/third_party/libosmium/include/osmium/tags/filter.hpp +++ b/third_party/libosmium/include/osmium/tags/filter.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,7 +46,7 @@ namespace osmium { namespace tags { - template + template struct match_key { bool operator()(const TKey& rule_key, const char* tag_key) { return rule_key == tag_key; @@ -59,7 +59,7 @@ namespace osmium { } }; // struct match_key_prefix - template + template struct match_value { bool operator()(const TValue& rule_value, const char* tag_value) { return rule_value == tag_value; @@ -73,7 +73,7 @@ namespace osmium { } }; // struct match_value - template , class TValueComp=match_value> + template , typename TValueComp=match_value> class Filter { typedef TKey key_type; @@ -115,7 +115,7 @@ namespace osmium { m_default_result(default_result) { } - template ::value, int>::type = 0> + template ::value, int>::type = 0> Filter& add(bool result, const key_type& key, const value_type& value) { m_rules.emplace_back(result, false, key, value); return *this; diff --git a/third_party/libosmium/include/osmium/tags/regex_filter.hpp b/third_party/libosmium/include/osmium/tags/regex_filter.hpp index 725c42376..8ea6d60c3 100644 --- a/third_party/libosmium/include/osmium/tags/regex_filter.hpp +++ b/third_party/libosmium/include/osmium/tags/regex_filter.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/tags/taglist.hpp b/third_party/libosmium/include/osmium/tags/taglist.hpp index d7c78dc79..b1f346fe1 100644 --- a/third_party/libosmium/include/osmium/tags/taglist.hpp +++ b/third_party/libosmium/include/osmium/tags/taglist.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -45,17 +45,17 @@ namespace osmium { */ namespace tags { - template + template inline bool match_any_of(const osmium::TagList& tag_list, TFilter&& filter) { return std::any_of(tag_list.cbegin(), tag_list.cend(), std::forward(filter)); } - template + template inline bool match_all_of(const osmium::TagList& tag_list, TFilter&& filter) { return std::all_of(tag_list.cbegin(), tag_list.cend(), std::forward(filter)); } - template + template inline bool match_none_of(const osmium::TagList& tag_list, TFilter&& filter) { return std::none_of(tag_list.cbegin(), tag_list.cend(), std::forward(filter)); } diff --git a/third_party/libosmium/include/osmium/thread/function_wrapper.hpp b/third_party/libosmium/include/osmium/thread/function_wrapper.hpp index fe0a49257..4366c0a7c 100644 --- a/third_party/libosmium/include/osmium/thread/function_wrapper.hpp +++ b/third_party/libosmium/include/osmium/thread/function_wrapper.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -50,7 +50,9 @@ namespace osmium { struct impl_base { virtual ~impl_base() = default; - virtual void call() = 0; + virtual bool call() { + return true; + } }; // struct impl_base @@ -58,28 +60,38 @@ namespace osmium { template struct impl_type : impl_base { + F m_functor; - impl_type(F&& functor) : - m_functor(std::move(functor)) { + explicit impl_type(F&& functor) : + m_functor(std::forward(functor)) { } - void call() override { + bool call() override { m_functor(); + return false; } + }; // struct impl_type public: // Constructor must not be "explicit" for wrapper // to work seemlessly. - template - function_wrapper(F&& f) : - impl(new impl_type(std::move(f))) { + template + function_wrapper(TFunction&& f) : + impl(new impl_type(std::forward(f))) { } - void operator()() { - impl->call(); + // The integer parameter is only used to signal that we want + // the special function wrapper that makes the worker thread + // shut down. + function_wrapper(int) : + impl(new impl_base()) { + } + + bool operator()() { + return impl->call(); } function_wrapper() = default; diff --git a/third_party/libosmium/include/osmium/thread/pool.hpp b/third_party/libosmium/include/osmium/thread/pool.hpp index 391603108..207f55514 100644 --- a/third_party/libosmium/include/osmium/thread/pool.hpp +++ b/third_party/libosmium/include/osmium/thread/pool.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,6 +54,32 @@ namespace osmium { */ namespace thread { + namespace detail { + + // Maximum number of allowed pool threads (just to keep the user + // from setting something silly). + constexpr const int max_pool_threads = 256; + + inline int get_pool_size(int num_threads, int user_setting, unsigned hardware_concurrency) { + if (num_threads == 0) { + num_threads = user_setting ? user_setting : -2; + } + + if (num_threads < 0) { + num_threads += hardware_concurrency; + } + + if (num_threads < 1) { + num_threads = 1; + } else if (num_threads > max_pool_threads) { + num_threads = max_pool_threads; + } + + return num_threads; + } + + } // namespace detail + /** * Thread pool. */ @@ -83,7 +109,6 @@ namespace osmium { }; // class thread_joiner - std::atomic m_done; osmium::thread::Queue m_work_queue; std::vector m_threads; thread_joiner m_joiner; @@ -91,11 +116,15 @@ namespace osmium { void worker_thread() { osmium::thread::set_thread_name("_osmium_worker"); - while (!m_done) { + while (true) { function_wrapper task; m_work_queue.wait_and_pop_with_timeout(task); if (task) { - task(); + if (task()) { + // The called tasks returns true only when the + // worker thread should shut down. + return; + } } } } @@ -113,26 +142,17 @@ namespace osmium { * In all cases the minimum number of threads in the pool is 1. */ explicit Pool(int num_threads, size_t max_queue_size) : - m_done(false), m_work_queue(max_queue_size, "work"), m_threads(), m_joiner(m_threads), - m_num_threads(num_threads) { - - if (m_num_threads == 0) { - m_num_threads = osmium::config::get_pool_threads(); - } - - if (m_num_threads <= 0) { - m_num_threads = std::max(1, static_cast(std::thread::hardware_concurrency()) + m_num_threads); - } + m_num_threads(detail::get_pool_size(num_threads, osmium::config::get_pool_threads(), std::thread::hardware_concurrency())) { try { for (int i = 0; i < m_num_threads; ++i) { m_threads.push_back(std::thread(&Pool::worker_thread, this)); } } catch (...) { - m_done = true; + shutdown_all_workers(); throw; } } @@ -147,8 +167,15 @@ namespace osmium { return pool; } + void shutdown_all_workers() { + for (int i = 0; i < m_num_threads; ++i) { + // The special function wrapper makes a worker shut down. + m_work_queue.push(function_wrapper{0}); + } + } + ~Pool() { - m_done = true; + shutdown_all_workers(); m_work_queue.shutdown(); } diff --git a/third_party/libosmium/include/osmium/thread/queue.hpp b/third_party/libosmium/include/osmium/thread/queue.hpp index 76ad9a020..771735883 100644 --- a/third_party/libosmium/include/osmium/thread/queue.hpp +++ b/third_party/libosmium/include/osmium/thread/queue.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -75,6 +75,9 @@ namespace osmium { /// The largest size the queue has been so far. size_t m_largest_size; + /// The number of times push() was called on the queue. + std::atomic m_push_counter; + /// The number of times the queue was full and a thread pushing /// to the queue was blocked. std::atomic m_full_counter; @@ -89,7 +92,7 @@ namespace osmium { * 0 for an unlimited size. * @param name Optional name for this queue. (Used for debugging.) */ - Queue(size_t max_size = 0, const std::string& name = "") : + explicit Queue(size_t max_size = 0, const std::string& name = "") : m_max_size(max_size), m_name(name), m_mutex(), @@ -99,6 +102,7 @@ namespace osmium { #ifdef OSMIUM_DEBUG_QUEUE_SIZE , m_largest_size(0), + m_push_counter(0), m_full_counter(0) #endif { @@ -107,7 +111,7 @@ namespace osmium { ~Queue() { shutdown(); #ifdef OSMIUM_DEBUG_QUEUE_SIZE - std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times\n"; + std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls\n"; #endif } @@ -116,6 +120,9 @@ namespace osmium { * call will block if the queue is full. */ void push(T value) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_push_counter; +#endif if (m_max_size) { while (size() >= m_max_size) { std::this_thread::sleep_for(full_queue_sleep_duration); diff --git a/third_party/libosmium/include/osmium/thread/sorted_queue.hpp b/third_party/libosmium/include/osmium/thread/sorted_queue.hpp index e76ade101..5478643c9 100644 --- a/third_party/libosmium/include/osmium/thread/sorted_queue.hpp +++ b/third_party/libosmium/include/osmium/thread/sorted_queue.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/thread/util.hpp b/third_party/libosmium/include/osmium/thread/util.hpp index ca4f6dd53..2ef331a2c 100644 --- a/third_party/libosmium/include/osmium/thread/util.hpp +++ b/third_party/libosmium/include/osmium/thread/util.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,7 +49,7 @@ namespace osmium { * the exception stored in the future if there was one. Otherwise it * will just return. */ - template + template inline void check_for_exception(std::future& future) { if (future.valid() && future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { future.get(); @@ -60,7 +60,7 @@ namespace osmium { * Wait until the given future becomes ready. Will block if the future * is not ready. Can be called more than once unlike future.get(). */ - template + template inline void wait_until_done(std::future& future) { if (future.valid()) { future.get(); @@ -71,15 +71,44 @@ namespace osmium { * Set name of current thread for debugging. This only works on Linux. */ #ifdef __linux__ - inline void set_thread_name(const char* name) { + inline void set_thread_name(const char* name) noexcept { prctl(PR_SET_NAME, name, 0, 0, 0); } #else - inline void set_thread_name(const char*) { + inline void set_thread_name(const char*) noexcept { // intentionally left blank } #endif + class thread_handler { + + std::thread m_thread; + + public: + + thread_handler() : + m_thread() { + } + + template + explicit thread_handler(TFunction&& f, TArgs&&... args) : + m_thread(std::forward(f), std::forward(args)...) { + } + + thread_handler(const thread_handler&) = delete; + thread_handler& operator=(const thread_handler&) = delete; + + thread_handler(thread_handler&&) = default; + thread_handler& operator=(thread_handler&&) = default; + + ~thread_handler() { + if (m_thread.joinable()) { + m_thread.join(); + } + } + + }; // class thread_handler + } // namespace thread } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/cast.hpp b/third_party/libosmium/include/osmium/util/cast.hpp index 4866fdec4..c31ffce79 100644 --- a/third_party/libosmium/include/osmium/util/cast.hpp +++ b/third_party/libosmium/include/osmium/util/cast.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/util/compatibility.hpp b/third_party/libosmium/include/osmium/util/compatibility.hpp index 90d85c502..b83cc1558 100644 --- a/third_party/libosmium/include/osmium/util/compatibility.hpp +++ b/third_party/libosmium/include/osmium/util/compatibility.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -34,14 +34,20 @@ DEALINGS IN THE SOFTWARE. */ // Workarounds for MSVC which doesn't support -// * constexpr in all cases yet // * [[noreturn]] #ifdef _MSC_VER -# define OSMIUM_CONSTEXPR # define OSMIUM_NORETURN __declspec(noreturn) #else -# define OSMIUM_CONSTEXPR constexpr # define OSMIUM_NORETURN [[noreturn]] #endif +// [[deprecated]] is only available in C++14, use this for the time being +#ifdef __GNUC__ +# define OSMIUM_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define OSMIUM_DEPRECATED __declspec(deprecated) +#else +# define OSMIUM_DEPRECATED +#endif + #endif // OSMIUM_UTIL_COMPATIBILITY_HPP diff --git a/third_party/libosmium/include/osmium/util/config.hpp b/third_party/libosmium/include/osmium/util/config.hpp index 3285eedbb..c40512322 100644 --- a/third_party/libosmium/include/osmium/util/config.hpp +++ b/third_party/libosmium/include/osmium/util/config.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,7 +49,7 @@ namespace osmium { if (env) { return std::atoi(env); } - return -2; + return 0; } inline bool use_pool_threads_for_pbf_parsing() { diff --git a/third_party/libosmium/include/osmium/util/data_file.hpp b/third_party/libosmium/include/osmium/util/data_file.hpp deleted file mode 100644 index 22bf1910a..000000000 --- a/third_party/libosmium/include/osmium/util/data_file.hpp +++ /dev/null @@ -1,194 +0,0 @@ -#ifndef OSMIUM_UTIL_DATA_FILE_HPP -#define OSMIUM_UTIL_DATA_FILE_HPP - -/* - -This file is part of Osmium (http://osmcode.org/libosmium). - -Copyright 2013-2015 Jochen Topf and others (see README). - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 -#include -#include -#include -#include - -#ifdef _WIN32 -# include -# include -#endif - -#include - -namespace osmium { - - namespace util { - - /** - * Class wrapper for convenient access to some low-level file - * functions. - */ - class DataFile { - - FILE* m_file; - - public: - - /** - * Create and open a temporary file. It is removed after opening. - * - * @throws std::system_error if something went wrong. - */ - DataFile() : - m_file(::tmpfile()) { - if (!m_file) { - throw std::system_error(errno, std::system_category(), "tmpfile failed"); - } - } - - /** - * Create and open a temporary file with the specified size. It - * is removed after opening. - * - * @throws std::system_error if something went wrong. - */ - explicit DataFile(size_t size) : - DataFile() { - grow(size); - } - - /** - * Create and open a named file. - * - * @param filename the name of the file - * @param writable should the file be writable? - * @throws std::system_error if something went wrong. - */ - DataFile(const char* filename, bool writable) : - m_file(::fopen(filename, writable ? "wb+" : "rb" )) { - if (!m_file) { - throw std::system_error(errno, std::system_category(), "fopen failed"); - } - } - - /** - * Create and open a named file. - * - * @param filename the name of the file - * @param writable should the file be writable? - * @throws std::system_error if something went wrong. - */ - DataFile(const std::string& filename, bool writable) : - DataFile(filename.c_str(), writable) { - } - - /** - * In boolean context the DataFile class returns true if the file - * is open. - */ - operator bool() const noexcept { - return m_file != nullptr; - } - - /** - * Close the file. - * - * Does nothing if the file is already closed. - * - * @throws std::system_error if file could not be closed - */ - void close() { - if (m_file) { - if (::fclose(m_file) != 0) { - throw std::system_error(errno, std::system_category(), "fclose failed"); - } - m_file = nullptr; - } - } - - ~DataFile() noexcept { - try { - close(); - } catch (std::system_error&) { - // ignore - } - } - - /** - * Get file descriptor of underlying file. - * - * @throws std::runtime_errro if file is not open - * @throws std::system_error if fileno(3) call failed - */ - int fd() const { - if (!m_file) { - throw std::runtime_error("no open file"); - } - - int fd = ::fileno(m_file); - - if (fd == -1) { - throw std::system_error(errno, std::system_category(), "fileno failed"); - } - - return fd; - } - - /** - * Ask the operating system for the size of this file. - * - * @throws std::system_error if fstat(2) call failed - */ - size_t size() const { - return osmium::util::file_size(fd()); - } - - /** - * Grow file to given size. - * - * If the file is large enough already, nothing is done. - * The file is never shrunk. - * - * @throws std::system_error if ftruncate(2) call failed - */ - void grow(size_t new_size) const { - if (size() < new_size) { - osmium::util::resize_file(fd(), new_size); - } - } - - }; // class DataFile - - } // namespace util - -} // namespace osmium - - -#endif // OSMIUM_UTIL_DATA_FILE_HPP diff --git a/third_party/libosmium/include/osmium/util/delta.hpp b/third_party/libosmium/include/osmium/util/delta.hpp index 0c77e5242..34c4eb228 100644 --- a/third_party/libosmium/include/osmium/util/delta.hpp +++ b/third_party/libosmium/include/osmium/util/delta.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -37,6 +37,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include + namespace osmium { namespace util { @@ -44,25 +46,39 @@ namespace osmium { /** * Helper class for delta encoding. */ - template + template class DeltaEncode { - T m_value; + static_assert(std::is_integral::value, + "DeltaEncode value type must be some integer"); + + static_assert(std::is_integral::value && std::is_signed::value, + "DeltaEncode delta type must be some signed integer"); + + TValue m_value; public: - DeltaEncode(T value = 0) : + using value_type = TValue; + using delta_type = TDelta; + + explicit DeltaEncode(TValue value = 0) : m_value(value) { } - void clear() { + void clear() noexcept { m_value = 0; } - T update(T new_value) { + TValue value() const noexcept { + return m_value; + } + + TDelta update(TValue new_value) noexcept { using std::swap; swap(m_value, new_value); - return m_value - new_value; + return static_cast_with_assert(m_value) - + static_cast_with_assert(new_value); } }; // class DeltaEncode @@ -70,52 +86,63 @@ namespace osmium { /** * Helper class for delta decoding. */ - template + template class DeltaDecode { - T m_value; + static_assert(std::is_integral::value, + "DeltaDecode value type must be some integer"); + + static_assert(std::is_integral::value && std::is_signed::value, + "DeltaDecode delta type must be some signed integer"); + + TValue m_value; public: + using value_type = TValue; + using delta_type = TDelta; + DeltaDecode() : m_value(0) { } - void clear() { + void clear() noexcept { m_value = 0; } - T update(T delta) { - m_value += delta; + TValue update(TDelta delta) noexcept { + m_value = static_cast_with_assert( + static_cast_with_assert(m_value) + delta); return m_value; } }; // class DeltaDecode - template + template class DeltaEncodeIterator : public std::iterator { - typedef TValue value_type; - TBaseIterator m_it; TBaseIterator m_end; - value_type m_delta; - DeltaEncode m_value; TTransform m_trans; + DeltaEncode m_value; + TDelta m_delta; public: + using value_type = TValue; + using delta_type = TDelta; + DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) : m_it(first), m_end(last), - m_delta(m_trans(m_it)), - m_value(m_delta), - m_trans(trans) { + m_trans(trans), + m_value(m_it != m_end ? m_trans(m_it) : 0), + m_delta(static_cast_with_assert(m_value.value())) { } DeltaEncodeIterator& operator++() { - if (m_it != m_end) { - m_delta = m_value.update(m_trans(++m_it)); + if (++m_it != m_end) { + m_delta = m_value.update(m_trans(m_it)); } return *this; } @@ -126,7 +153,7 @@ namespace osmium { return tmp; } - value_type operator*() { + TDelta operator*() { return m_delta; } diff --git a/third_party/libosmium/include/osmium/util/double.hpp b/third_party/libosmium/include/osmium/util/double.hpp index 85a250807..1352c5fb1 100644 --- a/third_party/libosmium/include/osmium/util/double.hpp +++ b/third_party/libosmium/include/osmium/util/double.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -68,8 +68,12 @@ namespace osmium { #endif assert(len > 0 && len < max_double_length); - while (buffer[len-1] == '0') --len; - if (buffer[len-1] == '.') --len; + while (buffer[len-1] == '0') { + --len; + } + if (buffer[len-1] == '.') { + --len; + } return std::copy_n(buffer, len, iterator); } diff --git a/third_party/libosmium/include/osmium/util/endian.hpp b/third_party/libosmium/include/osmium/util/endian.hpp index a5d91543e..fe23a5517 100644 --- a/third_party/libosmium/include/osmium/util/endian.hpp +++ b/third_party/libosmium/include/osmium/util/endian.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/util/file.hpp b/third_party/libosmium/include/osmium/util/file.hpp index 461f4e642..86b93ff72 100644 --- a/third_party/libosmium/include/osmium/util/file.hpp +++ b/third_party/libosmium/include/osmium/util/file.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -52,6 +52,8 @@ DEALINGS IN THE SOFTWARE. # define ftruncate _chsize_s #endif +#include + namespace osmium { namespace util { @@ -92,7 +94,7 @@ namespace osmium { * @throws std::system_error If ftruncate(2) call failed */ inline void resize_file(int fd, size_t new_size) { - if (::ftruncate(fd, new_size) != 0) { + if (::ftruncate(fd, static_cast_with_assert(new_size)) != 0) { throw std::system_error(errno, std::system_category(), "ftruncate failed"); } } @@ -108,7 +110,7 @@ namespace osmium { return si.dwPageSize; #else // Unix implementation - return ::sysconf(_SC_PAGESIZE); + return size_t(::sysconf(_SC_PAGESIZE)); #endif } diff --git a/third_party/libosmium/include/osmium/util/iterator.hpp b/third_party/libosmium/include/osmium/util/iterator.hpp new file mode 100644 index 000000000..4cef51950 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/iterator.hpp @@ -0,0 +1,74 @@ +#ifndef OSMIUM_UTIL_ITERATOR_HPP +#define OSMIUM_UTIL_ITERATOR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 + +namespace osmium { + + template > + struct iterator_range : public P { + + using iterator = It; + + iterator_range(P&& p) : + P(std::forward

(p)) { + } + + It begin() { + return this->first; + } + + It end() { + return this->second; + } + + It begin() const { + return this->first; + } + + It end() const { + return this->second; + } + + size_t empty() const { + return begin() == end(); + } + + }; + +} // namespace osmium + +#endif // OSMIUM_UTIL_ITERATOR_HPP diff --git a/third_party/libosmium/include/osmium/util/memory.hpp b/third_party/libosmium/include/osmium/util/memory.hpp new file mode 100644 index 000000000..777a6e07b --- /dev/null +++ b/third_party/libosmium/include/osmium/util/memory.hpp @@ -0,0 +1,99 @@ +#ifndef OSMIUM_UTIL_MEMORY_HPP +#define OSMIUM_UTIL_MEMORY_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 +#include + +namespace osmium { + + class MemoryUsage { + + int m_current = 0; + int m_peak = 0; + +#ifdef __linux__ + static int parse_number(const std::string& line) { + const auto f = line.find_first_of("0123456789"); + const auto l = line.find_last_of("0123456789"); + return std::atoi(line.substr(f, l-f+1).c_str()); + } +#endif + + public: + + /** + * Get the memory usage for the current process. The constructor will + * get the memory usage. Use the current() and peak() calls to access + * the result. + * + * This will only work on Linux, on other architectures this will + * always return 0. + */ + MemoryUsage() { +#ifdef __linux__ + static const char* filename = "/proc/self/status"; + std::ifstream status_file(filename); + + if (status_file.is_open()) { + std::string line; + while (! status_file.eof() ) { + std::getline(status_file, line); + if (line.substr(0, 6) == "VmPeak") { + m_peak = parse_number(line); + } + if (line.substr(0, 6) == "VmSize") { + m_current = parse_number(line); + } + } + } +#endif + } + + /// Return current memory usage in MBytes + int current() const { + return m_current / 1024; + } + + /// Return peak memory usage in MBytes + int peak() const { + return m_peak / 1024; + } + + }; // class MemoryUsage + +} // namespace osmium + +#endif // OSMIUM_UTIL_MEMORY_HPP diff --git a/third_party/libosmium/include/osmium/util/memory_mapping.hpp b/third_party/libosmium/include/osmium/util/memory_mapping.hpp index e48aff282..67e944e70 100644 --- a/third_party/libosmium/include/osmium/util/memory_mapping.hpp +++ b/third_party/libosmium/include/osmium/util/memory_mapping.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -38,11 +38,13 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #ifndef _WIN32 # include #else +# include # include # include # include @@ -85,6 +87,9 @@ namespace osmium { * On Unix systems this wraps the mmap(), munmap(), and the mremap() * system calls. On Windows it wraps the CreateFileMapping(), * CloseHandle(), MapViewOfFile(), and UnmapViewOfFile() functions. + * + * On Windows the file will be set to binary mode before the memory + * mapping. */ class MemoryMapping { @@ -169,7 +174,8 @@ private: * created, otherwise a mapping based on the file descriptor will * be created. * - * @pre size > 0 or mode == write_shared oder write_private + * @pre @code size > 0 @endcode or + * @code mode == write_shared || mode == write_private @endcode * * @param size Size of the mapping in bytes * @param mode Mapping mode: readonly, or writable (shared or private) @@ -179,8 +185,12 @@ private: */ MemoryMapping(size_t size, mapping_mode mode, int fd=-1, off_t offset=0); - /// DEPRECATED: For backwards compatibility - MemoryMapping(size_t size, bool writable=true, int fd=-1, off_t offset=0) : + /** + * @deprecated + * For backwards compatibility only. Use the constructor taking + * a mapping_mode as second argument instead. + */ + OSMIUM_DEPRECATED MemoryMapping(size_t size, bool writable=true, int fd=-1, off_t offset=0) : MemoryMapping(size, writable ? mapping_mode::write_shared : mapping_mode::readonly, fd, offset) { } @@ -209,7 +219,7 @@ private: try { unmap(); } catch (std::system_error&) { - // ignore + // Ignore any exceptions because destructor must not throw. } } @@ -228,8 +238,9 @@ private: * systems it will unmap and remap the memory. This can only be * done for file-based mappings, not anonymous mappings! * - * @param new_size Number of bytes to resize to - * @throws std::system_error if the remapping fails + * @param new_size Number of bytes to resize to (must be > 0). + * + * @throws std::system_error if the remapping fails. */ void resize(size_t new_size); @@ -237,7 +248,7 @@ private: * In a boolean context a MemoryMapping is true when it is a valid * existing mapping. */ - operator bool() const noexcept { + explicit operator bool() const noexcept { return is_valid(); } @@ -349,8 +360,12 @@ private: m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) { } - /// DEPRECATED: For backwards compatibility - TypedMemoryMapping(size_t size, bool writable, int fd, off_t offset = 0) : + /** + * @deprecated + * For backwards compatibility only. Use the constructor taking + * a mapping_mode as second argument instead. + */ + OSMIUM_DEPRECATED TypedMemoryMapping(size_t size, bool writable, int fd, off_t offset = 0) : m_mapping(sizeof(T) * size, writable ? MemoryMapping::mapping_mode::write_shared : MemoryMapping::mapping_mode::readonly, fd, sizeof(T) * offset) { } @@ -375,7 +390,7 @@ private: * Releases the mapping by calling unmap(). Will never throw. * Call unmap() instead if you want to be notified of any error. */ - ~TypedMemoryMapping() = default; + ~TypedMemoryMapping() noexcept = default; /** * Unmap a mapping. If the mapping is not valid, it will do @@ -405,7 +420,7 @@ private: * In a boolean context a TypedMemoryMapping is true when it is * a valid existing mapping. */ - operator bool() const noexcept { + explicit operator bool() const noexcept { return !!m_mapping; } @@ -655,6 +670,9 @@ inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept { } inline HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept { + if (m_fd != -1) { + _setmode(m_fd, _O_BINARY); + } return CreateFileMapping(get_handle(), nullptr, get_protection(), osmium::util::dword_hi(static_cast(m_size) + m_offset), osmium::util::dword_lo(static_cast(m_size) + m_offset), nullptr); } diff --git a/third_party/libosmium/include/osmium/util/minmax.hpp b/third_party/libosmium/include/osmium/util/minmax.hpp index 2eb601a24..b4ff4ea90 100644 --- a/third_party/libosmium/include/osmium/util/minmax.hpp +++ b/third_party/libosmium/include/osmium/util/minmax.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/util/options.hpp b/third_party/libosmium/include/osmium/util/options.hpp index fea075230..9b00b4811 100644 --- a/third_party/libosmium/include/osmium/util/options.hpp +++ b/third_party/libosmium/include/osmium/util/options.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,46 +47,65 @@ namespace osmium { * as a base class. Options are stored and retrieved by key using the * different set() and get() methods. * + * Both keys and values are stored as strings. The values "true", + * "yes", "false", and "no" are interpreted as boolean values in some + * functions. + * * You can iterate over all set options. Dereferencing an iterator * yields a std::pair of the key and value strings. */ class Options { - typedef std::map option_map; + using option_map = std::map; option_map m_options; public: - typedef option_map::iterator iterator; - typedef option_map::const_iterator const_iterator; - typedef option_map::value_type value_type; + using iterator = option_map::iterator; + using const_iterator = option_map::const_iterator; + using value_type = option_map::value_type; + /** + * Construct empty option set. + */ Options() = default; + /** + * Construct option set from initializer list: + * @code + * Options options{ { "foo", "true" }, { "bar", "17" } }; + * @endcode + */ explicit Options(const std::initializer_list& values) : m_options(values) { } - Options(const Options&) = default; - Options& operator=(const Options&) = default; - - Options(Options&&) = default; - Options& operator=(Options&&) = default; - - ~Options() = default; - + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, const std::string& value) { m_options[key] = value; } + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, const char* value) { m_options[key] = value; } + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, bool value) { m_options[key] = value ? "true" : "false"; } + /** + * Set option from string in the form 'key=value'. If the string + * contains no equal sign, the whole string is the key and it will + * be set to "true". + */ void set(std::string data) { size_t pos = data.find_first_of('='); if (pos == std::string::npos) { @@ -99,7 +118,7 @@ namespace osmium { } /** - * Get value of "key" option. If not set the default_value (or + * Get value of "key" option. If not set, the default_value (or * empty string) is returned. */ std::string get(const std::string& key, const std::string& default_value="") const noexcept { @@ -112,36 +131,67 @@ namespace osmium { /** * Is this option set to a true value ("true" or "yes")? + * Will return false if the value is unset. */ bool is_true(const std::string& key) const noexcept { std::string value = get(key); return (value == "true" || value == "yes"); } + /** + * Is this option not set to a false value ("false" or "no")? + * Will return true if the value is unset. + */ + bool is_not_false(const std::string& key) const noexcept { + std::string value = get(key); + return !(value == "false" || value == "no"); + } + + /** + * The number of options set. + */ size_t size() const noexcept { return m_options.size(); } + /** + * Returns an iterator to the beginning. + */ iterator begin() noexcept { return m_options.begin(); } + /** + * Returns an iterator to the end. + */ iterator end() noexcept { return m_options.end(); } + /** + * Returns an iterator to the beginning. + */ const_iterator begin() const noexcept { return m_options.cbegin(); } + /** + * Returns an iterator to the end. + */ const_iterator end() const noexcept { return m_options.cend(); } + /** + * Returns an iterator to the beginning. + */ const_iterator cbegin() const noexcept { return m_options.cbegin(); } + /** + * Returns a iterator to the end. + */ const_iterator cend() const noexcept { return m_options.cend(); } diff --git a/third_party/libosmium/include/osmium/util/string.hpp b/third_party/libosmium/include/osmium/util/string.hpp index 55bfc6cd5..1198288c0 100644 --- a/third_party/libosmium/include/osmium/util/string.hpp +++ b/third_party/libosmium/include/osmium/util/string.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/util/verbose_output.hpp b/third_party/libosmium/include/osmium/util/verbose_output.hpp index 249d67fbf..c7677a436 100644 --- a/third_party/libosmium/include/osmium/util/verbose_output.hpp +++ b/third_party/libosmium/include/osmium/util/verbose_output.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -115,23 +115,28 @@ namespace osmium { } template - friend VerboseOutput& operator<<(VerboseOutput& verbose_output, const T& value) { - if (verbose_output.m_verbose) { - verbose_output.start_line(); + void print(const T& value) { + if (m_verbose) { + start_line(); std::cerr << value; // check if there was a newline a the end and remember that std::ostringstream output_buffer; output_buffer << value; if (!output_buffer.str().empty() && output_buffer.str().back() == '\n') { - verbose_output.m_newline = true; + m_newline = true; } } - return verbose_output; } }; // class VerboseOutput + template + inline VerboseOutput& operator<<(VerboseOutput& verbose_output, const T& value) { + verbose_output.print(value); + return verbose_output; + } + } // namespace util } // namespace osmium diff --git a/third_party/libosmium/include/osmium/visitor.hpp b/third_party/libosmium/include/osmium/visitor.hpp index 0250f11d4..427096864 100644 --- a/third_party/libosmium/include/osmium/visitor.hpp +++ b/third_party/libosmium/include/osmium/visitor.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2015 Jochen Topf and others (see README). +Copyright 2013-2016 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include +#include #include // IWYU pragma: keep #include #include @@ -43,22 +44,16 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - class TagList; - class WayNodeList; - class RelationMemberList; - class OuterRing; - class InnerRing; - namespace memory { class Item; - } + } // namespace memory namespace detail { template using ConstIfConst = typename std::conditional::value, typename std::add_const::type, U>::type; - template + template inline void apply_item_recurse(TItem& item, THandler& handler) { switch (item.type()) { case osmium::item_type::undefined: @@ -98,10 +93,13 @@ namespace osmium { case osmium::item_type::inner_ring: handler.inner_ring(static_cast&>(item)); break; + case osmium::item_type::changeset_discussion: + handler.changeset_discussion(static_cast&>(item)); + break; } } - template + template inline void apply_item_recurse(const osmium::OSMEntity& item, THandler& handler) { switch (item.type()) { case osmium::item_type::node: @@ -128,7 +126,7 @@ namespace osmium { } } - template + template inline void apply_item_recurse(osmium::OSMEntity& item, THandler& handler) { switch (item.type()) { case osmium::item_type::node: @@ -155,7 +153,7 @@ namespace osmium { } } - template + template inline void apply_item_recurse(const osmium::OSMObject& item, THandler& handler) { switch (item.type()) { case osmium::item_type::node: @@ -179,7 +177,7 @@ namespace osmium { } } - template + template inline void apply_item_recurse(osmium::OSMObject& item, THandler& handler) { switch (item.type()) { case osmium::item_type::node: @@ -203,18 +201,18 @@ namespace osmium { } } - template + template inline void apply_item_recurse(TItem& item, THandler& handler, TRest&... more) { apply_item_recurse(item, handler); apply_item_recurse(item, more...); } - template + template inline void flush_recurse(THandler& handler) { handler.flush(); } - template + template inline void flush_recurse(THandler& handler, TRest&... more) { flush_recurse(handler); flush_recurse(more...); @@ -222,17 +220,17 @@ namespace osmium { } // namespace detail - template + template inline void apply_item(const osmium::memory::Item& item, THandlers&... handlers) { detail::apply_item_recurse(item, handlers...); } - template + template inline void apply_item(osmium::memory::Item& item, THandlers&... handlers) { detail::apply_item_recurse(item, handlers...); } - template + template inline void apply(TIterator it, TIterator end, THandlers&... handlers) { for (; it != end; ++it) { detail::apply_item_recurse(*it, handlers...); @@ -240,12 +238,12 @@ namespace osmium { detail::flush_recurse(handlers...); } - template + template inline void apply(TContainer& c, THandlers&... handlers) { apply(std::begin(c), std::end(c), handlers...); } - template + template inline void apply(const osmium::memory::Buffer& buffer, THandlers&... handlers) { apply(buffer.cbegin(), buffer.cend(), handlers...); } diff --git a/third_party/libosmium/include/protozero/byteswap.hpp b/third_party/libosmium/include/protozero/byteswap.hpp index d019c28c5..a018c1c17 100644 --- a/third_party/libosmium/include/protozero/byteswap.hpp +++ b/third_party/libosmium/include/protozero/byteswap.hpp @@ -10,30 +10,51 @@ documentation. *****************************************************************************/ +/** + * @file byteswap.hpp + * + * @brief Contains functions to swap bytes in values (for different endianness). + */ + +#include #include +#include + namespace protozero { +/** + * Swap N byte value between endianness formats. This template function must + * be specialized to actually work. + */ template inline void byteswap(const char* /*data*/, char* /*result*/) { - assert(false); -} - -template <> -inline void byteswap<1>(const char* data, char* result) { - result[0] = data[0]; + static_assert(N == 1, "Can only swap 4 or 8 byte values"); } +/** + * Swap 4 byte value (int32_t, uint32_t, float) between endianness formats. + */ template <> inline void byteswap<4>(const char* data, char* result) { +#ifdef PROTOZERO_USE_BUILTIN_BSWAP + *reinterpret_cast(result) = __builtin_bswap32(*reinterpret_cast(data)); +#else result[3] = data[0]; result[2] = data[1]; result[1] = data[2]; result[0] = data[3]; +#endif } +/** + * Swap 8 byte value (int64_t, uint64_t, double) between endianness formats. + */ template <> inline void byteswap<8>(const char* data, char* result) { +#ifdef PROTOZERO_USE_BUILTIN_BSWAP + *reinterpret_cast(result) = __builtin_bswap64(*reinterpret_cast(data)); +#else result[7] = data[0]; result[6] = data[1]; result[5] = data[2]; @@ -42,6 +63,7 @@ inline void byteswap<8>(const char* data, char* result) { result[2] = data[5]; result[1] = data[6]; result[0] = data[7]; +#endif } } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/config.hpp b/third_party/libosmium/include/protozero/config.hpp new file mode 100644 index 000000000..8465c9688 --- /dev/null +++ b/third_party/libosmium/include/protozero/config.hpp @@ -0,0 +1,59 @@ +#ifndef PROTOZERO_CONFIG_HPP +#define PROTOZERO_CONFIG_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +#include + +/** + * @file config.hpp + * + * @brief Contains macro checks for different configurations. + */ + +#define PROTOZERO_LITTLE_ENDIAN 1234 +#define PROTOZERO_BIG_ENDIAN 4321 + +// Find out which byte order the machine has. +#if defined(__BYTE_ORDER) +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN +# endif +# if (__BYTE_ORDER == __BIG_ENDIAN) +# define PROTOZERO_BYTE_ORDER PROTOZERO_BIG_ENDIAN +# endif +#else +// This probably isn't a very good default, but might do until we figure +// out something better. +# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN +#endif + +// On some ARM machines and depending on compiler settings access to unaligned +// floating point values will result in a SIGBUS. Do not use the bare pointers +// in this case. +#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN +# if !defined(__arm__) && !defined(_M_ARM) +# ifndef PROTOZERO_DO_NOT_USE_BARE_POINTER +# define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED +# endif +# endif +#endif + +// Check whether __builtin_bswap is available +#if defined(__GNUC__) || defined(__clang__) +# define PROTOZERO_USE_BUILTIN_BSWAP +#endif + +// Wrapper for assert() used for testing +#ifndef protozero_assert +# define protozero_assert(x) assert(x) +#endif + +#endif // PROTOZERO_CONFIG_HPP diff --git a/third_party/libosmium/include/protozero/exception.hpp b/third_party/libosmium/include/protozero/exception.hpp index 1229f7dcb..5c7ab5478 100644 --- a/third_party/libosmium/include/protozero/exception.hpp +++ b/third_party/libosmium/include/protozero/exception.hpp @@ -29,7 +29,7 @@ namespace protozero { */ struct exception : std::exception { /// Returns the explanatory string. - const char *what() const noexcept { return "pbf exception"; } + const char *what() const noexcept override { return "pbf exception"; } }; /** @@ -38,7 +38,7 @@ struct exception : std::exception { */ struct varint_too_long_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept { return "varint too long exception"; } + const char *what() const noexcept override { return "varint too long exception"; } }; /** @@ -47,7 +47,7 @@ struct varint_too_long_exception : exception { */ struct unknown_pbf_wire_type_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept { return "unknown pbf field type exception"; } + const char *what() const noexcept override { return "unknown pbf field type exception"; } }; /** @@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception { */ struct end_of_buffer_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept { return "end of buffer exception"; } + const char *what() const noexcept override { return "end of buffer exception"; } }; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_builder.hpp b/third_party/libosmium/include/protozero/pbf_builder.hpp index d49a7ba59..548f4cecb 100644 --- a/third_party/libosmium/include/protozero/pbf_builder.hpp +++ b/third_party/libosmium/include/protozero/pbf_builder.hpp @@ -10,17 +10,37 @@ documentation. *****************************************************************************/ +/** + * @file pbf_builder.hpp + * + * @brief Contains the pbf_builder template class. + */ + #include -#include +#include #include namespace protozero { +/** + * The pbf_builder is used to write PBF formatted messages into a buffer. It + * is based on the pbf_writer class and has all the same methods. The + * difference is that while the pbf_writer class takes an integer tag, + * this template class takes a tag of the template type T. The idea is that + * T will be an enumeration value and this helps reduce the possibility of + * programming errors. + * + * Almost all methods in this class can throw an std::bad_alloc exception if + * the std::string used as a buffer wants to resize. + * + * Read the tutorial to understand how this class is used. + */ template class pbf_builder : public pbf_writer { - static_assert(std::is_same::type>::value, "T must be enum with underlying type protozero::pbf_tag_type"); + static_assert(std::is_same::type>::value, + "T must be enum with underlying type protozero::pbf_tag_type"); public: @@ -35,6 +55,7 @@ public: pbf_writer(parent_writer, pbf_tag_type(tag)) { } +/// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ inline void add_##name(T tag, type value) { \ pbf_writer::add_##name(pbf_tag_type(tag), value); \ @@ -55,7 +76,10 @@ public: PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) - inline void add_bytes(T tag, const char* value, size_t size) { +#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR +/// @endcond + + inline void add_bytes(T tag, const char* value, std::size_t size) { pbf_writer::add_bytes(pbf_tag_type(tag), value, size); } @@ -63,7 +87,7 @@ public: pbf_writer::add_bytes(pbf_tag_type(tag), value); } - inline void add_string(T tag, const char* value, size_t size) { + inline void add_string(T tag, const char* value, std::size_t size) { pbf_writer::add_string(pbf_tag_type(tag), value, size); } @@ -75,7 +99,7 @@ public: pbf_writer::add_string(pbf_tag_type(tag), value); } - inline void add_message(T tag, const char* value, size_t size) { + inline void add_message(T tag, const char* value, std::size_t size) { pbf_writer::add_message(pbf_tag_type(tag), value, size); } @@ -83,6 +107,7 @@ public: pbf_writer::add_message(pbf_tag_type(tag), value); } +/// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ template \ inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ @@ -104,6 +129,9 @@ public: PROTOZERO_WRITER_WRAP_ADD_PACKED(float) PROTOZERO_WRITER_WRAP_ADD_PACKED(double) +#undef PROTOZERO_WRITER_WRAP_ADD_PACKED +/// @endcond + }; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_message.hpp b/third_party/libosmium/include/protozero/pbf_message.hpp index af29a00f4..45f01c149 100644 --- a/third_party/libosmium/include/protozero/pbf_message.hpp +++ b/third_party/libosmium/include/protozero/pbf_message.hpp @@ -10,13 +10,57 @@ documentation. *****************************************************************************/ +/** + * @file pbf_message.hpp + * + * @brief Contains the pbf_message class. + */ + #include #include -#include +#include namespace protozero { +/** + * This class represents a protobuf message. Either a top-level message or + * a nested sub-message. Top-level messages can be created from any buffer + * with a pointer and length: + * + * @code + * enum class Message : protozero::pbf_tag_type { + * ... + * }; + * + * std::string buffer; + * // fill buffer... + * pbf_message message(buffer.data(), buffer.size()); + * @endcode + * + * Sub-messages are created using get_message(): + * + * @code + * enum class SubMessage : protozero::pbf_tag_type { + * ... + * }; + * + * pbf_message message(...); + * message.next(); + * pbf_message submessage = message.get_message(); + * @endcode + * + * All methods of the pbf_message class except get_bytes() and get_string() + * provide the strong exception guarantee, ie they either succeed or do not + * change the pbf_message object they are called on. Use the get_data() method + * instead of get_bytes() or get_string(), if you need this guarantee. + * + * This template class is based on the pbf_reader class and has all the same + * methods. The difference is that whereever the pbf_reader class takes an + * integer tag, this template class takes a tag of the template type T. + * + * Read the tutorial to understand how this class is used. + */ template class pbf_message : public pbf_reader { diff --git a/third_party/libosmium/include/protozero/pbf_reader.hpp b/third_party/libosmium/include/protozero/pbf_reader.hpp index 1c5ed0d70..58b388455 100644 --- a/third_party/libosmium/include/protozero/pbf_reader.hpp +++ b/third_party/libosmium/include/protozero/pbf_reader.hpp @@ -16,7 +16,6 @@ documentation. * @brief Contains the pbf_reader class. */ -#include #include #include #include @@ -24,19 +23,15 @@ documentation. #include #include -#include +#include #include +#include #include -#if __BYTE_ORDER != __LITTLE_ENDIAN +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include #endif -/// Wrapper for assert() used for testing -#ifndef protozero_assert -# define protozero_assert(x) assert(x) -#endif - namespace protozero { /** @@ -77,25 +72,34 @@ class pbf_reader { // The tag of the current field. pbf_tag_type m_tag = 0; + // Copy N bytes from src to dest on little endian machines, on big endian + // swap the bytes in the process. + template + static void copy_or_byteswap(const char* src, void* dest) noexcept { +#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN + memcpy(dest, src, N); +#else + byteswap(src, reinterpret_cast(dest)); +#endif + } + template inline T get_fixed() { T result; skip_bytes(sizeof(T)); -#if __BYTE_ORDER == __LITTLE_ENDIAN - memcpy(&result, m_data - sizeof(T), sizeof(T)); -#else - byteswap(m_data - sizeof(T), reinterpret_cast(&result)); -#endif + copy_or_byteswap(m_data - sizeof(T), &result); return result; } -#if __BYTE_ORDER == __LITTLE_ENDIAN +#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED + template - inline std::pair packed_fixed() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - protozero_assert(len % sizeof(T) == 0); - return std::make_pair(reinterpret_cast(m_data-len), reinterpret_cast(m_data)); + using const_fixed_iterator = const T*; + + template + inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { + return std::make_pair(reinterpret_cast(first), + reinterpret_cast(last)); } #else @@ -128,7 +132,7 @@ class pbf_reader { T operator*() { T result; - byteswap(m_data, reinterpret_cast(&result)); + copy_or_byteswap(m_data , &result); return result; } @@ -153,15 +157,21 @@ class pbf_reader { }; // class const_fixed_iterator + template + inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { + return std::make_pair(const_fixed_iterator(first, last), + const_fixed_iterator(last, last)); + } + +#endif + template inline std::pair, const_fixed_iterator> packed_fixed() { protozero_assert(tag() != 0 && "call next() before accessing field value"); auto len = get_len_and_skip(); protozero_assert(len % sizeof(T) == 0); - return std::make_pair(const_fixed_iterator(m_data-len, m_data), - const_fixed_iterator(m_data, m_data)); + return create_fixed_iterator_pair(m_data-len, m_data); } -#endif template inline T get_varint(); template inline T get_svarint(); @@ -183,7 +193,7 @@ public: * * @post There is no current field. */ - inline pbf_reader(const char *data, size_t length) noexcept; + inline pbf_reader(const char *data, std::size_t length) noexcept; /** * Construct a pbf_reader message from a data pointer and a length. The pointer @@ -194,7 +204,7 @@ public: * * @post There is no current field. */ - inline pbf_reader(std::pair data) noexcept; + inline pbf_reader(std::pair data) noexcept; /** * Construct a pbf_reader message from a std::string. A pointer to the string @@ -243,8 +253,8 @@ public: * buffer. Of course you have to know reasonably well what data to expect * and how it is encoded for this number to have any meaning. */ - size_t length() const noexcept { - return size_t(m_end - m_data); + std::size_t length() const noexcept { + return std::size_t(m_end - m_data); } /** @@ -828,14 +838,14 @@ public: }; // class pbf_reader -pbf_reader::pbf_reader(const char *data, size_t length) noexcept +pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept : m_data(data), m_end(data + length), m_wire_type(pbf_wire_type::unknown), m_tag(0) { } -pbf_reader::pbf_reader(std::pair data) noexcept +pbf_reader::pbf_reader(std::pair data) noexcept : m_data(data.first), m_end(data.first + data.second), m_wire_type(pbf_wire_type::unknown), @@ -866,8 +876,16 @@ bool pbf_reader::next() { protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range"); m_wire_type = pbf_wire_type(value & 0x07); -// XXX do we want this check? or should it throw an exception? -// protozero_assert((m_wire_type <=2 || m_wire_type == 5) && "illegal wire type"); + switch (m_wire_type) { + case pbf_wire_type::varint: + case pbf_wire_type::fixed64: + case pbf_wire_type::length_delimited: + case pbf_wire_type::fixed32: + break; + default: + throw unknown_pbf_wire_type_exception(); + } + return true; } @@ -923,7 +941,7 @@ void pbf_reader::skip() { skip_bytes(4); break; default: - throw unknown_pbf_wire_type_exception(); + protozero_assert(false && "can not be here because next() should have thrown already"); } } diff --git a/third_party/libosmium/include/protozero/pbf_writer.hpp b/third_party/libosmium/include/protozero/pbf_writer.hpp index 53cbfdf1c..422e14772 100644 --- a/third_party/libosmium/include/protozero/pbf_writer.hpp +++ b/third_party/libosmium/include/protozero/pbf_writer.hpp @@ -16,7 +16,6 @@ documentation. * @brief Contains the pbf_writer class. */ -#include #include #include #include @@ -24,20 +23,24 @@ documentation. #include #include -#include +#include +#include #include -#if __BYTE_ORDER != __LITTLE_ENDIAN +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include #endif -/// Wrapper for assert() used for testing -#ifndef protozero_assert -# define protozero_assert(x) assert(x) -#endif - namespace protozero { +namespace detail { + + template class packed_field_varint; + template class packed_field_svarint; + template class packed_field_fixed; + +} // end namespace detail + /** * The pbf_writer is used to write PBF formatted messages into a buffer. * @@ -46,9 +49,24 @@ namespace protozero { */ class pbf_writer { + // A pointer to a string buffer holding the data already written to the + // PBF message. For default constructed writers or writers that have been + // rolled back, this is a nullptr. std::string* m_data; + + // A pointer to a parent writer object if this is a submessage. If this + // is a top-level writer, it is a nullptr. pbf_writer* m_parent_writer; - size_t m_pos = 0; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the rollback position, ie. the last position before the + // submessage was started. This is the position where the header of the + // submessage starts. + std::size_t m_rollback_pos = 0; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the position where the data of the submessage is written to. + std::size_t m_pos = 0; inline void add_varint(uint64_t value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); @@ -71,7 +89,7 @@ class pbf_writer { inline void add_fixed(T value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN m_data->append(reinterpret_cast(&value), sizeof(T)); #else auto size = m_data->size(); @@ -99,7 +117,9 @@ class pbf_writer { return; } - add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last))); + auto length = std::distance(first, last); + add_length_varint(tag, sizeof(T) * pbf_length_type(length)); + reserve(sizeof(T) * std::size_t(length)); while (first != last) { add_fixed(*first++); @@ -137,16 +157,37 @@ class pbf_writer { // and a varint needs 8 bit for every 7 bit. static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1; - inline void open_submessage(pbf_tag_type tag) { + // If m_rollpack_pos is set to this special value, it means that when + // the submessage is closed, nothing needs to be done, because the length + // of the submessage has already been written correctly. + static const std::size_t size_is_known = std::numeric_limits::max(); + + inline void open_submessage(pbf_tag_type tag, std::size_t size) { protozero_assert(m_pos == 0); protozero_assert(m_data); - add_field(tag, pbf_wire_type::length_delimited); - m_data->append(size_t(reserve_bytes), '\0'); + if (size == 0) { + m_rollback_pos = m_data->size(); + add_field(tag, pbf_wire_type::length_delimited); + m_data->append(std::size_t(reserve_bytes), '\0'); + } else { + m_rollback_pos = size_is_known; + add_length_varint(tag, pbf_length_type(size)); + reserve(size); + } m_pos = m_data->size(); } - inline void close_submessage() { + inline void rollback_submessage() { protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); + protozero_assert(m_data); + m_data->resize(m_rollback_pos); + m_pos = 0; + } + + inline void commit_submessage() { + protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_data); auto length = pbf_length_type(m_data->size() - m_pos); @@ -157,6 +198,18 @@ class pbf_writer { m_pos = 0; } + inline void close_submessage() { + protozero_assert(m_data); + if (m_pos == 0 || m_rollback_pos == size_is_known) { + return; + } + if (m_data->size() - m_pos == 0) { + rollback_submessage(); + } else { + commit_submessage(); + } + } + inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) { add_field(tag, pbf_wire_type::length_delimited); add_varint(length); @@ -166,7 +219,8 @@ public: /** * Create a writer using the given string as a data store. The pbf_writer - * stores a reference to that string and adds all data to it. + * stores a reference to that string and adds all data to it. The string + * doesn't have to be empty. The pbf_writer will just append data. */ inline explicit pbf_writer(std::string& data) noexcept : m_data(&data), @@ -190,12 +244,15 @@ public: * * @param parent_writer The pbf_writer * @param tag Tag (field number) of the field that will be written + * @param size Optional size of the submessage in bytes (use 0 for unknown). + * Setting this allows some optimizations but is only possible in + * a few very specific cases. */ - inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) : + inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : m_data(parent_writer.m_data), m_parent_writer(&parent_writer), m_pos(0) { - m_parent_writer->open_submessage(tag); + m_parent_writer->open_submessage(tag, size); } /// A pbf_writer object can be copied @@ -216,6 +273,26 @@ public: } } + /** + * Reserve size bytes in the underlying message store in addition to + * whatever the message store already holds. So unlike + * the `std::string::reserve()` method this is not an absolute size, + * but additional memory that should be reserved. + * + * @param size Number of bytes to reserve in underlying message store. + */ + void reserve(std::size_t size) { + protozero_assert(m_data); + m_data->reserve(m_data->size() + size); + } + + inline void rollback() { + protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent"); + protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage"); + m_parent_writer->rollback_submessage(); + m_data = nullptr; + } + ///@{ /** * @name Scalar field writer functions @@ -229,7 +306,9 @@ public: */ inline void add_bool(pbf_tag_type tag, bool value) { add_field(tag, pbf_wire_type::varint); - add_fixed(value); + protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); + protozero_assert(m_data); + m_data->append(1, value); } /** @@ -375,10 +454,10 @@ public: * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) { + inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); - assert(size <= std::numeric_limits::max()); + protozero_assert(size <= std::numeric_limits::max()); add_length_varint(tag, pbf_length_type(size)); m_data->append(value, size); } @@ -400,7 +479,7 @@ public: * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_string(pbf_tag_type tag, const char* value, size_t size) { + inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -432,7 +511,7 @@ public: * @param value Pointer to message to be written * @param size Length of the message */ - inline void add_message(pbf_tag_type tag, const char* value, size_t size) { + inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -657,8 +736,102 @@ public: ///@} + template friend class detail::packed_field_varint; + template friend class detail::packed_field_svarint; + template friend class detail::packed_field_fixed; + }; // class pbf_writer +namespace detail { + + class packed_field { + + protected: + + pbf_writer m_writer; + + public: + + packed_field(pbf_writer& parent_writer, pbf_tag_type tag) : + m_writer(parent_writer, tag) { + } + + packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : + m_writer(parent_writer, tag, size) { + } + + void rollback() { + m_writer.rollback(); + } + + }; // class packed_field + + template + class packed_field_fixed : public packed_field { + + public: + + packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag) : + packed_field(parent_writer, tag) { + } + + packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : + packed_field(parent_writer, tag, size * sizeof(T)) { + } + + void add_element(T value) { + m_writer.add_fixed(value); + } + + }; // class packed_field_fixed + + template + class packed_field_varint : public packed_field { + + public: + + packed_field_varint(pbf_writer& parent_writer, pbf_tag_type tag) : + packed_field(parent_writer, tag) { + } + + void add_element(T value) { + m_writer.add_varint(uint64_t(value)); + } + + }; // class packed_field_varint + + template + class packed_field_svarint : public packed_field { + + public: + + packed_field_svarint(pbf_writer& parent_writer, pbf_tag_type tag) : + packed_field(parent_writer, tag) { + } + + void add_element(T value) { + m_writer.add_varint(encode_zigzag64(value)); + } + + }; // class packed_field_svarint + +} // end namespace detail + +using packed_field_bool = detail::packed_field_varint; +using packed_field_enum = detail::packed_field_varint; +using packed_field_int32 = detail::packed_field_varint; +using packed_field_sint32 = detail::packed_field_svarint; +using packed_field_uint32 = detail::packed_field_varint; +using packed_field_int64 = detail::packed_field_varint; +using packed_field_sint64 = detail::packed_field_svarint; +using packed_field_uint64 = detail::packed_field_varint; +using packed_field_fixed32 = detail::packed_field_fixed; +using packed_field_sfixed32 = detail::packed_field_fixed; +using packed_field_fixed64 = detail::packed_field_fixed; +using packed_field_sfixed64 = detail::packed_field_fixed; +using packed_field_float = detail::packed_field_fixed; +using packed_field_double = detail::packed_field_fixed; + } // end namespace protozero #endif // PROTOZERO_PBF_WRITER_HPP diff --git a/third_party/libosmium/include/protozero/pbf_types.hpp b/third_party/libosmium/include/protozero/types.hpp similarity index 91% rename from third_party/libosmium/include/protozero/pbf_types.hpp rename to third_party/libosmium/include/protozero/types.hpp index 9f38584f4..6856b3dde 100644 --- a/third_party/libosmium/include/protozero/pbf_types.hpp +++ b/third_party/libosmium/include/protozero/types.hpp @@ -1,5 +1,5 @@ -#ifndef PROTOZERO_PBF_TYPES_HPP -#define PROTOZERO_PBF_TYPES_HPP +#ifndef PROTOZERO_TYPES_HPP +#define PROTOZERO_TYPES_HPP /***************************************************************************** @@ -11,7 +11,7 @@ documentation. *****************************************************************************/ /** - * @file pbf_types.hpp + * @file types.hpp * * @brief Contains the declaration of low-level types used in the pbf format. */ @@ -46,4 +46,4 @@ namespace protozero { } // end namespace protozero -#endif // PROTOZERO_PBF_TYPES_HPP +#endif // PROTOZERO_TYPES_HPP diff --git a/third_party/libosmium/include/protozero/varint.hpp b/third_party/libosmium/include/protozero/varint.hpp index bc9c3296d..4242df951 100644 --- a/third_party/libosmium/include/protozero/varint.hpp +++ b/third_party/libosmium/include/protozero/varint.hpp @@ -16,10 +16,6 @@ documentation. * @brief Contains low-level varint and zigzag encoding and decoding functions. */ -#if __BYTE_ORDER != __LITTLE_ENDIAN -# error "This code only works on little endian machines." -#endif - #include #include @@ -29,13 +25,13 @@ namespace protozero { /** * The maximum length of a 64bit varint. */ -const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; +constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; // from https://github.com/facebook/folly/blob/master/folly/Varint.h /** * Decode a 64bit varint. * - * String exception guarantee: if there is an exception the data pointer will + * Strong exception guarantee: if there is an exception the data pointer will * not be changed. * * @param[in,out] data Pointer to pointer to the input data. After the function diff --git a/third_party/libosmium/include/protozero/version.hpp b/third_party/libosmium/include/protozero/version.hpp new file mode 100644 index 000000000..7b60e2eda --- /dev/null +++ b/third_party/libosmium/include/protozero/version.hpp @@ -0,0 +1,22 @@ +#ifndef PROTOZERO_VERSION_HPP +#define PROTOZERO_VERSION_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +#define PROTOZERO_VERSION_MAJOR 1 +#define PROTOZERO_VERSION_MINOR 3 +#define PROTOZERO_VERSION_PATCH 0 + +#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) + +#define PROTOZERO_VERSION_STRING "1.3.0" + + +#endif // PROTOZERO_VERSION_HPP diff --git a/third_party/libosmium/scripts/travis_install.sh b/third_party/libosmium/scripts/travis_install.sh deleted file mode 100755 index 119e9fd15..000000000 --- a/third_party/libosmium/scripts/travis_install.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# -# travis_install.sh -# - -if [ "$TRAVIS_OS_NAME" = "osx" ]; then - - brew install google-sparsehash || true - - brew install --without-python boost || true - - # workaround for gdal homebrew problem - brew remove gdal - brew install gdal - -fi - -cd .. -git clone --quiet --depth 1 https://github.com/osmcode/osm-testdata.git - diff --git a/third_party/libosmium/scripts/travis_script.sh b/third_party/libosmium/scripts/travis_script.sh deleted file mode 100755 index 75b3b3657..000000000 --- a/third_party/libosmium/scripts/travis_script.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# -# travis_script.sh -# - -mkdir build -cd build - -# GCC ignores the pragmas in the code that disable the "return-type" warning -# selectively, so use this workaround. -if [ "${CXX}" = "g++" ]; then - WORKAROUND="-DCMAKE_CXX_FLAGS=-Wno-return-type" -else - WORKAROUND="" -fi - -if [ "${CXX}" = "g++" ]; then - CXX=g++-4.8 - CC=gcc-4.8 -fi - -cmake -LA \ - -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ - ${WORKAROUND} \ - .. - -make VERBOSE=1 -ctest --output-on-failure - diff --git a/third_party/libosmium/test/CMakeLists.txt b/third_party/libosmium/test/CMakeLists.txt index 00474577a..cea67ecd8 100644 --- a/third_party/libosmium/test/CMakeLists.txt +++ b/third_party/libosmium/test/CMakeLists.txt @@ -106,9 +106,12 @@ add_unit_test(basic test_timestamp) add_unit_test(basic test_types_from_string) add_unit_test(basic test_way) +add_unit_test(buffer test_buffer_basics) add_unit_test(buffer test_buffer_node) add_unit_test(buffer test_buffer_purge) +add_unit_test(builder test_attr) + if(GEOS_FOUND AND PROJ_FOUND) set(GEOS_AND_PROJ_FOUND TRUE) else() @@ -118,6 +121,7 @@ add_unit_test(geom test_factory_with_projection ENABLE_IF ${GEOS_AND_PROJ_FOUND} LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY}) +add_unit_test(geom test_crs ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) add_unit_test(geom test_exception) add_unit_test(geom test_geojson) add_unit_test(geom test_geos ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY}) @@ -133,9 +137,15 @@ add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND}) add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES}) add_unit_test(io test_file_formats) -add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES}") +add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES};${OSMIUM_PBF_LIBRARIES}") +add_unit_test(io test_reader_with_mock_decompression ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) +add_unit_test(io test_reader_with_mock_parser ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) +add_unit_test(io test_output_utils) add_unit_test(io test_output_iterator ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(io test_string_table) +add_unit_test(io test_writer ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) +add_unit_test(io test_writer_with_mock_compression ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) +add_unit_test(io test_writer_with_mock_encoder ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) add_unit_test(tags test_filter) add_unit_test(tags test_operators) @@ -144,10 +154,10 @@ add_unit_test(tags test_tag_list) add_unit_test(thread test_pool ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(util test_cast_with_assert) -add_unit_test(util test_data_file) add_unit_test(util test_delta) add_unit_test(util test_double) add_unit_test(util test_file) +add_unit_test(util test_memory) add_unit_test(util test_memory_mapping) add_unit_test(util test_minmax) add_unit_test(util test_options) diff --git a/third_party/libosmium/test/data-tests/CMakeLists.txt b/third_party/libosmium/test/data-tests/CMakeLists.txt index 89aead98e..a36c31cfd 100644 --- a/third_party/libosmium/test/data-tests/CMakeLists.txt +++ b/third_party/libosmium/test/data-tests/CMakeLists.txt @@ -88,11 +88,11 @@ set_tests_properties(testdata-overview PROPERTIES # #----------------------------------------------------------------------------- -find_package(Ruby 1.9) +find_program(RUBY ruby) find_package(Gem COMPONENTS json) find_program(SPATIALITE spatialite) -if(RUBY_FOUND AND GEM_json_FOUND AND SPATIALITE) +if(RUBY AND GEM_json_FOUND AND SPATIALITE) add_executable(testdata-multipolygon testdata-multipolygon.cpp) target_link_libraries(testdata-multipolygon ${OSMIUM_XML_LIBRARIES} @@ -102,7 +102,7 @@ if(RUBY_FOUND AND GEM_json_FOUND AND SPATIALITE) add_test(NAME testdata-multipolygon COMMAND ${CMAKE_COMMAND} -D OSM_TESTDATA=${OSM_TESTDATA} - -D RUBY=${RUBY_EXECUTABLE} + -D RUBY=${RUBY} -P ${CMAKE_CURRENT_SOURCE_DIR}/run-testdata-multipolygon.cmake) set_tests_properties(testdata-multipolygon PROPERTIES LABELS "data;slow") diff --git a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp index 0fd0d9849..cf4fc521a 100644 --- a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp +++ b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include @@ -41,10 +43,9 @@ inline tagmap_type create_map(const osmium::TagList& taglist) { class TestHandler : public osmium::handler::Handler { - OGRDataSource* m_data_source; - OGRLayer* m_layer_point; - OGRLayer* m_layer_linestring; - OGRLayer* m_layer_polygon; + gdalcpp::Layer m_layer_point; + gdalcpp::Layer m_layer_lines; + gdalcpp::Layer m_layer_mpoly; osmium::geom::OGRFactory<> m_ogr_factory; osmium::geom::WKTFactory<> m_wkt_factory; @@ -55,84 +56,20 @@ class TestHandler : public osmium::handler::Handler { public: - TestHandler(OGRDataSource* data_source) : - m_data_source(data_source), + explicit TestHandler(gdalcpp::Dataset& dataset) : + m_layer_point(dataset, "points", wkbPoint), + m_layer_lines(dataset, "lines", wkbLineString), + m_layer_mpoly(dataset, "multipolygons", wkbMultiPolygon), m_out("multipolygon-tests.json") { - OGRSpatialReference sparef; - sparef.SetWellKnownGeogCS("WGS84"); + m_layer_point.add_field("id", OFTReal, 10); + m_layer_point.add_field("type", OFTString, 30); - /**************/ + m_layer_lines.add_field("id", OFTReal, 10); + m_layer_lines.add_field("type", OFTString, 30); - m_layer_point = m_data_source->CreateLayer("points", &sparef, wkbPoint, nullptr); - if (!m_layer_point) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_id("id", OFTReal); - layer_point_field_id.SetWidth(10); - - if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_point_field_type("type", OFTString); - layer_point_field_type.SetWidth(30); - - if (m_layer_point->CreateField(&layer_point_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - /**************/ - - m_layer_linestring = m_data_source->CreateLayer("lines", &sparef, wkbLineString, nullptr); - if (!m_layer_linestring) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_id("id", OFTReal); - layer_linestring_field_id.SetWidth(10); - - if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_linestring_field_type("type", OFTString); - layer_linestring_field_type.SetWidth(30); - - if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) { - std::cerr << "Creating type field failed.\n"; - exit(1); - } - - /**************/ - - m_layer_polygon = m_data_source->CreateLayer("multipolygons", &sparef, wkbMultiPolygon, nullptr); - if (!m_layer_polygon) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_id("id", OFTInteger); - layer_polygon_field_id.SetWidth(10); - - if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_polygon_field_from_type("from_type", OFTString); - layer_polygon_field_from_type.SetWidth(1); - - if (m_layer_polygon->CreateField(&layer_polygon_field_from_type) != OGRERR_NONE) { - std::cerr << "Creating from_type field failed.\n"; - exit(1); - } + m_layer_mpoly.add_field("id", OFTReal, 10); + m_layer_mpoly.add_field("from_type", OFTString, 1); } ~TestHandler() { @@ -140,34 +77,18 @@ public: } void node(const osmium::Node& node) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn()); - std::unique_ptr ogr_point = m_ogr_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - feature->SetField("type", node.tags().get_value_by_key("type")); - - if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_point, m_ogr_factory.create_point(node)); + feature.set_field("id", static_cast(node.id())); + feature.set_field("type", node.tags().get_value_by_key("type")); + feature.add_to_layer(); } void way(const osmium::Way& way) { try { - std::unique_ptr ogr_linestring = m_ogr_factory.create_linestring(way); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id", static_cast(way.id())); - feature->SetField("type", way.tags().get_value_by_key("type")); - - if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_lines, m_ogr_factory.create_linestring(way)); + feature.set_field("id", static_cast(way.id())); + feature.set_field("type", way.tags().get_value_by_key("type")); + feature.add_to_layer(); } catch (osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } @@ -200,10 +121,8 @@ public: m_out << "INVALID\"\n}"; } try { - std::unique_ptr ogr_polygon = m_ogr_factory.create_multipolygon(area); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn()); - feature->SetGeometry(ogr_polygon.get()); - feature->SetField("id", static_cast(area.orig_id())); + gdalcpp::Feature feature(m_layer_mpoly, m_ogr_factory.create_multipolygon(area)); + feature.set_field("id", static_cast(area.orig_id())); std::string from_type; if (area.from_way()) { @@ -211,14 +130,8 @@ public: } else { from_type = "r"; } - feature->SetField("from_type", from_type.c_str()); - - if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); + feature.set_field("from_type", from_type.c_str()); + feature.add_to_layer(); } catch (osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n"; } @@ -228,26 +141,6 @@ public: /* ================================================== */ -OGRDataSource* initialize_database(const std::string& output_format, const std::string& output_filename) { - OGRRegisterAll(); - - OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(output_format.c_str()); - if (!driver) { - std::cerr << output_format << " driver not available.\n"; - exit(1); - } - - CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - const char* options[] = { "SPATIALITE=TRUE", nullptr }; - OGRDataSource* data_source = driver->CreateDataSource(output_filename.c_str(), const_cast(options)); - if (!data_source) { - std::cerr << "Creation of output file failed.\n"; - exit(1); - } - - return data_source; -} - int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " INFILE\n"; @@ -258,9 +151,10 @@ int main(int argc, char* argv[]) { std::string input_filename(argv[1]); std::string output_filename("multipolygon.db"); - OGRDataSource* data_source = initialize_database(output_format, output_filename); + CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); + gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }}; - osmium::area::ProblemReporterOGR problem_reporter(data_source); + osmium::area::ProblemReporterOGR problem_reporter(dataset); osmium::area::Assembler::config_type assembler_config(&problem_reporter); assembler_config.enable_debug_output(); osmium::area::MultipolygonCollector collector(assembler_config); @@ -275,7 +169,7 @@ int main(int argc, char* argv[]) { location_handler_type location_handler(index); location_handler.ignore_errors(); - TestHandler test_handler(data_source); + TestHandler test_handler(dataset); std::cerr << "Pass 2...\n"; osmium::io::Reader reader2(input_filename); @@ -284,8 +178,5 @@ int main(int argc, char* argv[]) { })); reader2.close(); std::cerr << "Pass 2 done\n"; - - OGRDataSource::DestroyDataSource(data_source); - OGRCleanupAll(); } diff --git a/third_party/libosmium/test/data-tests/testdata-overview.cpp b/third_party/libosmium/test/data-tests/testdata-overview.cpp index 2d63dc684..43d672dd1 100644 --- a/third_party/libosmium/test/data-tests/testdata-overview.cpp +++ b/third_party/libosmium/test/data-tests/testdata-overview.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -15,154 +17,53 @@ typedef osmium::handler::NodeLocationsForWays location_handler_type; class TestOverviewHandler : public osmium::handler::Handler { - OGRDataSource* m_data_source; - - OGRLayer* m_layer_nodes; - OGRLayer* m_layer_labels; - OGRLayer* m_layer_ways; + gdalcpp::Layer m_layer_nodes; + gdalcpp::Layer m_layer_labels; + gdalcpp::Layer m_layer_ways; osmium::geom::OGRFactory<> m_factory; public: - TestOverviewHandler(const std::string& driver_name, const std::string& filename) { + explicit TestOverviewHandler(gdalcpp::Dataset& dataset) : + m_layer_nodes(dataset, "nodes", wkbPoint), + m_layer_labels(dataset, "labels", wkbPoint), + m_layer_ways(dataset, "ways", wkbLineString) { - OGRRegisterAll(); + m_layer_nodes.add_field("id", OFTReal, 10); - OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str()); - if (!driver) { - std::cerr << driver_name << " driver not available.\n"; - exit(1); - } + m_layer_labels.add_field("id", OFTReal, 10); + m_layer_labels.add_field("label", OFTString, 30); - CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - const char* options[] = { "SPATIALITE=TRUE", nullptr }; - m_data_source = driver->CreateDataSource(filename.c_str(), const_cast(options)); - if (!m_data_source) { - std::cerr << "Creation of output file failed.\n"; - exit(1); - } - - OGRSpatialReference sparef; - sparef.SetWellKnownGeogCS("WGS84"); - - // nodes layer - - m_layer_nodes = m_data_source->CreateLayer("nodes", &sparef, wkbPoint, nullptr); - if (!m_layer_nodes) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_nodes_field_id("id", OFTReal); - layer_nodes_field_id.SetWidth(10); - - if (m_layer_nodes->CreateField(&layer_nodes_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - // labels layer - - m_layer_labels = m_data_source->CreateLayer("labels", &sparef, wkbPoint, nullptr); - if (!m_layer_labels) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_labels_field_id("id", OFTReal); - layer_labels_field_id.SetWidth(10); - - if (m_layer_labels->CreateField(&layer_labels_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_labels_field_label("label", OFTString); - layer_labels_field_label.SetWidth(30); - - if (m_layer_labels->CreateField(&layer_labels_field_label) != OGRERR_NONE) { - std::cerr << "Creating label field failed.\n"; - exit(1); - } - - // ways layer - - m_layer_ways = m_data_source->CreateLayer("ways", &sparef, wkbLineString, nullptr); - if (!m_layer_ways) { - std::cerr << "Layer creation failed.\n"; - exit(1); - } - - OGRFieldDefn layer_way_field_id("id", OFTReal); - layer_way_field_id.SetWidth(10); - - if (m_layer_ways->CreateField(&layer_way_field_id) != OGRERR_NONE) { - std::cerr << "Creating id field failed.\n"; - exit(1); - } - - OGRFieldDefn layer_way_field_test("test", OFTInteger); - layer_way_field_test.SetWidth(3); - - if (m_layer_ways->CreateField(&layer_way_field_test) != OGRERR_NONE) { - std::cerr << "Creating test field failed.\n"; - exit(1); - } - } - - ~TestOverviewHandler() { - OGRDataSource::DestroyDataSource(m_data_source); - OGRCleanupAll(); + m_layer_ways.add_field("id", OFTReal, 10); + m_layer_ways.add_field("test", OFTInteger, 3); } void node(const osmium::Node& node) { const char* label = node.tags().get_value_by_key("label"); if (label) { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_labels->GetLayerDefn()); - std::unique_ptr ogr_point = m_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - feature->SetField("label", label); - - if (m_layer_labels->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_labels, m_factory.create_point(node)); + feature.set_field("id", static_cast(node.id())); + feature.set_field("label", label); + feature.add_to_layer(); } else { - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_nodes->GetLayerDefn()); - std::unique_ptr ogr_point = m_factory.create_point(node); - feature->SetGeometry(ogr_point.get()); - feature->SetField("id", static_cast(node.id())); - - if (m_layer_nodes->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - OGRFeature::DestroyFeature(feature); + gdalcpp::Feature feature(m_layer_nodes, m_factory.create_point(node)); + feature.set_field("id", static_cast(node.id())); + feature.add_to_layer(); } } void way(const osmium::Way& way) { try { - std::unique_ptr ogr_linestring = m_factory.create_linestring(way); - OGRFeature* feature = OGRFeature::CreateFeature(m_layer_ways->GetLayerDefn()); - feature->SetGeometry(ogr_linestring.get()); - feature->SetField("id", static_cast(way.id())); + gdalcpp::Feature feature(m_layer_ways, m_factory.create_linestring(way)); + feature.set_field("id", static_cast(way.id())); const char* test = way.tags().get_value_by_key("test"); if (test) { - feature->SetField("test", test); + feature.set_field("test", test); } - if (m_layer_ways->CreateFeature(feature) != OGRERR_NONE) { - std::cerr << "Failed to create feature.\n"; - exit(1); - } - - OGRFeature::DestroyFeature(feature); + feature.add_to_layer(); } catch (osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } @@ -183,13 +84,16 @@ int main(int argc, char* argv[]) { std::string output_filename("testdata-overview.db"); ::unlink(output_filename.c_str()); + CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); + gdalcpp::Dataset dataset(output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }); + osmium::io::Reader reader(input_filename); index_type index; location_handler_type location_handler(index); location_handler.ignore_errors(); - TestOverviewHandler handler(output_format, output_filename); + TestOverviewHandler handler(dataset); osmium::apply(reader, location_handler, handler); reader.close(); diff --git a/third_party/libosmium/test/data-tests/testdata-xml.cpp b/third_party/libosmium/test/data-tests/testdata-xml.cpp index 8102759d1..b5a0e9028 100644 --- a/third_party/libosmium/test/data-tests/testdata-xml.cpp +++ b/third_party/libosmium/test/data-tests/testdata-xml.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -72,24 +73,27 @@ std::string read_gz_file(const char* test_id, const char* suffix) { header_buffer_type parse_xml(std::string input) { - osmium::thread::Queue input_queue; - osmium::thread::Queue output_queue; + osmium::io::detail::future_string_queue_type input_queue; + osmium::io::detail::future_buffer_queue_type output_queue; std::promise header_promise; - std::atomic done {false}; - input_queue.push(input); - input_queue.push(std::string()); // EOF marker + std::future header_future = header_promise.get_future(); - osmium::io::detail::XMLParser parser(input_queue, output_queue, header_promise, osmium::osm_entity_bits::all, done); - parser(); + osmium::io::detail::add_to_queue(input_queue, std::move(input)); + osmium::io::detail::add_to_queue(input_queue, std::string{}); + + osmium::io::detail::XMLParser parser(input_queue, output_queue, header_promise, osmium::osm_entity_bits::all); + parser.parse(); header_buffer_type result; - result.header = header_promise.get_future().get(); - output_queue.wait_and_pop(result.buffer); + result.header = header_future.get(); + std::future future_buffer; + output_queue.wait_and_pop(future_buffer); + result.buffer = future_buffer.get(); if (result.buffer) { - osmium::memory::Buffer buffer; - output_queue.wait_and_pop(buffer); - assert(!buffer); + std::future future_buffer2; + output_queue.wait_and_pop(future_buffer2); + assert(!future_buffer2.get()); } return result; @@ -534,9 +538,10 @@ TEST_CASE("Reading OSM XML 200") { osmium::io::Header header = reader.header(); REQUIRE(header.get("generator") == "testdata"); - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(0 == buffer.committed()); - REQUIRE(! buffer); + REQUIRE_THROWS({ + reader.read(); + }); + reader.close(); } diff --git a/third_party/libosmium/test/include/catch.hpp b/third_party/libosmium/test/include/catch.hpp index 73abfe8c6..f04214990 100644 --- a/third_party/libosmium/test/include/catch.hpp +++ b/third_party/libosmium/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.2.1 - * Generated: 2015-06-30 18:23:27.961086 + * Catch v1.3.3 + * Generated: 2016-01-22 07:51:36.661106 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -21,8 +21,6 @@ // #included from: internal/catch_suppress_warnings.h -#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED - #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) @@ -37,6 +35,7 @@ # 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" @@ -44,7 +43,6 @@ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif - #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL #endif @@ -84,11 +82,19 @@ // 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? +// **************** +// 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 @@ -130,10 +136,13 @@ // GCC #ifdef __GNUC__ -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR #endif +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// @@ -142,6 +151,7 @@ #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)) @@ -151,6 +161,8 @@ #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 ) || \ @@ -165,7 +177,7 @@ // C++ language feature support // catch all support for C++11 -#if (__cplusplus >= 201103L) +#if defined(__cplusplus) && __cplusplus >= 201103L # define CATCH_CPP11_OR_GREATER @@ -193,6 +205,17 @@ # 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 @@ -212,7 +235,16 @@ # 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 +# 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 // noexcept support: @@ -224,8 +256,36 @@ # 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; @@ -312,6 +372,9 @@ namespace Catch { 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 @@ -397,7 +460,7 @@ namespace Catch { template class Ptr { public: - Ptr() : m_p( NULL ){} + Ptr() : m_p( CATCH_NULL ){} Ptr( T* p ) : m_p( p ){ if( m_p ) m_p->addRef(); @@ -413,7 +476,7 @@ namespace Catch { void reset() { if( m_p ) m_p->release(); - m_p = NULL; + m_p = CATCH_NULL; } Ptr& operator = ( T* p ){ Ptr temp( p ); @@ -426,12 +489,11 @@ namespace Catch { return *this; } void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() { return m_p; } - const T* get() const{ return m_p; } + T* get() const{ return m_p; } T& operator*() const { return *m_p; } T* operator->() const { return m_p; } - bool operator !() const { return m_p == NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } + bool operator !() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } private: T* m_p; @@ -528,9 +590,13 @@ namespace Catch { struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const = 0; - + 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 { @@ -563,27 +629,32 @@ struct NameAndDesc { 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 ); + 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 + ( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { - void registerTestCase( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); + registerTestCase + ( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } ~AutoReg(); @@ -592,6 +663,11 @@ private: void operator= ( AutoReg const& ); }; +void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS @@ -615,6 +691,10 @@ private: } \ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ @@ -636,6 +716,9 @@ private: } \ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + /////////////////////////////////////////////////////////////////////////////// + #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 @@ -758,6 +841,323 @@ namespace Catch { } // 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{}; @@ -784,7 +1184,8 @@ namespace Catch { ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, - ResultDisposition::Flags resultDisposition ); + ResultDisposition::Flags resultDisposition, + char const* secondArg = "" ); template ExpressionLhs operator <= ( T const& operand ); @@ -813,6 +1214,9 @@ namespace Catch { 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; @@ -991,13 +1395,51 @@ namespace Internal { 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( NULL, rhs ); + return Evaluator::evaluate( nullptr, rhs ); } template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, NULL ); + return Evaluator::evaluate( lhs, nullptr ); } #endif // CATCH_CONFIG_CPP11_NULLPTR @@ -1095,6 +1537,11 @@ 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 @@ -1107,7 +1554,7 @@ std::string toString( std::nullptr_t ); namespace Detail { - extern std::string unprintableString; + extern const std::string unprintableString; struct BorgType { template BorgType( T const& ); @@ -1190,7 +1637,7 @@ struct StringMaker { template static std::string convert( U* p ) { if( !p ) - return INTERNAL_CATCH_STRINGIFY( NULL ); + return "NULL"; else return Detail::rawMemoryToString( p ); } @@ -1200,7 +1647,7 @@ template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) - return INTERNAL_CATCH_STRINGIFY( NULL ); + return "NULL"; else return Detail::rawMemoryToString( p ); } @@ -1472,6 +1919,7 @@ namespace Catch { class AssertionResult; struct AssertionInfo; struct SectionInfo; + struct SectionEndInfo; struct MessageInfo; class ScopedMessageBuilder; struct Counts; @@ -1483,7 +1931,8 @@ namespace Catch { virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; - virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; + virtual void 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; @@ -1604,16 +2053,16 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ + __catchResult.captureExpectedException( matcher ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ @@ -1666,14 +2115,14 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ try { \ - std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \ + std::string matcherAsString = (matcher).toString(); \ __catchResult \ .setLhs( Catch::toString( arg ) ) \ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ .setOp( "matches" ) \ - .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \ + .setResultType( (matcher).match( arg ) ); \ __catchResult.captureExpression(); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ @@ -1687,21 +2136,6 @@ namespace Catch { // #included from: catch_section_info.h #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED -namespace Catch { - - struct SectionInfo { - SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description = std::string() ); - - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; - -} // end namespace Catch - // #included from: catch_totals.hpp #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED @@ -1772,6 +2206,31 @@ namespace Catch { }; } +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 @@ -2012,6 +2471,8 @@ using namespace Generators; #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED #include +#include + // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED @@ -2036,7 +2497,8 @@ namespace Catch { struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; + 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; }; @@ -2048,14 +2510,16 @@ namespace Catch { } - namespace Catch { typedef std::string(*exceptionTranslateFunction)(); + struct IExceptionTranslator; + typedef std::vector ExceptionTranslators; + struct IExceptionTranslator { virtual ~IExceptionTranslator(); - virtual std::string translate() const = 0; + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; struct IExceptionTranslatorRegistry { @@ -2073,9 +2537,12 @@ namespace Catch { : m_translateFunction( translateFunction ) {} - virtual std::string translate() const { + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { try { - throw; + if( it == itEnd ) + throw; + else + return (*it)->translate( it+1, itEnd ); } catch( T& ex ) { return m_translateFunction( ex ); @@ -2182,231 +2649,6 @@ inline std::string toString( Detail::Approx const& value ) { } // end namespace Catch -// #included from: internal/catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - - template - class AllOf : public MatcherImpl, ExpressionT> { - public: - - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} - - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) - return false; - return true; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) - return true; - return false; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct Equals : MatcherImpl { - Equals( std::string const& str ) : m_str( str ){} - Equals( Equals const& other ) : m_str( other.m_str ){} - - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_str == expr; - } - virtual std::string toString() const { - return "equals: \"" + m_str + "\""; - } - - std::string m_str; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr ) : m_substr( substr ){} - Contains( Contains const& other ) : m_substr( other.m_substr ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr ) : m_substr( substr ){} - StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == 0; - } - virtual std::string toString() const { - return "starts with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr ) : m_substr( substr ){} - EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == expr.size() - m_substr.size(); - } - virtual std::string toString() const { - return "ends with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - } // namespace StdString - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str ) { - return Impl::StdString::Equals( str ); - } - inline Impl::StdString::Equals Equals( const char* str ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); - } - inline Impl::StdString::Contains Contains( std::string const& substr ) { - return Impl::StdString::Contains( substr ); - } - inline Impl::StdString::Contains Contains( const char* substr ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); - } - -} // namespace Matchers - -using namespace Matchers; - -} // namespace Catch - // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -2440,12 +2682,12 @@ namespace Catch { template class Option { public: - Option() : nullableValue( NULL ) {} + Option() : nullableValue( CATCH_NULL ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) + : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) {} ~Option() { @@ -2469,7 +2711,7 @@ namespace Catch { void reset() { if( nullableValue ) nullableValue->~T(); - nullableValue = NULL; + nullableValue = CATCH_NULL; } T& operator*() { return *nullableValue; } @@ -2481,10 +2723,10 @@ namespace Catch { return nullableValue ? *nullableValue : defaultValue; } - bool some() const { return nullableValue != NULL; } - bool none() const { return nullableValue == NULL; } + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } - bool operator !() const { return nullableValue == NULL; } + bool operator !() const { return nullableValue == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( some() ); } @@ -2542,6 +2784,8 @@ namespace Catch { TestCaseInfo( TestCaseInfo const& other ); + friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + bool isHidden() const; bool throws() const; bool okToFail() const; @@ -2654,7 +2898,7 @@ namespace Catch { inline size_t registerTestMethods() { size_t noTestMethods = 0; - int noClasses = objc_getClassList( NULL, 0 ); + int noClasses = objc_getClassList( CATCH_NULL, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); @@ -2796,7 +3040,7 @@ return @ desc; \ #pragma clang diagnostic ignored "-Wweak-vtables" #endif -// #included from: ../catch_runner.hpp +// #included from: ../catch_session.hpp #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED // #included from: internal/catch_commandline.hpp @@ -2821,6 +3065,67 @@ return @ desc; \ #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 @@ -2832,50 +3137,18 @@ namespace Catch { virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; class NamePattern : public Pattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - public: - NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { - if( startsWith( m_name, "*" ) ) { - m_name = m_name.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_name, "*" ) ) { - m_name = m_name.substr( 0, m_name.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } + NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} virtual ~NamePattern(); virtual bool matches( TestCaseInfo const& testCase ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_name == toLower( testCase.name ); - case WildcardAtStart: - return endsWith( toLower( testCase.name ), m_name ); - case WildcardAtEnd: - return startsWith( toLower( testCase.name ), m_name ); - case WildcardAtBothEnds: - return contains( toLower( testCase.name ), m_name ); - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif + return m_wildcardPattern.matches( toLower( testCase.name ) ); } private: - std::string m_name; - WildcardPosition m_wildcard; + WildcardPattern m_wildcardPattern; }; + class TagPattern : public Pattern { public: TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} @@ -2886,6 +3159,7 @@ namespace Catch { private: std::string m_tag; }; + class ExcludedPattern : public Pattern { public: ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} @@ -3083,28 +3357,62 @@ namespace Catch { // #included from: catch_stream.h #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED -#include +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif +#include namespace Catch { - class Stream { + class StreamBufBase : public std::streambuf { public: - Stream(); - Stream( std::streambuf* _streamBuf, bool _isOwned ); - void release(); - - std::streambuf* streamBuf; - - private: - bool isOwned; + 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 @@ -3132,6 +3440,7 @@ namespace Catch { showHelp( false ), showInvisibles( false ), forceColour( false ), + filenamesAsTags( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), @@ -3151,6 +3460,7 @@ namespace Catch { bool showHelp; bool showInvisibles; bool forceColour; + bool filenamesAsTags; int abortAfter; unsigned int rngSeed; @@ -3160,11 +3470,11 @@ namespace Catch { ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; - std::string reporterName; std::string outputFilename; std::string name; std::string processName; + std::vector reporterNames; std::vector testsOrTags; }; @@ -3176,12 +3486,11 @@ namespace Catch { public: Config() - : m_os( Catch::cout().rdbuf() ) {} Config( ConfigData const& data ) : m_data( data ), - m_os( Catch::cout().rdbuf() ) + m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); @@ -3192,12 +3501,6 @@ namespace Catch { } virtual ~Config() { - m_os.rdbuf( Catch::cout().rdbuf() ); - m_stream.release(); - } - - void setFilename( std::string const& filename ) { - m_data.outputFilename = filename; } std::string const& getFilename() const { @@ -3213,18 +3516,7 @@ namespace Catch { bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() ); - } - - void useStream( std::string const& streamName ) { - Stream stream = createStream( streamName ); - setStreamBuf( stream.streamBuf ); - m_stream.release(); - m_stream = stream; - } - - std::string getReporterName() const { return m_data.reporterName; } + std::vector getReporterNames() const { return m_data.reporterNames; } int abortAfter() const { return m_data.abortAfter; } @@ -3235,7 +3527,7 @@ namespace Catch { // IConfig interface virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_os; } + 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; } @@ -3245,10 +3537,22 @@ namespace Catch { virtual bool forceColour() const { return m_data.forceColour; } 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; - Stream m_stream; - mutable std::ostream m_os; + std::auto_ptr m_stream; TestSpec m_testSpec; }; @@ -3517,11 +3821,11 @@ namespace Clara { template class BoundArgFunction { public: - BoundArgFunction() : functionObj( NULL ) {} + BoundArgFunction() : functionObj( CATCH_NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; delete functionObj; functionObj = newFunctionObj; return *this; @@ -3537,7 +3841,7 @@ namespace Clara { bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { - return functionObj != NULL; + return functionObj != CATCH_NULL; } private: IArgFunction* functionObj; @@ -3755,12 +4059,7 @@ namespace Clara { } }; - // NOTE: std::auto_ptr is deprecated in c++11/c++0x -#if defined(__cplusplus) && __cplusplus > 199711L - typedef std::unique_ptr ArgAutoPtr; -#else - typedef std::auto_ptr ArgAutoPtr; -#endif + typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr; friend void addOptName( Arg& arg, std::string const& optName ) { @@ -3836,8 +4135,8 @@ namespace Clara { m_arg->description = description; return *this; } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; + ArgBuilder& detail( std::string const& _detail ) { + m_arg->detail = _detail; return *this; } @@ -3920,14 +4219,14 @@ namespace Clara { maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() + Detail::Text usageText( 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] : ""; + for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) { + std::string usageCol = i < usageText.size() ? usageText[i] : ""; os << usageCol; if( i < desc.size() && !desc[i].empty() ) @@ -4133,6 +4432,7 @@ namespace Catch { 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" ) @@ -4226,7 +4526,7 @@ namespace Catch { cli["-r"]["--reporter"] // .placeholder( "name[:filename]" ) .describe( "reporter to use (defaults to console)" ) - .bind( &ConfigData::reporterName, "name" ); + .bind( &addReporterName, "name" ); cli["-n"]["--name"] .describe( "suite name" ) @@ -4263,6 +4563,10 @@ namespace Catch { .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" ) @@ -4520,18 +4824,18 @@ namespace Catch { namespace Catch { struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) + explicit ReporterConfig( Ptr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + 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; } + Ptr fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; - Ptr m_fullConfig; + Ptr m_fullConfig; }; struct ReporterPreferences { @@ -4733,6 +5037,7 @@ namespace Catch // 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; @@ -4741,20 +5046,24 @@ namespace Catch virtual void skipTest( TestCaseInfo const& testInfo ) = 0; }; - struct IReporterFactory { + 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::map > FactoryMap; + typedef std::vector > Listeners; virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + 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 @@ -4777,8 +5086,7 @@ namespace Catch { nameAttr.setInitialIndent( 2 ).setIndent( 4 ); tagsAttr.setIndent( 6 ); - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { @@ -4806,8 +5114,7 @@ namespace Catch { if( !config.testSpec().hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { @@ -4847,8 +5154,7 @@ namespace Catch { std::map tagCounts; - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { @@ -4919,7 +5225,7 @@ namespace Catch { } // end namespace Catch -// #included from: internal/catch_runner_impl.hpp +// #included from: internal/catch_run_context.hpp #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED // #included from: catch_test_case_tracker.hpp @@ -4928,137 +5234,300 @@ namespace Catch { #include #include #include +#include namespace Catch { -namespace SectionTracking { +namespace TestCaseTracking { - class TrackedSection { + struct ITracker : SharedImpl<> { + virtual ~ITracker(); - typedef std::map TrackedSections; + // 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 { - public: enum RunState { NotStarted, Executing, - ExecutingChildren, - Completed + CompletedCycle }; - TrackedSection( std::string const& name, TrackedSection* parent ) - : m_name( name ), m_runState( NotStarted ), m_parent( parent ) + 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 ) {} - RunState runState() const { return m_runState; } + ITracker& startRun(); - TrackedSection* findChild( std::string const& childName ); - TrackedSection* acquireChild( std::string const& childName ); - - void enter() { - if( m_runState == NotStarted ) - m_runState = Executing; + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; } - void leave(); - TrackedSection* getParent() { - return m_parent; + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; } - bool hasChildren() const { + 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(); } - private: - std::string m_name; - RunState m_runState; - TrackedSections m_children; - TrackedSection* m_parent; - }; + virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { + m_children.push_back( child ); + } - inline TrackedSection* TrackedSection::findChild( std::string const& childName ) { - TrackedSections::iterator it = m_children.find( childName ); - return it != m_children.end() - ? &it->second - : NULL; - } - inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) { - if( TrackedSection* child = findChild( childName ) ) - return child; - m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); - return findChild( childName ); - } - inline void TrackedSection::leave() { - for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); - it != itEnd; - ++it ) - if( it->second.runState() != Completed ) { + 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; - return; + if( m_parent ) + m_parent->openChild(); } - m_runState = Completed; - } - - class TestCaseTracker { - public: - TestCaseTracker( std::string const& testCaseName ) - : m_testCase( testCaseName, NULL ), - m_currentSection( &m_testCase ), - m_completedASectionThisRun( false ) - {} - - bool enterSection( std::string const& name ) { - TrackedSection* child = m_currentSection->acquireChild( name ); - if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) - return false; - - m_currentSection = child; - m_currentSection->enter(); - return true; } - void leaveSection() { - m_currentSection->leave(); - m_currentSection = m_currentSection->getParent(); - assert( m_currentSection != NULL ); - m_completedASectionThisRun = true; + void open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); } - bool currentSectionHasChildren() const { - return m_currentSection->hasChildren(); - } - bool isCompleted() const { - return m_testCase.runState() == TrackedSection::Completed; - } + virtual void close() CATCH_OVERRIDE { - class Guard { - public: - Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { - m_tracker.enterTestCase(); + // 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" ); } - ~Guard() { - m_tracker.leaveTestCase(); - } - private: - Guard( Guard const& ); - void operator = ( Guard const& ); - TestCaseTracker& m_tracker; - }; - + 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 enterTestCase() { - m_currentSection = &m_testCase; - m_completedASectionThisRun = false; - m_testCase.enter(); + void moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); } - void leaveTestCase() { - m_testCase.leave(); + void moveToThis() { + m_ctx.setCurrentTracker( this ); } - - TrackedSection m_testCase; - TrackedSection* m_currentSection; - bool m_completedASectionThisRun; }; -} // namespace SectionTracking + class SectionTracker : public TrackerBase { + public: + SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( name, ctx, parent ) + {} + virtual ~SectionTracker(); -using SectionTracking::TestCaseTracker; + 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 @@ -5174,15 +5643,12 @@ namespace Catch { public: - explicit RunContext( Ptr const& config, Ptr const& reporter ) - : m_runInfo( config->name() ), + explicit RunContext( Ptr const& _config, Ptr const& reporter ) + : m_runInfo( _config->name() ), m_context( getCurrentMutableContext() ), - m_activeTestCase( NULL ), - m_config( config ), - m_reporter( reporter ), - m_prevRunner( m_context.getRunner() ), - m_prevResultCapture( m_context.getResultCapture() ), - m_prevConfig( m_context.getConfig() ) + m_activeTestCase( CATCH_NULL ), + m_config( _config ), + m_reporter( reporter ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -5192,10 +5658,6 @@ namespace Catch { virtual ~RunContext() { m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - m_context.setRunner( m_prevRunner ); - m_context.setConfig( NULL ); - m_context.setResultCapture( m_prevResultCapture ); - m_context.setConfig( m_prevConfig ); } void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { @@ -5216,14 +5678,17 @@ namespace Catch { m_reporter->testCaseStarting( testInfo ); m_activeTestCase = &testCase; - m_testCaseTracker = TestCaseTracker( testInfo.name ); do { + m_trackerContext.startRun(); do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); runCurrentTest( redirectedCout, redirectedCerr ); } - while( !m_testCaseTracker->isCompleted() && !aborting() ); + while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); } + // !TBD: deprecated - this will be replaced by indexed trackers while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); @@ -5234,8 +5699,8 @@ namespace Catch { redirectedCerr, aborting() ) ); - m_activeTestCase = NULL; - m_testCaseTracker.reset(); + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; return deltaTotals; } @@ -5270,8 +5735,10 @@ namespace Catch { std::ostringstream oss; oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - if( !m_testCaseTracker->enterSection( oss.str() ) ) + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + if( !sectionTracker.isOpen() ) return false; + m_activeSections.push_back( §ionTracker ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; @@ -5282,30 +5749,40 @@ namespace Catch { return true; } bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 || - !m_config->warnAboutMissingAssertions() || - m_testCaseTracker->currentSectionHasChildren() ) + 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( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { - /* if( std::uncaught_exception() ) { // XXX Hack that makes Catch not run in loop in certain situations - m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); - return; - }*/ - - Counts assertions = m_totals.assertions - prevAssertions; + virtual void sectionEnded( SectionEndInfo const& endInfo ) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); - m_testCaseTracker->leaveSection(); + if( !m_activeSections.empty() ) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } - m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); + 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 ); } @@ -5371,7 +5848,8 @@ namespace Catch { double duration = 0; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); - TestCaseTracker::Guard guard( *m_testCaseTracker ); + + seedRng( *m_config ); Timer timer; timer.start(); @@ -5391,6 +5869,7 @@ namespace Catch { catch(...) { makeUnexpectedResultBuilder().useActiveException(); } + m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); @@ -5425,39 +5904,29 @@ namespace Catch { 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(), + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it ) - sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); + sectionEnded( *it ); m_unfinishedSections.clear(); } - struct UnfinishedSections { - UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) - : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - - SectionInfo info; - Counts prevAssertions; - double durationInSeconds; - }; - TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; - Option m_testCaseTracker; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; AssertionResult m_lastResult; Ptr m_config; Totals m_totals; Ptr m_reporter; std::vector m_messages; - IRunner* m_prevRunner; - IResultCapture* m_prevResultCapture; - Ptr m_prevConfig; AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; }; IResultCapture& getResultCapture() { @@ -5505,89 +5974,87 @@ namespace Catch { namespace Catch { - class Runner { + 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; + } - public: - Runner( Ptr const& config ) - : m_config( config ) - { - openStream(); - makeReporter(); + 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 ); } - Totals runTests() { + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); + return totals; + } - RunContext context( m_config.get(), m_reporter ); + 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; - Totals totals; + 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 ); - context.testGroupStarting( "all tests", 1, 1 ); // deprecated? + std::string::size_type lastDot = filename.find_last_of( "." ); + if( lastDot != std::string::npos ) + filename = filename.substr( 0, lastDot ); - TestSpec testSpec = m_config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector testCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); - - int testsRunForGroup = 0; - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) { - testsRunForGroup++; - if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { - - if( context.aborting() ) - break; - - totals += context.runTest( *it ); - m_testsAlreadyRun.insert( *it ); - } - } - std::vector skippedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true ); - - for( std::vector::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end(); - it != itEnd; - ++it ) - m_reporter->skipTest( *it ); - - context.testGroupEnded( "all tests", totals, 1, 1 ); - return totals; + tags.insert( "#" + filename ); + setTags( test, tags ); } - - private: - void openStream() { - // Open output file, if specified - if( !m_config->getFilename().empty() ) { - m_ofs.open( m_config->getFilename().c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << m_config->getFilename() << "'"; - throw std::domain_error( oss.str() ); - } - m_config->setStreamBuf( m_ofs.rdbuf() ); - } - } - void makeReporter() { - std::string reporterName = m_config->getReporterName().empty() - ? "console" - : m_config->getReporterName(); - - m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); - if( !m_reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - } - - private: - Ptr m_config; - std::ofstream m_ofs; - Ptr m_reporter; - std::set m_testsAlreadyRun; - }; + } class Session : NonCopyable { static bool alreadyInstantiated; @@ -5616,7 +6083,7 @@ namespace Catch { Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } - int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); @@ -5643,7 +6110,7 @@ namespace Catch { m_config.reset(); } - int run( int argc, char* const argv[] ) { + int run( int argc, char const* const argv[] ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) @@ -5659,15 +6126,16 @@ namespace Catch { { config(); // Force config to be constructed - std::srand( m_configData.rngSeed ); + seedRng( *m_config ); - Runner runner( m_config ); + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); // Handle list request if( Option listed = list( config() ) ) return static_cast( *listed ); - return static_cast( runner.runTests().assertions.failed ); + return static_cast( runTests( m_config ).assertions.failed ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; @@ -5689,7 +6157,6 @@ namespace Catch { m_config = new Config( m_configData ); return *m_config; } - private: Clara::CommandLine m_cli; std::vector m_unusedTokens; @@ -5715,16 +6182,76 @@ namespace Catch { namespace Catch { - class TestRegistry : public ITestCaseRegistry { - 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_unnamedCount( 0 ) {} + TestRegistry() + : m_currentSortOrder( RunTests::InDeclarationOrder ), + m_unnamedCount( 0 ) + {} virtual ~TestRegistry(); virtual void registerTest( TestCase const& testCase ) { @@ -5734,69 +6261,29 @@ namespace Catch { oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); } - - if( m_functions.find( testCase ) == m_functions.end() ) { - m_functions.insert( testCase ); - m_functionsInOrder.push_back( testCase ); - if( !testCase.isHidden() ) - m_nonHiddenFunctions.push_back( testCase ); - } - else { - TestCase const& prev = *m_functions.find( testCase ); - { - Colour colourGuard( Colour::Red ); - Catch::cerr() << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; - } - exit(1); - } + m_functions.push_back( testCase ); } virtual std::vector const& getAllTests() const { - return m_functionsInOrder; + return m_functions; } + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); - virtual std::vector const& getAllNonHiddenTests() const { - return m_nonHiddenFunctions; - } - - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const { - - for( std::vector::const_iterator it = m_functionsInOrder.begin(), - itEnd = m_functionsInOrder.end(); - it != itEnd; - ++it ) { - bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ); - if( includeTest != negated ) - matchingTestCases.push_back( *it ); + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); } - sortTests( config, matchingTestCases ); + return m_sortedFunctions; } private: - - static void sortTests( IConfig const& config, std::vector& matchingTestCases ) { - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() ); - break; - case RunTests::InRandomOrder: - { - RandomNumberGenerator rng; - std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - } - std::set m_functions; - std::vector m_functionsInOrder; - std::vector m_nonHiddenFunctions; + 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 }; /////////////////////////////////////////////////////////////////////////// @@ -5829,29 +6316,38 @@ namespace Catch { return className; } - /////////////////////////////////////////////////////////////////////////// + void registerTestCase + ( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { - AutoReg::AutoReg( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { + 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() {} + /////////////////////////////////////////////////////////////////////////// - void AutoReg::registerTestCase( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); + 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 @@ -5865,27 +6361,32 @@ namespace Catch { public: - virtual ~ReporterRegistry() { - deleteAllValues( m_factories ); - } + virtual ~ReporterRegistry() CATCH_OVERRIDE {} - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const { + 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 NULL; + return CATCH_NULL; return it->second->create( ReporterConfig( config ) ); } - void registerReporter( std::string const& name, IReporterFactory* factory ) { + 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 ); + } - FactoryMap const& getFactories() const { + 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; }; } @@ -5913,13 +6414,13 @@ namespace Catch { #ifdef __OBJC__ // In Objective-C try objective-c exceptions first @try { - throw; + return tryTranslators(); } @catch (NSException *exception) { return Catch::toString( [exception description] ); } #else - throw; + return tryTranslators(); #endif } catch( TestFailureException& ) { @@ -5935,20 +6436,15 @@ namespace Catch { return msg; } catch(...) { - return tryTranslators( m_translators.begin() ); + return "Unknown exception"; } } - std::string tryTranslators( std::vector::const_iterator it ) const { - if( it == m_translators.end() ) - return "Unknown exception"; - - try { - return (*it)->translate(); - } - catch(...) { - return tryTranslators( it+1 ); - } + std::string tryTranslators() const { + if( m_translators.empty() ) + throw; + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); } private: @@ -5968,24 +6464,27 @@ namespace Catch { public: // IRegistryHub RegistryHub() { } - virtual IReporterRegistry const& getReporterRegistry() const { + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { return m_reporterRegistry; } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { return m_testCaseRegistry; } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { + virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerReporter( name, factory ); } - virtual void registerTest( TestCase const& testInfo ) { + 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 ) { + virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } @@ -5997,7 +6496,7 @@ namespace Catch { // Single, global, instance inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = NULL; + static RegistryHub* theRegistryHub = CATCH_NULL; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; @@ -6012,7 +6511,7 @@ namespace Catch { } void cleanUp() { delete getTheRegistryHub(); - getTheRegistryHub() = NULL; + getTheRegistryHub() = CATCH_NULL; cleanUpContext(); } std::string translateActiveException() { @@ -6048,19 +6547,6 @@ namespace Catch { // #included from: catch_stream.hpp #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; - }; -} - #include #include #include @@ -6105,6 +6591,19 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// + 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 ) { @@ -6112,23 +6611,26 @@ namespace Catch { } }; - Stream::Stream() - : streamBuf( NULL ), isOwned( false ) + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) {} - Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) - {} - - void Stream::release() { - if( isOwned ) { - delete streamBuf; - streamBuf = NULL; - isOwned = false; - } + std::ostream& DebugOutStream::stream() const { + return m_os; } -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions + // 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; } @@ -6142,7 +6644,7 @@ namespace Catch { class Context : public IMutableContext { - Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} + Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} Context( Context const& ); void operator=( Context const& ); @@ -6188,7 +6690,7 @@ namespace Catch { m_generatorsByTestName.find( testName ); return it != m_generatorsByTestName.end() ? it->second - : NULL; + : CATCH_NULL; } IGeneratorsForTest& getGeneratorsForCurrentTest() { @@ -6209,7 +6711,7 @@ namespace Catch { }; namespace { - Context* currentContext = NULL; + Context* currentContext = CATCH_NULL; } IMutableContext& getCurrentMutableContext() { if( !currentContext ) @@ -6220,17 +6722,9 @@ namespace Catch { return getCurrentMutableContext(); } - Stream createStream( std::string const& streamName ) { - if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false ); - if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false ); - if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); - - throw std::domain_error( "Unknown stream: " + streamName ); - } - void cleanUpContext() { delete currentContext; - currentContext = NULL; + currentContext = CATCH_NULL; } } @@ -6286,12 +6780,13 @@ namespace { { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); - originalAttributes = csbiInfo.wAttributes; + 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( originalAttributes ); + 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 ); @@ -6311,10 +6806,11 @@ namespace { private: void setTextAttribute( WORD _textAttribute ) { - SetConsoleTextAttribute( stdoutHandle, _textAttribute ); + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); } HANDLE stdoutHandle; - WORD originalAttributes; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; }; IColourImpl* platformColourInstance() { @@ -6640,6 +7136,21 @@ namespace Catch { 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, @@ -6648,18 +7159,10 @@ namespace Catch { : name( _name ), className( _className ), description( _description ), - tags( _tags ), lineInfo( _lineInfo ), properties( None ) { - std::ostringstream oss; - for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; - std::string lcaseTag = toLower( *it ); - properties = static_cast( properties | parseSpecialTag( lcaseTag ) ); - lcaseTags.insert( lcaseTag ); - } - tagsAsString = oss.str(); + setTags( *this, _tags ); } TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) @@ -6767,7 +7270,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 2, 1, "", 0 ); + Version libraryVersion( 1, 3, 3, "", 0 ); } @@ -6960,7 +7463,7 @@ namespace Catch { #else uint64_t getCurrentTicks() { timeval t; - gettimeofday(&t,NULL); + gettimeofday(&t,CATCH_NULL); return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif @@ -7059,6 +7562,14 @@ namespace Catch { 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 << ")"; @@ -7098,8 +7609,13 @@ namespace Catch { } Section::~Section() { - if( m_sectionIncluded ) - getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); + 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 @@ -7151,7 +7667,7 @@ namespace Catch { // Call sysctl. size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { + 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; } @@ -7205,9 +7721,11 @@ namespace Catch { namespace Detail { - std::string unprintableString = "{?}"; + const std::string unprintableString = "{?}"; namespace { + const int hexThreshold = 255; + struct Endianness { enum Arch { Big, Little }; @@ -7289,7 +7807,7 @@ std::string toString( wchar_t* const value ) std::string toString( int value ) { std::ostringstream oss; oss << value; - if( value >= 255 ) + if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } @@ -7297,7 +7815,7 @@ std::string toString( int value ) { std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; - if( value >= 255 ) + if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } @@ -7347,6 +7865,23 @@ 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"; @@ -7376,11 +7911,17 @@ std::string toString( std::nullptr_t ) { 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 ) - : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ), + ResultDisposition::Flags resultDisposition, + char const* secondArg ) + : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), m_shouldDebugBreak( false ), m_shouldThrow( false ) {} @@ -7421,9 +7962,35 @@ namespace Catch { 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() ) { @@ -7576,6 +8143,137 @@ namespace Catch { } // 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 @@ -7591,47 +8289,53 @@ namespace Catch { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) - {} + { + m_reporterPrefs.shouldRedirectStdOut = false; + } - virtual ~StreamingReporterBase(); + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } - virtual void noMatchingTestCases( std::string const& ) {} + virtual ~StreamingReporterBase() CATCH_OVERRIDE; - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { + virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { currentTestRunInfo = _testRunInfo; } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) { + virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { currentGroupInfo = _groupInfo; } - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { currentTestCaseInfo = _testInfo; } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_sectionStack.push_back( _sectionInfo ); } - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { m_sectionStack.pop_back(); } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { currentGroupInfo.reset(); } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } - virtual void skipTest( TestCaseInfo const& ) { + 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; + Ptr m_config; std::ostream& stream; LazyStat currentTestRunInfo; @@ -7639,6 +8343,7 @@ namespace Catch { LazyStat currentTestCaseInfo; std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; }; struct CumulativeReporterBase : SharedImpl { @@ -7689,15 +8394,21 @@ namespace Catch { CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) - {} + { + m_reporterPrefs.shouldRedirectStdOut = false; + } ~CumulativeReporterBase(); - virtual void testRunStarting( TestRunInfo const& ) {} - virtual void testGroupStarting( GroupInfo const& ) {} + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } - virtual void testCaseStarting( TestCaseInfo const& ) {} + virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} + virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} - virtual void sectionStarting( SectionInfo const& sectionInfo ) { + 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() ) { @@ -7722,7 +8433,7 @@ namespace Catch { m_deepestSection = node; } - virtual void assertionStarting( AssertionInfo const& ) {} + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& assertionStats ) { assert( !m_sectionStack.empty() ); @@ -7730,13 +8441,13 @@ namespace Catch { sectionNode.assertions.push_back( assertionStats ); return true; } - virtual void sectionEnded( SectionStats const& sectionStats ) { + 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 ) { + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { Ptr node = new TestCaseNode( testCaseStats ); assert( m_sectionStack.size() == 0 ); node->children.push_back( m_rootSection ); @@ -7747,12 +8458,12 @@ namespace Catch { m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + 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 ) { + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { Ptr node = new TestRunNode( testRunStats ); node->children.swap( m_testGroups ); m_testRuns.push_back( node ); @@ -7760,9 +8471,9 @@ namespace Catch { } virtual void testRunEndedCumulative() = 0; - virtual void skipTest( TestCaseInfo const& ) {} + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} - Ptr m_config; + Ptr m_config; std::ostream& stream; std::vector m_assertions; std::vector > > m_sections; @@ -7774,6 +8485,7 @@ namespace Catch { Ptr m_rootSection; Ptr m_deepestSection; std::vector > m_sectionStack; + ReporterPreferences m_reporterPrefs; }; @@ -7787,6 +8499,17 @@ namespace Catch { 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 @@ -7817,7 +8540,7 @@ namespace Catch { template class ReporterRegistrar { - class ReporterFactory : public IReporterFactory { + class ReporterFactory : public SharedImpl { // *** Please Note ***: // - If you end up here looking at a compiler error because it's trying to register @@ -7845,22 +8568,102 @@ namespace Catch { 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: @@ -7872,7 +8675,7 @@ namespace Catch { ScopedElement( ScopedElement const& other ) : m_writer( other.m_writer ){ - other.m_writer = NULL; + other.m_writer = CATCH_NULL; } ~ScopedElement() { @@ -7943,11 +8746,8 @@ namespace Catch { } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) { - stream() << " " << name << "=\""; - writeEncodedText( attribute ); - stream() << "\""; - } + if( !name.empty() && !attribute.empty() ) + stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; return *this; } @@ -7958,9 +8758,9 @@ namespace Catch { template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - if( !name.empty() ) - stream() << " " << name << "=\"" << attribute << "\""; - return *this; + std::ostringstream oss; + oss << attribute; + return writeAttribute( name, oss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ) { @@ -7969,7 +8769,7 @@ namespace Catch { ensureTagClosed(); if( tagWasOpen && indent ) stream() << m_indent; - writeEncodedText( text ); + stream() << XmlEncode( text ); m_needsNewline = true; } return *this; @@ -8014,30 +8814,6 @@ namespace Catch { } } - void writeEncodedText( std::string const& text ) { - static const char* charsToEncode = "<&\""; - std::string mtext = text; - std::string::size_type pos = mtext.find_first_of( charsToEncode ); - while( pos != std::string::npos ) { - stream() << mtext.substr( 0, pos ); - - switch( mtext[pos] ) { - case '<': - stream() << "<"; - break; - case '&': - stream() << "&"; - break; - case '\"': - stream() << """; - break; - } - mtext = mtext.substr( pos+1 ); - pos = mtext.find_first_of( charsToEncode ); - } - stream() << mtext; - } - bool m_tagIsOpen; bool m_needsNewline; std::vector m_tags; @@ -8046,32 +8822,44 @@ namespace Catch { }; } +// #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(); + virtual ~XmlReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as an XML document"; } public: // StreamingReporterBase - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = true; - return prefs; - } - virtual void noMatchingTestCases( std::string const& s ) { + virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { StreamingReporterBase::noMatchingTestCases( s ); } - virtual void testRunStarting( TestRunInfo const& testInfo ) { + virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); m_xml.setStream( stream ); m_xml.startElement( "Catch" ); @@ -8079,13 +8867,13 @@ namespace Catch { m_xml.writeAttribute( "name", m_config->name() ); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) { + 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 ) { + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); @@ -8093,7 +8881,7 @@ namespace Catch { m_testCaseTimer.start(); } - virtual void sectionStarting( SectionInfo const& sectionInfo ) { + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) @@ -8102,9 +8890,9 @@ namespace Catch { } } - virtual void assertionStarting( AssertionInfo const& ) { } + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } - virtual bool assertionEnded( AssertionStats const& assertionStats ) { + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { const AssertionResult& assertionResult = assertionStats.assertionResult; // Print any info messages in tags. @@ -8175,7 +8963,7 @@ namespace Catch { return true; } - virtual void sectionEnded( SectionStats const& sectionStats ) { + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); @@ -8190,7 +8978,7 @@ namespace Catch { } } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + 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() ); @@ -8201,7 +8989,7 @@ namespace Catch { m_xml.endElement(); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) @@ -8211,7 +8999,7 @@ namespace Catch { m_xml.endElement(); } - virtual void testRunEnded( TestRunStats const& testRunStats ) { + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) @@ -8242,28 +9030,24 @@ namespace Catch { JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) - {} + { + m_reporterPrefs.shouldRedirectStdOut = true; + } - ~JunitReporter(); + 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*/ ) {} + virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = true; - return prefs; - } - - virtual void testRunStarting( TestRunInfo const& runInfo ) { + virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) { + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); @@ -8271,25 +9055,25 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } - virtual bool assertionEnded( AssertionStats const& assertionStats ) { + 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 ) { + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } - virtual void testRunEndedCumulative() { + virtual void testRunEndedCumulative() CATCH_OVERRIDE { xml.endElement(); } @@ -8454,24 +9238,19 @@ namespace Catch { m_headerPrinted( false ) {} - virtual ~ConsoleReporter(); + virtual ~ConsoleReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as plain lines of text"; } - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - virtual void noMatchingTestCases( std::string const& spec ) { + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { stream << "No test cases matched '" << spec << "'" << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) { + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; @@ -8491,11 +9270,11 @@ namespace Catch { return true; } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } - virtual void sectionEnded( SectionStats const& _sectionStats ) { + virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); @@ -8517,11 +9296,11 @@ namespace Catch { StreamingReporterBase::sectionEnded( _sectionStats ); } - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; @@ -8530,7 +9309,7 @@ namespace Catch { } StreamingReporterBase::testGroupEnded( _testGroupStats ); } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { + virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; @@ -9169,8 +9948,14 @@ namespace Catch { } // 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() {} @@ -9204,6 +9989,7 @@ namespace Catch { FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} + WildcardPattern::~WildcardPattern() {} TestSpec::Pattern::~Pattern() {} TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} @@ -9215,6 +10001,13 @@ namespace Catch { Matchers::Impl::StdString::EndsWith::~EndsWith() {} void Config::dummy() {} + + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } } #ifdef __clang__ @@ -9230,7 +10023,7 @@ namespace Catch { #ifndef __OBJC__ // Standard C/C++ main entry point -int main (int argc, char * const argv[]) { +int main (int argc, char * argv[]) { return Catch::Session().run( argc, argv ); } @@ -9268,8 +10061,9 @@ int main (int argc, char * const argv[]) { #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( 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" ) @@ -9280,6 +10074,7 @@ int main (int argc, char * const argv[]) { #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" ) @@ -9295,6 +10090,7 @@ int main (int argc, char * const argv[]) { #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__ ) @@ -9302,6 +10098,7 @@ int main (int argc, char * const argv[]) { #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 ) @@ -9321,11 +10118,11 @@ int main (int argc, char * const argv[]) { #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) +#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 @@ -9333,8 +10130,9 @@ int main (int argc, char * const argv[]) { #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( 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" ) @@ -9343,8 +10141,9 @@ int main (int argc, char * const argv[]) { #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( 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" ) @@ -9360,6 +10159,7 @@ int main (int argc, char * const argv[]) { #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__ ) @@ -9367,6 +10167,7 @@ int main (int argc, char * const argv[]) { #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 ) @@ -9390,27 +10191,13 @@ int main (int argc, char * const argv[]) { #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif -#define GIVEN( desc ) SECTION( " Given: " desc, "" ) -#define WHEN( desc ) SECTION( " When: " desc, "" ) -#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) -#define THEN( desc ) SECTION( " Then: " desc, "" ) -#define AND_THEN( desc ) SECTION( " And: " desc, "" ) +#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; -// #included from: internal/catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/third_party/libosmium/test/include/catch_orig.hpp b/third_party/libosmium/test/include/catch_orig.hpp deleted file mode 100644 index de61226cf..000000000 --- a/third_party/libosmium/test/include/catch_orig.hpp +++ /dev/null @@ -1,9416 +0,0 @@ -/* - * Catch v1.2.1 - * Generated: 2015-06-30 18:23:27.961086 - * ---------------------------------------------------------- - * 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 - -#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -# pragma clang diagnostic ignored "-Wswitch-enum" -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wpadded" -#endif - -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -#endif - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED - -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED - -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include -#include - -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED - -// 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_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// 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 - -#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 - -#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__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#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 - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if (__cplusplus >= 201103L) - -# define 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 - -#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 - -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) -#endif - -namespace Catch { - - class NonCopyable { -#ifdef CATCH_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 ); - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() { - return std::string(); - } - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); - -#include - -namespace Catch { - - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; - }; - -} // end namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) - -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED - -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() { return m_p; } - const T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#include -#include -#include - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED - -#include - -namespace Catch { - - class TestSpec; - - struct ITestCase : IShared { - virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const = 0; - - }; -} - -namespace Catch { - -template -class MethodTestCase : public SharedImpl { - -public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} - - virtual void invoke() const { - C obj; - (obj.*m_method)(); - } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); -}; - -typedef void(*TestFunction)(); - -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} - - const char* name; - const char* description; -}; - -struct AutoReg { - - AutoReg( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - registerTestCase( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - - void registerTestCase( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - - ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); -}; - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE( ... ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ - namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() - -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() - -#endif - -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED - -// #included from: catch_result_builder.h -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED - -// #included from: catch_result_type.h -#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED - -namespace Catch { - - // ResultWas::OfType enum - struct ResultWas { enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, - - FailureBit = 0x10, - - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, - - Exception = 0x100 | FailureBit, - - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, - - FatalErrorCondition = 0x200 | FailureBit - - }; }; - - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 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 - -namespace Catch { - - struct TestFailureException{}; - - template class ExpressionLhs; - - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; - }; - - class ResultBuilder { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition ); - - template - ExpressionLhs operator <= ( T const& operand ); - ExpressionLhs operator <= ( bool value ); - - template - ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; - return *this; - } - - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - - void endExpression(); - - std::string reconstructExpression() const; - AssertionResult build() const; - - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; - - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; - - bool m_shouldDebugBreak; - bool m_shouldThrow; - }; - -} // namespace Catch - -// Include after due to circular dependency: -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED - -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#endif - -#include - -namespace Catch { -namespace Internal { - - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo - }; - - template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; - - template - inline T& opCast(T const& t) { return const_cast(t); } - -// nullptr_t support based on pull request #154 from Konstantin Baumann -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } -#endif // CATCH_CONFIG_CPP11_NULLPTR - - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template - class Evaluator{}; - - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return opCast( lhs ) == opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) != opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) < opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) > opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) >= opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) <= opCast( rhs ); - } - }; - - template - bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // This level of indirection allows us to specialise for integer types - // to avoid signed/ unsigned warnings - - // "base" overload - template - bool compare( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // unsigned X to int - template bool compare( unsigned int lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // unsigned X to long - template bool compare( unsigned int lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // int to unsigned X - template bool compare( int lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // long to unsigned X - template bool compare( long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long (when comparing against NULL) - template bool compare( long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - - // pointer to int (when comparing against NULL) - template bool compare( int lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, int rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template bool compare( std::nullptr_t, T* rhs ) { - return Evaluator::evaluate( NULL, rhs ); - } - template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, NULL ); - } -#endif // CATCH_CONFIG_CPP11_NULLPTR - -} // end of namespace Internal -} // end of namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED - -#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_NULLPTR -std::string toString( std::nullptr_t ); -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif - -namespace Detail { - - extern std::string unprintableString; - - 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 INTERNAL_CATCH_STRINGIFY( NULL ); - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return INTERNAL_CATCH_STRINGIFY( NULL ); - else - return Detail::rawMemoryToString( p ); - } -}; - -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template -//struct StringMaker > { -// static std::string convert( std::vector const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template -std::string toString( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - -#ifdef CATCH_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 MessageInfo; - class ScopedMessageBuilder; - struct Counts; - - struct IResultCapture { - - virtual ~IResultCapture(); - - virtual void assertionEnded( AssertionResult const& result ) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; - - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - - virtual void handleFatalErrorCondition( std::string const& message ) = 0; - }; - - IResultCapture& getResultCapture(); -} - -// #included from: catch_debugger.h -#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED - -// #included from: catch_platform.h -#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED - -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS -#endif - -#include - -namespace Catch{ - - bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); -} - -#ifdef CATCH_PLATFORM_MAC - - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_BREAK_INTO_DEBUGGER() \ - if( Catch::isDebuggerActive() ) { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } - #else - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} - #endif - #endif - -#elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } -#endif - -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); -#endif - -// #included from: catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} - -/////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - ( __catchResult <= expr ).endExpression(); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - if( __catchResult.allowThrows() ) \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( ... ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - if( __catchResult.allowThrows() ) \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( exceptionType ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#endif - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ - try { \ - std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \ - __catchResult.captureExpression(); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED - -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED - -namespace Catch { - - struct SectionInfo { - SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description = std::string() ); - - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; - -} // end namespace Catch - -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED - -#include - -namespace Catch { - - struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } - - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } - - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; - }; - - struct Totals { - - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } - - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } - - Counts assertions; - Counts testCases; - }; -} - -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED - -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; -#else -#include -#endif - -namespace Catch { - - class Timer { - public: - Timer() : m_ticks( 0 ) {} - void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - uint64_t m_ticks; - }; - -} // namespace Catch - -#include - -namespace Catch { - - class Section : NonCopyable { - public: - Section( SectionInfo const& info ); - ~Section(); - - // This indicates whether the section should be executed or not - operator bool() const; - - private: - SectionInfo m_info; - - std::string m_name; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; - }; - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_SECTION( ... ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif - -// #included from: internal/catch_generators.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - -template -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; - -template -class BetweenGenerator : public IGenerator { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} - - virtual T getValue( std::size_t index ) const { - return m_from+static_cast( index ); - } - - virtual std::size_t size() const { - return static_cast( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template -class ValuesGenerator : public IGenerator { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector m_values; -}; - -template -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector*>::const_iterator it = m_composed.begin(); - typename std::vector*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) - { - const IGenerator* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); - } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - - void add( const IGenerator* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } - - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } - - CompositeGenerator& then( T value ) { - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template - CompositeGenerator between( T from, T to ) { - CompositeGenerator generators; - generators.add( new BetweenGenerator( from, to ) ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3 ){ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } - -} // end namespace Generators - -using namespace Generators; - -} // end namespace Catch - -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) - -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) - -// #included from: internal/catch_interfaces_exception.h -#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED - -#include -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED - -#include - -namespace Catch { - - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - struct IReporterRegistry; - struct IReporterFactory; - - struct IRegistryHub { - virtual ~IRegistryHub(); - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; - }; - - struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; - virtual void registerTest( TestCase const& testInfo ) = 0; - virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; - }; - - IRegistryHub& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - - -namespace Catch { - - typedef std::string(*exceptionTranslateFunction)(); - - struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate() const = 0; - }; - - struct IExceptionTranslatorRegistry { - virtual ~IExceptionTranslatorRegistry(); - - virtual std::string translateActiveException() const = 0; - }; - - class ExceptionTranslatorRegistrar { - template - class ExceptionTranslator : public IExceptionTranslator { - public: - - ExceptionTranslator( std::string(*translateFunction)( T& ) ) - : m_translateFunction( translateFunction ) - {} - - virtual std::string translate() const { - try { - throw; - } - catch( T& ex ) { - return m_translateFunction( ex ); - } - } - - protected: - std::string(*m_translateFunction)( T& ); - }; - - public: - template - ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { - getMutableRegistryHub().registerTranslator - ( new ExceptionTranslator( translateFunction ) ); - } - }; -} - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) - -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED - -#include -#include - -namespace Catch { -namespace Detail { - - class Approx { - public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_scale( 1.0 ), - m_value( value ) - {} - - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - - static Approx custom() { - return Approx( 0 ); - } - - Approx operator()( double value ) { - Approx approx( value ); - approx.epsilon( m_epsilon ); - approx.scale( m_scale ); - return approx; - } - - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); - } - - friend bool operator == ( Approx const& lhs, double rhs ) { - return operator==( rhs, lhs ); - } - - friend bool operator != ( double lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - friend bool operator != ( Approx const& lhs, double rhs ) { - return !operator==( rhs, lhs ); - } - - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; - return *this; - } - - Approx& scale( double newScale ) { - m_scale = newScale; - return *this; - } - - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); - } - - private: - double m_epsilon; - double m_scale; - double m_value; - }; -} - -template<> -inline std::string toString( Detail::Approx const& value ) { - return value.toString(); -} - -} // end namespace Catch - -// #included from: internal/catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - - template - class AllOf : public MatcherImpl, ExpressionT> { - public: - - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} - - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) - return false; - return true; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) - return true; - return false; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct Equals : MatcherImpl { - Equals( std::string const& str ) : m_str( str ){} - Equals( Equals const& other ) : m_str( other.m_str ){} - - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_str == expr; - } - virtual std::string toString() const { - return "equals: \"" + m_str + "\""; - } - - std::string m_str; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr ) : m_substr( substr ){} - Contains( Contains const& other ) : m_substr( other.m_substr ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr ) : m_substr( substr ){} - StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == 0; - } - virtual std::string toString() const { - return "starts with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr ) : m_substr( substr ){} - EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == expr.size() - m_substr.size(); - } - virtual std::string toString() const { - return "ends with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - } // namespace StdString - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str ) { - return Impl::StdString::Equals( str ); - } - inline Impl::StdString::Equals Equals( const char* str ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); - } - inline Impl::StdString::Contains Contains( std::string const& substr ) { - return Impl::StdString::Contains( substr ); - } - inline Impl::StdString::Contains Contains( const char* substr ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); - } - -} // namespace Matchers - -using namespace Matchers; - -} // namespace Catch - -// #included from: internal/catch_interfaces_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED - -// #included from: catch_tag_alias.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED - -#include - -namespace Catch { - - struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} - - std::string tag; - SourceLineInfo lineInfo; - }; - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED - -namespace Catch { - - // An optional type - template - class Option { - public: - Option() : nullableValue( NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } - - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = NULL; - } - - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } - - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != NULL; } - bool none() const { return nullableValue == NULL; } - - bool operator !() const { return nullableValue == NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } - - private: - T* nullableValue; - char storage[sizeof(T)]; - }; - -} // end namespace Catch - -namespace Catch { - - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - virtual Option find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - - static ITagAliasRegistry const& get(); - }; - -} // end namespace Catch - -// These files are included here so the single_include script doesn't put them -// in the conditionally compiled sections -// #included from: internal/catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED - -#include -#include - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - struct ITestCase; - - struct TestCaseInfo { - enum SpecialProperties{ - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4 - }; - - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ); - - TestCaseInfo( TestCaseInfo const& other ); - - bool isHidden() const; - bool throws() const; - bool okToFail() const; - bool expectedToFail() const; - - std::string name; - std::string className; - std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; - SourceLineInfo lineInfo; - SpecialProperties properties; - }; - - class TestCase : public TestCaseInfo { - public: - - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); - - TestCase withName( std::string const& _newName ) const; - - void invoke() const; - - TestCaseInfo const& getTestCaseInfo() const; - - void swap( TestCase& other ); - bool operator == ( TestCase const& other ) const; - bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); - - private: - Ptr test; - }; - - TestCase makeTestCase( ITestCase* testCase, - std::string const& className, - std::string const& name, - std::string const& description, - SourceLineInfo const& lineInfo ); -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - -#ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED - -#import - -#include - -// NB. Any general catch headers included here must be included -// in catch.hpp first to make sure they are included by the single -// header for non obj-usage - -/////////////////////////////////////////////////////////////////////////////// -// This protocol is really only here for (self) documenting purposes, since -// all its methods are optional. -@protocol OcFixture - -@optional - --(void) setUp; --(void) tearDown; - -@end - -namespace Catch { - - class OcMethod : public SharedImpl { - - public: - OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} - - virtual void invoke() const { - id obj = [[m_cls alloc] init]; - - performOptionalSelector( obj, @selector(setUp) ); - performOptionalSelector( obj, m_sel ); - performOptionalSelector( obj, @selector(tearDown) ); - - arcSafeRelease( obj ); - } - private: - virtual ~OcMethod() {} - - Class m_cls; - SEL m_sel; - }; - - namespace Detail{ - - inline std::string getAnnotation( Class cls, - std::string const& annotationName, - std::string const& testCaseName ) { - NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; - SEL sel = NSSelectorFromString( selStr ); - arcSafeRelease( selStr ); - id value = performOptionalSelector( cls, sel ); - if( value ) - return [(NSString*)value UTF8String]; - return ""; - } - } - - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( NULL, 0 ); - - Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); - objc_getClassList( classes, noClasses ); - - for( int c = 0; c < noClasses; c++ ) { - Class cls = classes[c]; - { - u_int count; - Method* methods = class_copyMethodList( cls, &count ); - for( u_int m = 0; m < count ; m++ ) { - SEL selector = method_getName(methods[m]); - std::string methodName = sel_getName(selector); - if( startsWith( methodName, "Catch_TestCase_" ) ) { - std::string testCaseName = methodName.substr( 15 ); - std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); - std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* className = class_getName( cls ); - - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); - noTestMethods++; - } - } - free(methods); - } - } - return noTestMethods; - } - - namespace Matchers { - namespace Impl { - namespace NSStringMatchers { - - template - struct StringHolder : MatcherImpl{ - StringHolder( NSString* substr ) : m_substr( [substr copy] ){} - StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} - StringHolder() { - arcSafeRelease( m_substr ); - } - - NSString* m_substr; - }; - - struct Equals : StringHolder { - Equals( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str isEqualToString:m_substr]; - } - - virtual std::string toString() const { - return "equals string: " + Catch::toString( m_substr ); - } - }; - - struct Contains : StringHolder { - Contains( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location != NSNotFound; - } - - virtual std::string toString() const { - return "contains string: " + Catch::toString( m_substr ); - } - }; - - struct StartsWith : StringHolder { - StartsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == 0; - } - - virtual std::string toString() const { - return "starts with: " + Catch::toString( m_substr ); - } - }; - struct EndsWith : StringHolder { - EndsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == [str length] - [m_substr length]; - } - - virtual std::string toString() const { - return "ends with: " + Catch::toString( m_substr ); - } - }; - - } // namespace NSStringMatchers - } // namespace Impl - - inline Impl::NSStringMatchers::Equals - Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } - - inline Impl::NSStringMatchers::Contains - Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } - - inline Impl::NSStringMatchers::StartsWith - StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } - - inline Impl::NSStringMatchers::EndsWith - EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } - - } // namespace Matchers - - using namespace Matchers; - -} // namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ -return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ -{ \ -return @ desc; \ -} \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) - -#endif - -#ifdef CATCH_IMPL -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED - -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif - -// #included from: ../catch_runner.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED - -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern : SharedImpl<> { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - class NamePattern : public Pattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { - if( startsWith( m_name, "*" ) ) { - m_name = m_name.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_name, "*" ) ) { - m_name = m_name.substr( 0, m_name.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_name == toLower( testCase.name ); - case WildcardAtStart: - return endsWith( toLower( testCase.name ), m_name ); - case WildcardAtEnd: - return startsWith( toLower( testCase.name ), m_name ); - case WildcardAtBothEnds: - return contains( toLower( testCase.name ), m_name ); - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - private: - std::string m_name; - WildcardPosition m_wildcard; - }; - class TagPattern : public Pattern { - public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } - private: - std::string m_tag; - }; - class ExcludedPattern : public Pattern { - public: - ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} - virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } - private: - Ptr m_underlyingPattern; - }; - - struct Filter { - std::vector > m_patterns; - - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) - if( !(*it)->matches( testCase ) ) - return false; - return true; - } - }; - - public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } - - private: - std::vector m_filters; - - friend class TestSpecParser; - }; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -namespace Catch { - - class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; - std::string m_arg; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; - - public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} - - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } - private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - } - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } - template - void addPattern() { - std::string token = subString(); - if( startsWith( token, "exclude:" ) ) { - m_exclusion = true; - token = token.substr( 8 ); - } - if( !token.empty() ) { - Ptr pattern = new T( token ); - if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); - m_currentFilter.m_patterns.push_back( pattern ); - } - m_exclusion = false; - m_mode = None; - } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } - }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -// #included from: catch_interfaces_config.h -#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; - - struct WarnAbout { enum What { - Nothing = 0x00, - NoAssertions = 0x01 - }; }; - - struct ShowDurations { enum OrNot { - DefaultForReporter, - Always, - Never - }; }; - struct RunTests { enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; }; - - class TestSpec; - - struct IConfig : IShared { - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual bool forceColour() const = 0; - }; -} - -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED - -#include - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - class Stream { - public: - Stream(); - Stream( std::streambuf* _streamBuf, bool _isOwned ); - void release(); - - std::streambuf* streamBuf; - - private: - bool isOwned; - }; - - std::ostream& cout(); - std::ostream& cerr(); -} - -#include -#include -#include -#include -#include - -#ifndef CATCH_CONFIG_CONSOLE_WIDTH -#define CATCH_CONFIG_CONSOLE_WIDTH 80 -#endif - -namespace Catch { - - struct ConfigData { - - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - forceColour( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ) - {} - - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool forceColour; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - - std::string reporterName; - std::string outputFilename; - std::string name; - std::string processName; - - std::vector testsOrTags; - }; - - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: - - Config() - : m_os( Catch::cout().rdbuf() ) - {} - - Config( ConfigData const& data ) - : m_data( data ), - m_os( Catch::cout().rdbuf() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } - - virtual ~Config() { - m_os.rdbuf( Catch::cout().rdbuf() ); - m_stream.release(); - } - - void setFilename( std::string const& filename ) { - m_data.outputFilename = filename; - } - - std::string const& getFilename() const { - return m_data.outputFilename ; - } - - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - - std::string getProcessName() const { return m_data.processName; } - - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - - void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() ); - } - - void useStream( std::string const& streamName ) { - Stream stream = createStream( streamName ); - setStreamBuf( stream.streamBuf ); - m_stream.release(); - m_stream = stream; - } - - std::string getReporterName() const { return m_data.reporterName; } - - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } - - // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_os; } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual bool forceColour() const { return m_data.forceColour; } - - private: - ConfigData m_data; - - Stream m_stream; - mutable std::ostream m_os; - TestSpec m_testSpec; - }; - -} // end namespace Catch - -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED - -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif - -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -#include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - inline void convertInto( bool _source, bool& _dest ) { - _dest = _source; - } - template - inline void convertInto( bool, T& ) { - throw std::runtime_error( "Invalid conversion" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -# endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual void setFlag( ConfigT& config ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - void setFlag( ConfigT& config ) const { - functionObj->setFlag( config ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual void setFlag( C& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual void setFlag( C& p ) const { - convertInto( true, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual void setFlag( C& p ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual void setFlag( C& p ) const { - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual void setFlag( C& p ) const { - function( p ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual void setFlag( C& obj ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - struct Parser { - Parser() : separators( " \t=:" ) {} - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { - const std::string doubleDash = "--"; - for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) - parseIntoTokens( argv[i] , tokens); - } - void parseIntoTokens( std::string arg, std::vector& tokens ) const { - while( !arg.empty() ) { - Parser::Token token( Parser::Token::Positional, arg ); - arg = ""; - if( token.data[0] == '-' ) { - if( token.data.size() > 1 && token.data[1] == '-' ) { - token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); - } - else { - token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); - if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { - arg = "-" + token.data.substr( 1 ); - token.data = token.data.substr( 0, 1 ); - } - } - } - if( token.type != Parser::Token::Positional ) { - std::size_t pos = token.data.find_first_of( separators ); - if( pos != std::string::npos ) { - arg = token.data.substr( pos+1 ); - token.data = token.data.substr( 0, pos ); - } - } - tokens.push_back( token ); - } - } - std::string separators; - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - // NOTE: std::auto_ptr is deprecated in c++11/c++0x -#if defined(__cplusplus) && __cplusplus > 199711L - typedef std::unique_ptr ArgAutoPtr; -#else - typedef std::auto_ptr ArgAutoPtr; -#endif - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } - - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } - - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( int argc, char const * const * argv ) const { - ConfigT config; - parseInto( argc, argv, config ); - return config; - } - - std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { - std::string processName = argv[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( argc, argv, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.setFlag( config ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE - -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif - -#include - -namespace Catch { - - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); - } - inline void setOrder( ConfigData& config, std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); - } - inline void setRngSeed( ConfigData& config, std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = static_cast( std::time(0) ); - } - else { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); - } - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = static_cast( level ); - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); - - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"," ); - } - } - - inline Clara::CommandLine makeCommandLineParser() { - - using namespace Clara; - CommandLine cli; - - cli.bindProcessName( &ConfigData::processName ); - - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); - - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); - - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); - - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); - - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); - - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); - - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); - - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); - - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &ConfigData::reporterName, "name" ); - - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); - - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); - - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); - - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); - -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); - - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); - - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes/no" ); - - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); - - cli["--order"] - .describe( "test case order (defaults to decl)" ) - .bind( &setOrder, "decl|lex|rand" ); - - cli["--rng-seed"] - .describe( "set a specific seed for random numbers" ) - .bind( &setRngSeed, "'time'|number" ); - - cli["--force-colour"] - .describe( "force colourised output" ) - .bind( &ConfigData::forceColour ); - - return cli; - } - -} // end namespace Catch - -// #included from: internal/catch_list.hpp -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED - -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED - -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -// #included from: ../external/tbc_text_format.h -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif -#endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include -#include -#include - -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE - -namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; -} - -// #included from: catch_console_colour.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED - -namespace Catch { - - struct Colour { - enum Code { - None = 0, - - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, - - Bright = 0x10, - - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, - - // By intention - FileName = LightGrey, - Warning = Yellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, - - Error = BrightRed, - Success = Green, - - OriginalExpression = Cyan, - ReconstructedExpression = Yellow, - - SecondaryText = LightGrey, - Headers = White - }; - - // Use constructed object for RAII guard - Colour( Code _colourCode ); - Colour( Colour const& other ); - ~Colour(); - - // Use static method for one-shot changes - static void use( Code _colourCode ); - - private: - bool m_moved; - }; - - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } - -} // end namespace Catch - -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED - -#include -#include -#include -#include - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template - struct LazyStat : Option { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_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 { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map FactoryMap; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - }; - -} - -#include -#include - -namespace Catch { - - inline std::size_t listTests( Config const& config ) { - - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; - return matchedTests; - } - - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; - } - return matchedTests; - } - - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); - } - std::string all() const { - std::string out; - for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; - }; - - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::map tagCounts; - - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); - } - } - - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; - return tagCounts.size(); - } - - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); - - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ":" - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; - } - Catch::cout() << std::endl; - return factories.size(); - } - - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } - -} // end namespace Catch - -// #included from: internal/catch_runner_impl.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED - -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { -namespace SectionTracking { - - class TrackedSection { - - typedef std::map TrackedSections; - - public: - enum RunState { - NotStarted, - Executing, - ExecutingChildren, - Completed - }; - - TrackedSection( std::string const& name, TrackedSection* parent ) - : m_name( name ), m_runState( NotStarted ), m_parent( parent ) - {} - - RunState runState() const { return m_runState; } - - TrackedSection* findChild( std::string const& childName ); - TrackedSection* acquireChild( std::string const& childName ); - - void enter() { - if( m_runState == NotStarted ) - m_runState = Executing; - } - void leave(); - - TrackedSection* getParent() { - return m_parent; - } - bool hasChildren() const { - return !m_children.empty(); - } - - private: - std::string m_name; - RunState m_runState; - TrackedSections m_children; - TrackedSection* m_parent; - }; - - inline TrackedSection* TrackedSection::findChild( std::string const& childName ) { - TrackedSections::iterator it = m_children.find( childName ); - return it != m_children.end() - ? &it->second - : NULL; - } - inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) { - if( TrackedSection* child = findChild( childName ) ) - return child; - m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); - return findChild( childName ); - } - inline void TrackedSection::leave() { - for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); - it != itEnd; - ++it ) - if( it->second.runState() != Completed ) { - m_runState = ExecutingChildren; - return; - } - m_runState = Completed; - } - - class TestCaseTracker { - public: - TestCaseTracker( std::string const& testCaseName ) - : m_testCase( testCaseName, NULL ), - m_currentSection( &m_testCase ), - m_completedASectionThisRun( false ) - {} - - bool enterSection( std::string const& name ) { - TrackedSection* child = m_currentSection->acquireChild( name ); - if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) - return false; - - m_currentSection = child; - m_currentSection->enter(); - return true; - } - void leaveSection() { - m_currentSection->leave(); - m_currentSection = m_currentSection->getParent(); - assert( m_currentSection != NULL ); - m_completedASectionThisRun = true; - } - - bool currentSectionHasChildren() const { - return m_currentSection->hasChildren(); - } - bool isCompleted() const { - return m_testCase.runState() == TrackedSection::Completed; - } - - class Guard { - public: - Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { - m_tracker.enterTestCase(); - } - ~Guard() { - m_tracker.leaveTestCase(); - } - private: - Guard( Guard const& ); - void operator = ( Guard const& ); - TestCaseTracker& m_tracker; - }; - - private: - void enterTestCase() { - m_currentSection = &m_testCase; - m_completedASectionThisRun = false; - m_testCase.enter(); - } - void leaveTestCase() { - m_testCase.leave(); - } - - TrackedSection m_testCase; - TrackedSection* m_currentSection; - bool m_completedASectionThisRun; - }; - -} // namespace SectionTracking - -using SectionTracking::TestCaseTracker; - -} // namespace Catch - -// #included from: catch_fatal_condition.hpp -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED - -namespace Catch { - - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); - - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); - } - -} // namespace Catch - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// - -namespace Catch { - - struct FatalConditionHandler { - void reset() {} - }; - -} // namespace Catch - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -#include - -namespace Catch { - - struct SignalDefs { int id; const char* name; }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - - struct FatalConditionHandler { - - static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); - } - - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); - } - ~FatalConditionHandler() { - reset(); - } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; - } - } - - bool m_isSet; - }; - -} // namespace Catch - -#endif // not Windows - -#include -#include - -namespace Catch { - - class StreamRedirect { - - public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } - - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; - - /////////////////////////////////////////////////////////////////////////// - - class RunContext : public IResultCapture, public IRunner { - - RunContext( RunContext const& ); - void operator =( RunContext const& ); - - public: - - explicit RunContext( Ptr const& config, Ptr const& reporter ) - : m_runInfo( config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( NULL ), - m_config( config ), - m_reporter( reporter ), - m_prevRunner( m_context.getRunner() ), - m_prevResultCapture( m_context.getResultCapture() ), - m_prevConfig( m_context.getConfig() ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } - - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - m_context.setRunner( m_prevRunner ); - m_context.setConfig( NULL ); - m_context.setResultCapture( m_prevResultCapture ); - m_context.setConfig( m_prevConfig ); - } - - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } - - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - TestCaseInfo testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting( testInfo ); - - m_activeTestCase = &testCase; - m_testCaseTracker = TestCaseTracker( testInfo.name ); - - do { - do { - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isCompleted() && !aborting() ); - } - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - - Totals deltaTotals = m_totals.delta( prevTotals ); - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - - m_activeTestCase = NULL; - m_testCaseTracker.reset(); - - return deltaTotals; - } - - Ptr config() const { - return m_config; - } - - private: // IResultCapture - - virtual void assertionEnded( AssertionResult const& result ) { - if( result.getResultType() == ResultWas::Ok ) { - m_totals.assertions.passed++; - } - else if( !result.isOk() ) { - m_totals.assertions.failed++; - } - - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); - - // Reset working state - m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); - m_lastResult = result; - } - - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - if( !m_testCaseTracker->enterSection( oss.str() ) ) - return false; - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 || - !m_config->warnAboutMissingAssertions() || - m_testCaseTracker->currentSectionHasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { - if( std::uncaught_exception() ) { - m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); - return; - } - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - m_testCaseTracker->leaveSection(); - - m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } - - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } - - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : ""; - } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; - } - - virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); - m_reporter->sectionEnded( testCaseSectionStats ); - - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - "", - "", - false ) ); - m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); - } - - public: - // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); - } - - private: - - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); - TestCaseTracker::Guard guard( *m_testCaseTracker ); - - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); - invokeActiveTestCase(); - } - else { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - makeUnexpectedResultBuilder().useActiveException(); - } - handleUnfinishedSections(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } - - void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } - - private: - - ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), - m_lastAssertionInfo.resultDisposition ); - } - - void handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); - m_unfinishedSections.clear(); - } - - struct UnfinishedSections { - UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) - : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - - SectionInfo info; - Counts prevAssertions; - double durationInSeconds; - }; - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - Option m_testCaseTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - IRunner* m_prevRunner; - IResultCapture* m_prevResultCapture; - Ptr m_prevConfig; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - }; - - IResultCapture& getResultCapture() { - if( IResultCapture* capture = getCurrentContext().getResultCapture() ) - return *capture; - else - throw std::logic_error( "No result capture instance" ); - } - -} // end namespace Catch - -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED - -namespace Catch { - - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _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 { - - class Runner { - - public: - Runner( Ptr const& config ) - : m_config( config ) - { - openStream(); - makeReporter(); - } - - Totals runTests() { - - RunContext context( m_config.get(), m_reporter ); - - Totals totals; - - context.testGroupStarting( "all tests", 1, 1 ); // deprecated? - - TestSpec testSpec = m_config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector testCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); - - int testsRunForGroup = 0; - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) { - testsRunForGroup++; - if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { - - if( context.aborting() ) - break; - - totals += context.runTest( *it ); - m_testsAlreadyRun.insert( *it ); - } - } - std::vector skippedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true ); - - for( std::vector::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end(); - it != itEnd; - ++it ) - m_reporter->skipTest( *it ); - - context.testGroupEnded( "all tests", totals, 1, 1 ); - return totals; - } - - private: - void openStream() { - // Open output file, if specified - if( !m_config->getFilename().empty() ) { - m_ofs.open( m_config->getFilename().c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << m_config->getFilename() << "'"; - throw std::domain_error( oss.str() ); - } - m_config->setStreamBuf( m_ofs.rdbuf() ); - } - } - void makeReporter() { - std::string reporterName = m_config->getReporterName().empty() - ? "console" - : m_config->getReporterName(); - - m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); - if( !m_reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - } - - private: - Ptr m_config; - std::ofstream m_ofs; - Ptr m_reporter; - std::set m_testsAlreadyRun; - }; - - class Session : NonCopyable { - static bool alreadyInstantiated; - - public: - - struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; - - Session() - : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } - - void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; - - m_cli.usage( Catch::cout(), processName ); - Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; - } - - int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; - } - m_cli.usage( Catch::cout(), m_configData.processName ); - return (std::numeric_limits::max)(); - } - return 0; - } - - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); - } - - int run( int argc, char* const argv[] ) { - - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - - int run() { - if( m_configData.showHelp ) - return 0; - - try - { - config(); // Force config to be constructed - - std::srand( m_configData.rngSeed ); - - Runner runner( m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runner.runTests().assertions.failed ); - } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); - } - } - - Clara::CommandLine const& cli() const { - return m_cli; - } - std::vector const& unusedTokens() const { - return m_unusedTokens; - } - ConfigData& configData() { - return m_configData; - } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; - } - - private: - Clara::CommandLine m_cli; - std::vector m_unusedTokens; - ConfigData m_configData; - Ptr m_config; - }; - - bool Session::alreadyInstantiated = false; - -} // end namespace Catch - -// #included from: catch_registry_hub.hpp -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED - -// #included from: catch_test_case_registry_impl.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED - -#include -#include -#include -#include -#include - -namespace Catch { - - class TestRegistry : public ITestCaseRegistry { - struct LexSort { - bool operator() (TestCase i,TestCase j) const { return (i const& getAllTests() const { - return m_functionsInOrder; - } - - virtual std::vector const& getAllNonHiddenTests() const { - return m_nonHiddenFunctions; - } - - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const { - - for( std::vector::const_iterator it = m_functionsInOrder.begin(), - itEnd = m_functionsInOrder.end(); - it != itEnd; - ++it ) { - bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ); - if( includeTest != negated ) - matchingTestCases.push_back( *it ); - } - sortTests( config, matchingTestCases ); - } - - private: - - static void sortTests( IConfig const& config, std::vector& matchingTestCases ) { - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() ); - break; - case RunTests::InRandomOrder: - { - RandomNumberGenerator rng; - std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - } - std::set m_functions; - std::vector m_functionsInOrder; - std::vector m_nonHiddenFunctions; - size_t m_unnamedCount; - }; - - /////////////////////////////////////////////////////////////////////////// - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; - }; - - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - AutoReg::~AutoReg() {} - - void AutoReg::registerTestCase( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - -} // end namespace Catch - -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() { - deleteAllValues( m_factories ); - } - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, IReporterFactory* factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - - FactoryMap const& getFactories() const { - return m_factories; - } - - private: - FactoryMap m_factories; - }; -} - -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" -#endif - -namespace Catch { - - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); - } - - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); - } - - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - throw; - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - throw; -#endif - } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return tryTranslators( m_translators.begin() ); - } - } - - std::string tryTranslators( std::vector::const_iterator it ) const { - if( it == m_translators.end() ) - return "Unknown exception"; - - try { - return (*it)->translate(); - } - catch(...) { - return tryTranslators( it+1 ); - } - } - - private: - std::vector m_translators; - }; -} - -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); - - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerTest( TestCase const& testInfo ) { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } - } - - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } - -} // end namespace Catch - -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED - -#include - -namespace Catch { - - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } - - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } - -} // end namespace Catch - -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; - }; -} - -#include -#include -#include - -namespace Catch { - - template - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - Stream::Stream() - : streamBuf( NULL ), isOwned( false ) - {} - - Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) - {} - - void Stream::release() { - if( isOwned ) { - delete streamBuf; - streamBuf = NULL; - isOwned = false; - } - } - -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions - std::ostream& cout() { - return std::cout; - } - std::ostream& cerr() { - return std::cerr; - } -#endif -} - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; - }; - - namespace { - Context* currentContext = NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - Stream createStream( std::string const& streamName ) { - if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false ); - if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false ); - if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); - - throw std::domain_error( "Unknown stream: " + streamName ); - } - - void cleanUpContext() { - delete currentContext; - currentContext = NULL; - } -} - -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED - -namespace Catch { - namespace { - - struct IColourImpl { - virtual ~IColourImpl() {} - virtual void use( Colour::Code _colourCode ) = 0; - }; - - struct NoColourImpl : IColourImpl { - void use( Colour::Code ) {} - - static IColourImpl* instance() { - static NoColourImpl s_instance; - return &s_instance; - } - }; - - } // anon namespace -} // namespace Catch - -#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) -# ifdef CATCH_PLATFORM_WINDOWS -# define CATCH_CONFIG_COLOUR_WINDOWS -# else -# define CATCH_CONFIG_COLOUR_ANSI -# endif -#endif - -#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -namespace Catch { -namespace { - - class Win32ColourImpl : public IColourImpl { - public: - Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) - { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); - originalAttributes = csbiInfo.wAttributes; - } - - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalAttributes ); - case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Red: return setTextAttribute( FOREGROUND_RED ); - case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); - case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); - case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); - case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); - case Colour::Grey: return setTextAttribute( 0 ); - - case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); - case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); - case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); - case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - - case Colour::Bright: throw std::logic_error( "not a colour" ); - } - } - - private: - void setTextAttribute( WORD _textAttribute ) { - SetConsoleTextAttribute( stdoutHandle, _textAttribute ); - } - HANDLE stdoutHandle; - WORD originalAttributes; - }; - - IColourImpl* platformColourInstance() { - static Win32ColourImpl s_instance; - return &s_instance; - } - -} // end anon namespace -} // end namespace Catch - -#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// - -#include - -namespace Catch { -namespace { - - // use POSIX/ ANSI console terminal codes - // Thanks to Adam Strzelecki for original contribution - // (http://github.com/nanoant) - // https://github.com/philsquared/Catch/pull/131 - class PosixColourImpl : public IColourImpl { - public: - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: - case Colour::White: return setColour( "[0m" ); - case Colour::Red: return setColour( "[0;31m" ); - case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0:34m" ); - case Colour::Cyan: return setColour( "[0;36m" ); - case Colour::Yellow: return setColour( "[0;33m" ); - case Colour::Grey: return setColour( "[1;30m" ); - - case Colour::LightGrey: return setColour( "[0;37m" ); - case Colour::BrightRed: return setColour( "[1;31m" ); - case Colour::BrightGreen: return setColour( "[1;32m" ); - case Colour::BrightWhite: return setColour( "[1;37m" ); - - case Colour::Bright: throw std::logic_error( "not a colour" ); - } - } - static IColourImpl* instance() { - static PosixColourImpl s_instance; - return &s_instance; - } - - private: - void setColour( const char* _escapeCode ) { - Catch::cout() << '\033' << _escapeCode; - } - }; - - IColourImpl* platformColourInstance() { - Ptr config = getCurrentContext().getConfig(); - return (config && config->forceColour()) || isatty(STDOUT_FILENO) - ? PosixColourImpl::instance() - : NoColourImpl::instance(); - } - -} // end anon namespace -} // end namespace Catch - -#else // not Windows or ANSI /////////////////////////////////////////////// - -namespace Catch { - - static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } - -} // end namespace Catch - -#endif // Windows/ ANSI/ None - -namespace Catch { - - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } - Colour::~Colour(){ if( !m_moved ) use( None ); } - - void Colour::use( Code _colourCode ) { - static IColourImpl* impl = isDebuggerActive() - ? NoColourImpl::instance() - : platformColourInstance(); - impl->use( _colourCode ); - } - -} // end namespace Catch - -// #included from: catch_generators_impl.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct GeneratorInfo : IGeneratorInfo { - - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} - - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; - } - - std::size_t getCurrentIndex() const { - return m_currentIndex; - } - - std::size_t m_size; - std::size_t m_currentIndex; - }; - - /////////////////////////////////////////////////////////////////////////// - - class GeneratorsForTest : public IGeneratorsForTest { - - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); - } - - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; - } - - bool moveNext() { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } - -} // end namespace Catch - -// #included from: catch_assertionresult.hpp -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED - -namespace Catch { - - AssertionInfo::AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) - {} - - AssertionResult::AssertionResult() {} - - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} - - AssertionResult::~AssertionResult() {} - - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } - - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } - - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } - - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); - } - - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } - - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; - else - return m_info.capturedExpression; - } - std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; - else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; - } - - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } - - std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; - } - - std::string AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } - - std::string AssertionResult::getTestMacroName() const { - return m_info.macroName; - } - -} // end namespace Catch - -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED - -namespace Catch { - - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || - tag == "hide" || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else - return TestCaseInfo::None; - } - inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); - } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); - } - } - - TestCase makeTestCase( ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support - - // Parse out tags - std::set tags; - std::string desc, tag; - bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); - - tags.insert( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); - } - - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - tags( _tags ), - lineInfo( _lineInfo ), - properties( None ) - { - std::ostringstream oss; - for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; - std::string lcaseTag = toLower( *it ); - properties = static_cast( properties | parseSpecialTag( lcaseTag ) ); - lcaseTags.insert( lcaseTag ); - } - tagsAsString = oss.str(); - } - - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} - - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; - } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; - } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; - } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; - } - - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} - - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} - - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; - } - - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast( other ).properties ); - std::swap( lineInfo, other.lineInfo ); - } - - void TestCase::invoke() const { - test->invoke(); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } - -} // end namespace Catch - -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED - -namespace Catch { - - 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, 2, 1, "", 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,NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); - } -#endif - } - - void Timer::start() { - m_ticks = getCurrentTicks(); - } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); - } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast(getElapsedMicroseconds()/1000); - } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -// #included from: catch_common.hpp -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED - -namespace Catch { - - bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; - } - bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; - } - bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } - void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); - } - std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } - std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of( whitespaceChars ); - std::string::size_type end = str.find_last_not_of( whitespaceChars ); - - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; - } - - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; - std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); - else - i = std::string::npos; - } - return replaced; - } - - pluralise::pluralise( std::size_t count, std::string const& label ) - : m_count( count ), - m_label( label ) - {} - - std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << "s"; - return os; - } - - SourceLineInfo::SourceLineInfo() : line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} - bool SourceLineInfo::empty() const { - return file.empty(); - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; -#else - os << info.file << ":" << info.line; -#endif - return os; - } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } -} - -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED - -namespace Catch { - - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - - Section::~Section() { - if( m_sectionIncluded ) - getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); - } - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; - } - -} // end namespace Catch - -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED - -#include - -#ifdef CATCH_PLATFORM_MAC - - #include - #include - #include - #include - #include - - namespace Catch{ - - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ - - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - // Call sysctl. - - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; - return false; - } - - // We're being debugged if the P_TRACED flag is set. - - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); - } - } // namespace Catch - -#elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#else - namespace Catch { - inline bool isDebuggerActive() { return false; } - } -#endif // Platform - -#ifdef CATCH_PLATFORM_WINDOWS - extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } -#endif // Platform - -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED - -namespace Catch { - -namespace Detail { - - std::string unprintableString = "{?}"; - - namespace { - struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - union _{ - int asInt; - char asChar[sizeof (int)]; - } u; - - u.asInt = 1; - return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; - } - }; - } - - std::string rawMemoryToString( const void *object, std::size_t size ) - { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); - } -} - -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } - } - return "\"" + s + "\""; -} -std::string toString( std::wstring const& value ) { - - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; - return Catch::toString( s ); -} - -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -std::string toString( char* const value ) { - return Catch::toString( static_cast( value ) ); -} - -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); -} - -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast( value ) ); -} - -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value >= 255 ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value >= 255 ) - 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_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; -} -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif - -} // end namespace Catch - -// #included from: catch_result_builder.hpp -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED - -namespace Catch { - - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition ) - : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ) - {} - - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); - } - - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); - } - - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); - } - - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - getResultCapture().assertionEnded( result ); - - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; - } - } - void ResultBuilder::react() { - if( m_shouldThrow ) - throw Catch::TestFailureException(); - } - - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } - - AssertionResult ResultBuilder::build() const - { - assert( m_data.resultType != ResultWas::Unknown ); - - AssertionResultData data = m_data; - - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - } - - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } - return AssertionResult( m_assertionInfo, data ); - } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; - } - -} // end namespace Catch - -// #included from: catch_tag_alias_registry.hpp -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -#include -#include - -namespace Catch { - - TagAliasRegistry::~TagAliasRegistry() {} - - Option TagAliasRegistry::find( std::string const& alias ) const { - std::map::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option(); - } - - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); - } - } - return expandedTestSpec; - } - - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } - } - - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; - - } - - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } - - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); - } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); - } - } - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_xml.hpp -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED - -// #included from: catch_reporter_bases.hpp -#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED - -#include - -namespace Catch { - - struct StreamingReporterBase : SharedImpl { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - - virtual ~StreamingReporterBase(); - - virtual void noMatchingTestCases( std::string const& ) {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest( TestCaseInfo const& ) { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - }; - - struct CumulativeReporterBase : SharedImpl { - template - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); - } - - SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; - } - private: - void operator=( BySectionInfo const& ); - SectionInfo const& m_other; - }; - - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - ~CumulativeReporterBase(); - - virtual void testRunStarting( TestRunInfo const& ) {} - virtual void testGroupStarting( GroupInfo const& ) {} - - virtual void testCaseStarting( TestCaseInfo const& ) {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest( TestCaseInfo const& ) {} - - Ptr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector > > m_sections; - std::vector > m_testCases; - std::vector > m_testGroups; - - std::vector > m_testRuns; - - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector > m_sectionStack; - - }; - - template - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; - } - return line; - } - -} // end namespace Catch - -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED - -namespace Catch { - - template - class LegacyReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; -} - -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = NULL; - } - - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } - - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer; - }; - - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &Catch::cout() ) - {} - - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &os ) - {} - - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } - - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - stream() << m_indent << "<" << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - stream() << "/>\n"; - m_tagIsOpen = false; - } - else { - stream() << m_indent << "\n"; - } - m_tags.pop_back(); - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) { - stream() << " " << name << "=\""; - writeEncodedText( attribute ); - stream() << "\""; - } - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; - return *this; - } - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - if( !name.empty() ) - stream() << " " << name << "=\"" << attribute << "\""; - return *this; - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - stream() << m_indent; - writeEncodedText( text ); - m_needsNewline = true; - } - return *this; - } - - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - stream() << m_indent << ""; - m_needsNewline = true; - return *this; - } - - XmlWriter& writeBlankLine() { - ensureTagClosed(); - stream() << "\n"; - return *this; - } - - void setStream( std::ostream& os ) { - m_os = &os; - } - - private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } - } - - void newlineIfNecessary() { - if( m_needsNewline ) { - stream() << "\n"; - m_needsNewline = false; - } - } - - void writeEncodedText( std::string const& text ) { - static const char* charsToEncode = "<&\""; - std::string mtext = text; - std::string::size_type pos = mtext.find_first_of( charsToEncode ); - while( pos != std::string::npos ) { - stream() << mtext.substr( 0, pos ); - - switch( mtext[pos] ) { - case '<': - stream() << "<"; - break; - case '&': - stream() << "&"; - break; - case '\"': - stream() << """; - break; - } - mtext = mtext.substr( pos+1 ); - pos = mtext.find_first_of( charsToEncode ); - } - stream() << mtext; - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream* m_os; - }; - -} -namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_sectionDepth( 0 ) - {} - - virtual ~XmlReporter(); - - static std::string getDescription() { - return "Reports test results as an XML document"; - } - - public: // StreamingReporterBase - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = true; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& s ) { - StreamingReporterBase::noMatchingTestCases( s ); - } - - virtual void testRunStarting( TestRunInfo const& testInfo ) { - StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); - } - } - - virtual void assertionStarting( AssertionInfo const& ) { } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - const AssertionResult& assertionResult = assertionStats.assertionResult; - - // Print any info messages in tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( it->message ); - } - } - } - - // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) - return true; - - // Print the expression if there is one. - if( assertionResult.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); - - m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); - } - - // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { - case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "Fatal Error Condition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); - break; - default: - break; - } - - if( assertionResult.hasExpression() ) - m_xml.endElement(); - - return true; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - - m_xml.endElement(); - } - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - m_xml.endElement(); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED - -#include - -namespace Catch { - - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), - xml( _config.stream() ) - {} - - ~JunitReporter(); - - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } - - virtual void noMatchingTestCases( std::string const& /*spec*/ ) {} - - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = true; - return prefs; - } - - virtual void testRunStarting( TestRunInfo const& runInfo ) { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() { - xml.endElement(); - } - - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD - - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); - } - - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; - } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + "/" + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); - } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) - if( className.empty() ) - writeSection( name, "", **it ); - else - writeSection( className, name, **it ); - } - - void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); - } - void writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - std::ostringstream oss; - if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; - for( std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << "\n"; - - oss << "at " << result.getSourceInfo(); - xml.writeText( oss.str(), false ); - } - } - - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_console.hpp -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED - -namespace Catch { - - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) - {} - - virtual ~ConsoleReporter(); - static std::string getDescription() { - return "Reports test results as plain lines of text"; - } - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - lazyPrint(); - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - stream << std::endl; - return true; - } - - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) { - if( _sectionStats.missingAssertions ) { - lazyPrint(); - Colour colour( Colour::ResultError ); - if( m_sectionStack.size() > 1 ) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - m_headerPrinted = false; - } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - } - StreamingReporterBase::sectionEnded( _sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { - if( currentGroupInfo.used ) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; - } - StreamingReporterBase::testGroupEnded( _testGroupStats ); - } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotalsDivider( _testRunStats.totals ); - printTotals( _testRunStats.totals ); - stream << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - private: - - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ), - stats( _stats ), - result( _stats.assertionResult ), - colour( Colour::None ), - message( result.getMessage() ), - messages( _stats.infoMessages ), - printInfoMessages( _printInfoMessages ) - { - switch( result.getResultType() ) { - case ResultWas::Ok: - colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; - } - else { - colour = Colour::Error; - passOrFail = "FAILED"; - } - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; - break; - case ResultWas::FatalErrorCondition: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info: - messageLabel = "info"; - break; - case ResultWas::Warning: - messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure: - passOrFail = "FAILED"; - colour = Colour::Error; - if( _stats.infoMessages.size() == 1 ) - messageLabel = "explicitly with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - passOrFail = "** internal error **"; - colour = Colour::Error; - break; - } - } - - void print() const { - printSourceInfo(); - if( stats.totals.assertions.total() > 0 ) { - if( result.isOk() ) - stream << "\n"; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } - else { - stream << "\n"; - } - printMessage(); - } - - private: - void printResultType() const { - if( !passOrFail.empty() ) { - Colour colourGuard( colour ); - stream << passOrFail << ":\n"; - } - } - void printOriginalExpression() const { - if( result.hasExpression() ) { - Colour colourGuard( Colour::OriginalExpression ); - stream << " "; - stream << result.getExpressionInMacro(); - stream << "\n"; - } - } - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - stream << "with expansion:\n"; - Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; - } - } - void printMessage() const { - if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; - for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; - } - } - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ": "; - } - - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; - }; - - void lazyPrint() { - - if( !currentTestRunInfo.used ) - lazyPrintRunInfo(); - if( !currentGroupInfo.used ) - lazyPrintGroupInfo(); - - if( !m_headerPrinted ) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } - } - void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; - Colour colour( Colour::SecondaryText ); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" - << "Run with -? for options\n\n"; - - if( m_config->rngSeed() != 0 ) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; - - currentTestRunInfo.used = true; - } - void lazyPrintGroupInfo() { - if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { - printClosedHeader( "Group: " + currentGroupInfo->name ); - currentGroupInfo.used = true; - } - } - void printTestCaseAndSectionHeader() { - assert( !m_sectionStack.empty() ); - printOpenHeader( currentTestCaseInfo->name ); - - if( m_sectionStack.size() > 1 ) { - Colour colourGuard( Colour::Headers ); - - std::vector::const_iterator - it = m_sectionStack.begin()+1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for( ; it != itEnd; ++it ) - printHeaderString( it->name, 2 ); - } - - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - - if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; - Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; - } - stream << getLineOfChars<'.'>() << "\n" << std::endl; - } - - void printClosedHeader( std::string const& _name ) { - printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; - } - void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; - { - Colour colourGuard( Colour::Headers ); - printHeaderString( _name ); - } - } - - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { - std::size_t i = _string.find( ": " ); - if( i != std::string::npos ) - i+=2; - else - i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; - } - - struct SummaryColumn { - - SummaryColumn( std::string const& _label, Colour::Code _colour ) - : label( _label ), - colour( _colour ) - {} - SummaryColumn addRow( std::size_t count ) { - std::ostringstream oss; - oss << count; - std::string row = oss.str(); - for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = " " + *it; - while( it->size() > row.size() ) - row = " " + row; - } - rows.push_back( row ); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; - - }; - - void printTotals( Totals const& totals ) { - if( totals.testCases.total() == 0 ) { - stream << Colour( Colour::Warning ) << "No tests ran\n"; - } - else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { - stream << Colour( Colour::ResultSuccess ) << "All tests passed"; - stream << " (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; - } - else { - - std::vector columns; - columns.push_back( SummaryColumn( "", Colour::None ) - .addRow( totals.testCases.total() ) - .addRow( totals.assertions.total() ) ); - columns.push_back( SummaryColumn( "passed", Colour::Success ) - .addRow( totals.testCases.passed ) - .addRow( totals.assertions.passed ) ); - columns.push_back( SummaryColumn( "failed", Colour::ResultError ) - .addRow( totals.testCases.failed ) - .addRow( totals.assertions.failed ) ); - columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) - .addRow( totals.testCases.failedButOk ) - .addRow( totals.assertions.failedButOk ) ); - - printSummaryRow( "test cases", columns, 0 ); - printSummaryRow( "assertions", columns, 1 ); - } - } - void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { - for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { - stream << label << ": "; - if( value != "0" ) - stream << value; - else - stream << Colour( Colour::Warning ) << "- none -"; - } - else if( value != "0" ) { - stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << " " << it->label; - } - } - stream << "\n"; - } - - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; - } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - - void printTotalsDivider( Totals const& totals ) { - if( totals.testCases.total() > 0 ) { - std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); - std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); - std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); - while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )++; - while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )--; - - stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); - stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); - if( totals.testCases.allPassed() ) - stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); - else - stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); - } - else { - stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); - } - stream << "\n"; - } - void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; - } - - private: - bool m_headerPrinted; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_compact.hpp -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED - -namespace Catch { - - struct CompactReporter : StreamingReporterBase { - - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual ~CompactReporter(); - - static std::string getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } - - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - - stream << std::endl; - return true; - } - - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - private: - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ) - , stats( _stats ) - , result( _stats.assertionResult ) - , messages( _stats.infoMessages ) - , itMessage( _stats.infoMessages.begin() ) - , printInfoMessages( _printInfoMessages ) - {} - - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - - switch( result.getResultType() ) { - case ResultWas::Ok: - printResultType( Colour::ResultSuccess, passedString() ); - printOriginalExpression(); - printReconstructedExpression(); - if ( ! result.hasExpression() ) - printRemainingMessages( Colour::None ); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) - printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); - else - printResultType( Colour::Error, failedString() ); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType( Colour::Error, failedString() ); - printIssue( "unexpected exception with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType( Colour::Error, failedString() ); - printIssue( "fatal error condition with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType( Colour::Error, failedString() ); - printIssue( "expected exception, got none" ); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType( Colour::None, "info" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType( Colour::None, "warning" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType( Colour::Error, failedString() ); - printIssue( "explicitly" ); - printRemainingMessages( Colour::None ); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType( Colour::Error, "** internal error **" ); - break; - } - } - - private: - // Colour::LightGrey - - static Colour::Code dimColour() { return Colour::FileName; } - -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } -#endif - - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; - } - - void printResultType( Colour::Code colour, std::string passOrFail ) const { - if( !passOrFail.empty() ) { - { - Colour colourGuard( colour ); - stream << " " << passOrFail; - } - stream << ":"; - } - } - - void printIssue( std::string issue ) const { - stream << " " << issue; - } - - void printExpressionWas() { - if( result.hasExpression() ) { - stream << ";"; - { - Colour colour( dimColour() ); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if( result.hasExpression() ) { - stream << " " << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - { - Colour colour( dimColour() ); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() { - if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; - ++itMessage; - } - } - - void printRemainingMessages( Colour::Code colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); - - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; - } - - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - }; - - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; - } - } - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) - -} // end namespace Catch - -namespace Catch { - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} - - void Config::dummy() {} -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif - -#ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED - -#ifndef __OBJC__ - -// Standard C/C++ main entry point -int main (int argc, char * const argv[]) { - return Catch::Session().run( argc, argv ); -} - -#else // __OBJC__ - -// Objective-C entry point -int main (int argc, char * const argv[]) { -#if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; -#endif - - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); - -#if !CATCH_ARC_ENABLED - [pool drain]; -#endif - - return result; -} - -#endif // __OBJC__ - -#endif - -#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED -# undef CLARA_CONFIG_MAIN -#endif - -////// - -// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ -#ifdef CATCH_CONFIG_PREFIX_ALL - -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) - -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) - -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) - -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) - -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) - -// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required -#else - -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) - -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) - -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) - -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) -#else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -#endif - -#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( " Given: " desc, "" ) -#define WHEN( desc ) SECTION( " When: " desc, "" ) -#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) -#define THEN( desc ) SECTION( " Then: " desc, "" ) -#define AND_THEN( desc ) SECTION( " And: " desc, "" ) - -using Catch::Detail::Approx; - -// #included from: internal/catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - -#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - diff --git a/third_party/libosmium/test/t/area/test_node_ref_segment.cpp b/third_party/libosmium/test/t/area/test_node_ref_segment.cpp index 309768744..3261c24e1 100644 --- a/third_party/libosmium/test/t/area/test_node_ref_segment.cpp +++ b/third_party/libosmium/test/t/area/test_node_ref_segment.cpp @@ -52,6 +52,20 @@ TEST_CASE("NodeRefSegmentClass") { REQUIRE(calculate_intersection(s1, s7) == osmium::Location()); } + SECTION("intersection of very long segments") { + NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}}, nullptr, nullptr); + NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); + REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0)); + + NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr); + NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); + REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0)); + + NodeRefSegment s5({ 1, {-90.0000001, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr); + NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); + REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0)); + } + SECTION("to_left_of") { osmium::Location loc { 2.0, 2.0 }; diff --git a/third_party/libosmium/test/t/basic/helper.hpp b/third_party/libosmium/test/t/basic/helper.hpp deleted file mode 100644 index 5a2130e4e..000000000 --- a/third_party/libosmium/test/t/basic/helper.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef TEST_BASIC_HELPER_HPP -#define TEST_BASIC_HELPER_HPP - -#include -#include -#include - -#include - -inline void add_tags(osmium::memory::Buffer& buffer, osmium::builder::Builder& builder, const std::vector>& tags) { - osmium::builder::TagListBuilder tl_builder(buffer, &builder); - for (const auto& tag : tags) { - tl_builder.add_tag(tag.first, tag.second); - } -} - -inline osmium::Node& buffer_add_node(osmium::memory::Buffer& buffer, const char* user, const std::vector>& tags, const osmium::Location& location) { - osmium::builder::NodeBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - buffer.commit(); - return builder.object().set_location(location); -} - -inline osmium::Way& buffer_add_way(osmium::memory::Buffer& buffer, const char* user, const std::vector>& tags, const std::vector& nodes) { - osmium::builder::WayBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - { - osmium::builder::WayNodeListBuilder wnl_builder(buffer, &builder); - for (const osmium::object_id_type ref : nodes) { - wnl_builder.add_node_ref(ref); - } - } - buffer.commit(); - return builder.object(); -} - -inline osmium::Way& buffer_add_way(osmium::memory::Buffer& buffer, const char* user, const std::vector>& tags, const std::vector>& nodes) { - osmium::builder::WayBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - { - osmium::builder::WayNodeListBuilder wnl_builder(buffer, &builder); - for (const auto& p : nodes) { - wnl_builder.add_node_ref(p.first, p.second); - } - } - buffer.commit(); - return builder.object(); -} - -inline osmium::Relation& buffer_add_relation( - osmium::memory::Buffer& buffer, - const char* user, - const std::vector>& tags, const std::vector>& members) { - osmium::builder::RelationBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - { - osmium::builder::RelationMemberListBuilder rml_builder(buffer, &builder); - for (const auto& member : members) { - rml_builder.add_member(osmium::char_to_item_type(std::get<0>(member)), std::get<1>(member), std::get<2>(member)); - } - } - buffer.commit(); - return builder.object(); -} - -inline osmium::Area& buffer_add_area(osmium::memory::Buffer& buffer, const char* user, - const std::vector>& tags, - const std::vector>>>& rings) { - osmium::builder::AreaBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - - for (const auto& ring : rings) { - if (ring.first) { - osmium::builder::OuterRingBuilder ring_builder(buffer, &builder); - for (const auto& p : ring.second) { - ring_builder.add_node_ref(p.first, p.second); - } - } else { - osmium::builder::InnerRingBuilder ring_builder(buffer, &builder); - for (const auto& p : ring.second) { - ring_builder.add_node_ref(p.first, p.second); - } - } - } - buffer.commit(); - return builder.object(); -} - -inline osmium::Changeset& buffer_add_changeset(osmium::memory::Buffer& buffer, const char* user, const std::vector>& tags) { - osmium::builder::ChangesetBuilder builder(buffer); - builder.add_user(user); - add_tags(buffer, builder, tags); - buffer.commit(); - return builder.object(); -} - -#endif // TEST_BASIC_HELPER_HPP diff --git a/third_party/libosmium/test/t/basic/test_box.cpp b/third_party/libosmium/test/t/basic/test_box.cpp index 768cf41f3..2b7fb3d01 100644 --- a/third_party/libosmium/test/t/basic/test_box.cpp +++ b/third_party/libosmium/test/t/basic/test_box.cpp @@ -8,26 +8,34 @@ #include #include -TEST_CASE("Box") { +TEST_CASE("Starting with default constructed box") { - SECTION("instantiation") { - osmium::Box b; + osmium::Box b; + + SECTION("default constructor creates invalid box") { REQUIRE(!b); REQUIRE(!b.bottom_left()); REQUIRE(!b.top_right()); REQUIRE_THROWS_AS(b.size(), osmium::invalid_location); } - SECTION("instantiation_and_extend_with_undefined") { - osmium::Box b; + SECTION("extend with undefined") { + REQUIRE(!b); + b.extend(osmium::Location{}); REQUIRE(!b); - b.extend(osmium::Location()); REQUIRE(!b.bottom_left()); REQUIRE(!b.top_right()); } - SECTION("instantiation_and_extend") { - osmium::Box b; + SECTION("extend with invalid") { + REQUIRE(!b); + b.extend(osmium::Location{200.0, 100.0}); + REQUIRE(!b); + REQUIRE(!b.bottom_left()); + REQUIRE(!b.top_right()); + } + + SECTION("extend with valid") { osmium::Location loc1 { 1.2, 3.4 }; b.extend(loc1); REQUIRE(!!b); @@ -57,8 +65,7 @@ TEST_CASE("Box") { REQUIRE(crc32().checksum() == 0xd381a838); } - SECTION("output_defined") { - osmium::Box b; + SECTION("output defined") { b.extend(osmium::Location(1.2, 3.4)); b.extend(osmium::Location(5.6, 7.8)); std::stringstream out; @@ -67,32 +74,61 @@ TEST_CASE("Box") { REQUIRE(b.size() == Approx(19.36).epsilon(0.000001)); } - SECTION("output_undefined") { - osmium::Box b; + SECTION("output undefined") { std::stringstream out; out << b; REQUIRE(out.str() == "(undefined)"); } - SECTION("box_inside_box") { - osmium::Box outer; - outer.extend(osmium::Location(1, 1)); - outer.extend(osmium::Location(10, 10)); + SECTION("output undefined bottom left") { + b.top_right() = osmium::Location(1.2, 3.4); + std::stringstream out; + out << b; + REQUIRE(out.str() == "(undefined)"); + } - osmium::Box inner; - inner.extend(osmium::Location(2, 2)); - inner.extend(osmium::Location(4, 4)); - - osmium::Box overlap; - overlap.extend(osmium::Location(3, 3)); - overlap.extend(osmium::Location(5, 5)); - - REQUIRE( osmium::geom::contains(inner, outer)); - REQUIRE(!osmium::geom::contains(outer, inner)); - - REQUIRE(!osmium::geom::contains(overlap, inner)); - REQUIRE(!osmium::geom::contains(inner, overlap)); + SECTION("output undefined top right") { + b.bottom_left() = osmium::Location(1.2, 3.4); + std::stringstream out; + out << b; + REQUIRE(out.str() == "(undefined)"); } } +TEST_CASE("Create box from locations") { + osmium::Box b{osmium::Location{1.23, 2.34}, osmium::Location{3.45, 4.56}}; + REQUIRE(!!b); + REQUIRE(b.bottom_left() == (osmium::Location{1.23, 2.34})); + REQUIRE(b.top_right() == (osmium::Location{3.45, 4.56})); +} + +TEST_CASE("Create box from doubles") { + osmium::Box b{1.23, 2.34, 3.45, 4.56}; + REQUIRE(!!b); + REQUIRE(b.bottom_left() == (osmium::Location{1.23, 2.34})); + REQUIRE(b.top_right() == (osmium::Location{3.45, 4.56})); +} + +TEST_CASE("Relationship between boxes") { + + osmium::Box outer; + outer.extend(osmium::Location(1, 1)); + outer.extend(osmium::Location(10, 10)); + + osmium::Box inner; + inner.extend(osmium::Location(2, 2)); + inner.extend(osmium::Location(4, 4)); + + osmium::Box overlap; + overlap.extend(osmium::Location(3, 3)); + overlap.extend(osmium::Location(5, 5)); + + REQUIRE( osmium::geom::contains(inner, outer)); + REQUIRE(!osmium::geom::contains(outer, inner)); + + REQUIRE(!osmium::geom::contains(overlap, inner)); + REQUIRE(!osmium::geom::contains(inner, overlap)); + +} + diff --git a/third_party/libosmium/test/t/basic/test_changeset.cpp b/third_party/libosmium/test/t/basic/test_changeset.cpp index fc9f1bdee..36c4778fe 100644 --- a/third_party/libosmium/test/t/basic/test_changeset.cpp +++ b/third_party/libosmium/test/t/basic/test_changeset.cpp @@ -2,51 +2,61 @@ #include +#include #include #include -#include "helper.hpp" - -TEST_CASE("Basic Changeset") { - - osmium::CRC crc32; +using namespace osmium::builder::attr; +TEST_CASE("Build changeset") { osmium::memory::Buffer buffer(10 * 1000); - osmium::Changeset& cs1 = buffer_add_changeset(buffer, - "user", - {{"comment", "foo"}}); + osmium::builder::add_changeset(buffer, + _cid(42), + _created_at(time_t(100)), + _closed_at(time_t(200)), + _num_changes(7), + _num_comments(3), + _uid(9), + _user("user"), + _tag("comment", "foo") + ); - cs1.set_id(42) - .set_created_at(100) - .set_closed_at(200) - .set_num_changes(7) - .set_uid(9); + const osmium::Changeset& cs1 = buffer.get(0); REQUIRE(42 == cs1.id()); REQUIRE(9 == cs1.uid()); REQUIRE(7 == cs1.num_changes()); + REQUIRE(3 == cs1.num_comments()); REQUIRE(true == cs1.closed()); REQUIRE(osmium::Timestamp(100) == cs1.created_at()); REQUIRE(osmium::Timestamp(200) == cs1.closed_at()); REQUIRE(1 == cs1.tags().size()); REQUIRE(std::string("user") == cs1.user()); + osmium::CRC crc32; crc32.update(cs1); - REQUIRE(crc32().checksum() == 0xf44aff25); + REQUIRE(crc32().checksum() == 0x502e8c0e); - osmium::Changeset& cs2 = buffer_add_changeset(buffer, - "user", - {{"comment", "foo"}, {"foo", "bar"}}); + auto pos = osmium::builder::add_changeset(buffer, + _cid(43), + _created_at(time_t(120)), + _num_changes(21), + _num_comments(0), + _uid(9), + _user("user"), + _tag("comment", "foo"), + _tag("foo", "bar"), + _comment({time_t(300), 10, "user2", "foo"}), + _comments({{time_t(400), 9, "user", "bar"}}) + ); - cs2.set_id(43) - .set_created_at(120) - .set_num_changes(21) - .set_uid(9); + const osmium::Changeset& cs2 = buffer.get(pos); REQUIRE(43 == cs2.id()); REQUIRE(9 == cs2.uid()); REQUIRE(21 == cs2.num_changes()); + REQUIRE(0 == cs2.num_comments()); REQUIRE(false == cs2.closed()); REQUIRE(osmium::Timestamp(120) == cs2.created_at()); REQUIRE(osmium::Timestamp() == cs2.closed_at()); @@ -60,4 +70,76 @@ TEST_CASE("Basic Changeset") { REQUIRE(false == (cs1 > cs2)); REQUIRE(false == (cs1 >= cs2)); + auto cit = cs2.discussion().begin(); + + REQUIRE(cit != cs2.discussion().end()); + REQUIRE(cit->date() == osmium::Timestamp(300)); + REQUIRE(cit->uid() == 10); + REQUIRE(std::string("user2") == cit->user()); + REQUIRE(std::string("foo") == cit->text()); + + REQUIRE(++cit != cs2.discussion().end()); + REQUIRE(cit->date() == osmium::Timestamp(400)); + REQUIRE(cit->uid() == 9); + REQUIRE(std::string("user") == cit->user()); + REQUIRE(std::string("bar") == cit->text()); + + REQUIRE(++cit == cs2.discussion().end()); } + +TEST_CASE("Create changeset without helper") { + osmium::memory::Buffer buffer(10 * 1000); + osmium::builder::ChangesetBuilder builder(buffer); + + osmium::Changeset& cs1 = builder.object(); + cs1.set_id(42) + .set_created_at(100) + .set_closed_at(200) + .set_num_changes(7) + .set_num_comments(2) + .set_uid(9); + + builder.add_user("user"); + { + osmium::builder::TagListBuilder tl_builder(buffer, &builder); + tl_builder.add_tag("key1", "val1"); + tl_builder.add_tag("key2", "val2"); + } + + { + osmium::builder::ChangesetDiscussionBuilder disc_builder(buffer, &builder); + disc_builder.add_comment(osmium::Timestamp(300), 10, "user2"); + disc_builder.add_comment_text("foo"); + disc_builder.add_comment(osmium::Timestamp(400), 9, "user"); + disc_builder.add_comment_text("bar"); + } + + buffer.commit(); + + REQUIRE(42 == cs1.id()); + REQUIRE(9 == cs1.uid()); + REQUIRE(7 == cs1.num_changes()); + REQUIRE(2 == cs1.num_comments()); + REQUIRE(true == cs1.closed()); + REQUIRE(osmium::Timestamp(100) == cs1.created_at()); + REQUIRE(osmium::Timestamp(200) == cs1.closed_at()); + REQUIRE(2 == cs1.tags().size()); + REQUIRE(std::string("user") == cs1.user()); + + auto cit = cs1.discussion().begin(); + + REQUIRE(cit != cs1.discussion().end()); + REQUIRE(cit->date() == osmium::Timestamp(300)); + REQUIRE(cit->uid() == 10); + REQUIRE(std::string("user2") == cit->user()); + REQUIRE(std::string("foo") == cit->text()); + + REQUIRE(++cit != cs1.discussion().end()); + REQUIRE(cit->date() == osmium::Timestamp(400)); + REQUIRE(cit->uid() == 9); + REQUIRE(std::string("user") == cit->user()); + REQUIRE(std::string("bar") == cit->text()); + + REQUIRE(++cit == cs1.discussion().end()); +} + diff --git a/third_party/libosmium/test/t/basic/test_crc.cpp b/third_party/libosmium/test/t/basic/test_crc.cpp index aab1013f4..253c21763 100644 --- a/third_party/libosmium/test/t/basic/test_crc.cpp +++ b/third_party/libosmium/test/t/basic/test_crc.cpp @@ -4,8 +4,6 @@ #include -#include "helper.hpp" - TEST_CASE("CRC of basic datatypes") { osmium::CRC crc32; @@ -24,6 +22,27 @@ TEST_CASE("CRC of basic datatypes") { REQUIRE(crc32().checksum() == 0x8fe62899); } + SECTION("Int16") { + crc32.update_int16(0x0123U); + crc32.update_int16(0x1234U); + + REQUIRE(crc32().checksum() == 0xda923744); + } + + SECTION("Int32") { + crc32.update_int32(0x01234567UL); + crc32.update_int32(0x12345678UL); + + REQUIRE(crc32().checksum() == 0x9b4e2af3); + } + + SECTION("Int64") { + crc32.update_int64(0x0123456789abcdefULL); + crc32.update_int64(0x123456789abcdef0ULL); + + REQUIRE(crc32().checksum() == 0x6d8b7267); + } + SECTION("String") { const char* str = "foobar"; crc32.update_string(str); diff --git a/third_party/libosmium/test/t/basic/test_node.cpp b/third_party/libosmium/test/t/basic/test_node.cpp index db5b4cd53..b7f4c6cce 100644 --- a/third_party/libosmium/test/t/basic/test_node.cpp +++ b/third_party/libosmium/test/t/basic/test_node.cpp @@ -2,29 +2,29 @@ #include +#include #include #include -#include "helper.hpp" +using namespace osmium::builder::attr; -TEST_CASE("Basic_Node") { - - osmium::CRC crc32; - -SECTION("node_builder") { +TEST_CASE("Build node") { osmium::memory::Buffer buffer(10000); - osmium::Node& node = buffer_add_node(buffer, - "foo", - {{"amenity", "pub"}, {"name", "OSM BAR"}}, - {3.5, 4.7}); + osmium::builder::add_node(buffer, + _id(17), + _version(3), + _visible(true), + _cid(333), + _uid(21), + _timestamp(time_t(123)), + _user("foo"), + _tag("amenity", "pub"), + _tag("name", "OSM BAR"), + _location(3.5, 4.7) + ); - node.set_id(17) - .set_version(3) - .set_visible(true) - .set_changeset(333) - .set_uid(21) - .set_timestamp(123); + osmium::Node& node = buffer.get(0); REQUIRE(osmium::item_type::node == node.type()); REQUIRE(node.type_is_in(osmium::osm_entity_bits::node)); @@ -37,23 +37,25 @@ SECTION("node_builder") { REQUIRE(333 == node.changeset()); REQUIRE(21 == node.uid()); REQUIRE(std::string("foo") == node.user()); - REQUIRE(123 == node.timestamp()); + REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); REQUIRE(2 == node.tags().size()); + osmium::CRC crc32; crc32.update(node); - REQUIRE(crc32().checksum() == 0xc696802f); + REQUIRE(crc32().checksum() == 0x7dc553f9); node.set_visible(false); REQUIRE(false == node.visible()); REQUIRE(true == node.deleted()); } -SECTION("node_default_attributes") { +TEST_CASE("default values for node attributes") { osmium::memory::Buffer buffer(10000); - osmium::Node& node = buffer_add_node(buffer, "", {}, osmium::Location{}); + osmium::builder::add_node(buffer, _id(0)); + const osmium::Node& node = buffer.get(0); REQUIRE(0l == node.id()); REQUIRE(0ul == node.positive_id()); REQUIRE(0 == node.version()); @@ -61,19 +63,17 @@ SECTION("node_default_attributes") { REQUIRE(0 == node.changeset()); REQUIRE(0 == node.uid()); REQUIRE(std::string("") == node.user()); - REQUIRE(0 == node.timestamp()); + REQUIRE(0 == uint32_t(node.timestamp())); REQUIRE(osmium::Location() == node.location()); REQUIRE(0 == node.tags().size()); } -SECTION("set_node_attributes_from_string") { +TEST_CASE("set node attributes from strings") { osmium::memory::Buffer buffer(10000); - osmium::Node& node = buffer_add_node(buffer, - "foo", - {{"amenity", "pub"}, {"name", "OSM BAR"}}, - {3.5, 4.7}); + osmium::builder::add_node(buffer, _id(0)); + osmium::Node& node = buffer.get(0); node.set_id("-17") .set_version("3") .set_visible(true) @@ -88,14 +88,13 @@ SECTION("set_node_attributes_from_string") { REQUIRE(21 == node.uid()); } -SECTION("large_id") { +TEST_CASE("set large id") { osmium::memory::Buffer buffer(10000); - osmium::Node& node = buffer_add_node(buffer, "", {}, osmium::Location{}); - int64_t id = 3000000000l; - node.set_id(id); + osmium::builder::add_node(buffer, _id(id)); + osmium::Node& node = buffer.get(0); REQUIRE(id == node.id()); REQUIRE(static_cast(id) == node.positive_id()); @@ -104,14 +103,16 @@ SECTION("large_id") { REQUIRE(static_cast(id) == node.positive_id()); } -SECTION("tags") { +TEST_CASE("set tags on node") { osmium::memory::Buffer buffer(10000); - osmium::Node& node = buffer_add_node(buffer, - "foo", - {{"amenity", "pub"}, {"name", "OSM BAR"}}, - {3.5, 4.7}); + osmium::builder::add_node(buffer, + _user("foo"), + _tag("amenity", "pub"), + _tag("name", "OSM BAR") + ); + const osmium::Node& node = buffer.get(0); REQUIRE(nullptr == node.tags().get_value_by_key("fail")); REQUIRE(std::string("pub") == node.tags().get_value_by_key("amenity")); REQUIRE(std::string("pub") == node.get_value_by_key("amenity")); @@ -121,5 +122,3 @@ SECTION("tags") { REQUIRE(std::string("pub") == node.get_value_by_key("amenity", "default")); } - -} diff --git a/third_party/libosmium/test/t/basic/test_node_ref.cpp b/third_party/libosmium/test/t/basic/test_node_ref.cpp index ac7ccbf4b..9932ff52d 100644 --- a/third_party/libosmium/test/t/basic/test_node_ref.cpp +++ b/third_party/libosmium/test/t/basic/test_node_ref.cpp @@ -1,6 +1,9 @@ #include "catch.hpp" +#include +#include #include +#include TEST_CASE("NodeRef") { @@ -55,3 +58,52 @@ TEST_CASE("NodeRef") { } +TEST_CASE("WayNodeList") { + osmium::memory::Buffer buffer(1024); + + SECTION("Empty list") { + { + osmium::builder::WayNodeListBuilder builder(buffer); + } + REQUIRE(buffer.commit() == 0); + REQUIRE(buffer.committed( )> 0); + + const osmium::WayNodeList& nrl = buffer.get(0); + REQUIRE(nrl.empty()); + REQUIRE(nrl.size() == 0); + } + + SECTION("Small area") { + osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({ + { 1, {0, 0}}, + { 2, {0, 1}}, + { 3, {1, 1}}, + { 4, {1, 0}}, + { 1, {0, 0}}, + })); + + const osmium::WayNodeList& nrl = buffer.get(0); + REQUIRE_FALSE(nrl.empty()); + REQUIRE(nrl.size() == 5); + REQUIRE(nrl.is_closed()); + REQUIRE(nrl.ends_have_same_id()); + REQUIRE(nrl.ends_have_same_location()); + } + + SECTION("Not an area") { + osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({ + { 1, {0, 0}}, + { 2, {1, 0}}, + { 1, {0, 0}}, + })); + + const osmium::WayNodeList& nrl = buffer.get(0); + REQUIRE_FALSE(nrl.empty()); + REQUIRE(nrl.size() == 3); + REQUIRE(nrl.is_closed()); + REQUIRE(nrl.ends_have_same_id()); + REQUIRE(nrl.ends_have_same_location()); + } + +} + diff --git a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp index 2bfdcad90..461969fe9 100644 --- a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp +++ b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp @@ -1,119 +1,48 @@ #include "catch.hpp" +#include #include #include #include TEST_CASE("Object_Comparisons") { + using namespace osmium::builder::attr; + SECTION("order") { osmium::memory::Buffer buffer(10 * 1000); - { - // add node 1 - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - buffer.commit(); - } - - { - // add node 2 - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - buffer.commit(); - } + osmium::builder::add_node(buffer, _id(10), _version(1)); + osmium::builder::add_node(buffer, _id(15), _version(2)); auto it = buffer.begin(); osmium::Node& node1 = static_cast(*it); osmium::Node& node2 = static_cast(*(++it)); - node1.set_id(10); - node1.set_version(1); - node2.set_id(15); - node2.set_version(2); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); node1.set_id(20); node1.set_version(1); node2.set_id(20); node2.set_version(2); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); node1.set_id(-10); node1.set_version(2); node2.set_id(-15); node2.set_version(1); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); } SECTION("order_types") { osmium::memory::Buffer buffer(10 * 1000); - { - // add node 1 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); - - node.set_id(3); - node.set_version(3); - node_builder.add_user("testuser"); - - buffer.commit(); - } - - { - // add node 2 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); - - node.set_id(3); - node.set_version(4); - node_builder.add_user("testuser"); - - buffer.commit(); - } - - { - // add node 3 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); - - node.set_id(3); - node.set_version(4); - node_builder.add_user("testuser"); - - buffer.commit(); - } - - { - // add way - osmium::builder::WayBuilder way_builder(buffer); - osmium::Way& way = way_builder.object(); - REQUIRE(osmium::item_type::way == way.type()); - - way.set_id(2); - way.set_version(2); - way_builder.add_user("testuser"); - - buffer.commit(); - } - - { - // add relation - osmium::builder::RelationBuilder relation_builder(buffer); - osmium::Relation& relation = relation_builder.object(); - REQUIRE(osmium::item_type::relation == relation.type()); - - relation.set_id(1); - relation.set_version(1); - relation_builder.add_user("testuser"); - - buffer.commit(); - } + osmium::builder::add_node(buffer, _id(3), _version(3)); + osmium::builder::add_node(buffer, _id(3), _version(4)); + osmium::builder::add_node(buffer, _id(3), _version(4)); + osmium::builder::add_way(buffer, _id(2), _version(2)); + osmium::builder::add_relation(buffer, _id(1), _version(1)); auto it = buffer.begin(); const osmium::Node& node1 = static_cast(*it); @@ -122,26 +51,26 @@ TEST_CASE("Object_Comparisons") { const osmium::Way& way = static_cast(*(++it)); const osmium::Relation& relation = static_cast(*(++it)); - REQUIRE(true == (node1 < node2)); - REQUIRE(true == (node2 < way)); - REQUIRE(false == (node2 > way)); - REQUIRE(true == (way < relation)); - REQUIRE(true == (node1 < relation)); + REQUIRE(node1 < node2); + REQUIRE(node2 < way); + REQUIRE_FALSE(node2 > way); + REQUIRE(way < relation); + REQUIRE(node1 < relation); - REQUIRE(true == osmium::object_order_type_id_version()(node1, node2)); - REQUIRE(true == osmium::object_order_type_id_reverse_version()(node2, node1)); - REQUIRE(true == osmium::object_order_type_id_version()(node1, way)); - REQUIRE(true == osmium::object_order_type_id_reverse_version()(node1, way)); + REQUIRE(osmium::object_order_type_id_version()(node1, node2)); + REQUIRE(osmium::object_order_type_id_reverse_version()(node2, node1)); + REQUIRE(osmium::object_order_type_id_version()(node1, way)); + REQUIRE(osmium::object_order_type_id_reverse_version()(node1, way)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, node2)); - REQUIRE(true == osmium::object_equal_type_id_version()(node2, node3)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, node2)); + REQUIRE(osmium::object_equal_type_id_version()(node2, node3)); - REQUIRE(true == osmium::object_equal_type_id()(node1, node2)); - REQUIRE(true == osmium::object_equal_type_id()(node2, node3)); + REQUIRE(osmium::object_equal_type_id()(node1, node2)); + REQUIRE(osmium::object_equal_type_id()(node2, node3)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, way)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, relation)); - REQUIRE(false == osmium::object_equal_type_id()(node1, relation)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, way)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, relation)); + REQUIRE_FALSE(osmium::object_equal_type_id()(node1, relation)); } } diff --git a/third_party/libosmium/test/t/basic/test_relation.cpp b/third_party/libosmium/test/t/basic/test_relation.cpp index fd5c7b4ad..dc6c2515c 100644 --- a/third_party/libosmium/test/t/basic/test_relation.cpp +++ b/third_party/libosmium/test/t/basic/test_relation.cpp @@ -2,33 +2,31 @@ #include +#include #include #include -#include "helper.hpp" +using namespace osmium::builder::attr; TEST_CASE("Build relation") { - - osmium::CRC crc32; - osmium::memory::Buffer buffer(10000); - osmium::Relation& relation = buffer_add_relation(buffer, - "foo", { - {"type", "multipolygon"}, - {"name", "Sherwood Forest"} - }, { - std::make_tuple('w', 1, "inner"), - std::make_tuple('w', 2, ""), - std::make_tuple('w', 3, "outer") - }); + osmium::builder::add_relation(buffer, + _id(17), + _version(3), + _visible(), + _cid(333), + _uid(21), + _timestamp(time_t(123)), + _user("foo"), + _tag("type", "multipolygon"), + _tag("name", "Sherwood Forest"), + _member(osmium::item_type::way, 1, "inner"), + _member(osmium::item_type::way, 2, ""), + _member(osmium::item_type::way, 3, "outer") + ); - relation.set_id(17) - .set_version(3) - .set_visible(true) - .set_changeset(333) - .set_uid(21) - .set_timestamp(123); + const osmium::Relation& relation = buffer.get(0); REQUIRE(17 == relation.id()); REQUIRE(3 == relation.version()); @@ -36,7 +34,7 @@ TEST_CASE("Build relation") { REQUIRE(333 == relation.changeset()); REQUIRE(21 == relation.uid()); REQUIRE(std::string("foo") == relation.user()); - REQUIRE(123 == relation.timestamp()); + REQUIRE(123 == uint32_t(relation.timestamp())); REQUIRE(2 == relation.tags().size()); REQUIRE(3 == relation.members().size()); @@ -60,6 +58,18 @@ TEST_CASE("Build relation") { ++n; } + osmium::CRC crc32; crc32.update(relation); - REQUIRE(crc32().checksum() == 0xebcd836d); + REQUIRE(crc32().checksum() == 0x2c2352e); } + +TEST_CASE("Member role too long") { + osmium::memory::Buffer buffer(10000); + + osmium::builder::RelationMemberListBuilder builder(buffer); + + const char role[2000] = ""; + builder.add_member(osmium::item_type::node, 1, role, 1024); + REQUIRE_THROWS(builder.add_member(osmium::item_type::node, 1, role, 1025)); +} + diff --git a/third_party/libosmium/test/t/basic/test_timestamp.cpp b/third_party/libosmium/test/t/basic/test_timestamp.cpp index f015730ea..f80ffcf5f 100644 --- a/third_party/libosmium/test/t/basic/test_timestamp.cpp +++ b/third_party/libosmium/test/t/basic/test_timestamp.cpp @@ -8,34 +8,45 @@ TEST_CASE("Timestamp") { SECTION("can be default initialized to invalid value") { osmium::Timestamp t; - REQUIRE(0 == t); + REQUIRE(0 == uint32_t(t)); REQUIRE("" == t.to_iso()); + REQUIRE_FALSE(t.valid()); } SECTION("invalid value is zero") { osmium::Timestamp t(static_cast(0)); - REQUIRE(0 == t); + REQUIRE(0 == uint32_t(t)); REQUIRE("" == t.to_iso()); + REQUIRE_FALSE(t.valid()); } SECTION("can be initialized from time_t") { osmium::Timestamp t(static_cast(1)); - REQUIRE(1 == t); + REQUIRE(1 == uint32_t(t)); REQUIRE("1970-01-01T00:00:01Z" == t.to_iso()); + REQUIRE(t.valid()); + } + + SECTION("can be initialized from const char*") { + osmium::Timestamp t("2000-01-01T00:00:00Z"); + REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); + REQUIRE(t.valid()); } SECTION("can be initialized from string") { - osmium::Timestamp t("2000-01-01T00:00:00Z"); + std::string s = "2000-01-01T00:00:00Z"; + osmium::Timestamp t(s); REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); + REQUIRE(t.valid()); } SECTION("throws if initialized from bad string") { REQUIRE_THROWS_AS(osmium::Timestamp("x"), std::invalid_argument); } - SECTION("can be implicitly cast to time_t") { + SECTION("can be explicitly cast to time_t") { osmium::Timestamp t(4242); - time_t x = t; + time_t x = t.seconds_since_epoch(); REQUIRE(x == 4242); } @@ -50,6 +61,10 @@ TEST_CASE("Timestamp") { osmium::Timestamp t1(10); osmium::Timestamp t2(50); REQUIRE(t1 < t2); + REQUIRE(t1 > osmium::start_of_time()); + REQUIRE(t2 > osmium::start_of_time()); + REQUIRE(t1 < osmium::end_of_time()); + REQUIRE(t2 < osmium::end_of_time()); } SECTION("can be written to stream") { diff --git a/third_party/libosmium/test/t/basic/test_way.cpp b/third_party/libosmium/test/t/basic/test_way.cpp index 7c7bc2148..c4fa9d542 100644 --- a/third_party/libosmium/test/t/basic/test_way.cpp +++ b/third_party/libosmium/test/t/basic/test_way.cpp @@ -2,30 +2,30 @@ #include +#include #include #include #include -#include "helper.hpp" +using namespace osmium::builder::attr; TEST_CASE("Build way") { - - osmium::CRC crc32; - -SECTION("way_builder") { osmium::memory::Buffer buffer(10000); - osmium::Way& way = buffer_add_way(buffer, - "foo", - {{"highway", "residential"}, {"name", "High Street"}}, - {1, 3, 2}); + osmium::builder::add_way(buffer, + _id(17), + _version(3), + _visible(true), + _cid(333), + _uid(21), + _timestamp(time_t(123)), + _user("foo"), + _tag("highway", "residential"), + _tag("name", "High Street"), + _nodes({1, 3, 2}) + ); - way.set_id(17) - .set_version(3) - .set_visible(true) - .set_changeset(333) - .set_uid(21) - .set_timestamp(123); + const osmium::Way& way = buffer.get(0); REQUIRE(osmium::item_type::way == way.type()); REQUIRE(way.type_is_in(osmium::osm_entity_bits::way)); @@ -36,7 +36,7 @@ SECTION("way_builder") { REQUIRE(333 == way.changeset()); REQUIRE(21 == way.uid()); REQUIRE(std::string("foo") == way.user()); - REQUIRE(123 == way.timestamp()); + REQUIRE(123 == uint32_t(way.timestamp())); REQUIRE(2 == way.tags().size()); REQUIRE(3 == way.nodes().size()); REQUIRE(1 == way.nodes()[0].ref()); @@ -44,23 +44,28 @@ SECTION("way_builder") { REQUIRE(2 == way.nodes()[2].ref()); REQUIRE(! way.is_closed()); + osmium::CRC crc32; crc32.update(way); - REQUIRE(crc32().checksum() == 0x20fe7a30); + REQUIRE(crc32().checksum() == 0x7676d0c2); } -SECTION("closed_way") { +TEST_CASE("build closed way") { osmium::memory::Buffer buffer(10000); - osmium::Way& way = buffer_add_way(buffer, - "foo", - {{"highway", "residential"}, {"name", "High Street"}}, - {1, 3, 1}); + osmium::builder::add_way(buffer, + _tag("highway", "residential"), + _tag("name", "High Street"), + _nodes({1, 3, 1}) + ); + + const osmium::Way& way = buffer.get(0); REQUIRE(way.is_closed()); } -SECTION("way_builder_with_helpers") { +TEST_CASE("build way with helpers") { osmium::memory::Buffer buffer(10000); + { osmium::builder::WayBuilder builder(buffer); builder.add_user("username"); @@ -74,7 +79,8 @@ SECTION("way_builder_with_helpers") { }); } buffer.commit(); - osmium::Way& way = buffer.get(0); + + const osmium::Way& way = buffer.get(0); REQUIRE(std::string("username") == way.user()); @@ -87,4 +93,3 @@ SECTION("way_builder_with_helpers") { REQUIRE(4.1 == way.nodes()[1].location().lon()); } -} diff --git a/third_party/libosmium/test/t/buffer/test_buffer_basics.cpp b/third_party/libosmium/test/t/buffer/test_buffer_basics.cpp new file mode 100644 index 000000000..ffe725168 --- /dev/null +++ b/third_party/libosmium/test/t/buffer/test_buffer_basics.cpp @@ -0,0 +1,34 @@ +#include "catch.hpp" + +#include + +TEST_CASE("Buffer basics") { + + osmium::memory::Buffer invalid_buffer1; + osmium::memory::Buffer invalid_buffer2; + osmium::memory::Buffer empty_buffer1(1024); + osmium::memory::Buffer empty_buffer2(2048); + + REQUIRE(!invalid_buffer1); + REQUIRE(!invalid_buffer2); + REQUIRE(empty_buffer1); + REQUIRE(empty_buffer2); + + REQUIRE(invalid_buffer1 == invalid_buffer2); + REQUIRE(invalid_buffer1 != empty_buffer1); + REQUIRE(empty_buffer1 != empty_buffer2); + + REQUIRE(invalid_buffer1.capacity() == 0); + REQUIRE(invalid_buffer1.written() == 0); + REQUIRE(invalid_buffer1.committed() == 0); + + REQUIRE(empty_buffer1.capacity() == 1024); + REQUIRE(empty_buffer1.written() == 0); + REQUIRE(empty_buffer1.committed() == 0); + + REQUIRE(empty_buffer2.capacity() == 2048); + REQUIRE(empty_buffer2.written() == 0); + REQUIRE(empty_buffer2.committed() == 0); + +} + diff --git a/third_party/libosmium/test/t/buffer/test_buffer_node.cpp b/third_party/libosmium/test/t/buffer/test_buffer_node.cpp index 9bc8f701d..ba2431b80 100644 --- a/third_party/libosmium/test/t/buffer/test_buffer_node.cpp +++ b/third_party/libosmium/test/t/buffer/test_buffer_node.cpp @@ -9,7 +9,7 @@ void check_node_1(osmium::Node& node) { REQUIRE(true == node.visible()); REQUIRE(333 == node.changeset()); REQUIRE(21 == node.uid()); - REQUIRE(123 == node.timestamp()); + REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); REQUIRE(std::string("testuser") == node.user()); @@ -28,7 +28,7 @@ void check_node_2(osmium::Node& node) { REQUIRE(true == node.visible()); REQUIRE(333 == node.changeset()); REQUIRE(21 == node.uid()); - REQUIRE(123 == node.timestamp()); + REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); REQUIRE(std::string("testuser") == node.user()); @@ -56,13 +56,14 @@ void check_node_2(osmium::Node& node) { REQUIRE(2 == n); } -TEST_CASE("Buffer_Node") { +TEST_CASE("Node in Buffer") { - SECTION("buffer_node") { - constexpr size_t buffer_size = 10000; - unsigned char data[buffer_size]; + constexpr size_t buffer_size = 10000; + unsigned char data[buffer_size]; - osmium::memory::Buffer buffer(data, buffer_size, 0); + osmium::memory::Buffer buffer(data, buffer_size, 0); + + SECTION("Add node to buffer") { { // add node 1 @@ -132,4 +133,67 @@ TEST_CASE("Buffer_Node") { } + SECTION("Add buffer to another one") { + + { + // add node 1 + osmium::builder::NodeBuilder node_builder(buffer); + osmium::Node& node = node_builder.object(); + REQUIRE(osmium::item_type::node == node.type()); + + node.set_id(1); + node.set_version(3); + node.set_visible(true); + node.set_changeset(333); + node.set_uid(21); + node.set_timestamp(123); + node.set_location(osmium::Location(3.5, 4.7)); + + node_builder.add_user("testuser"); + + buffer.commit(); + } + + osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes); + + buffer2.add_buffer(buffer); + buffer2.commit(); + + REQUIRE(buffer.committed() == buffer2.committed()); + const osmium::Node& node = buffer2.get(0); + REQUIRE(node.id() == 1); + REQUIRE(123 == uint32_t(node.timestamp())); + } + + SECTION("Use back_inserter on buffer") { + + { + // add node 1 + osmium::builder::NodeBuilder node_builder(buffer); + osmium::Node& node = node_builder.object(); + REQUIRE(osmium::item_type::node == node.type()); + + node.set_id(1); + node.set_version(3); + node.set_visible(true); + node.set_changeset(333); + node.set_uid(21); + node.set_timestamp(123); + node.set_location(osmium::Location(3.5, 4.7)); + + node_builder.add_user("testuser"); + + buffer.commit(); + } + + osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes); + + std::copy(buffer.begin(), buffer.end(), std::back_inserter(buffer2)); + + REQUIRE(buffer.committed() == buffer2.committed()); + const osmium::Node& node = buffer2.get(0); + REQUIRE(node.id() == 1); + REQUIRE(123 == uint32_t(node.timestamp())); + } } + diff --git a/third_party/libosmium/test/t/builder/test_attr.cpp b/third_party/libosmium/test/t/builder/test_attr.cpp new file mode 100644 index 000000000..258ae7a91 --- /dev/null +++ b/third_party/libosmium/test/t/builder/test_attr.cpp @@ -0,0 +1,567 @@ + +#include "catch.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +TEST_CASE("create node using builders") { + + using namespace osmium::builder::attr; + + osmium::memory::Buffer buffer(1024*10); + + SECTION("add node with only id") { + const auto pos = osmium::builder::add_node(buffer, _id(22)); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 22); + REQUIRE(node.version() == 0); + REQUIRE(node.timestamp() == osmium::Timestamp{}); + REQUIRE(node.changeset() == 0); + REQUIRE(node.uid() == 0); + REQUIRE(std::string(node.user()) == ""); + REQUIRE(node.location() == osmium::Location{}); + REQUIRE(node.tags().size() == 0); + } + + SECTION("add node with complete info but no tags") { + const auto loc = osmium::Location(3.14, 1.59); + const auto pos = osmium::builder::add_node(buffer, + _id(1), + _version(17), + _timestamp(osmium::Timestamp("2015-01-01T10:20:30Z")), + _cid(21), + _uid(222), + _location(loc), + _user("foo") + ); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 1); + REQUIRE(node.version() == 17); + REQUIRE(node.timestamp() == osmium::Timestamp{"2015-01-01T10:20:30Z"}); + REQUIRE(node.changeset() == 21); + REQUIRE(node.uid() == 222); + REQUIRE(std::string(node.user()) == "foo"); + REQUIRE(node.location() == loc); + REQUIRE(node.tags().size() == 0); + REQUIRE(std::distance(node.cbegin(), node.cend()) == 0); + } + + SECTION("visible/deleted flag") { + osmium::builder::add_node(buffer, _id(1), _deleted()); + osmium::builder::add_node(buffer, _id(2), _deleted(true)); + osmium::builder::add_node(buffer, _id(3), _deleted(false)); + osmium::builder::add_node(buffer, _id(4), _visible()); + osmium::builder::add_node(buffer, _id(5), _visible(true)); + osmium::builder::add_node(buffer, _id(6), _visible(false)); + + auto it = buffer.cbegin(); + REQUIRE_FALSE(it++->visible()); + REQUIRE_FALSE(it++->visible()); + REQUIRE(it++->visible()); + REQUIRE(it++->visible()); + REQUIRE(it++->visible()); + REQUIRE_FALSE(it++->visible()); + REQUIRE(it == buffer.cend()); + } + + SECTION("order of attributes doesn't matter") { + const auto loc = osmium::Location(3.14, 1.59); + const auto pos = osmium::builder::add_node(buffer, + _timestamp("2015-01-01T10:20:30Z"), + _version(17), + _cid(21), + _uid(222), + _user(std::string("foo")), + _id(1), + _location(3.14, 1.59) + ); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 1); + REQUIRE(node.version() == 17); + REQUIRE(node.timestamp() == osmium::Timestamp{"2015-01-01T10:20:30Z"}); + REQUIRE(node.changeset() == 21); + REQUIRE(node.uid() == 222); + REQUIRE(std::string(node.user()) == "foo"); + REQUIRE(node.location() == loc); + REQUIRE(node.tags().size() == 0); + } + + SECTION("add tags using _tag") { + std::pair t1 = {"name", "Node Inn"}; + std::pair t2 = {"phone", "+1-123-555-4567"}; + + const auto pos = osmium::builder::add_node(buffer, + _id(2), + _tag("amenity", "restaurant"), + _tag(t1), + _tag(t2), + _tag(std::string{"cuisine"}, std::string{"italian"}) + ); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 2); + REQUIRE(node.tags().size() == 4); + REQUIRE(std::distance(node.cbegin(), node.cend()) == 1); + + auto it = node.tags().cbegin(); + REQUIRE(std::string(it->key()) == "amenity"); + REQUIRE(std::string(it->value()) == "restaurant"); + ++it; + REQUIRE(std::string(it->key()) == "name"); + REQUIRE(std::string(it->value()) == "Node Inn"); + ++it; + REQUIRE(std::string(it->key()) == "phone"); + REQUIRE(std::string(it->value()) == "+1-123-555-4567"); + ++it; + REQUIRE(std::string(it->key()) == "cuisine"); + REQUIRE(std::string(it->value()) == "italian"); + ++it; + REQUIRE(it == node.tags().cend()); + } + + SECTION("add tags using _tags from initializer list") { + const auto pos = osmium::builder::add_node(buffer, + _id(3), + _tags({{"amenity", "post_box"}}) + ); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 3); + REQUIRE(node.tags().size() == 1); + + auto it = node.tags().cbegin(); + REQUIRE(std::string(it->key()) == "amenity"); + REQUIRE(std::string(it->value()) == "post_box"); + ++it; + REQUIRE(it == node.tags().cend()); + REQUIRE(std::distance(node.cbegin(), node.cend()) == 1); + } + + SECTION("add tags using _tags from TagList") { + const auto pos1 = osmium::builder::add_node(buffer, + _id(3), + _tag("a", "d"), + _tag("b", "e"), + _tag("c", "f") + ); + + const auto& node1 = buffer.get(pos1); + + const auto pos2 = osmium::builder::add_node(buffer, + _id(4), + _tags(node1.tags()) + ); + + const auto& node2 = buffer.get(pos2); + + REQUIRE(node2.id() == 4); + REQUIRE(node2.tags().size() == 3); + + auto it = node2.tags().cbegin(); + REQUIRE(std::string(it++->key()) == "a"); + REQUIRE(std::string(it++->key()) == "b"); + REQUIRE(std::string(it++->key()) == "c"); + REQUIRE(it == node2.tags().cend()); + REQUIRE(std::distance(node2.cbegin(), node2.cend()) == 1); + } + + SECTION("add tags using mixed tag sources") { + const std::vector tags = { + {"t5", "t5"}, + {"t6", "t6"} + }; + + const auto pos = osmium::builder::add_node(buffer, + _id(4), + _tag("t1", "t1"), + _tags({{"t2", "t2"}, {"t3", "t3"}}), + _tag("t4", "t4"), + _tags(tags) + ); + + const auto& node = buffer.get(pos); + + REQUIRE(node.id() == 4); + REQUIRE(node.tags().size() == 6); + + auto it = node.tags().cbegin(); + REQUIRE(std::string(it->key()) == "t1"); + ++it; + REQUIRE(std::string(it->key()) == "t2"); + ++it; + REQUIRE(std::string(it->key()) == "t3"); + ++it; + REQUIRE(std::string(it->key()) == "t4"); + ++it; + REQUIRE(std::string(it->key()) == "t5"); + ++it; + REQUIRE(std::string(it->key()) == "t6"); + ++it; + REQUIRE(it == node.tags().cend()); + REQUIRE(std::distance(node.cbegin(), node.cend()) == 1); + } + +} + +TEST_CASE("create way using builders") { + + using namespace osmium::builder::attr; + + osmium::memory::Buffer buffer(1024*10); + + SECTION("add way without nodes") { + const auto pos = osmium::builder::add_way(buffer, + _id(999), + _cid(21), + _uid(222), + _user("foo") + ); + + const auto& way = buffer.get(pos); + + REQUIRE(way.id() == 999); + REQUIRE(way.version() == 0); + REQUIRE(way.timestamp() == osmium::Timestamp{}); + REQUIRE(way.changeset() == 21); + REQUIRE(way.uid() == 222); + REQUIRE(std::string(way.user()) == "foo"); + REQUIRE(way.tags().size() == 0); + REQUIRE(way.nodes().size() == 0); + REQUIRE(std::distance(way.cbegin(), way.cend()) == 0); + } + +} + +TEST_CASE("create way with nodes") { + std::vector nrvec = { + { 1, osmium::Location{1.1, 0.1} }, + { 2, osmium::Location{2.2, 0.2} }, + { 4, osmium::Location{4.4, 0.4} }, + { 8, osmium::Location{8.8, 0.8} } + }; + + using namespace osmium::builder::attr; + + osmium::memory::Buffer wbuffer(1024*10); + osmium::builder::add_way(wbuffer, + _id(1), + _nodes({1, 2, 4, 8}) + ); + + const osmium::NodeRefList& nodes = wbuffer.get(0).nodes(); + + osmium::memory::Buffer buffer(1024*10); + + SECTION("add nodes using an OSM object id or NodeRef") { + osmium::builder::add_way(buffer, + _id(1), + _node(1), + _node(2), + _node(osmium::NodeRef{4}), + _node(8) + ); + + } + + SECTION("add nodes using iterator list with object ids") { + osmium::builder::add_way(buffer, + _id(1), + _nodes({1, 2, 4, 8}) + ); + } + + SECTION("add way with nodes in initializer_list of NodeRefs") { + osmium::builder::add_way(buffer, + _id(1), + _nodes({ + { 1, {1.1, 0.1} }, + { 2, {2.2, 0.2} }, + { 4, {4.4, 0.4} }, + { 8, {8.8, 0.8} } + }) + ); + } + + SECTION("add nodes using WayNodeList") { + osmium::builder::add_way(buffer, + _id(1), + _nodes(nodes) + ); + } + + SECTION("add nodes using vector of OSM object ids") { + const std::vector some_nodes = { + 1, 2, 4, 8 + }; + + osmium::builder::add_way(buffer, + _id(1), + _nodes(some_nodes) + ); + } + + SECTION("add nodes using vector of NodeRefs") { + osmium::builder::add_way(buffer, + _id(1), + _nodes(nrvec) + ); + } + + SECTION("add nodes using different means together") { + osmium::builder::add_way(buffer, + _id(1), + _node(1), + _nodes({2, 4}), + _node(8) + ); + } + + SECTION("add nodes using different means together") { + osmium::builder::add_way(buffer, + _id(1), + _nodes(nodes.begin(), nodes.begin() + 1), + _nodes({2, 4, 8}) + ); + } + + const auto& way = buffer.get(0); + + REQUIRE(way.id() == 1); + REQUIRE(way.nodes().size() == 4); + REQUIRE(std::distance(way.cbegin(), way.cend()) == 1); + + auto it = way.nodes().cbegin(); + + REQUIRE(it->ref() == 1); + if (it->location().valid()) { + REQUIRE(*it == nrvec[0]); + } + it++; + + REQUIRE(it->ref() == 2); + if (it->location().valid()) { + REQUIRE(*it == nrvec[1]); + } + it++; + + REQUIRE(it->ref() == 4); + if (it->location().valid()) { + REQUIRE(*it == nrvec[2]); + } + it++; + + REQUIRE(it->ref() == 8); + if (it->location().valid()) { + REQUIRE(*it == nrvec[3]); + } + it++; + + REQUIRE(it == way.nodes().cend()); +} + +TEST_CASE("create relation using builders") { + + using namespace osmium::builder::attr; + + osmium::memory::Buffer buffer(1024*10); + + SECTION("create relation") { + osmium::builder::attr::member_type m{osmium::item_type::way, 113, "inner"}; + + osmium::builder::add_relation(buffer, + _id(123), + _member(osmium::item_type::node, 123, ""), + _member(osmium::item_type::node, 132), + _member(osmium::item_type::way, 111, "outer"), + _member(osmium::builder::attr::member_type{osmium::item_type::way, 112, "inner"}), + _member(m) + ); + + const auto& relation = buffer.get(0); + + REQUIRE(relation.id() == 123); + REQUIRE(relation.members().size() == 5); + REQUIRE(std::distance(relation.cbegin(), relation.cend()) == 1); + + auto it = relation.members().begin(); + + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string(it->role()) == ""); + ++it; + + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 132); + REQUIRE(std::string(it->role()) == ""); + ++it; + + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 111); + REQUIRE(std::string(it->role()) == "outer"); + ++it; + + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 112); + REQUIRE(std::string(it->role()) == "inner"); + ++it; + + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 113); + REQUIRE(std::string(it->role()) == "inner"); + ++it; + + REQUIRE(it == relation.members().end()); + } + + SECTION("create relation member from existing relation member") { + osmium::builder::add_relation(buffer, + _id(123), + _member(osmium::item_type::way, 111, "outer"), + _member(osmium::item_type::way, 112, "inner") + ); + + const auto& relation1 = buffer.get(0); + + const auto pos = osmium::builder::add_relation(buffer, + _id(124), + _member(*relation1.members().begin()), + _members(std::next(relation1.members().begin()), relation1.members().end()) + ); + + const auto& relation = buffer.get(pos); + + REQUIRE(relation.id() == 124); + REQUIRE(relation.members().size() == 2); + + auto it = relation.members().begin(); + + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 111); + REQUIRE(std::string(it->role()) == "outer"); + ++it; + + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 112); + REQUIRE(std::string(it->role()) == "inner"); + ++it; + + REQUIRE(it == relation.members().end()); + } + + SECTION("create relation with members from initializer list") { + const auto pos = osmium::builder::add_relation(buffer, + _id(123), + _members({ + {osmium::item_type::node, 123, ""}, + {osmium::item_type::way, 111, "outer"} + }) + ); + + const auto& relation = buffer.get(pos); + + REQUIRE(relation.id() == 123); + REQUIRE(relation.members().size() == 2); + REQUIRE(std::distance(relation.cbegin(), relation.cend()) == 1); + + auto it = relation.members().begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string(it->role()) == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 111); + REQUIRE(std::string(it->role()) == "outer"); + ++it; + REQUIRE(it == relation.members().end()); + } + + SECTION("create relation with members from iterators and some tags") { + const std::vector members = { + {osmium::item_type::node, 123}, + {osmium::item_type::way, 111, "outer"} + }; + + SECTION("using iterators") { + osmium::builder::add_relation(buffer, + _id(123), + _members(members.begin(), members.end()), + _tag("a", "x"), + _tag("b", "y") + ); + } + SECTION("using container") { + osmium::builder::add_relation(buffer, + _id(123), + _members(members), + _tag("a", "x"), + _tag("b", "y") + ); + } + + const auto& relation = buffer.get(0); + + REQUIRE(relation.id() == 123); + REQUIRE(relation.members().size() == 2); + REQUIRE(relation.tags().size() == 2); + REQUIRE(std::distance(relation.cbegin(), relation.cend()) == 2); + + auto it = relation.members().begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string(it->role()) == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 111); + REQUIRE(std::string(it->role()) == "outer"); + ++it; + REQUIRE(it == relation.members().end()); + } + +} + +TEST_CASE("create area using builders") { + + using namespace osmium::builder::attr; + + osmium::memory::Buffer buffer(1024*10); + + SECTION("add area without rings") { + const auto pos = osmium::builder::add_area(buffer, + _id(999), + _cid(21), + _uid(222), + _user("foo"), + _tag("landuse", "residential") + ); + + const auto& area = buffer.get(pos); + + REQUIRE(area.id() == 999); + REQUIRE(area.version() == 0); + REQUIRE(area.timestamp() == osmium::Timestamp{}); + REQUIRE(area.changeset() == 21); + REQUIRE(area.uid() == 222); + REQUIRE(std::string(area.user()) == "foo"); + REQUIRE(area.tags().size() == 1); + REQUIRE(std::distance(area.cbegin(), area.cend()) == 1); + } + +} + diff --git a/third_party/libosmium/test/t/geom/area_helper.hpp b/third_party/libosmium/test/t/geom/area_helper.hpp new file mode 100644 index 000000000..7e2a1ce6b --- /dev/null +++ b/third_party/libosmium/test/t/geom/area_helper.hpp @@ -0,0 +1,81 @@ + +#include +#include +#include + +using namespace osmium::builder::attr; + +inline const osmium::Area& create_test_area_1outer_0inner(osmium::memory::Buffer& buffer) { + osmium::builder::add_area(buffer, + _user("foo"), + _tag("building", "true"), + _outer_ring({ + {1, {3.2, 4.2}}, + {2, {3.5, 4.7}}, + {3, {3.6, 4.9}}, + {1, {3.2, 4.2}} + }) + ); + + return buffer.get(0); +} + +inline const osmium::Area& create_test_area_1outer_1inner(osmium::memory::Buffer& buffer) { + osmium::builder::add_area(buffer, + _user("foo"), + _tag("building", "true"), + _outer_ring({ + {1, {0.1, 0.1}}, + {2, {9.1, 0.1}}, + {3, {9.1, 9.1}}, + {4, {0.1, 9.1}}, + {1, {0.1, 0.1}} + }), + _inner_ring({ + {5, {1.0, 1.0}}, + {6, {8.0, 1.0}}, + {7, {8.0, 8.0}}, + {8, {1.0, 8.0}}, + {5, {1.0, 1.0}} + }) + ); + + return buffer.get(0); +} + +inline const osmium::Area& create_test_area_2outer_2inner(osmium::memory::Buffer& buffer) { + osmium::builder::add_area(buffer, + _user("foo"), + _tag("building", "true"), + _outer_ring({ + {1, {0.1, 0.1}}, + {2, {9.1, 0.1}}, + {3, {9.1, 9.1}}, + {4, {0.1, 9.1}}, + {1, {0.1, 0.1}} + }), + _inner_ring({ + {5, {1.0, 1.0}}, + {6, {4.0, 1.0}}, + {7, {4.0, 4.0}}, + {8, {1.0, 4.0}}, + {5, {1.0, 1.0}} + }), + _inner_ring({ + {10, {5.0, 5.0}}, + {11, {5.0, 7.0}}, + {12, {7.0, 7.0}}, + {10, {5.0, 5.0}} + }), + _outer_ring({ + {100, {10.0, 10.0}}, + {101, {11.0, 10.0}}, + {102, {11.0, 11.0}}, + {103, {10.0, 11.0}}, + {100, {10.0, 10.0}} + }) + ); + + return buffer.get(0); +} + diff --git a/third_party/libosmium/test/t/geom/test_crs.cpp b/third_party/libosmium/test/t/geom/test_crs.cpp new file mode 100644 index 000000000..c8fbc098f --- /dev/null +++ b/third_party/libosmium/test/t/geom/test_crs.cpp @@ -0,0 +1,18 @@ +#include "catch.hpp" + +#include + +#include + +TEST_CASE("CRS") { + osmium::geom::CRS wgs84{4326}; + osmium::geom::CRS mercator{3857}; + + osmium::geom::Coordinates c{osmium::geom::deg_to_rad(1.2), osmium::geom::deg_to_rad(3.4)}; + auto ct = osmium::geom::transform(wgs84, mercator, c); + auto c2 = osmium::geom::transform(mercator, wgs84, ct); + + REQUIRE(c.x == Approx(c2.x)); + REQUIRE(c.y == Approx(c2.y)); +} + diff --git a/third_party/libosmium/test/t/geom/test_geojson.cpp b/third_party/libosmium/test/t/geom/test_geojson.cpp index 8d7df3521..a9e839822 100644 --- a/third_party/libosmium/test/t/geom/test_geojson.cpp +++ b/third_party/libosmium/test/t/geom/test_geojson.cpp @@ -1,9 +1,9 @@ #include "catch.hpp" -#include #include -#include "../basic/helper.hpp" +#include "area_helper.hpp" +#include "wnl_helper.hpp" TEST_CASE("GeoJSON_Geometry") { @@ -23,13 +23,8 @@ SECTION("empty_point") { SECTION("linestring") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + osmium::memory::Buffer buffer(1000); + auto &wnl = create_test_wnl_okay(buffer); { std::string json {factory.create_linestring(wnl)}; @@ -55,8 +50,8 @@ SECTION("linestring") { SECTION("empty_linestring") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, {}); + osmium::memory::Buffer buffer(1000); + auto& wnl = create_test_wnl_empty(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); @@ -67,11 +62,8 @@ SECTION("empty_linestring") { SECTION("linestring_with_two_same_locations") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, {3.5, 4.7}} - }); + osmium::memory::Buffer buffer(1000); + auto& wnl = create_test_wnl_same_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); @@ -90,11 +82,8 @@ SECTION("linestring_with_two_same_locations") { SECTION("linestring_with_undefined_location") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, osmium::Location()} - }); + osmium::memory::Buffer buffer(1000); + auto& wnl = create_test_wnl_undefined_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); } @@ -102,18 +91,8 @@ SECTION("linestring_with_undefined_location") { SECTION("area_1outer_0inner") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {3.2, 4.2}}, - {2, {3.5, 4.7}}, - {3, {3.6, 4.9}}, - {1, {3.2, 4.2}} - }} - }); + osmium::memory::Buffer buffer(1000); + const osmium::Area& area = create_test_area_1outer_0inner(buffer); REQUIRE(!area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 2); @@ -128,26 +107,8 @@ SECTION("area_1outer_0inner") { SECTION("area_1outer_1inner") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {8.0, 1.0}}, - {7, {8.0, 8.0}}, - {8, {1.0, 8.0}}, - {5, {1.0, 1.0}} - }} - }); + osmium::memory::Buffer buffer(1000); + const osmium::Area& area = create_test_area_1outer_1inner(buffer); REQUIRE(!area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 3); @@ -163,39 +124,8 @@ SECTION("area_1outer_1inner") { SECTION("area_2outer_2inner") { osmium::geom::GeoJSONFactory<> factory; - osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {4.0, 1.0}}, - {7, {4.0, 4.0}}, - {8, {1.0, 4.0}}, - {5, {1.0, 1.0}} - }}, - { false, { - {10, {5.0, 5.0}}, - {11, {5.0, 7.0}}, - {12, {7.0, 7.0}}, - {10, {5.0, 5.0}} - }}, - { true, { - {100, {10.0, 10.0}}, - {101, {11.0, 10.0}}, - {102, {11.0, 11.0}}, - {103, {10.0, 11.0}}, - {100, {10.0, 10.0}} - }} - }); + osmium::memory::Buffer buffer(1000); + const osmium::Area& area = create_test_area_2outer_2inner(buffer); REQUIRE(area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 5); diff --git a/third_party/libosmium/test/t/geom/test_geos.cpp b/third_party/libosmium/test/t/geom/test_geos.cpp index d849e3b16..8bf11c93e 100644 --- a/third_party/libosmium/test/t/geom/test_geos.cpp +++ b/third_party/libosmium/test/t/geom/test_geos.cpp @@ -1,9 +1,9 @@ #include "catch.hpp" -#include #include -#include "../basic/helper.hpp" +#include "area_helper.hpp" +#include "wnl_helper.hpp" TEST_CASE("GEOS geometry factory - create point") { osmium::geom::GEOSFactory<> factory; @@ -43,12 +43,7 @@ TEST_CASE("GEOS geometry factory - create linestring") { osmium::geom::GEOSFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); SECTION("from way node list") { std::unique_ptr linestring {factory.create_linestring(wnl)}; @@ -88,17 +83,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings osmium::geom::GEOSFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {3.2, 4.2}}, - {2, {3.5, 4.7}}, - {3, {3.6, 4.9}}, - {1, {3.2, 4.2}} - }} - }); + const osmium::Area& area = create_test_area_1outer_0inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); @@ -117,25 +102,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring osmium::geom::GEOSFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {8.0, 1.0}}, - {7, {8.0, 8.0}}, - {8, {1.0, 8.0}}, - {5, {1.0, 1.0}} - }} - }); + const osmium::Area& area = create_test_area_1outer_1inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); @@ -154,38 +121,7 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring osmium::geom::GEOSFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {4.0, 1.0}}, - {7, {4.0, 4.0}}, - {8, {1.0, 4.0}}, - {5, {1.0, 1.0}} - }}, - { false, { - {10, {5.0, 5.0}}, - {11, {5.0, 7.0}}, - {12, {7.0, 7.0}}, - {10, {5.0, 5.0}} - }}, - { true, { - {100, {10.0, 10.0}}, - {101, {11.0, 10.0}}, - {102, {11.0, 11.0}}, - {103, {10.0, 11.0}}, - {100, {10.0, 10.0}} - }} - }); + const osmium::Area& area = create_test_area_2outer_2inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(2 == mp->getNumGeometries()); diff --git a/third_party/libosmium/test/t/geom/test_geos_wkb.cpp b/third_party/libosmium/test/t/geom/test_geos_wkb.cpp index 0f54427e5..1fca63bc8 100644 --- a/third_party/libosmium/test/t/geom/test_geos_wkb.cpp +++ b/third_party/libosmium/test/t/geom/test_geos_wkb.cpp @@ -1,11 +1,11 @@ #include "catch.hpp" -#include #include #include -#include "../basic/helper.hpp" #include "helper.hpp" +#include "area_helper.hpp" +#include "wnl_helper.hpp" TEST_CASE("WKB_Geometry_with_GEOS") { @@ -25,12 +25,7 @@ SECTION("linestring") { osmium::geom::GEOSFactory<> geos_factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); { std::string wkb = wkb_factory.create_linestring(wnl); @@ -62,17 +57,7 @@ SECTION("area_1outer_0inner") { osmium::geom::GEOSFactory<> geos_factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {3.2, 4.2}}, - {2, {3.5, 4.7}}, - {3, {3.6, 4.9}}, - {1, {3.2, 4.2}} - }} - }); + const osmium::Area& area = create_test_area_1outer_0inner(buffer); std::string wkb = wkb_factory.create_multipolygon(area); std::unique_ptr geos = geos_factory.create_multipolygon(area); @@ -84,25 +69,7 @@ SECTION("area_1outer_1inner") { osmium::geom::GEOSFactory<> geos_factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {8.0, 1.0}}, - {7, {8.0, 8.0}}, - {8, {1.0, 8.0}}, - {5, {1.0, 1.0}} - }} - }); + const osmium::Area& area = create_test_area_1outer_1inner(buffer); std::string wkb = wkb_factory.create_multipolygon(area); std::unique_ptr geos = geos_factory.create_multipolygon(area); @@ -114,38 +81,7 @@ SECTION("area_2outer_2inner") { osmium::geom::GEOSFactory<> geos_factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {4.0, 1.0}}, - {7, {4.0, 4.0}}, - {8, {1.0, 4.0}}, - {5, {1.0, 1.0}} - }}, - { false, { - {10, {5.0, 5.0}}, - {11, {5.0, 7.0}}, - {12, {7.0, 7.0}}, - {10, {5.0, 5.0}} - }}, - { true, { - {100, {10.0, 10.0}}, - {101, {11.0, 10.0}}, - {102, {11.0, 11.0}}, - {103, {10.0, 11.0}}, - {100, {10.0, 10.0}} - }} - }); + const osmium::Area& area = create_test_area_2outer_2inner(buffer); std::string wkb = wkb_factory.create_multipolygon(area); std::unique_ptr geos = geos_factory.create_multipolygon(area); diff --git a/third_party/libosmium/test/t/geom/test_ogr.cpp b/third_party/libosmium/test/t/geom/test_ogr.cpp index 28a10717d..26d34c13a 100644 --- a/third_party/libosmium/test/t/geom/test_ogr.cpp +++ b/third_party/libosmium/test/t/geom/test_ogr.cpp @@ -1,9 +1,9 @@ #include "catch.hpp" -#include #include -#include "../basic/helper.hpp" +#include "area_helper.hpp" +#include "wnl_helper.hpp" TEST_CASE("OGR_Geometry") { @@ -25,12 +25,7 @@ SECTION("linestring") { osmium::geom::OGRFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); { std::unique_ptr linestring {factory.create_linestring(wnl)}; @@ -67,17 +62,7 @@ SECTION("area_1outer_0inner") { osmium::geom::OGRFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {3.2, 4.2}}, - {2, {3.5, 4.7}}, - {3, {3.6, 4.9}}, - {1, {3.2, 4.2}} - }} - }); + const osmium::Area& area = create_test_area_1outer_0inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); @@ -95,25 +80,7 @@ SECTION("area_1outer_1inner") { osmium::geom::OGRFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {8.0, 1.0}}, - {7, {8.0, 8.0}}, - {8, {1.0, 8.0}}, - {5, {1.0, 1.0}} - }} - }); + const osmium::Area& area = create_test_area_1outer_1inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); @@ -132,38 +99,7 @@ SECTION("area_2outer_2inner") { osmium::geom::OGRFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {4.0, 1.0}}, - {7, {4.0, 4.0}}, - {8, {1.0, 4.0}}, - {5, {1.0, 1.0}} - }}, - { false, { - {10, {5.0, 5.0}}, - {11, {5.0, 7.0}}, - {12, {7.0, 7.0}}, - {10, {5.0, 5.0}} - }}, - { true, { - {100, {10.0, 10.0}}, - {101, {11.0, 10.0}}, - {102, {11.0, 11.0}}, - {103, {10.0, 11.0}}, - {100, {10.0, 10.0}} - }} - }); + const osmium::Area& area = create_test_area_2outer_2inner(buffer); std::unique_ptr mp {factory.create_multipolygon(area)}; REQUIRE(2 == mp->getNumGeometries()); diff --git a/third_party/libosmium/test/t/geom/test_tile_data.hpp b/third_party/libosmium/test/t/geom/test_tile_data.hpp index e5c0953c0..8a22cfa51 100644 --- a/third_party/libosmium/test/t/geom/test_tile_data.hpp +++ b/third_party/libosmium/test/t/geom/test_tile_data.hpp @@ -1,5 +1,7 @@ -std::string s = R"(127.4864358 16.8380041 223904 118630 18 +#include + +static std::string s = R"(127.4864358 16.8380041 223904 118630 18 163.1103174 39.4760232 121 48 7 -4.1372725 -22.5105386 31 36 6 98.7193066 -36.2312406 1 1 1 diff --git a/third_party/libosmium/test/t/geom/test_wkb.cpp b/third_party/libosmium/test/t/geom/test_wkb.cpp index 730ee75ad..49710cbc2 100644 --- a/third_party/libosmium/test/t/geom/test_wkb.cpp +++ b/third_party/libosmium/test/t/geom/test_wkb.cpp @@ -1,9 +1,7 @@ #include "catch.hpp" -#include #include - -#include "../basic/helper.hpp" +#include "wnl_helper.hpp" #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -27,12 +25,7 @@ SECTION("linestring") { osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); { std::string wkb {factory.create_linestring(wnl)}; @@ -59,12 +52,7 @@ SECTION("linestring_ewkb") { osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); std::string ewkb {factory.create_linestring(wnl)}; REQUIRE(std::string{"0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == ewkb); @@ -74,10 +62,7 @@ SECTION("linestring_with_two_same_locations") { osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, {3.5, 4.7}} - }); + auto &wnl = create_test_wnl_same_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); @@ -97,10 +82,7 @@ SECTION("linestring_with_undefined_location") { osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, osmium::Location()} - }); + auto &wnl = create_test_wnl_undefined_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); } @@ -121,7 +103,7 @@ SECTION("empty_linestring") { osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, {}); + auto &wnl = create_test_wnl_empty(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); diff --git a/third_party/libosmium/test/t/geom/test_wkt.cpp b/third_party/libosmium/test/t/geom/test_wkt.cpp index 538307409..55ccb4ada 100644 --- a/third_party/libosmium/test/t/geom/test_wkt.cpp +++ b/third_party/libosmium/test/t/geom/test_wkt.cpp @@ -1,9 +1,9 @@ #include "catch.hpp" -#include #include -#include "../basic/helper.hpp" +#include "area_helper.hpp" +#include "wnl_helper.hpp" TEST_CASE("WKT_Geometry") { @@ -24,12 +24,7 @@ SECTION("linestring") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.2, 4.2}}, - {3, {3.5, 4.7}}, - {4, {3.5, 4.7}}, - {2, {3.6, 4.9}} - }); + auto &wnl = create_test_wnl_okay(buffer); { std::string wkt {factory.create_linestring(wnl)}; @@ -56,7 +51,7 @@ SECTION("empty_linestring") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, {}); + auto &wnl = create_test_wnl_empty(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); @@ -68,10 +63,7 @@ SECTION("linestring_with_two_same_locations") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, {3.5, 4.7}} - }); + auto &wnl = create_test_wnl_same_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); @@ -99,10 +91,7 @@ SECTION("linestring_with_undefined_location") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - auto& wnl = osmium::builder::build_way_node_list(buffer, { - {1, {3.5, 4.7}}, - {2, osmium::Location()} - }); + auto &wnl = create_test_wnl_undefined_location(buffer); REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); } @@ -111,17 +100,7 @@ SECTION("area_1outer_0inner") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {3.2, 4.2}}, - {2, {3.5, 4.7}}, - {3, {3.6, 4.9}}, - {1, {3.2, 4.2}} - }} - }); + const osmium::Area& area = create_test_area_1outer_0inner(buffer); { std::string wkt {factory.create_multipolygon(area)}; @@ -133,25 +112,7 @@ SECTION("area_1outer_1inner") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {8.0, 1.0}}, - {7, {8.0, 8.0}}, - {8, {1.0, 8.0}}, - {5, {1.0, 1.0}} - }} - }); + const osmium::Area& area = create_test_area_1outer_1inner(buffer); { std::string wkt {factory.create_multipolygon(area)}; @@ -163,38 +124,7 @@ SECTION("area_2outer_2inner") { osmium::geom::WKTFactory<> factory; osmium::memory::Buffer buffer(10000); - osmium::Area& area = buffer_add_area(buffer, - "foo", - {}, - { - { true, { - {1, {0.1, 0.1}}, - {2, {9.1, 0.1}}, - {3, {9.1, 9.1}}, - {4, {0.1, 9.1}}, - {1, {0.1, 0.1}} - }}, - { false, { - {5, {1.0, 1.0}}, - {6, {4.0, 1.0}}, - {7, {4.0, 4.0}}, - {8, {1.0, 4.0}}, - {5, {1.0, 1.0}} - }}, - { false, { - {10, {5.0, 5.0}}, - {11, {5.0, 7.0}}, - {12, {7.0, 7.0}}, - {10, {5.0, 5.0}} - }}, - { true, { - {100, {10.0, 10.0}}, - {101, {11.0, 10.0}}, - {102, {11.0, 11.0}}, - {103, {10.0, 11.0}}, - {100, {10.0, 10.0}} - }} - }); + const osmium::Area& area = create_test_area_2outer_2inner(buffer); { std::string wkt {factory.create_multipolygon(area)}; diff --git a/third_party/libosmium/test/t/geom/wnl_helper.hpp b/third_party/libosmium/test/t/geom/wnl_helper.hpp new file mode 100644 index 000000000..91ac114dd --- /dev/null +++ b/third_party/libosmium/test/t/geom/wnl_helper.hpp @@ -0,0 +1,44 @@ + +#include +#include +#include + +using namespace osmium::builder::attr; + +inline const osmium::WayNodeList& create_test_wnl_okay(osmium::memory::Buffer& buffer) { + auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + {1, {3.2, 4.2}}, + {3, {3.5, 4.7}}, + {4, {3.5, 4.7}}, + {2, {3.6, 4.9}} + })); + + return buffer.get(pos); +} + +inline const osmium::WayNodeList& create_test_wnl_empty(osmium::memory::Buffer& buffer) { + { + osmium::builder::WayNodeListBuilder wnl_builder(buffer); + } + + return buffer.get(buffer.commit()); +} + +inline const osmium::WayNodeList& create_test_wnl_same_location(osmium::memory::Buffer& buffer) { + auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + {1, {3.5, 4.7}}, + {2, {3.5, 4.7}} + })); + + return buffer.get(pos); +} + +inline const osmium::WayNodeList& create_test_wnl_undefined_location(osmium::memory::Buffer& buffer) { + auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + {1, {3.5, 4.7}}, + {2, osmium::Location()} + })); + + return buffer.get(pos); +} + diff --git a/third_party/libosmium/test/t/io/deleted_nodes.osh b/third_party/libosmium/test/t/io/deleted_nodes.osh new file mode 100644 index 000000000..639e05149 --- /dev/null +++ b/third_party/libosmium/test/t/io/deleted_nodes.osh @@ -0,0 +1,5 @@ + + + + + diff --git a/third_party/libosmium/test/t/io/deleted_nodes.osh.pbf b/third_party/libosmium/test/t/io/deleted_nodes.osh.pbf new file mode 100644 index 000000000..8a94870d1 Binary files /dev/null and b/third_party/libosmium/test/t/io/deleted_nodes.osh.pbf differ diff --git a/third_party/libosmium/test/t/io/test_output_utils.cpp b/third_party/libosmium/test/t/io/test_output_utils.cpp new file mode 100644 index 000000000..a76068348 --- /dev/null +++ b/third_party/libosmium/test/t/io/test_output_utils.cpp @@ -0,0 +1,153 @@ + +#include "catch.hpp" + +#include + +#include + +TEST_CASE("output formatted") { + + std::string out; + + SECTION("small results") { + osmium::io::detail::append_printf_formatted_string(out, "%d", 17); + REQUIRE(out == "17"); + } + + SECTION("several parameters") { + osmium::io::detail::append_printf_formatted_string(out, "%d %s", 17, "foo"); + REQUIRE(out == "17 foo"); + + } + + SECTION("string already containing something") { + out += "foo"; + osmium::io::detail::append_printf_formatted_string(out, " %d", 23); + REQUIRE(out == "foo 23"); + } + + SECTION("large results") { + const char* str = + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + + osmium::io::detail::append_printf_formatted_string(out, "%s", str); + + REQUIRE(out == str); + } + +} + +TEST_CASE("UTF8 encoding") { + + std::string out; + + SECTION("append to string") { + out += "1234"; + osmium::io::detail::append_utf8_encoded_string(out, "abc"); + REQUIRE(out == "1234abc"); + } + + SECTION("don't encode alphabetic characters") { + const char* s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + osmium::io::detail::append_utf8_encoded_string(out, s); + REQUIRE(out == s); + } + + SECTION("don't encode numeric characters") { + const char* s = "0123456789"; + osmium::io::detail::append_utf8_encoded_string(out, s); + REQUIRE(out == s); + } + + SECTION("don't encode lots of often used characters characters") { + const char* s = ".-;:_#+"; + osmium::io::detail::append_utf8_encoded_string(out, s); + REQUIRE(out == s); + } + + SECTION("encode characters that are special in OPL") { + osmium::io::detail::append_utf8_encoded_string(out, " \n,=@"); + REQUIRE(out == "%20%%0a%%2c%%3d%%40%"); + } + +// workaround for missing support for u8 string literals on Windows +#if !defined(_MSC_VER) + + SECTION("encode multibyte character") { + osmium::io::detail::append_utf8_encoded_string(out, u8"\u30dc_\U0001d11e_\U0001f6eb"); + REQUIRE(out == "%30dc%_%1d11e%_%1f6eb%"); + } + +#endif + +} + +TEST_CASE("html encoding") { + + std::string out; + + SECTION("do not encode normal characters") { + const char* s = "abc123,.-"; + osmium::io::detail::append_xml_encoded_string(out, s); + REQUIRE(out == s); + } + + SECTION("encode special XML characters") { + const char* s = "& \" \' < > \n \r \t"; + osmium::io::detail::append_xml_encoded_string(out, s); + REQUIRE(out == "& " ' < > "); + } + +} + +TEST_CASE("debug encoding") { + + std::string out; + + SECTION("do not encode normal characters") { + const char* s = "abc123,.-"; + osmium::io::detail::append_debug_encoded_string(out, s, "[", "]"); + REQUIRE(out == s); + } + + SECTION("encode some unicode characters") { + const char* s = u8"\n_\u30dc_\U0001d11e_\U0001f6eb"; + osmium::io::detail::append_debug_encoded_string(out, s, "[", "]"); + REQUIRE(out == "[]_[]_[]_[]"); + } + +} + +TEST_CASE("encoding of non-printable characters in the first 127 characters") { + + std::locale cloc("C"); + char s[] = "a\0"; + + for (char c = 1; c < 0x7f; ++c) { + std::string out; + s[0] = c; + + SECTION("utf8 encode") { + osmium::io::detail::append_utf8_encoded_string(out, s); + + if (!std::isprint(c, cloc)) { + REQUIRE(out[0] == '%'); + } + } + + SECTION("debug encode") { + osmium::io::detail::append_debug_encoded_string(out, s, "", ""); + + if (!std::isprint(c, cloc)) { + REQUIRE(out[0] == '<'); + } + } + + } + +} + diff --git a/third_party/libosmium/test/t/io/test_reader.cpp b/third_party/libosmium/test/t/io/test_reader.cpp index 9a06d8408..a83af52ab 100644 --- a/third_party/libosmium/test/t/io/test_reader.cpp +++ b/third_party/libosmium/test/t/io/test_reader.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,27 @@ struct CountHandler : public osmium::handler::Handler { }; // class CountHandler +struct ZeroPositionNodeCountHandler : public osmium::handler::Handler { + + // number of nodes seen at zero position, or visible with undefined + // location. + int count = 0; + int total_count = 0; // total number of nodes seen + const osmium::Location zero = osmium::Location(int32_t(0), int32_t(0)); + + void node(osmium::Node &n) { + // no nodes in the history file have a zero location, and + // no visible nodes should have an undefined location. + if ((n.location() == zero) || + (n.visible() && !n.location())) { + ++count; + } + ++total_count; + } + +}; // class ZeroPositionNodeCountHandler + + TEST_CASE("Reader") { SECTION("reader can be initialized with file") { @@ -34,7 +56,7 @@ TEST_CASE("Reader") { osmium::apply(reader, handler); } - SECTION("should return invalid buffer after eof") { + SECTION("should throw after eof") { osmium::io::File file(with_data_dir("t/io/data.osm")); osmium::io::Reader reader(file); @@ -45,9 +67,9 @@ TEST_CASE("Reader") { REQUIRE(reader.eof()); - // extra read always returns invalid buffer - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(!buffer); + REQUIRE_THROWS_AS({ + reader.read(); + }, osmium::io_error); } SECTION("should not hang when apply() is called twice on reader") { @@ -56,7 +78,9 @@ TEST_CASE("Reader") { osmium::handler::Handler handler; osmium::apply(reader, handler); - osmium::apply(reader, handler); + REQUIRE_THROWS_AS({ + osmium::apply(reader, handler); + }, osmium::io_error); } SECTION("should work with a buffer with uncompressed data") { @@ -113,5 +137,76 @@ TEST_CASE("Reader") { REQUIRE(handler.count == 1); } + SECTION("should decode zero node positions in history (XML)") { + osmium::io::Reader reader(with_data_dir("t/io/deleted_nodes.osh"), + osmium::osm_entity_bits::node); + ZeroPositionNodeCountHandler handler; + + REQUIRE(handler.count == 0); + REQUIRE(handler.total_count == 0); + + osmium::apply(reader, handler); + + REQUIRE(handler.count == 0); + REQUIRE(handler.total_count == 2); + } + + SECTION("should decode zero node positions in history (PBF)") { + osmium::io::Reader reader(with_data_dir("t/io/deleted_nodes.osh.pbf"), + osmium::osm_entity_bits::node); + ZeroPositionNodeCountHandler handler; + + REQUIRE(handler.count == 0); + REQUIRE(handler.total_count == 0); + + osmium::apply(reader, handler); + + REQUIRE(handler.count == 0); + REQUIRE(handler.total_count == 2); + } + +} + +TEST_CASE("Reader failure modes") { + + SECTION("should fail with nonexistent file") { + REQUIRE_THROWS({ + osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm")); + }); + } + + SECTION("should fail with nonexistent file (gz)") { + REQUIRE_THROWS({ + osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm.gz")); + }); + } + + SECTION("should fail with nonexistent file (pbf)") { + REQUIRE_THROWS({ + osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm.pbf")); + }); + } + + SECTION("should work when there is an exception in main thread before getting header") { + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + REQUIRE(!reader.eof()); + throw std::runtime_error("foo"); + } catch (...) { + } + + } + + SECTION("should work when there is an exception in main thread while reading") { + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + REQUIRE(!reader.eof()); + auto header = reader.header(); + throw std::runtime_error("foo"); + } catch (...) { + } + + } + } diff --git a/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp new file mode 100644 index 000000000..566295aff --- /dev/null +++ b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp @@ -0,0 +1,145 @@ + +#include "catch.hpp" +#include "utils.hpp" + +#include + +#include +#include + +// The MockDecompressor behaves like other Decompressor classes, but "invents" +// OSM data in XML format that can be read. Through a parameter to the +// constructor it can be instructed to throw an exception in specific parts +// of its code. This is then used to test the internals of the Reader. + +class MockDecompressor : public osmium::io::Decompressor { + + std::string m_fail_in; + int m_read_count = 0; + +public: + + MockDecompressor(const std::string& fail_in) : + Decompressor(), + m_fail_in(fail_in) { + if (m_fail_in == "constructor") { + throw std::runtime_error("error constructor"); + } + } + + ~MockDecompressor() noexcept final = default; + + void add_node(std::string& s, int i) { + s += "\n"; + } + + std::string read() final { + std::string buffer; + ++m_read_count; + + if (m_read_count == 1) { + if (m_fail_in == "first read") { + throw std::runtime_error("error first read"); + } else { + buffer += "\n\n"; + for (int i = 0; i < 1000; ++i) { + add_node(buffer, i); + } + } + } else if (m_read_count == 2) { + if (m_fail_in == "second read") { + throw std::runtime_error("error second read"); + } else { + for (int i = 1000; i < 2000; ++i) { + add_node(buffer, i); + } + } + } else if (m_read_count == 3) { + buffer += ""; + } + + return buffer; + } + + void close() final { + if (m_fail_in == "close") { + throw std::runtime_error("error close"); + } + } + +}; // class MockDecompressor + +TEST_CASE("Test Reader using MockDecompressor") { + + std::string fail_in; + + osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::gzip, + [](int, osmium::io::fsync) { return nullptr; }, + [&](int) { return new MockDecompressor(fail_in); }, + [](const char*, size_t) { return nullptr; } + ); + + SECTION("fail in constructor") { + fail_in = "constructor"; + + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); + REQUIRE(false); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error constructor"); + } + } + + SECTION("fail in first read") { + fail_in = "first read"; + + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); + reader.read(); + REQUIRE(false); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error first read"); + } + } + + SECTION("fail in second read") { + fail_in = "second read"; + + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); + reader.read(); + reader.read(); + REQUIRE(false); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error second read"); + } + } + + SECTION("fail in close") { + fail_in = "close"; + + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); + reader.read(); + reader.read(); + reader.read(); + reader.close(); + REQUIRE(false); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error close"); + } + } + + SECTION("not failing") { + fail_in = "not"; + + osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); + reader.read(); + reader.close(); + REQUIRE(true); + } + +} + diff --git a/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp b/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp new file mode 100644 index 000000000..c71847ce5 --- /dev/null +++ b/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp @@ -0,0 +1,114 @@ + +#include "catch.hpp" +#include "utils.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MockParser : public osmium::io::detail::Parser { + + std::string m_fail_in; + +public: + + MockParser(osmium::io::detail::future_string_queue_type& input_queue, + osmium::io::detail::future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types, + const std::string& fail_in) : + Parser(input_queue, output_queue, header_promise, read_types), + m_fail_in(fail_in) { + } + + void run() final { + osmium::thread::set_thread_name("_osmium_mock_in"); + + if (m_fail_in == "header") { + throw std::runtime_error("error in header"); + } + + set_header_value(osmium::io::Header{}); + + osmium::memory::Buffer buffer(1000); + osmium::builder::add_node(buffer, osmium::builder::attr::_user("foo")); + send_to_output_queue(std::move(buffer)); + + if (m_fail_in == "read") { + throw std::runtime_error("error in read"); + } + } + +}; // class MockParser + +TEST_CASE("Test Reader using MockParser") { + + std::string fail_in; + + osmium::io::detail::ParserFactory::instance().register_parser( + osmium::io::file_format::xml, + [&](osmium::io::detail::future_string_queue_type& input_queue, + osmium::io::detail::future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new MockParser(input_queue, output_queue, header_promise, read_which_entities, fail_in)); + }); + + SECTION("no failure") { + fail_in = ""; + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + auto header = reader.header(); + REQUIRE(reader.read()); + REQUIRE(!reader.read()); + REQUIRE(reader.eof()); + reader.close(); + } + + SECTION("throw in header") { + fail_in = "header"; + try { + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + reader.header(); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error in header"); + } + } + + SECTION("throw in read") { + fail_in = "read"; + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + reader.header(); + try { + reader.read(); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error in read"); + } + reader.close(); + } + + SECTION("throw in user code") { + fail_in = ""; + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + reader.header(); + try { + throw std::runtime_error("error in user code"); + } catch (std::runtime_error& e) { + REQUIRE(std::string{e.what()} == "error in user code"); + } + REQUIRE(reader.read()); + REQUIRE(!reader.read()); + REQUIRE(reader.eof()); + reader.close(); + } + +} + diff --git a/third_party/libosmium/test/t/io/test_string_table.cpp b/third_party/libosmium/test/t/io/test_string_table.cpp index 7fedfcfca..ab977e8bc 100644 --- a/third_party/libosmium/test/t/io/test_string_table.cpp +++ b/third_party/libosmium/test/t/io/test_string_table.cpp @@ -33,9 +33,9 @@ TEST_CASE("String store") { } SECTION("add zero-length string and longer strings") { - const char* s1 = ss.add(""); - const char* s2 = ss.add("xxx"); - const char* s3 = ss.add("yyyyy"); + ss.add(""); + ss.add("xxx"); + ss.add("yyyyy"); auto it = ss.begin(); REQUIRE(std::string(*it++) == ""); diff --git a/third_party/libosmium/test/t/io/test_writer.cpp b/third_party/libosmium/test/t/io/test_writer.cpp new file mode 100644 index 000000000..b56dfeb13 --- /dev/null +++ b/third_party/libosmium/test/t/io/test_writer.cpp @@ -0,0 +1,118 @@ +#include "catch.hpp" +#include "utils.hpp" + +#include + +#include +#include +#include +#include +#include + +TEST_CASE("Writer") { + + osmium::io::Header header; + header.set("generator", "test_writer.cpp"); + + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer); + REQUIRE(buffer.committed() > 0); + auto num = std::distance(buffer.cbegin(), buffer.cend()); + REQUIRE(num > 0); + REQUIRE(buffer.cbegin()->id() == 1); + + std::string filename; + + SECTION("Empty writes") { + + SECTION("Empty buffer") { + filename = "test-writer-out-empty-buffer.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + osmium::memory::Buffer empty_buffer(1024); + writer(std::move(empty_buffer)); + writer.close(); + } + + SECTION("Invalid buffer") { + filename = "test-writer-out-invalid-buffer.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + osmium::memory::Buffer invalid_buffer; + writer(std::move(invalid_buffer)); + writer.close(); + } + + osmium::io::Reader reader_check(filename); + osmium::memory::Buffer buffer_check = reader_check.read(); + REQUIRE(!buffer_check); + } + + SECTION("Successfull writes") { + + SECTION("Writer buffer") { + filename = "test-writer-out-buffer.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + + REQUIRE_THROWS_AS({ + writer(osmium::memory::Buffer{}); + }, osmium::io_error); + } + + SECTION("Writer item") { + filename = "test-writer-out-item.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + for (const auto& item : buffer) { + writer(item); + } + writer.close(); + } + + SECTION("Writer output iterator") { + filename = "test-writer-out-iterator.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + auto it = osmium::io::make_output_iterator(writer); + std::copy(buffer.cbegin(), buffer.cend(), it); + writer.close(); + } + + osmium::io::Reader reader_check(filename); + osmium::memory::Buffer buffer_check = reader_check.read(); + REQUIRE(buffer_check); + REQUIRE(buffer_check.committed() > 0); + REQUIRE(std::distance(buffer_check.cbegin(), buffer_check.cend()) == num); + REQUIRE(buffer_check.cbegin()->id() == 1); + + } + + SECTION("Interrupted writer after open") { + int error = 0; + try { + filename = "test-writer-out-fail1.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + throw 1; + } catch (int e) { + error = e; + } + + REQUIRE(error > 0); + } + + SECTION("Interrupted writer after write") { + int error = 0; + try { + filename = "test-writer-out-fail2.osm"; + osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + throw 2; + } catch (int e) { + error = e; + } + + REQUIRE(error > 0); + } + +} + + diff --git a/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp b/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp new file mode 100644 index 000000000..c2d3bbd49 --- /dev/null +++ b/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp @@ -0,0 +1,99 @@ + +#include "catch.hpp" +#include "utils.hpp" + +#include +#include + +#include +#include +#include + +class MockCompressor : public osmium::io::Compressor { + + std::string m_fail_in; + +public: + + MockCompressor(const std::string& fail_in) : + Compressor(osmium::io::fsync::no), + m_fail_in(fail_in) { + if (m_fail_in == "constructor") { + throw std::logic_error("constructor"); + } + } + + ~MockCompressor() noexcept final = default; + + void write(const std::string&) final { + if (m_fail_in == "write") { + throw std::logic_error("write"); + } + } + + void close() final { + if (m_fail_in == "close") { + throw std::logic_error("close"); + } + } + +}; // class MockCompressor + +TEST_CASE("Write with mock compressor") { + + std::string fail_in; + + osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::gzip, + [&](int, osmium::io::fsync) { return new MockCompressor(fail_in); }, + [](int) { return nullptr; }, + [](const char*, size_t) { return nullptr; } + ); + + osmium::io::Header header; + header.set("generator", "test_writer_with_mock_compression.cpp"); + + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer); + REQUIRE(buffer.committed() > 0); + auto num = std::distance(buffer.cbegin(), buffer.cend()); + REQUIRE(num > 0); + + SECTION("fail on construction") { + + fail_in = "constructor"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-construction.osm.gz", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + + SECTION("fail on write") { + + fail_in = "write"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-write.osm.gz", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + + SECTION("fail on close") { + + fail_in = "close"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-close.osm.gz", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + +} + diff --git a/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp b/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp new file mode 100644 index 000000000..a43d59183 --- /dev/null +++ b/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp @@ -0,0 +1,105 @@ + +#include "catch.hpp" +#include "utils.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +class MockOutputFormat : public osmium::io::detail::OutputFormat { + + std::string m_fail_in; + +public: + + MockOutputFormat(const osmium::io::File&, osmium::io::detail::future_string_queue_type& output_queue, const std::string& fail_in) : + OutputFormat(output_queue), + m_fail_in(fail_in) { + } + + void write_header(const osmium::io::Header&) final { + if (m_fail_in == "header") { + throw std::logic_error("header"); + } + send_to_output_queue(std::string{"header"}); + } + + void write_buffer(osmium::memory::Buffer&&) final { + if (m_fail_in == "write") { + throw std::logic_error("write"); + } + send_to_output_queue(std::string{"write"}); + } + + void write_end() final { + if (m_fail_in == "write_end") { + throw std::logic_error("write_end"); + } + send_to_output_queue(std::string{"end"}); + } + +}; // class MockOutputFormat + +TEST_CASE("Test Writer with MockOutputFormat") { + + std::string fail_in; + + osmium::io::detail::OutputFormatFactory::instance().register_output_format( + osmium::io::file_format::xml, + [&](const osmium::io::File& file, osmium::io::detail::future_string_queue_type& output_queue) { + return new MockOutputFormat(file, output_queue, fail_in); + }); + + osmium::io::Header header; + header.set("generator", "test_writer_with_mock_encoder.cpp"); + + osmium::io::Reader reader(with_data_dir("t/io/data.osm")); + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer); + REQUIRE(buffer.committed() > 0); + auto num = std::distance(buffer.cbegin(), buffer.cend()); + REQUIRE(num > 0); + + SECTION("error in header") { + + fail_in = "header"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-construction.osm", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + + SECTION("error in write") { + + fail_in = "write"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-construction.osm", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + + SECTION("error in write_end") { + + fail_in = "write_end"; + + REQUIRE_THROWS_AS({ + osmium::io::Writer writer("test-writer-mock-fail-on-construction.osm", header, osmium::io::overwrite::allow); + writer(std::move(buffer)); + writer.close(); + }, std::logic_error); + + } + +} + diff --git a/third_party/libosmium/test/t/tags/test_filter.cpp b/third_party/libosmium/test/t/tags/test_filter.cpp index eefa5b076..fa21de1c5 100644 --- a/third_party/libosmium/test/t/tags/test_filter.cpp +++ b/third_party/libosmium/test/t/tags/test_filter.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include #include @@ -23,6 +23,12 @@ void check_filter(const osmium::TagList& tag_list, const TFilter filter, const s REQUIRE(std::distance(fi_begin, fi_end) == std::count(reference.begin(), reference.end(), true)); } +const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer, std::initializer_list> tags) { + auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); + return buffer.get(pos); +} + + TEST_CASE("Filter") { SECTION("KeyFilter_matches_some_tags") { @@ -30,7 +36,7 @@ TEST_CASE("Filter") { filter.add(true, "highway").add(true, "railway"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, // match { "name", "Main Street" }, // no match { "source", "GPS" } // no match @@ -46,7 +52,7 @@ TEST_CASE("Filter") { filter.add(true, "highway").add(true, "source"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tl = make_tag_list(buffer, { { "highway", "primary" }, // match { "name", "Main Street" }, // no match { "source", "GPS" } // no match @@ -72,7 +78,7 @@ TEST_CASE("Filter") { filter.add(true, "highway", "residential").add(true, "highway", "primary").add(true, "railway"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "railway", "tram" }, { "source", "GPS" } @@ -92,12 +98,12 @@ TEST_CASE("Filter") { osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list1 = make_tag_list(buffer, { { "highway", "road" }, { "name", "Main Street" } }); - const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list2 = make_tag_list(buffer, { { "highway", "primary" }, { "name", "Main Street" } }); @@ -113,7 +119,7 @@ TEST_CASE("Filter") { filter.add(true, "highway", "primary").add(true, "name"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "railway", "tram" }, { "source", "GPS" } @@ -130,7 +136,7 @@ TEST_CASE("Filter") { filter.add(true, "highway", "primary").add(true, "name"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "name", "Main Street" } }); @@ -146,7 +152,7 @@ TEST_CASE("Filter") { filter.add(true, "highway", "road").add(true, "source"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "name", "Main Street" } }); @@ -158,7 +164,7 @@ TEST_CASE("Filter") { SECTION("KeyValueFilter_matches_against_taglist_with_any_called_with_rvalue") { osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "railway", "tram" }, { "source", "GPS" } @@ -173,11 +179,11 @@ TEST_CASE("Filter") { filter.add(true, "highway", std::regex(".*_link")); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list1 = make_tag_list(buffer, { { "highway", "primary_link" }, { "source", "GPS" } }); - const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list2 = make_tag_list(buffer, { { "highway", "primary" }, { "source", "GPS" } }); @@ -192,7 +198,7 @@ TEST_CASE("Filter") { filter.add(true, "name", r); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "name", "Hauptstraße" } }); @@ -205,7 +211,7 @@ TEST_CASE("Filter") { filter.add(true, "name:"); osmium::memory::Buffer buffer(10240); - const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, { + const osmium::TagList& tag_list = make_tag_list(buffer, { { "highway", "primary" }, { "name:de", "Hauptstraße" } }); diff --git a/third_party/libosmium/test/t/tags/test_tag_list.cpp b/third_party/libosmium/test/t/tags/test_tag_list.cpp index 77523e796..470a20002 100644 --- a/third_party/libosmium/test/t/tags/test_tag_list.cpp +++ b/third_party/libosmium/test/t/tags/test_tag_list.cpp @@ -1,9 +1,15 @@ #include "catch.hpp" +#include +#include + +#include #include #include #include +using namespace osmium::builder::attr; + TEST_CASE("create tag list") { osmium::memory::Buffer buffer(10240); @@ -16,6 +22,42 @@ TEST_CASE("create tag list") { buffer.commit(); } + SECTION("with TagListBuilder from pair") { + { + osmium::builder::TagListBuilder builder(buffer); + builder.add_tag(std::pair{"highway", "primary"}); + builder.add_tag("name", "Main Street"); + } + buffer.commit(); + } + + SECTION("with TagListBuilder from pair") { + { + osmium::builder::TagListBuilder builder(buffer); + builder.add_tag(std::pair{"highway", "primary"}); + builder.add_tag("name", "Main Street"); + } + buffer.commit(); + } + + SECTION("with TagListBuilder from pair") { + { + osmium::builder::TagListBuilder builder(buffer); + builder.add_tag(std::pair{"highway", "primary"}); + builder.add_tag("name", "Main Street"); + } + buffer.commit(); + } + + SECTION("with TagListBuilder from pair") { + { + osmium::builder::TagListBuilder builder(buffer); + builder.add_tag(std::pair{"highway", "primary"}); + builder.add_tag("name", "Main Street"); + } + buffer.commit(); + } + SECTION("with TagListBuilder from char* with length") { { osmium::builder::TagListBuilder builder(buffer); @@ -36,20 +78,88 @@ TEST_CASE("create tag list") { buffer.commit(); } - SECTION("with build_tag_list from initializer list") { - osmium::builder::build_tag_list(buffer, { - { "highway", "primary" }, - { "name", "Main Street" } - }); + SECTION("with add_tag_list from pair") { + osmium::builder::add_tag_list(buffer, + _tag(std::pair{"highway", "primary"}), + _tag("name", "Main Street") + ); } - SECTION("with build_tag_list_from_map") { - osmium::builder::build_tag_list_from_map(buffer, std::map({ + SECTION("with add_tag_list from pair") { + osmium::builder::add_tag_list(buffer, + _tag(std::pair{"highway", "primary"}), + _tag("name", "Main Street") + ); + } + + SECTION("with add_tag_list from pair") { + osmium::builder::add_tag_list(buffer, + _tag(std::pair{"highway", "primary"}), + _tag("name", "Main Street") + ); + } + + SECTION("with add_tag_list from pair") { + osmium::builder::add_tag_list(buffer, + _tag(std::pair{"highway", "primary"}), + _tag("name", "Main Street") + ); + } + + SECTION("with add_tag_list from vector of pairs (const/const)") { + std::vector> v{ + { "highway", "primary" }, + { "name", "Main Street" } + }; + osmium::builder::add_tag_list(buffer, _tags(v)); + } + + SECTION("with add_tag_list from vector of pairs (const/nc)") { + std::vector> v{ + { "highway", "primary" }, + { "name", "Main Street" } + }; + osmium::builder::add_tag_list(buffer, _tags(v)); + } + + SECTION("with add_tag_list from vector of pairs (nc/const)") { + std::vector> v{ + { "highway", "primary" }, + { "name", "Main Street" } + }; + osmium::builder::add_tag_list(buffer, _tags(v)); + } + + SECTION("with add_tag_list from vector of pairs (nc/nc)") { + std::vector> v{ + { "highway", "primary" }, + { "name", "Main Street" } + }; + osmium::builder::add_tag_list(buffer, _tags(v)); + } + + SECTION("with add_tag_list from initializer list") { + osmium::builder::add_tag_list(buffer, _tags({ { "highway", "primary" }, { "name", "Main Street" } })); } + SECTION("with add_tag_list from _tag") { + osmium::builder::add_tag_list(buffer, + _tag("highway", "primary"), + _tag("name", "Main Street") + ); + } + + SECTION("with add_tag_list from map") { + std::map m{ + { "highway", "primary" }, + { "name", "Main Street" } + }; + osmium::builder::add_tag_list(buffer, _tags(m)); + } + SECTION("with build_tag_list_from_func") { osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) { tlb.add_tag("highway", "primary"); @@ -80,10 +190,11 @@ TEST_CASE("create tag list") { TEST_CASE("empty keys and values are okay") { osmium::memory::Buffer buffer(10240); - const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, { - { "empty value", "" }, - { "", "empty key" } - }); + auto pos = osmium::builder::add_tag_list(buffer, + _tag("empty value", ""), + _tag("", "empty key") + ); + const osmium::TagList& tl = buffer.get(pos); REQUIRE(osmium::item_type::tag_list == tl.type()); REQUIRE(2 == tl.size()); @@ -100,3 +211,14 @@ TEST_CASE("empty keys and values are okay") { REQUIRE(std::string("") == tl.get_value_by_key("empty value")); REQUIRE(std::string("empty key") == tl.get_value_by_key("")); } + +TEST_CASE("tag key or value is too long") { + osmium::memory::Buffer buffer(10240); + osmium::builder::TagListBuilder builder(buffer); + + const char kv[2000] = ""; + builder.add_tag(kv, 1, kv, 1000); + REQUIRE_THROWS(builder.add_tag(kv, 1500, kv, 1)); + REQUIRE_THROWS(builder.add_tag(kv, 1, kv, 1500)); +} + diff --git a/third_party/libosmium/test/t/thread/test_pool.cpp b/third_party/libosmium/test/t/thread/test_pool.cpp index 5fa6bbab5..c1047db49 100644 --- a/third_party/libosmium/test/t/thread/test_pool.cpp +++ b/third_party/libosmium/test/t/thread/test_pool.cpp @@ -5,14 +5,7 @@ #include #include - -static std::atomic result; - -struct test_job_ok { - void operator()() const { - result = 1; - } -}; +#include struct test_job_with_result { int operator()() const { @@ -21,44 +14,55 @@ struct test_job_with_result { }; struct test_job_throw { - void operator()() const { + OSMIUM_NORETURN void operator()() const { throw std::runtime_error("exception in pool thread"); } }; +TEST_CASE("number of threads in pool") { + + // hardcoded setting + REQUIRE(osmium::thread::detail::get_pool_size( 1, 0, 2) == 1); + REQUIRE(osmium::thread::detail::get_pool_size( 4, 0, 2) == 4); + REQUIRE(osmium::thread::detail::get_pool_size( 4, 0, 4) == 4); + REQUIRE(osmium::thread::detail::get_pool_size(16, 0, 4) == 16); + REQUIRE(osmium::thread::detail::get_pool_size(16, 0, 16) == 16); + REQUIRE(osmium::thread::detail::get_pool_size( 8, 4, 2) == 8); + REQUIRE(osmium::thread::detail::get_pool_size( 8, 16, 2) == 8); + REQUIRE(osmium::thread::detail::get_pool_size(-2, 16, 2) == 1); + REQUIRE(osmium::thread::detail::get_pool_size(-2, 16, 8) == 6); + + // user decides through OSMIUM_POOL_THREADS env variable + REQUIRE(osmium::thread::detail::get_pool_size( 0, 0, 2) == 1); + REQUIRE(osmium::thread::detail::get_pool_size( 0, -2, 4) == 2); + REQUIRE(osmium::thread::detail::get_pool_size( 0, -1, 8) == 7); + REQUIRE(osmium::thread::detail::get_pool_size( 0, 0, 16) == 14); + REQUIRE(osmium::thread::detail::get_pool_size( 0, 1, 16) == 1); + REQUIRE(osmium::thread::detail::get_pool_size( 0, 2, 16) == 2); + REQUIRE(osmium::thread::detail::get_pool_size( 0, 4, 16) == 4); + REQUIRE(osmium::thread::detail::get_pool_size( 0, 8, 16) == 8); + + // outliers + REQUIRE(osmium::thread::detail::get_pool_size(-100, 0, 16) == 1); + REQUIRE(osmium::thread::detail::get_pool_size(1000, 0, 16) == 256); + +} + TEST_CASE("thread") { + auto& pool = osmium::thread::Pool::instance(); + SECTION("can get access to thread pool") { - auto& pool = osmium::thread::Pool::instance(); REQUIRE(pool.queue_empty()); } SECTION("can send job to thread pool") { - auto& pool = osmium::thread::Pool::instance(); - result = 0; - auto future = pool.submit(test_job_ok {}); - - // wait a bit for the other thread to get a chance to run - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - - REQUIRE(result == 1); - - future.get(); - - REQUIRE(true); - } - - SECTION("can send job to thread pool") { - auto& pool = osmium::thread::Pool::instance(); auto future = pool.submit(test_job_with_result {}); REQUIRE(future.get() == 42); } SECTION("can throw from job in thread pool") { - auto& pool = osmium::thread::Pool::instance(); - result = 0; - auto future = pool.submit(test_job_throw {}); REQUIRE_THROWS_AS(future.get(), std::runtime_error); diff --git a/third_party/libosmium/test/t/util/test_data_file.cpp b/third_party/libosmium/test/t/util/test_data_file.cpp deleted file mode 100644 index 3f432f943..000000000 --- a/third_party/libosmium/test/t/util/test_data_file.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "catch.hpp" - -#include - -#include - -TEST_CASE("temporary file") { - - SECTION("create/open") { - osmium::util::DataFile file; - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - const char buf[] = "foobar"; - REQUIRE(::write(fd, buf, sizeof(buf)) == sizeof(buf)); - - file.close(); - - REQUIRE(!file); - } - -} - -TEST_CASE("named file") { - - SECTION("create/open") { - { - osmium::util::DataFile file("test.data", true); - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - REQUIRE(file.size() == 0); - - const char buf[] = "foobar"; - REQUIRE(::write(fd, buf, sizeof(buf) - 1) == sizeof(buf) - 1); - - file.close(); - - REQUIRE(!file); - } - { - osmium::util::DataFile file("test.data", false); - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - REQUIRE(file.size() == 6); - - char buf[10]; - int len = ::read(fd, buf, sizeof(buf)); - - REQUIRE(len == 6); - REQUIRE(!strncmp(buf, "foobar", 6)); - - file.close(); - - REQUIRE(!file); - REQUIRE(unlink("test.data") == 0); - } - } - - SECTION("grow file") { - osmium::util::DataFile file("test.data", true); - - REQUIRE(!!file); - - REQUIRE(file.size() == 0); - file.grow(10); - REQUIRE(file.size() == 10); - } - -} - diff --git a/third_party/libosmium/test/t/util/test_delta.cpp b/third_party/libosmium/test/t/util/test_delta.cpp index cebcca8ed..667c9b443 100644 --- a/third_party/libosmium/test/t/util/test_delta.cpp +++ b/third_party/libosmium/test/t/util/test_delta.cpp @@ -4,24 +4,50 @@ #include -TEST_CASE("delta encode") { +TEST_CASE("delta encode int") { osmium::util::DeltaEncode x; SECTION("int") { REQUIRE(x.update(17) == 17); REQUIRE(x.update(10) == -7); + REQUIRE(x.update(-10) == -20); } } -TEST_CASE("delta decode") { +TEST_CASE("delta decode int") { osmium::util::DeltaDecode x; SECTION("int") { REQUIRE(x.update(17) == 17); REQUIRE(x.update(10) == 27); + REQUIRE(x.update(-40) == -13); + } + +} + +TEST_CASE("delta encode unsigned int") { + + osmium::util::DeltaEncode x; + + SECTION("int") { + REQUIRE(x.update(17) == 17); + REQUIRE(x.update(10) == -7); + REQUIRE(x.update(0) == -10); + } + +} + +TEST_CASE("delta decode unsigned int") { + + osmium::util::DeltaDecode x; + + SECTION("int") { + REQUIRE(x.update(17) == 17); + REQUIRE(x.update(10) == 27); + REQUIRE(x.update(-15) == 12); } } @@ -30,13 +56,13 @@ TEST_CASE("delta encode and decode") { std::vector a = { 5, -9, 22, 13, 0, 23 }; - osmium::util::DeltaEncode de; + osmium::util::DeltaEncode de; std::vector b; for (int x : a) { b.push_back(de.update(x)); } - osmium::util::DeltaDecode dd; + osmium::util::DeltaDecode dd; std::vector c; for (int x : b) { c.push_back(dd.update(x)); diff --git a/third_party/libosmium/test/t/util/test_file.cpp b/third_party/libosmium/test/t/util/test_file.cpp index 2787261a5..475f28596 100644 --- a/third_party/libosmium/test/t/util/test_file.cpp +++ b/third_party/libosmium/test/t/util/test_file.cpp @@ -3,6 +3,7 @@ #include #ifdef _WIN32 +#include // https://msdn.microsoft.com/en-us/library/ksazx244.aspx // https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx class DoNothingInvalidParameterHandler { @@ -23,6 +24,7 @@ public: DoNothingInvalidParameterHandler() : old_handler(_set_invalid_parameter_handler(invalid_parameter_handler)) { + _CrtSetReportMode(_CRT_ASSERT, 0); } ~DoNothingInvalidParameterHandler() { diff --git a/third_party/libosmium/test/t/util/test_memory.cpp b/third_party/libosmium/test/t/util/test_memory.cpp new file mode 100644 index 000000000..fa905c8b9 --- /dev/null +++ b/third_party/libosmium/test/t/util/test_memory.cpp @@ -0,0 +1,34 @@ + +#include "catch.hpp" + +#include + +TEST_CASE("Check memory usage") { +#ifdef __linux__ + const int size_in_mbytes = 10; + + osmium::MemoryUsage m1; + REQUIRE(m1.current() > 1); + REQUIRE(m1.peak() > 1); + + { + std::vector v; + v.reserve(size_in_mbytes * 1024 * 1024); + + osmium::MemoryUsage m2; + REQUIRE(m2.current() >= m1.current() + size_in_mbytes); + REQUIRE(m2.peak() >= m1.peak() + size_in_mbytes); + REQUIRE(m2.peak() - m2.current() <= 1); + } + + osmium::MemoryUsage m3; + REQUIRE(m3.current() > 1); + REQUIRE(m3.current() < m3.peak()); + REQUIRE(m3.peak() >= m1.peak() + size_in_mbytes); +#else + osmium::MemoryUsage m; + REQUIRE(m.current() == 0); + REQUIRE(m.peak() == 0); +#endif +} + diff --git a/third_party/libosmium/test/t/util/test_options.cpp b/third_party/libosmium/test/t/util/test_options.cpp index 969f20103..8cba0950b 100644 --- a/third_party/libosmium/test/t/util/test_options.cpp +++ b/third_party/libosmium/test/t/util/test_options.cpp @@ -6,43 +6,76 @@ TEST_CASE("Options") { - SECTION("set_simple") { - osmium::util::Options o; + osmium::util::Options o; + + SECTION("set a single value from string") { o.set("foo", "bar"); REQUIRE("bar" == o.get("foo")); REQUIRE("" == o.get("empty")); REQUIRE("default" == o.get("empty", "default")); + REQUIRE(!o.is_true("foo")); REQUIRE(!o.is_true("empty")); + + REQUIRE(o.is_not_false("foo")); + REQUIRE(o.is_not_false("empty")); + REQUIRE(1 == o.size()); } - SECTION("set_from_bool") { - osmium::util::Options o; + SECTION("set values from booleans") { o.set("t", true); o.set("f", false); REQUIRE("true" == o.get("t")); REQUIRE("false" == o.get("f")); REQUIRE("" == o.get("empty")); + REQUIRE(o.is_true("t")); REQUIRE(!o.is_true("f")); + + REQUIRE(o.is_not_false("t")); + REQUIRE(!o.is_not_false("f")); + REQUIRE(2 == o.size()); } - SECTION("set_from_single_string_with_equals") { - osmium::util::Options o; + SECTION("set value from string with equal sign") { o.set("foo=bar"); REQUIRE("bar" == o.get("foo")); REQUIRE(1 == o.size()); } - SECTION("set_from_single_string_without_equals") { - osmium::util::Options o; + SECTION("set value from string without equal sign") { o.set("foo"); REQUIRE("true" == o.get("foo")); + REQUIRE(o.is_true("foo")); + REQUIRE(o.is_not_false("foo")); + REQUIRE(1 == o.size()); } } +TEST_CASE("Options with initializer list") { + + osmium::util::Options o{ { "foo", "true" }, { "bar", "17" } }; + + REQUIRE(o.get("foo") == "true"); + REQUIRE(o.get("bar") == "17"); + REQUIRE(o.is_true("foo")); + REQUIRE_FALSE(o.is_true("bar")); + REQUIRE(o.size() == 2); + + SECTION("Change existing value") { + o.set("foo", "false"); + REQUIRE_FALSE(o.is_true("foo")); + } + + SECTION("Add new value") { + o.set("new", "something"); + REQUIRE_FALSE(o.is_true("new")); + REQUIRE(o.get("new") == "something"); + } +} + diff --git a/third_party/variant/.clang-format b/third_party/variant/.clang-format new file mode 100644 index 000000000..c1aca2a57 --- /dev/null +++ b/third_party/variant/.clang-format @@ -0,0 +1,89 @@ +--- +# Mapbox.Variant C/C+ style +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/third_party/variant/.gitignore b/third_party/variant/.gitignore index 984cc2fe8..a9bc8605e 100644 --- a/third_party/variant/.gitignore +++ b/third_party/variant/.gitignore @@ -3,3 +3,6 @@ out profiling build deps +*.gcda +*.gcno +.ycm_extra_conf.pyc diff --git a/third_party/variant/.travis.yml b/third_party/variant/.travis.yml index cd1e65578..99d0b48c2 100644 --- a/third_party/variant/.travis.yml +++ b/third_party/variant/.travis.yml @@ -1,22 +1,127 @@ -language: cpp +language: c -# http://docs.travis-ci.com/user/multi-os/ -os: - - linux - - osx +sudo: false -compiler: - - clang - - gcc +# Save common build configurations as shortcuts, so we can reference them later. +addons_shortcuts: + addons_clang35: &clang35 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5', 'boost-latest' ] + packages: [ 'clang-3.5', 'libboost1.55-all-dev' ] + addons_clang36: &clang36 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6', 'boost-latest' ] + packages: [ 'clang-3.6', 'libboost1.55-all-dev' ] + addons_clang37: &clang37 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7', 'boost-latest' ] + packages: [ 'clang-3.7', 'libboost1.55-all-dev' ] + addons_clang38: &clang38 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise', 'boost-latest' ] + packages: [ 'clang-3.8', 'libboost1.55-all-dev'] + addons_gcc47: &gcc47 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'g++-4.7', 'libboost1.55-all-dev' ] + addons_gcc48: &gcc48 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'g++-4.8', 'libboost1.55-all-dev' ] + addons_gcc49: &gcc49 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'g++-4.9', 'libboost1.55-all-dev' ] + addons_gcc5: &gcc5 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'g++-5', 'libboost1.55-all-dev' ] + +matrix: + include: + - os: osx + osx_image: xcode6 + compiler: clang + - os: osx + osx_image: xcode7 + env: TEST_GYP_BUILD=True + compiler: clang + - os: linux + compiler: "clang35" + env: CXX=clang++-3.5 + addons: *clang35 + - os: linux + compiler: "clang36" + env: CXX=clang++-3.6 + addons: *clang36 + - os: linux + compiler: "clang37" + env: CXX=clang++-3.7 COVERAGE=True + addons: *clang37 + - os: linux + compiler: "clang38" + env: CXX=clang++-3.8 + addons: *clang38 + - os: linux + compiler: "clang38" + env: CXX=clang++-3.8 CXX_STD=c++14 + addons: *clang38 + - os: linux + compiler: "gcc47" + env: CXX=g++-4.7 + addons: *gcc47 + - os: linux + compiler: "gcc48" + env: CXX=g++-4.8 + addons: *gcc48 + - os: linux + compiler: "gcc49" + env: CXX=g++-4.9 + addons: *gcc49 + - os: linux + compiler: "gcc49" + env: CXX=g++-4.9 CXX_STD=c++14 + addons: *gcc49 + - os: linux + compiler: "gcc5" + env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" + addons: *gcc5 + - os: linux + compiler: "gcc5" + env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1" + addons: *gcc5 before_install: - - true + - echo ${CXX} + - if [[ $(uname -s) == 'Linux' ]]; then + export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages; + else + brew install boost; + export PYTHONPATH=$(pwd)/.local/lib/python/site-packages; + fi + - if [[ ${COVERAGE:-0} == 'True' ]]; then + PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls; + fi install: - - true - -before_script: - - true + - make test + - make bench + - if [[ $(uname -s) == 'Linux' ]]; then + make sizes /usr/include/boost/variant.hpp; + else + make sizes `brew --prefix`/include/boost/variant.hpp; + fi + - scripts/run_compilation_failure_tests.sh + - if [[ ${TEST_GYP_BUILD:-0} == 'True' ]]; then + make clean; + make gyp; + fi script: - - source "scripts/${TRAVIS_OS_NAME}.sh" + - if [[ ${COVERAGE:-0} == 'True' ]]; then + make clean; + make coverage; + ./out/cov-test; + cp unit*gc* test/; + ./.local/bin/cpp-coveralls -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp --gcov-options '\-lp'; + fi diff --git a/third_party/variant/.ycm_extra_conf.py b/third_party/variant/.ycm_extra_conf.py new file mode 100644 index 000000000..ba746684b --- /dev/null +++ b/third_party/variant/.ycm_extra_conf.py @@ -0,0 +1,40 @@ +#----------------------------------------------------------------------------- +# +# Configuration for YouCompleteMe Vim plugin +# +# http://valloric.github.io/YouCompleteMe/ +# +#----------------------------------------------------------------------------- + +from os.path import realpath, dirname + +basedir = dirname(realpath(__file__)) + +# some default flags +# for more information install clang-3.2-doc package and +# check UsersManual.html +flags = [ +'-Werror', +'-Wall', +'-Wextra', +'-pedantic', + +'-std=c++11', + +# '-x' and 'c++' also required +# use 'c' for C projects +'-x', +'c++', + +# include third party libraries +'-I.', +] + +# youcompleteme is calling this function to get flags +# You can also set database for flags. Check: JSONCompilationDatabase.html in +# clang-3.2-doc package +def FlagsForFile( filename ): + return { + 'flags': flags, + 'do_cache': True + } diff --git a/third_party/variant/Jamroot b/third_party/variant/Jamroot index aa2137537..1e98cf555 100644 --- a/third_party/variant/Jamroot +++ b/third_party/variant/Jamroot @@ -1,3 +1,6 @@ +# Inofficial and incomplete build file using Boost build system. +# You should use make unless you know what you are doing. + local BOOST_DIR = "/usr/local" ; #using clang : : ; @@ -15,7 +18,6 @@ exe variant-test : $(BOOST_DIR)/include ./ - -std=c++11 #SINGLE_THREADED release:-march=native ; @@ -30,7 +32,6 @@ exe binary-visitor-test : $(BOOST_DIR)/include ./ - -std=c++11 release:-march=native ; @@ -43,7 +44,6 @@ exe recursive-wrapper-test : $(BOOST_DIR)/include ./ - -std=c++11 release:-march=native ; @@ -56,7 +56,6 @@ exe unique-ptr-test : $(BOOST_DIR)/include ./ - -std=c++11 release:-march=native ; @@ -70,6 +69,5 @@ exe reference_wrapper_test : $(BOOST_DIR)/include ./ - -std=c++11 release:-march=native ; diff --git a/third_party/variant/LICENSE_1_0.txt b/third_party/variant/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/third_party/variant/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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/third_party/variant/Makefile b/third_party/variant/Makefile index cae3d46bd..36652511d 100644 --- a/third_party/variant/Makefile +++ b/third_party/variant/Makefile @@ -1,8 +1,11 @@ + CXX := $(CXX) +CXX_STD ?= c++11 + BOOST_LIBS = -lboost_timer -lboost_system -lboost_chrono -RELEASE_FLAGS = -O3 -DNDEBUG -finline-functions -march=native -DSINGLE_THREADED +RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions -COMMON_FLAGS = -Wall -Wsign-compare -Wsign-conversion -Wshadow -Wunused-parameter -pedantic -fvisibility-inlines-hidden -std=c++11 +COMMON_FLAGS = -Wall -pedantic -Wextra -Wsign-compare -Wsign-conversion -Wshadow -Wunused-parameter -std=$(CXX_STD) CXXFLAGS := $(CXXFLAGS) LDFLAGS := $(LDFLAGS) @@ -33,23 +36,23 @@ gyp: ./deps/gyp make V=1 -C ./out tests ./out/Release/tests -out/bench-variant-debug: Makefile test/bench_variant.cpp variant.hpp +out/bench-variant-debug: Makefile test/bench_variant.cpp variant.hpp recursive_wrapper.hpp mkdir -p ./out - $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./ $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) + $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./ -pthreads $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -out/bench-variant: Makefile test/bench_variant.cpp variant.hpp +out/bench-variant: Makefile test/bench_variant.cpp variant.hpp recursive_wrapper.hpp mkdir -p ./out $(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -out/unique_ptr_test: Makefile test/unique_ptr_test.cpp variant.hpp +out/unique_ptr_test: Makefile test/unique_ptr_test.cpp variant.hpp recursive_wrapper.hpp mkdir -p ./out $(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -out/recursive_wrapper_test: Makefile test/recursive_wrapper_test.cpp variant.hpp +out/recursive_wrapper_test: Makefile test/recursive_wrapper_test.cpp variant.hpp recursive_wrapper.hpp mkdir -p ./out $(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -out/binary_visitor_test: Makefile test/binary_visitor_test.cpp variant.hpp +out/binary_visitor_test: Makefile test/binary_visitor_test.cpp variant.hpp variant_io.hpp recursive_wrapper.hpp mkdir -p ./out $(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) @@ -59,24 +62,30 @@ bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_w ./out/recursive_wrapper_test 100000 ./out/binary_visitor_test 100000 -out/unit: Makefile test/unit.cpp test/optional_unit.cpp optional.hpp variant.hpp +out/unit.o: Makefile test/unit.cpp mkdir -p ./out - $(CXX) -o out/unit test/unit.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) - $(CXX) -o out/optional_unit test/optional_unit.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) + $(CXX) -c -o $@ test/unit.cpp -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) + +out/%.o: test/t/%.cpp Makefile optional.hpp recursive_wrapper.hpp variant.hpp variant_io.hpp + mkdir -p ./out + $(CXX) -c -o $@ $< -I. -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) + +out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o + mkdir -p ./out + $(CXX) -o $@ $^ $(LDFLAGS) test: out/unit ./out/unit - ./out/optional_unit coverage: mkdir -p ./out - $(CXX) -o out/cov-test --coverage test/unit.cpp -I./ $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) + $(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./ -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) -sizes: Makefile variant.hpp +sizes: Makefile variant.hpp recursive_wrapper.hpp mkdir -p ./out - @$(CXX) -o ./out/variant_hello_world.out variant.hpp $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/variant_hello_world.out + @$(CXX) -o ./out/our_variant_hello_world.out variant.hpp $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world.out @$(CXX) -o ./out/boost_variant_hello_world.out $(RUN_ARGS) $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/boost_variant_hello_world.out - @$(CXX) -o ./out/variant_hello_world ./test/variant_hello_world.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/variant_hello_world + @$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world @$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/boost_variant_hello_world profile: out/bench-variant-debug @@ -91,8 +100,9 @@ clean: rm -f *gcov rm -f test/unit.gc* rm -f test/*gcov + rm -f *.gcda *.gcno -pgo: out Makefile variant.hpp +pgo: out Makefile variant.hpp recursive_wrapper.hpp $(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -pg -fprofile-generate ./test-variant 500000 >/dev/null 2>/dev/null $(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -fprofile-use diff --git a/third_party/variant/README.md b/third_party/variant/README.md index 791131d39..24cdb570b 100644 --- a/third_party/variant/README.md +++ b/third_party/variant/README.md @@ -4,11 +4,13 @@ An alternative to `boost::variant` for C++11. [![Build Status](https://secure.travis-ci.org/mapbox/variant.svg)](https://travis-ci.org/mapbox/variant) [![Build status](https://ci.appveyor.com/api/projects/status/v9tatx21j1k0fcgy)](https://ci.appveyor.com/project/Mapbox/variant) -[![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master)](https://coveralls.io/r/mapbox/variant?branch=master) +[![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master&service=github)](https://coveralls.io/r/mapbox/variant?branch=master) -# Why use Mapbox Variant? -Mapbox variant has the same speedy performance of `boost::variant` but is faster to compile, results in smaller binaries, and has no dependencies. +## Why use Mapbox Variant? + +Mapbox variant has the same speedy performance of `boost::variant` but is +faster to compile, results in smaller binaries, and has no dependencies. For example on OS X 10.9 with clang++ and libc++: @@ -18,29 +20,80 @@ Size of pre-compiled header (release / debug) | 2.8/2.8 MB | 12/15 MB Size of simple program linking variant (release / debug) | 8/24 K | 12/40 K Time to compile header | 185 ms | 675 ms +(Numbers from an older version of Mapbox variant.) -# Depends - - Compiler supporting `-std=c++11` +## Goals -Tested with +Mapbox `variant` has been a very valuable, lightweight alternative for apps +that can use c++11 or c++14 but that do not want a boost dependency. +Mapbox `variant` has also been useful in apps that do depend on boost, like +mapnik, to help (slightly) with compile times and to majorly lessen dependence +on boost in core headers. The original goal and near term goal is to maintain +external API compatibility with `boost::variant` such that Mapbox `variant` +can be a "drop in". At the same time the goal is to stay minimal: Only +implement the features that are actually needed in existing software. So being +an "incomplete" implementation is just fine. + +Currently Mapbox variant doesn't try to be API compatible with the upcoming +variant standard, because the standard is not finished and it would be too much +work. But we'll revisit this decision in the future if needed. + +If Mapbox variant is not for you, have a look at [these other +implementations](doc/other_implementations.md). + +Want to know more about the upcoming standard? Have a look at our +[overview](doc/standards_effort.md). + + +## Depends + + - Compiler supporting `-std=c++11` or `-std=c++14` + +Tested with: - g++-4.7 - g++-4.8 - - clang++-3.4 + - g++-4.9 + - g++-5 - clang++-3.5 - - Visual C++ Compiler November 2013 CTP - - Visual C++ Compiler 2014 CTP 4 + - clang++-3.6 + - clang++-3.7 + - clang++-3.8 + - Visual Studio 2015 -Note: get the "2013 Nov CTP" release at http://www.microsoft.com/en-us/download/details.aspx?id=41151 and the 2014 CTP at http://www.visualstudio.com/en-us/downloads/visual-studio-14-ctp-vs.aspx +## Usage -# Usage +There is nothing to build, just include `variant.hpp` and +`recursive_wrapper.hpp` in your project. Include `variant_io.hpp` if you need +the `operator<<` overload for variant. -There is nothing to build, just include `variant.hpp` and `recursive_wrapper.hpp` in your project. -# Tests +## Unit Tests -The tests depend on: +On Unix systems compile and run the unit tests with `make test`. + +On Windows run `scripts/build-local.bat`. + + +## Limitations + +* The `variant` can not hold references (something like `variant` is + not possible). You might want to try `std::reference_wrapper` instead. + + +## Deprecations + +* The included implementation of `optional` is deprecated and will be removed + in a future version. See https://github.com/mapbox/variant/issues/64. +* Old versions of the code needed visitors to derive from `static_visitor`. + This is not needed any more and marked as deprecated. The `static_visitor` + class will be removed in future versions. + + +## Benchmarks + +The benchmarks depend on: - Boost headers (for benchmarking against `boost::variant`) - Boost built with `--with-timer` (used for benchmark timing) @@ -49,18 +102,9 @@ On Unix systems set your boost includes and libs locations and run `make test`: export LDFLAGS='-L/opt/boost/lib' export CXXFLAGS='-I/opt/boost/include' - make test - -On windows do: - - vcbuild - -## Benchmark - -On Unix systems run the benchmark like: - make bench + ## Check object sizes make sizes /path/to/boost/variant.hpp diff --git a/third_party/variant/doc/other_implementations.md b/third_party/variant/doc/other_implementations.md new file mode 100644 index 000000000..a0d8b9b3f --- /dev/null +++ b/third_party/variant/doc/other_implementations.md @@ -0,0 +1,15 @@ + +# Other implementations of variant and optional + +These are some other implementations of `variant` and/or `optional` types. +They are not necessarily compatible with this implementation. This is an +incomplete list. + +* [Boost Variant](http://www.boost.org/doc/libs/1_59_0/doc/html/variant.html) and [Boost Optional](http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/index.html) +* [Eggs Variant](http://eggs-cpp.github.io/variant/) by [Agustín Bergé](http://talesofcpp.fusionfenix.com/) +* [anthonyw/variant](https://bitbucket.org/anthonyw/variant) (implementation of P0110R0) +* [JasonL9000/cppcon14](https://github.com/JasonL9000/cppcon14) +* [tomilov/variant](https://github.com/tomilov/variant) +* [akrzemi1/Optional](https://github.com/akrzemi1/Optional) +* [mpark/variant](https://github.com/mpark/variant) + diff --git a/third_party/variant/doc/standards_effort.md b/third_party/variant/doc/standards_effort.md new file mode 100644 index 000000000..d2df488f0 --- /dev/null +++ b/third_party/variant/doc/standards_effort.md @@ -0,0 +1,28 @@ + +# Standards efforts + +A `variant` type is on planned for inclusion in the C++ Standard, probably in +C++17. Current working papers are (list extracted from [2015 working group +papers](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/)): + +* 2015-09-28: Variant design review. [P0086R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0086r0.pdf) +* 2015-09-28: Variant: a type-safe union without undefined behavior (v2) [P0087R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0087r0.pdf) +* 2015-09-27: Variant: a type-safe union that is rarely invalid (v5) [P0088R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0088r0.pdf) +* 2015-09-24: Simply a Strong Variant [P0093R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0093r0.html) +* 2015-09-24: Simply a Basic Variant [P0094R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0094r0.html) +* 2015-09-24: The Case for a Language Based Variant [P0096R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0095r0.html) +* 2015-09-25: Implementing the strong guarantee for variant<> assignment [P0110R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0110r0.html) +* 2015-09-24: Homogeneous interface for variant, any and optional (Revision 1) [P0032R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0032r1.pdf) + +Last state can be seen from +[The Variant Saga: A happy ending?](https://isocpp.org/blog/2015/11/the-variant-saga-a-happy-ending). + +The `optional` type is also on the way into the standard. The papers are: +* 2013-10-03: A proposal to add a utility class to represent optional objects (Revision 5) [N3793](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html) +* 2014-01-18: Working Draft, Technical Specification on C++ Extensions for Library Fundamentals [N3848](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html) + +## Older Papers + +* Older working drafts are: N4218 (rev 1), N4516 (rev 2), N4450 (rev 3), and [N4542](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4542.pdf) (rev 4). They have been split into P0086 (general design discussions) and P0087 and P0088 (containing two competing? specs). +* 2015-07-28: Variant: Discriminated Union with Value Semantics [P0080R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0080r0.pdf) An alternative proposal to N4542. + diff --git a/third_party/variant/optional.hpp b/third_party/variant/optional.hpp index 133e2c8f9..1185894e7 100644 --- a/third_party/variant/optional.hpp +++ b/third_party/variant/optional.hpp @@ -1,16 +1,18 @@ #ifndef MAPBOX_UTIL_OPTIONAL_HPP #define MAPBOX_UTIL_OPTIONAL_HPP +#pragma message("This implementation of optional is deprecated. See https://github.com/mapbox/variant/issues/64.") + #include +#include #include "variant.hpp" -namespace mapbox -{ -namespace util -{ +namespace mapbox { +namespace util { -template class optional +template +class optional { static_assert(!std::is_reference::value, "optional doesn't support references"); @@ -23,7 +25,7 @@ template class optional public: optional() = default; - optional(optional const &rhs) + optional(optional const& rhs) { if (this != &rhs) { // protect against invalid self-assignment @@ -31,23 +33,23 @@ template class optional } } - optional(T const &v) { variant_ = v; } + optional(T const& v) { variant_ = v; } explicit operator bool() const noexcept { return variant_.template is(); } - T const &get() const { return variant_.template get(); } - T &get() { return variant_.template get(); } + T const& get() const { return variant_.template get(); } + T& get() { return variant_.template get(); } - T const &operator*() const { return this->get(); } + T const& operator*() const { return this->get(); } T operator*() { return this->get(); } - optional &operator=(T const &v) + optional& operator=(T const& v) { variant_ = v; return *this; } - optional &operator=(optional const &rhs) + optional& operator=(optional const& rhs) { if (this != &rhs) { @@ -56,14 +58,17 @@ template class optional return *this; } - template void emplace(Args &&... args) + template + void emplace(Args&&... args) { variant_ = T{std::forward(args)...}; } void reset() { variant_ = none_type{}; } -}; -} -} -#endif +}; // class optional + +} // namespace util +} // namespace mapbox + +#endif // MAPBOX_UTIL_OPTIONAL_HPP diff --git a/third_party/variant/recursive_wrapper.hpp b/third_party/variant/recursive_wrapper.hpp index 54b27634a..4becdd689 100644 --- a/third_party/variant/recursive_wrapper.hpp +++ b/third_party/variant/recursive_wrapper.hpp @@ -1,44 +1,72 @@ -#ifndef MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP -#define MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP +#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP +#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP +// Based on variant/recursive_wrapper.hpp from boost. +// +// Original license: +// +// Copyright (c) 2002-2003 +// Eric Friedman, Itay Maman +// +// 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) + +#include #include -namespace mapbox { namespace util { +namespace mapbox { +namespace util { template class recursive_wrapper { -public: - using type = T; -private: T* p_; -public: + void assign(T const& rhs) + { + this->get() = rhs; + } - ~recursive_wrapper(); - recursive_wrapper(); + public: + using type = T; - recursive_wrapper(recursive_wrapper const& operand); - recursive_wrapper(T const& operand); - recursive_wrapper(recursive_wrapper&& operand); - recursive_wrapper(T&& operand); + /** + * Default constructor default initializes the internally stored value. + * For POD types this means nothing is done and the storage is + * uninitialized. + * + * @throws std::bad_alloc if there is insufficient memory for an object + * of type T. + * @throws any exception thrown by the default constructur of T. + */ + recursive_wrapper() + : p_(new T){}; -private: + ~recursive_wrapper() noexcept { delete p_; }; - void assign(const T& rhs); + recursive_wrapper(recursive_wrapper const& operand) + : p_(new T(operand.get())) {} -public: + recursive_wrapper(T const& operand) + : p_(new T(operand)) {} + + recursive_wrapper(recursive_wrapper&& operand) + : p_(new T(std::move(operand.get()))) {} + + recursive_wrapper(T&& operand) + : p_(new T(std::move(operand))) {} inline recursive_wrapper& operator=(recursive_wrapper const& rhs) { - assign( rhs.get() ); + assign(rhs.get()); return *this; } inline recursive_wrapper& operator=(T const& rhs) { - assign( rhs ); + assign(rhs); return *this; } @@ -49,7 +77,6 @@ public: p_ = temp; } - recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept { swap(rhs); @@ -62,66 +89,34 @@ public: return *this; } + T& get() + { + assert(p_); + return *get_pointer(); + } -public: + T const& get() const + { + assert(p_); + return *get_pointer(); + } - T& get() { return *get_pointer(); } - const T& get() const { return *get_pointer(); } T* get_pointer() { return p_; } + const T* get_pointer() const { return p_; } + operator T const&() const { return this->get(); } + operator T&() { return this->get(); } -}; -template -recursive_wrapper::~recursive_wrapper() -{ - delete p_; -} - -template -recursive_wrapper::recursive_wrapper() - : p_(new T) -{ -} - -template -recursive_wrapper::recursive_wrapper(recursive_wrapper const& operand) - : p_(new T( operand.get() )) -{ -} - -template -recursive_wrapper::recursive_wrapper(T const& operand) - : p_(new T(operand)) -{ -} - -template -recursive_wrapper::recursive_wrapper(recursive_wrapper&& operand) - : p_(operand.p_) -{ - operand.p_ = nullptr; -} - -template -recursive_wrapper::recursive_wrapper(T&& operand) - : p_(new T( std::move(operand) )) -{ -} - -template -void recursive_wrapper::assign(const T& rhs) -{ - this->get() = rhs; -} +}; // class recursive_wrapper template inline void swap(recursive_wrapper& lhs, recursive_wrapper& rhs) noexcept { lhs.swap(rhs); } +} // namespace util +} // namespace mapbox -}} - -#endif // MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP +#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP diff --git a/third_party/variant/scripts/build-appveyor.bat b/third_party/variant/scripts/build-appveyor.bat index 9b9d7c8db..bdee7ea8d 100644 --- a/third_party/variant/scripts/build-appveyor.bat +++ b/third_party/variant/scripts/build-appveyor.bat @@ -10,7 +10,7 @@ IF /I "%PLATFORM"=="x64" ( CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 ) -IF NOT EXIST deps\gyp git clone --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp +IF NOT EXIST deps\gyp git clone --quiet --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp CALL deps\gyp\gyp.bat variant.gyp --depth=. ^ -f msvs ^ diff --git a/third_party/variant/scripts/linux.sh b/third_party/variant/scripts/linux.sh deleted file mode 100644 index f173db5c3..000000000 --- a/third_party/variant/scripts/linux.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -set -e -u -set -o pipefail - -# ppa for latest boost -sudo add-apt-repository -y ppa:boost-latest/ppa -# ppa for g++ 4.7 and 4.8 -sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test -sudo apt-get update -y - -# install boost headers and g++ upgrades -sudo apt-get -y -qq install boost1.55 gcc-4.8 g++-4.8 gcc-4.7 g++-4.7 - -if [[ "$CXX" == "clang++" ]]; then - echo 'running tests against clang++' - make test - make bench - make clean -else - # run tests against g++ 4.7 - export CXX="g++-4.7"; export CC="gcc-4.7" - echo 'running tests against g++ 4.7' - make test - make bench - make clean - - # run tests against g++ 4.8 - export CXX="g++-4.8"; export CC="gcc-4.8" - echo 'running tests against g++ 4.8' - make test - make bench - make clean - -fi - -# compare object sizes against boost::variant -echo 'comparing object sizes to boost::variant' -make sizes /usr/include/boost/variant.hpp -make clean - -# test building with gyp -echo 'testing build with gyp' -make gyp - -# run coverage when using clang++ -if [[ $CXX == "clang++" ]]; then - make clean - make coverage - git status - ./out/cov-test - cp unit*gc* test/ - sudo pip install cpp-coveralls - coveralls -i variant.hpp -i recursive_wrapper.hpp --gcov-options '\-lp' -fi - -# set strictness back to normal -# to avoid tripping up travis -set +e +u diff --git a/third_party/variant/scripts/osx.sh b/third_party/variant/scripts/osx.sh deleted file mode 100644 index 14149ca04..000000000 --- a/third_party/variant/scripts/osx.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -e -u -set -o pipefail - -# install boost headers -brew unlink boost -brew install boost - -# run tests -make test -make bench -make clean - -# compare object sizes against boost::variant -make sizes `brew --prefix`/include/boost/variant.hpp -make clean - -# test building with gyp -make gyp \ No newline at end of file diff --git a/third_party/variant/scripts/run_compilation_failure_tests.sh b/third_party/variant/scripts/run_compilation_failure_tests.sh new file mode 100755 index 000000000..c2608fee5 --- /dev/null +++ b/third_party/variant/scripts/run_compilation_failure_tests.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# Try to compile all programs in the test/compilation_failure directory. +# Compilation must fail and the error message must match the pattern in the +# corresponding .pattern file. +# + +DIR="test/compilation_failure" +CXX=${CXX:-clang++} + +if [ `uname -s` = "Darwin" ]; then + CXXFLAGS="$CXXFLAGS -stdlib=libc++" +fi + +error_msg() { + if [ ! -z "$1" ]; then + printf 'output was:\n=======\n%s\n=======\n' "$1" + fi +} + +exit_code=0 +for test_code in $DIR/*.cpp; do + name=`basename $test_code .cpp` + + result=`${CXX} -std=c++11 -c -o /dev/null -I. ${CXXFLAGS} ${test_code} 2>&1` + status=$? + + if [ $status = 1 ]; then + expected=`sed -n -e '/@EXPECTED/s/.*: \+//p' ${test_code}` + if echo $result | grep -q "$expected"; then + echo "$name [OK]" + else + echo "$name [FAILED - wrong error message]" + echo "Expected error message: $expected" + error_msg "$result" + exit_code=1 + fi + elif [ $status = 0 ]; then + echo "$name [FAILED - compile was successful]" + error_msg "$result" + exit_code=1 + else + echo "$name [FAILED - unknown error in compile]" + error_msg "$result" + exit_code=1 + fi +done + +exit ${exit_code} + diff --git a/third_party/variant/test/bench_variant.cpp b/third_party/variant/test/bench_variant.cpp index 474e3c36c..700dac735 100644 --- a/third_party/variant/test/bench_variant.cpp +++ b/third_party/variant/test/bench_variant.cpp @@ -1,10 +1,16 @@ + +#include +#include #include -#include -#include +#include #include +#include #include -#include +#include + #include +#include + #include "variant.hpp" #define TEXT_SHORT "Test" @@ -23,7 +29,7 @@ struct Holder std::vector data; template - void append_move(T && obj) + void append_move(T&& obj) { data.emplace_back(std::forward(obj)); } @@ -37,49 +43,48 @@ struct Holder } // namespace test -struct print : util::static_visitor<> +struct print { template - void operator() (T const& val) const + void operator()(T const& val) const { std::cerr << val << ":" << typeid(T).name() << std::endl; } }; - template struct dummy : boost::static_visitor<> { - dummy(V & v) + dummy(V& v) : v_(v) {} template - void operator() (T && val) const + void operator()(T&& val) const { v_ = std::move(val); } - V & v_; + V& v_; }; template -struct dummy2 : util::static_visitor<> +struct dummy2 { - dummy2(V & v) + dummy2(V& v) : v_(v) {} template - void operator() (T && val) const + void operator()(T&& val) const { v_ = std::move(val); } - V & v_; + V& v_; }; void run_boost_test(std::size_t runs) { test::Holder> h; h.data.reserve(runs); - for (std::size_t i=0; i< runs; ++i) + for (std::size_t i = 0; i < runs; ++i) { h.append_move(std::string(TEXT_SHORT)); h.append_move(std::string(TEXT_LONG)); @@ -99,7 +104,7 @@ void run_variant_test(std::size_t runs) { test::Holder> h; h.data.reserve(runs); - for (std::size_t i=0; i< runs; ++i) + for (std::size_t i = 0; i < runs; ++i) { h.append_move(std::string(TEXT_SHORT)); h.append_move(std::string(TEXT_LONG)); @@ -111,13 +116,13 @@ void run_variant_test(std::size_t runs) for (auto const& v2 : h.data) { dummy2> d(v); - util::apply_visitor (d, v2); + util::apply_visitor(d, v2); } } -int main (int argc, char** argv) +int main(int argc, char** argv) { - if (argc!=2) + if (argc != 2) { std::cerr << "Usage:" << argv[0] << " " << std::endl; return 1; @@ -154,11 +159,11 @@ int main (int argc, char** argv) thread_group tg; std::cerr << "custom variant: "; boost::timer::auto_cpu_timer timer; - for (std::size_t i=0; ijoinable()) t->join();}); + std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); } { @@ -167,15 +172,14 @@ int main (int argc, char** argv) thread_group tg; std::cerr << "boost variant: "; boost::timer::auto_cpu_timer timer; - for (std::size_t i=0; ijoinable()) t->join();}); + std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); } } #endif - return EXIT_SUCCESS; } diff --git a/third_party/variant/test/binary_visitor_test.cpp b/third_party/variant/test/binary_visitor_test.cpp index de2a30e6c..fa0f2ead8 100644 --- a/third_party/variant/test/binary_visitor_test.cpp +++ b/third_party/variant/test/binary_visitor_test.cpp @@ -1,13 +1,14 @@ + +#include #include +#include #include -#include -#include -#include #include -#include +#include #include -#include -#include +#include +#include + #include "variant.hpp" #include "variant_io.hpp" @@ -16,12 +17,14 @@ using namespace mapbox; namespace test { template -struct string_to_number {}; +struct string_to_number +{ +}; template <> struct string_to_number { - double operator() (std::string const& str) const + double operator()(std::string const& str) const { return std::stod(str); } @@ -30,7 +33,7 @@ struct string_to_number template <> struct string_to_number { - std::int64_t operator() (std::string const& str) const + std::int64_t operator()(std::string const& str) const { return std::stoll(str); } @@ -39,7 +42,7 @@ struct string_to_number template <> struct string_to_number { - std::uint64_t operator() (std::string const& str) const + std::uint64_t operator()(std::string const& str) const { return std::stoull(str); } @@ -48,7 +51,7 @@ struct string_to_number template <> struct string_to_number { - bool operator() (std::string const& str) const + bool operator()(std::string const& str) const { bool result; std::istringstream(str) >> std::boolalpha >> result; @@ -56,28 +59,28 @@ struct string_to_number } }; -struct javascript_equal_visitor : util::static_visitor +struct javascript_equal_visitor { template - bool operator() (T lhs, T rhs) const + bool operator()(T lhs, T rhs) const { return lhs == rhs; } template ::value>::type> - bool operator() (T lhs, std::string const& rhs) const + bool operator()(T lhs, std::string const& rhs) const { return lhs == string_to_number()(rhs); } template ::value>::type> - bool operator() (std::string const& lhs, T rhs) const + bool operator()(std::string const& lhs, T rhs) const { return string_to_number()(lhs) == rhs; } template - bool operator() (T0 lhs, T1 rhs) const + bool operator()(T0 lhs, T1 rhs) const { return lhs == static_cast(rhs); } @@ -89,7 +92,7 @@ struct javascript_equal javascript_equal(T const& lhs) : lhs_(lhs) {} - bool operator() (T const& rhs) const + bool operator()(T const& rhs) const { return util::apply_visitor(test::javascript_equal_visitor(), lhs_, rhs); } @@ -98,7 +101,7 @@ struct javascript_equal } // namespace test -int main (/*int argc, char** argv*/) +int main() { typedef util::variant variant_type; variant_type v0(3.14159); @@ -108,7 +111,6 @@ int main (/*int argc, char** argv*/) std::cerr << v0 << " == " << v1 << " -> " << std::boolalpha << util::apply_visitor(test::javascript_equal_visitor(), v0, v1) << std::endl; - std::vector vec; vec.emplace_back(std::string("1")); diff --git a/third_party/variant/test/boost_variant_hello_world.cpp b/third_party/variant/test/boost_variant_hello_world.cpp index 0d0925a7e..fdb200e73 100644 --- a/third_party/variant/test/boost_variant_hello_world.cpp +++ b/third_party/variant/test/boost_variant_hello_world.cpp @@ -1,17 +1,18 @@ #include -#include + #include struct check : boost::static_visitor<> { template - void operator() (T const& val) const + void operator()(T const& val) const { if (val != 0) throw std::runtime_error("invalid"); } }; -int main() { +int main() +{ typedef boost::variant variant_type; variant_type v(0); boost::apply_visitor(check(), v); diff --git a/third_party/variant/test/catch.hpp b/third_party/variant/test/catch.hpp deleted file mode 100644 index 057c82ee0..000000000 --- a/third_party/variant/test/catch.hpp +++ /dev/null @@ -1,8683 +0,0 @@ -/* - * CATCH v1.0 build 45 (master branch) - * Generated: 2014-05-19 18:22:42.461908 - * ---------------------------------------------------------- - * 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 diagnostic ignored "-Wglobal-constructors" -#pragma clang diagnostic ignored "-Wvariadic-macros" -#pragma clang diagnostic ignored "-Wc99-extensions" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif - -#ifdef CATCH_CONFIG_MAIN -# define CATCH_CONFIG_RUNNER -#endif - -#ifdef CATCH_CONFIG_RUNNER -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED - -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED - -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include -#include - -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED - -// Much of the following code is based on Boost (1.53) - -#ifdef __clang__ - -# if __has_feature(cxx_nullptr) -# define CATCH_CONFIG_CPP11_NULLPTR -# endif - -# if __has_feature(cxx_noexcept) -# define CATCH_CONFIG_CPP11_NOEXCEPT -# endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#if (__BORLANDC__ > 0x582 ) -//#define CATCH_CONFIG_SFINAE // Not confirmed -#endif - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#if (__EDG_VERSION__ > 238 ) -//#define CATCH_CONFIG_SFINAE // Not confirmed -#endif - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#if (__DMC__ > 0x840 ) -//#define CATCH_CONFIG_SFINAE // Not confirmed -#endif - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ < 3 - -#if (__GNUC_MINOR__ >= 96 ) -//#define CATCH_CONFIG_SFINAE -#endif - -#elif __GNUC__ >= 3 - -// #define CATCH_CONFIG_SFINAE // Taking this out completely for now - -#endif // __GNUC__ < 3 - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) - -#define CATCH_CONFIG_CPP11_NULLPTR -#endif - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1310 ) // (VC++ 7.0+) -//#define CATCH_CONFIG_SFINAE // Not confirmed -#endif - -#endif // _MSC_VER - -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS -#define CATCH_CONFIG_VARIADIC_MACROS -#endif - -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// detect language version: -#if (__cplusplus == 201103L) -# define CATCH_CPP11 -# define CATCH_CPP11_OR_GREATER -#elif (__cplusplus >= 201103L) -# define CATCH_CPP11_OR_GREATER -#endif - -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) -#endif - -namespace Catch { - - class NonCopyable { - NonCopyable( NonCopyable const& ); - void operator = ( NonCopyable const& ); - 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 ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - - struct SourceLineInfo { - - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); -# ifdef CATCH_CPP11_OR_GREATER - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - - 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; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() { - return std::string(); - } - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); - -#include - -namespace Catch { - - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; - }; - -} // end namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) - -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED - -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() { return m_p; } - const T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#include -#include -#include - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture& getResultCapture() = 0; - virtual IRunner& getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED - -#include - -namespace Catch { - - class TestSpec; - - struct ITestCase : IShared { - virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases ) const = 0; - - }; -} - -namespace Catch { - -template -class MethodTestCase : public SharedImpl { - -public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} - - virtual void invoke() const { - C obj; - (obj.*m_method)(); - } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); -}; - -typedef void(*TestFunction)(); - -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} - - const char* name; - const char* description; -}; - -struct AutoReg { - - AutoReg( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - registerTestCase( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - - void registerTestCase( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - - ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); -}; - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE( ... ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ - namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() - -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() - -#endif - -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED - -// #included from: catch_expression_decomposer.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_DECOMPOSER_HPP_INCLUDED - -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED - -// #included from: catch_expressionresult_builder.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_BUILDER_H_INCLUDED - -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED - -// #included from: catch_sfinae.hpp -#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED - -// Try to detect if the current compiler supports SFINAE - -namespace Catch { - - struct TrueType { - static const bool value = true; - typedef void Enable; - char sizer[1]; - }; - struct FalseType { - static const bool value = false; - typedef void Disable; - char sizer[2]; - }; - -#ifdef CATCH_CONFIG_SFINAE - - template struct NotABooleanExpression; - - template struct If : NotABooleanExpression {}; - template<> struct If : TrueType {}; - template<> struct If : FalseType {}; - - template struct SizedIf; - template<> struct SizedIf : TrueType {}; - template<> struct SizedIf : FalseType {}; - -#endif // CATCH_CONFIG_SFINAE - -} // end namespace Catch - -#include -#include -#include -#include -#include - -#ifdef __OBJC__ -// #included from: catch_objc_arc.hpp -#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED - -#import - -#ifdef __has_feature -#define CATCH_ARC_ENABLED __has_feature(objc_arc) -#else -#define CATCH_ARC_ENABLED 0 -#endif - -void arcSafeRelease( NSObject* obj ); -id performOptionalSelector( id obj, SEL sel ); - -#if !CATCH_ARC_ENABLED -inline void arcSafeRelease( NSObject* obj ) { - [obj release]; -} -inline id performOptionalSelector( id obj, SEL sel ) { - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; - return nil; -} -#define CATCH_UNSAFE_UNRETAINED -#define CATCH_ARC_STRONG -#else -inline void arcSafeRelease( NSObject* ){} -inline id performOptionalSelector( id obj, SEL sel ) { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" -#endif - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - return nil; -} -#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained -#define CATCH_ARC_STRONG __strong -#endif - -#endif - -namespace Catch { -namespace Detail { - -// SFINAE is currently disabled by default for all compilers. -// If the non SFINAE version of IsStreamInsertable is ambiguous for you -// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE -#ifdef CATCH_CONFIG_SFINAE - - template - class IsStreamInsertableHelper { - template struct TrueIfSizeable : TrueType {}; - - template - static TrueIfSizeable dummy(T2*); - static FalseType dummy(...); - - public: - typedef SizedIf type; - }; - - template - struct IsStreamInsertable : IsStreamInsertableHelper::type {}; - -#else - - struct BorgType { - template BorgType( T const& ); - }; - - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); - - FalseType operator<<( std::ostream const&, BorgType const& ); - - template - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; - -#endif - - template - struct StringMakerBase { - template - static std::string convert( T const& ) { return "{?}"; } - }; - - template<> - struct StringMakerBase { - template - static std::string convert( T const& _value ) { - std::ostringstream oss; - oss << _value; - return oss.str(); - } - }; - - 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; - } - }; - - // Writes the raw memory into a string, considering endianness - template - std::string rawMemoryToString( T value ) { - union _ { - T typedValue; - unsigned char bytes[sizeof(T)]; - } u; - - u.typedValue = value; - - std::ostringstream oss; - oss << "0x"; - - int i = 0, end = sizeof(T), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - for( ; i != end; i += inc ) - oss << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)u.bytes[i]; - return oss.str(); - } - -} // end namespace Detail - -template -std::string toString( T const& value ); - -template -struct StringMaker : - Detail::StringMakerBase::value> {}; - -template -struct StringMaker { - template - static std::string convert( U* p ) { - if( !p ) - return INTERNAL_CATCH_STRINGIFY( NULL ); - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return INTERNAL_CATCH_STRINGIFY( NULL ); - else - return Detail::rawMemoryToString( p ); - } -}; - -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -template -struct StringMaker > { - static std::string convert( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); - } -}; - -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 ); -} - -// 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( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif - - namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << toString( *first ); - for( ++first ; first != last ; ++first ) { - oss << ", " << toString( *first ); - } - } - oss << " }"; - return oss.str(); - } -} - -} // end namespace Catch - -// #included from: catch_assertionresult.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED - -#include -// #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 - - }; }; - - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - // ResultAction::Value enum - struct ResultAction { enum Value { - None, - Failed = 1, // Failure - but no debug break if Debug bit not set - Debug = 2, // If this bit is set, invoke the debugger - Abort = 4 // Test run should abort - }; }; - - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 0x00, - - ContinueOnFailure = 0x01, // Failures fail test, but execution continues - NegateResult = 0x02, // Prefix expressiom with ! - SuppressFail = 0x04 // Failures are reported but do not fail the test - }; }; - - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool shouldNegate( int flags ) { return ( flags & ResultDisposition::NegateResult ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch - - -namespace Catch { - - struct AssertionInfo - { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); - - std::string macroName; - SourceLineInfo lineInfo; - std::string capturedExpression; - ResultDisposition::Flags resultDisposition; - }; - - struct AssertionResultData - { - AssertionResultData() : resultType( ResultWas::Unknown ) {} - - std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; - }; - - class AssertionResult { - public: - AssertionResult(); - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CPP11_OR_GREATER - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; - - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#endif - -#include - -namespace Catch { -namespace Internal { - - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo - }; - - template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; - - template - inline T& opCast(T const& t) { return const_cast(t); } - -// nullptr_t support based on pull request #154 from Konstantin Baumann -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } -#endif // CATCH_CONFIG_CPP11_NULLPTR - - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template - class Evaluator{}; - - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return opCast( lhs ) == opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) != opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) < opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) > opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) >= opCast( rhs ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) <= opCast( rhs ); - } - }; - - template - bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // This level of indirection allows us to specialise for integer types - // to avoid signed/ unsigned warnings - - // "base" overload - template - bool compare( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // unsigned X to int - template bool compare( unsigned int lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // unsigned X to long - template bool compare( unsigned int lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // int to unsigned X - template bool compare( int lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // long to unsigned X - template bool compare( long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long (when comparing against NULL) - template bool compare( long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - - // pointer to int (when comparing against NULL) - template bool compare( int lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, int rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template bool compare( std::nullptr_t, T* rhs ) { - return Evaluator::evaluate( NULL, rhs ); - } - template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, NULL ); - } -#endif // CATCH_CONFIG_CPP11_NULLPTR - -} // end of namespace Internal -} // end of namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -namespace Catch { - -struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - -// Wraps the (stringised versions of) the lhs, operator and rhs of an expression - as well as -// the result of evaluating it. This is used to build an AssertionResult object -class ExpressionResultBuilder { -public: - - ExpressionResultBuilder( ResultWas::OfType resultType = ResultWas::Unknown ); - ExpressionResultBuilder( ExpressionResultBuilder const& other ); - ExpressionResultBuilder& operator=(ExpressionResultBuilder const& other ); - - ExpressionResultBuilder& setResultType( ResultWas::OfType result ); - ExpressionResultBuilder& setResultType( bool result ); - ExpressionResultBuilder& setLhs( std::string const& lhs ); - ExpressionResultBuilder& setRhs( std::string const& rhs ); - ExpressionResultBuilder& setOp( std::string const& op ); - - ExpressionResultBuilder& endExpression( ResultDisposition::Flags resultDisposition ); - - template - ExpressionResultBuilder& operator << ( T const& value ) { - m_stream << value; - return *this; - } - - std::string reconstructExpression( AssertionInfo const& info ) const; - - AssertionResult buildResult( AssertionInfo const& info ) 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: - AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : shouldNegate( false ) {} - bool shouldNegate; - std::string lhs, rhs, op; - } m_exprComponents; - std::ostringstream m_stream; -}; - -} // end namespace Catch - -namespace Catch { - -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in an ExpressionResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CPP11_OR_GREATER - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif - -public: - ExpressionLhs( T lhs ) : m_lhs( lhs ) {} -# ifdef CATCH_CPP11_OR_GREATER - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif - - template - ExpressionResultBuilder& operator == ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ExpressionResultBuilder& operator != ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ExpressionResultBuilder& operator < ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ExpressionResultBuilder& operator > ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ExpressionResultBuilder& operator <= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ExpressionResultBuilder& operator >= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - ExpressionResultBuilder& operator == ( bool rhs ) { - return captureExpression( rhs ); - } - - ExpressionResultBuilder& operator != ( bool rhs ) { - return captureExpression( rhs ); - } - - ExpressionResultBuilder& endExpression( ResultDisposition::Flags resultDisposition ) { - bool value = m_lhs ? true : false; - return m_result - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression( resultDisposition ); - } - - // 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 - ExpressionResultBuilder& captureExpression( RhsT const& rhs ) { - return m_result - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); - } - -private: - ExpressionResultBuilder m_result; - T m_lhs; -}; - -} // end namespace Catch - -namespace Catch { - -// Captures the LHS of the expression and wraps it in an Expression Lhs object -class ExpressionDecomposer { -public: - - template - ExpressionLhs operator->* ( T const& operand ) { - return ExpressionLhs( operand ); - } - - ExpressionLhs operator->* ( bool value ) { - return ExpressionLhs( value ); - } -}; - -} // end 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(); - - 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 ExpressionResultBuilder; - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct MessageInfo; - class ScopedMessageBuilder; - struct Counts; - - struct IResultCapture { - - virtual ~IResultCapture(); - - virtual void assertionEnded( AssertionResult const& result ) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; - - virtual bool shouldDebugBreak() const = 0; - - virtual ResultAction::Value acceptExpression( ExpressionResultBuilder const& assertionResult, AssertionInfo const& assertionInfo ) = 0; - - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - }; -} - -// #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::isTrue( true ); -#endif - -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED - -#include - -namespace Catch { - - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - struct IReporterRegistry; - struct IReporterFactory; - - struct IRegistryHub { - virtual ~IRegistryHub(); - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; - }; - - struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; - virtual void registerTest( TestCase const& testInfo ) = 0; - virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; - }; - - IRegistryHub& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - -// #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 - }; }; - - 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; - }; -} - -#include - -namespace Catch { - - inline IResultCapture& getResultCapture() { - return getCurrentContext().getResultCapture(); - } - - template - ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, - std::string const& matcherCallAsString ) { - std::string matcherAsString = matcher.toString(); - if( matcherAsString == "{?}" ) - matcherAsString = matcherCallAsString; - return ExpressionResultBuilder() - .setRhs( matcherAsString ) - .setOp( "matches" ); - } - - template - ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, - ArgT const& arg, - std::string const& matcherCallAsString ) { - return expressionResultBuilderFromMatcher( matcher, matcherCallAsString ) - .setLhs( Catch::toString( arg ) ) - .setResultType( matcher.match( arg ) ); - } - - template - ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, - ArgT* arg, - std::string const& matcherCallAsString ) { - return expressionResultBuilderFromMatcher( matcher, matcherCallAsString ) - .setLhs( Catch::toString( arg ) ) - .setResultType( matcher.match( arg ) ); - } - -struct TestFailureException{}; - -} // end namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ACCEPT_EXPR( evaluatedExpr, resultDisposition, originalExpr ) \ - if( Catch::ResultAction::Value internal_catch_action = Catch::getResultCapture().acceptExpression( evaluatedExpr, __assertionInfo ) ) { \ - if( internal_catch_action & Catch::ResultAction::Debug ) CATCH_BREAK_INTO_DEBUGGER(); \ - if( internal_catch_action & Catch::ResultAction::Abort ) throw Catch::TestFailureException(); \ - if( !Catch::shouldContinueOnFailure( resultDisposition ) ) throw Catch::TestFailureException(); \ - Catch::isTrue( false && originalExpr ); \ - } - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ - do { \ - Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionDecomposer()->*expr ).endExpression( resultDisposition ), resultDisposition, expr ); \ - } catch( Catch::TestFailureException& ) { \ - throw; \ - } catch( ... ) { \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), \ - Catch::ResultDisposition::Normal, expr ); \ - } \ - } while( Catch::isTrue( false ) ) - -/////////////////////////////////////////////////////////////////////////////// -#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::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - expr; \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::Ok ), resultDisposition, false ); \ - } \ - catch( ... ) { \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), resultDisposition, false ); \ - } \ -} while( Catch::isTrue( false ) ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ - try { \ - if( Catch::getCurrentContext().getConfig()->allowThrows() ) { \ - expr; \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::DidntThrowException ), resultDisposition, false ); \ - } \ - } \ - catch( Catch::TestFailureException& ) { \ - throw; \ - } \ - catch( exceptionType ) { \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::Ok ), resultDisposition, false ); \ - } - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, exceptionType, resultDisposition, macroName ) \ - do { \ - Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ - } while( Catch::isTrue( false ) ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ - do { \ - Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ - catch( ... ) { \ - INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ - resultDisposition | Catch::ResultDisposition::ContinueOnFailure, false ); \ - } \ - } while( Catch::isTrue( false ) ) - -/////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ - do { \ - Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << __VA_ARGS__ +::Catch::StreamEndStop(), resultDisposition, true ) \ - } while( Catch::isTrue( false ) ) -#else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ - do { \ - Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << log, resultDisposition, true ) \ - } while( Catch::isTrue( false ) ) -#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::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ - try { \ - INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::expressionResultBuilderFromMatcher( ::Catch::Matchers::matcher, arg, #matcher ) ), resultDisposition, false ); \ - } catch( Catch::TestFailureException& ) { \ - throw; \ - } catch( ... ) { \ - INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ - resultDisposition | Catch::ResultDisposition::ContinueOnFailure, false ); \ - } \ - } while( Catch::isTrue( false ) ) - -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED - -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED - -namespace Catch { - - struct SectionInfo { - SectionInfo( std::string const& _name, - std::string const& _description, - SourceLineInfo const& _lineInfo ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; - -} // end namespace Catch - -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED - -#include - -namespace Catch { - - struct Counts { - Counts() : passed( 0 ), failed( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - return *this; - } - - std::size_t total() const { - return passed + failed; - } - - std::size_t passed; - std::size_t failed; - }; - - 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 - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } - - Counts assertions; - Counts testCases; - }; -} - -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED - -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; -#else -#include -#endif - -namespace Catch { - - class Timer { - public: - Timer() : m_ticks( 0 ) {} - void start(); - unsigned int getElapsedNanoseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - uint64_t m_ticks; - }; - -} // namespace Catch - -#include - -namespace Catch { - - class Section { - public: - Section( SourceLineInfo const& lineInfo, - std::string const& name, - std::string const& description = "" ); - ~Section(); -# ifdef CATCH_CPP11_OR_GREATER - Section( Section const& ) = default; - Section( Section && ) = default; - Section& operator = ( Section const& ) = default; - Section& operator = ( Section && ) = default; -# endif - - // This indicates whether the section should be executed or not - operator bool(); - - 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 INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( 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 - -namespace Catch { - - typedef std::string(*exceptionTranslateFunction)(); - - struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate() const = 0; - }; - - struct IExceptionTranslatorRegistry { - virtual ~IExceptionTranslatorRegistry(); - - virtual std::string translateActiveException() const = 0; - }; - - class ExceptionTranslatorRegistrar { - template - class ExceptionTranslator : public IExceptionTranslator { - public: - - ExceptionTranslator( std::string(*translateFunction)( T& ) ) - : m_translateFunction( translateFunction ) - {} - - virtual std::string translate() const { - try { - throw; - } - catch( T& ex ) { - return m_translateFunction( ex ); - } - } - - protected: - std::string(*m_translateFunction)( T& ); - }; - - public: - template - ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { - getMutableRegistryHub().registerTranslator - ( new ExceptionTranslator( translateFunction ) ); - } - }; -} - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) - -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED - -#include -#include - -namespace Catch { -namespace Detail { - - class Approx { - public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_scale( 1.0 ), - m_value( value ) - {} - - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - - static Approx custom() { - return Approx( 0 ); - } - - Approx operator()( double value ) { - Approx approx( value ); - approx.epsilon( m_epsilon ); - approx.scale( m_scale ); - return approx; - } - - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); - } - - friend bool operator == ( Approx const& lhs, double rhs ) { - return operator==( rhs, lhs ); - } - - friend bool operator != ( double lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - friend bool operator != ( Approx const& lhs, double rhs ) { - return !operator==( rhs, lhs ); - } - - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; - return *this; - } - - Approx& scale( double newScale ) { - m_scale = newScale; - return *this; - } - - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); - } - - private: - double m_epsilon; - double m_scale; - double m_value; - }; -} - -template<> -inline std::string toString( Detail::Approx const& value ) { - return value.toString(); -} - -} // end namespace Catch - -// #included from: internal/catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - - template - class AllOf : public MatcherImpl, ExpressionT> { - public: - - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} - - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) - return false; - return true; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) - return true; - return false; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - private: - std::vector > > m_matchers; - }; - - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct Equals : MatcherImpl { - Equals( std::string const& str ) : m_str( str ){} - Equals( Equals const& other ) : m_str( other.m_str ){} - - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_str == expr; - } - virtual std::string toString() const { - return "equals: \"" + m_str + "\""; - } - - std::string m_str; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr ) : m_substr( substr ){} - Contains( Contains const& other ) : m_substr( other.m_substr ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr ) : m_substr( substr ){} - StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == 0; - } - virtual std::string toString() const { - return "starts with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr ) : m_substr( substr ){} - EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return expr.find( m_substr ) == expr.size() - m_substr.size(); - } - virtual std::string toString() const { - return "ends with: \"" + m_substr + "\""; - } - - std::string m_substr; - }; - } // namespace StdString - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str ) { - return Impl::StdString::Equals( str ); - } - inline Impl::StdString::Equals Equals( const char* str ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); - } - inline Impl::StdString::Contains Contains( std::string const& substr ) { - return Impl::StdString::Contains( substr ); - } - inline Impl::StdString::Contains Contains( const char* substr ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); - } - -} // namespace Matchers - -using namespace Matchers; - -} // namespace Catch - -// 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 { - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - bool _isHidden, - SourceLineInfo const& _lineInfo ); - - TestCaseInfo( TestCaseInfo const& other ); - - std::string name; - std::string className; - std::string description; - std::set tags; - std::string tagsAsString; - SourceLineInfo lineInfo; - bool isHidden; - bool throws; - }; - - 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; - - bool isHidden() const; - bool throws() 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 - -// #included from: internal/catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - }; -} - - -#ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED - -#import - -#include - -// NB. Any general catch headers included here must be included -// in catch.hpp first to make sure they are included by the single -// header for non obj-usage - -/////////////////////////////////////////////////////////////////////////////// -// This protocol is really only here for (self) documenting purposes, since -// all its methods are optional. -@protocol OcFixture - -@optional - --(void) setUp; --(void) tearDown; - -@end - -namespace Catch { - - class OcMethod : public SharedImpl { - - public: - OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} - - virtual void invoke() const { - id obj = [[m_cls alloc] init]; - - performOptionalSelector( obj, @selector(setUp) ); - performOptionalSelector( obj, m_sel ); - performOptionalSelector( obj, @selector(tearDown) ); - - arcSafeRelease( obj ); - } - private: - virtual ~OcMethod() {} - - Class m_cls; - SEL m_sel; - }; - - namespace Detail{ - - inline std::string getAnnotation( Class cls, - std::string const& annotationName, - std::string const& testCaseName ) { - NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; - SEL sel = NSSelectorFromString( selStr ); - arcSafeRelease( selStr ); - id value = performOptionalSelector( cls, sel ); - if( value ) - return [(NSString*)value UTF8String]; - return ""; - } - } - - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( NULL, 0 ); - - Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); - objc_getClassList( classes, noClasses ); - - for( int c = 0; c < noClasses; c++ ) { - Class cls = classes[c]; - { - u_int count; - Method* methods = class_copyMethodList( cls, &count ); - for( u_int m = 0; m < count ; m++ ) { - SEL selector = method_getName(methods[m]); - std::string methodName = sel_getName(selector); - if( startsWith( methodName, "Catch_TestCase_" ) ) { - std::string testCaseName = methodName.substr( 15 ); - std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); - std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* className = class_getName( cls ); - - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); - noTestMethods++; - } - } - free(methods); - } - } - return noTestMethods; - } - - namespace Matchers { - namespace Impl { - namespace NSStringMatchers { - - template - struct StringHolder : MatcherImpl{ - StringHolder( NSString* substr ) : m_substr( [substr copy] ){} - StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} - StringHolder() { - arcSafeRelease( m_substr ); - } - - NSString* m_substr; - }; - - struct Equals : StringHolder { - Equals( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str isEqualToString:m_substr]; - } - - virtual std::string toString() const { - return "equals string: \"" + Catch::toString( m_substr ) + "\""; - } - }; - - struct Contains : StringHolder { - Contains( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location != NSNotFound; - } - - virtual std::string toString() const { - return "contains string: \"" + Catch::toString( m_substr ) + "\""; - } - }; - - struct StartsWith : StringHolder { - StartsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == 0; - } - - virtual std::string toString() const { - return "starts with: \"" + Catch::toString( m_substr ) + "\""; - } - }; - struct EndsWith : StringHolder { - EndsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( ExpressionType const& str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == [str length] - [m_substr length]; - } - - virtual std::string toString() const { - return "ends with: \"" + Catch::toString( m_substr ) + "\""; - } - }; - - } // namespace NSStringMatchers - } // namespace Impl - - inline Impl::NSStringMatchers::Equals - Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } - - inline Impl::NSStringMatchers::Contains - Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } - - inline Impl::NSStringMatchers::StartsWith - StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } - - inline Impl::NSStringMatchers::EndsWith - EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } - - } // namespace Matchers - - using namespace Matchers; - -} // namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ -return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ -{ \ -return @ desc; \ -} \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) - -#endif - -#ifdef CATCH_CONFIG_RUNNER -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED - -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif - -// #included from: catch_runner.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED - -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern : SharedImpl<> { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - class NamePattern : public Pattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { - if( startsWith( m_name, "*" ) ) { - m_name = m_name.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_name, "*" ) ) { - m_name = m_name.substr( 0, m_name.size()-1 ); - m_wildcard = (WildcardPosition)( m_wildcard | WildcardAtEnd ); - } - } - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_name == toLower( testCase.name ); - case WildcardAtStart: - return endsWith( toLower( testCase.name ), m_name ); - case WildcardAtEnd: - return startsWith( toLower( testCase.name ), m_name ); - case WildcardAtBothEnds: - return contains( toLower( testCase.name ), m_name ); - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - private: - std::string m_name; - WildcardPosition m_wildcard; - }; - class TagPattern : public Pattern { - public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.tags.find( m_tag ) != testCase.tags.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; - - public: - TestSpecParser parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = arg; - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - addFilter(); - return *this; - } - TestSpec testSpec() { - 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().parse( arg ).testSpec(); - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED - -#include - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - class Stream { - public: - Stream(); - Stream( std::streambuf* _streamBuf, bool _isOwned ); - void release(); - - std::streambuf* streamBuf; - - private: - bool isOwned; - }; -} - -#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 ), - abortAfter( -1 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ) - {} - - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - - int abortAfter; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - - std::string reporterName; - std::string outputFilename; - std::string name; - std::string processName; - - std::vector testsOrTags; - }; - - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: - - Config() - : m_os( std::cout.rdbuf() ) - {} - - Config( ConfigData const& data ) - : m_data( data ), - m_os( std::cout.rdbuf() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser; - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } - - virtual ~Config() { - m_os.rdbuf( std::cout.rdbuf() ); - m_stream.release(); - } - - void setFilename( std::string const& filename ) { - m_data.outputFilename = filename; - } - - std::string const& getFilename() const { - return m_data.outputFilename ; - } - - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - - std::string getProcessName() const { return m_data.processName; } - - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - - void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); - } - - void useStream( std::string const& streamName ) { - Stream stream = createStream( streamName ); - setStreamBuf( stream.streamBuf ); - m_stream.release(); - m_stream = stream; - } - - std::string getReporterName() const { return m_data.reporterName; } - - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } - - // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_os; } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - - private: - ConfigData m_data; - - Stream m_stream; - mutable std::ostream m_os; - TestSpec m_testSpec; - }; - -} // end namespace Catch - -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED - -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif - -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -#include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - inline void convertInto( bool _source, bool& _dest ) { - _dest = _source; - } - template - inline void convertInto( bool, T& ) { - throw std::runtime_error( "Invalid conversion" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -# ifdef CATCH_CPP11_OR_GREATER - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -# endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual void setFlag( ConfigT& config ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - void setFlag( ConfigT& config ) const { - functionObj->setFlag( config ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual void setFlag( C& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual void setFlag( C& p ) const { - convertInto( true, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual void setFlag( C& p ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual void setFlag( C& p ) const { - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual void setFlag( C& p ) const { - function( p ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual void setFlag( C& obj ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - struct Parser { - Parser() : separators( " \t=:" ) {} - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { - const std::string doubleDash = "--"; - for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) - parseIntoTokens( argv[i] , tokens); - } - void parseIntoTokens( std::string arg, std::vector& tokens ) const { - while( !arg.empty() ) { - Parser::Token token( Parser::Token::Positional, arg ); - arg = ""; - if( token.data[0] == '-' ) { - if( token.data.size() > 1 && token.data[1] == '-' ) { - token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); - } - else { - token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); - if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { - arg = "-" + token.data.substr( 1 ); - token.data = token.data.substr( 0, 1 ); - } - } - } - if( token.type != Parser::Token::Positional ) { - std::size_t pos = token.data.find_first_of( separators ); - if( pos != std::string::npos ) { - arg = token.data.substr( pos+1 ); - token.data = token.data.substr( 0, pos ); - } - } - tokens.push_back( token ); - } - } - std::string separators; - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - // NOTE: std::auto_ptr is deprecated in c++11/c++0x -#if defined(__cplusplus) && __cplusplus > 199711L - typedef std::unique_ptr ArgAutoPtr; -#else - typedef std::auto_ptr ArgAutoPtr; -#endif - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg = ArgAutoPtr( 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 = ArgAutoPtr( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( int argc, char const * const * argv ) const { - ConfigT config; - parseInto( argc, argv, config ); - return config; - } - - std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { - std::string processName = argv[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( argc, argv, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.setFlag( config ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE - -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif - -#include - -namespace Catch { - - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = (WarnAbout::What)( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); - - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = (Verbosity::Level)level; - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); - - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"" ); - } - } - - inline Clara::CommandLine makeCommandLineParser() { - - using namespace Clara; - CommandLine cli; - - cli.bindProcessName( &ConfigData::processName ); - - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); - - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); - - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); - - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); - - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); - - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); - - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); - - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); - - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &ConfigData::reporterName, "name" ); - - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); - - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); - - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); - - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); - -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); - - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); - - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes/no" ); - - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); - - 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 { - - namespace Detail { - struct IColourImpl; - } - - 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, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - - Error = BrightRed, - Success = Green, - - OriginalExpression = Cyan, - ReconstructedExpression = Yellow, - - SecondaryText = LightGrey, - Headers = White - }; - - // Use constructed object for RAII guard - Colour( Code _colourCode ); - ~Colour(); - - // Use static method for one-shot changes - static void use( Code _colourCode ); - - private: - static Detail::IColourImpl* impl(); - }; - -} // end namespace Catch - -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED - -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED - -namespace Catch { - - // An optional type - template - class Option { - public: - Option() : nullableValue( NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } - - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = NULL; - } - - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } - - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != NULL; } - bool none() const { return nullableValue == NULL; } - - bool operator !() const { return nullableValue == NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } - - private: - T* nullableValue; - char storage[sizeof(T)]; - }; - -} // end namespace Catch - -#include -#include -#include -#include - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template - struct LazyStat : Option { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_CPP11_OR_GREATER - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; -# endif - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CPP11_OR_GREATER - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; -# endif - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); - -# ifdef CATCH_CPP11_OR_GREATER - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); - -# ifdef CATCH_CPP11_OR_GREATER - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif - - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); - -# ifndef CATCH_CPP11_OR_GREATER - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; -# endif - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); - - // Implementing class must also provide the following static method: - // static std::string getDescription(); - - virtual ReporterPreferences getPreferences() const = 0; - - virtual void noMatchingTestCases( std::string const& spec ) = 0; - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - 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; - }; - - struct IReporterFactory { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map FactoryMap; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - }; - -} - -#include -#include - -namespace Catch { - - inline std::size_t listTests( Config const& config ) { - - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - std::cout << "Matching test cases:\n"; - else { - std::cout << "All available test cases:\n"; - testSpec = TestSpecParser().parse( "*" ).testSpec(); - } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - - if( !config.testSpec().hasFilters() ) - std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; - else - std::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().parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - std::cout << testCaseInfo.name << std::endl; - } - return matchedTests; - } - - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - std::cout << "Tags for matching test cases:\n"; - else { - std::cout << "All available tags:\n"; - testSpec = TestSpecParser().parse( "*" ).testSpec(); - } - - std::map tagCounts; - - std::vector matchedTestCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::map::iterator countIt = tagCounts.find( tagName ); - if( countIt == tagCounts.end() ) - tagCounts.insert( std::make_pair( tagName, 1 ) ); - else - countIt->second++; - } - } - - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << countIt->second << " "; - Text wrapper( "[" + countIt->first + "]", TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - std::cout << oss.str() << wrapper << "\n"; - } - std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; - return tagCounts.size(); - } - - inline std::size_t listReporters( Config const& /*config*/ ) { - std::cout << "Available reports:\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 ) ); - std::cout << " " - << it->first - << ":" - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; - } - std::cout << std::endl; - return factories.size(); - } - - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } - -} // end namespace Catch - -// #included from: internal/catch_runner_impl.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED - -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { -namespace SectionTracking { - - class TrackedSection { - - typedef std::map TrackedSections; - - public: - enum RunState { - NotStarted, - Executing, - ExecutingChildren, - Completed - }; - - TrackedSection( std::string const& name, TrackedSection* parent ) - : m_name( name ), m_runState( NotStarted ), m_parent( parent ) - {} - - RunState runState() const { return m_runState; } - - TrackedSection* findChild( std::string const& childName ) { - TrackedSections::iterator it = m_children.find( childName ); - return it != m_children.end() - ? &it->second - : NULL; - } - TrackedSection* acquireChild( std::string const& childName ) { - if( TrackedSection* child = findChild( childName ) ) - return child; - m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); - return findChild( childName ); - } - void enter() { - if( m_runState == NotStarted ) - m_runState = Executing; - } - void leave() { - for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); - it != itEnd; - ++it ) - if( it->second.runState() != Completed ) { - m_runState = ExecutingChildren; - return; - } - m_runState = Completed; - } - TrackedSection* getParent() { - return m_parent; - } - bool hasChildren() const { - return !m_children.empty(); - } - - private: - std::string m_name; - RunState m_runState; - TrackedSections m_children; - TrackedSection* m_parent; - - }; - - class TestCaseTracker { - public: - TestCaseTracker( std::string const& testCaseName ) - : m_testCase( testCaseName, NULL ), - m_currentSection( &m_testCase ), - m_completedASectionThisRun( false ) - {} - - bool enterSection( std::string const& name ) { - TrackedSection* child = m_currentSection->acquireChild( name ); - if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) - return false; - - m_currentSection = child; - m_currentSection->enter(); - return true; - } - void leaveSection() { - m_currentSection->leave(); - m_currentSection = m_currentSection->getParent(); - assert( m_currentSection != NULL ); - m_completedASectionThisRun = true; - } - - bool currentSectionHasChildren() const { - return m_currentSection->hasChildren(); - } - bool isCompleted() const { - return m_testCase.runState() == TrackedSection::Completed; - } - - class Guard { - public: - Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { - m_tracker.enterTestCase(); - } - ~Guard() { - m_tracker.leaveTestCase(); - } - private: - Guard( Guard const& ); - void operator = ( Guard const& ); - TestCaseTracker& m_tracker; - }; - - private: - void enterTestCase() { - m_currentSection = &m_testCase; - m_completedASectionThisRun = false; - m_testCase.enter(); - } - void leaveTestCase() { - m_testCase.leave(); - } - - TrackedSection m_testCase; - TrackedSection* m_currentSection; - bool m_completedASectionThisRun; - }; - -} // namespace SectionTracking - -using SectionTracking::TestCaseTracker; - -} // namespace Catch - -#include -#include - -namespace Catch { - - class StreamRedirect { - - public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } - - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; - - /////////////////////////////////////////////////////////////////////////// - - class RunContext : public IResultCapture, public IRunner { - - RunContext( RunContext const& ); - void operator =( RunContext const& ); - - public: - - explicit RunContext( Ptr const& config, Ptr const& reporter ) - : m_runInfo( config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( NULL ), - m_config( config ), - m_reporter( reporter ), - m_prevRunner( &m_context.getRunner() ), - m_prevResultCapture( &m_context.getResultCapture() ), - m_prevConfig( m_context.getConfig() ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } - - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - m_context.setRunner( m_prevRunner ); - m_context.setConfig( NULL ); - m_context.setResultCapture( m_prevResultCapture ); - m_context.setConfig( m_prevConfig ); - } - - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } - - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - TestCaseInfo testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting( testInfo ); - - m_activeTestCase = &testCase; - m_testCaseTracker = TestCaseTracker( testInfo.name ); - - do { - do { - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isCompleted() && !aborting() ); - } - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - - Totals deltaTotals = m_totals.delta( prevTotals ); - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - - m_activeTestCase = NULL; - m_testCaseTracker.reset(); - - return deltaTotals; - } - - Ptr config() const { - return m_config; - } - - private: // IResultCapture - - virtual ResultAction::Value acceptExpression( ExpressionResultBuilder const& assertionResult, AssertionInfo const& assertionInfo ) { - m_lastAssertionInfo = assertionInfo; - return actOnCurrentResult( assertionResult.buildResult( assertionInfo ) ); - } - - 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 ); - } - - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - if( !m_testCaseTracker->enterSection( oss.str() ) ) - return false; - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 || - !m_config->warnAboutMissingAssertions() || - m_testCaseTracker->currentSectionHasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { - if( std::uncaught_exception() ) { - m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); - return; - } - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - m_testCaseTracker->leaveSection(); - - m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } - - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } - - virtual bool shouldDebugBreak() const { - return m_config->shouldDebugBreak(); - } - - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : ""; - } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; - } - - public: - // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); - } - - private: - - ResultAction::Value actOnCurrentResult( AssertionResult const& result ) { - m_lastResult = result; - assertionEnded( m_lastResult ); - - ResultAction::Value action = ResultAction::None; - - if( !m_lastResult.isOk() ) { - action = ResultAction::Failed; - if( shouldDebugBreak() ) - action = (ResultAction::Value)( action | ResultAction::Debug ); - if( aborting() ) - action = (ResultAction::Value)( action | ResultAction::Abort ); - } - return action; - } - - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.name, testCaseInfo.description, testCaseInfo.lineInfo ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); - TestCaseTracker::Guard guard( *m_testCaseTracker ); - - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( std::cout, redirectedCout ); - StreamRedirect cerrRedir( std::cerr, redirectedCerr ); - m_activeTestCase->invoke(); - } - else { - m_activeTestCase->invoke(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - ExpressionResultBuilder exResult( ResultWas::ThrewException ); - exResult << translateActiveException(); - actOnCurrentResult( exResult.buildResult( m_lastAssertionInfo ) ); - } - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); - m_unfinishedSections.clear(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } - - private: - struct UnfinishedSections { - UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) - : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - - SectionInfo info; - Counts prevAssertions; - double durationInSeconds; - }; - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - Option m_testCaseTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - IRunner* m_prevRunner; - IResultCapture* m_prevResultCapture; - Ptr m_prevConfig; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - }; - -} // end namespace Catch - -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED - -namespace Catch { - - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _buildNumber, - char const* const _branchName ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - buildNumber( _buildNumber ), - branchName( _branchName ) - {} - - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const buildNumber; - char const* const branchName; - - private: - void operator=( Version const& ); - }; - - extern Version libraryVersion; -} - -#include -#include -#include - -namespace Catch { - - class Runner { - - public: - Runner( Ptr const& config ) - : m_config( config ) - { - openStream(); - makeReporter(); - } - - Totals runTests() { - - RunContext context( m_config.get(), m_reporter ); - - Totals totals; - - context.testGroupStarting( "", 1, 1 ); // deprecated? - - TestSpec testSpec = m_config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser().parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector testCases; - getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases ); - - int testsRunForGroup = 0; - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) { - testsRunForGroup++; - if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { - - if( context.aborting() ) - break; - - totals += context.runTest( *it ); - m_testsAlreadyRun.insert( *it ); - } - } - context.testGroupEnded( "", totals, 1, 1 ); - return totals; - } - - private: - void openStream() { - // Open output file, if specified - if( !m_config->getFilename().empty() ) { - m_ofs.open( m_config->getFilename().c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << m_config->getFilename() << "'"; - throw std::domain_error( oss.str() ); - } - m_config->setStreamBuf( m_ofs.rdbuf() ); - } - } - void makeReporter() { - std::string reporterName = m_config->getReporterName().empty() - ? "console" - : m_config->getReporterName(); - - m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); - if( !m_reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - } - - private: - Ptr m_config; - std::ofstream m_ofs; - Ptr m_reporter; - std::set m_testsAlreadyRun; - }; - - class Session { - 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"; - std::cerr << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } - - void showHelp( std::string const& processName ) { - std::cout << "\nCatch v" << libraryVersion.majorVersion << "." - << libraryVersion.minorVersion << " build " - << libraryVersion.buildNumber; - if( libraryVersion.branchName != std::string( "master" ) ) - std::cout << " (" << libraryVersion.branchName << " branch)"; - std::cout << "\n"; - - m_cli.usage( std::cout, processName ); - std::cout << "For more detail usage please see the project docs\n" << std::endl; - } - - int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - std::cerr << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; - } - m_cli.usage( std::cout, m_configData.processName ); - return (std::numeric_limits::max)(); - } - return 0; - } - - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); - } - - int run( int argc, char* const argv[] ) { - - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - - int run() { - if( m_configData.showHelp ) - return 0; - - try - { - config(); // Force config to be constructed - Runner runner( m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runner.runTests().assertions.failed ); - } - catch( std::exception& ex ) { - std::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 - -namespace Catch { - - class TestRegistry : public ITestCaseRegistry { - public: - TestRegistry() : 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() ) ); - } - - if( m_functions.find( testCase ) == m_functions.end() ) { - m_functions.insert( testCase ); - m_functionsInOrder.push_back( testCase ); - if( !testCase.isHidden() ) - m_nonHiddenFunctions.push_back( testCase ); - } - else { - TestCase const& prev = *m_functions.find( testCase ); - { - Colour colourGuard( Colour::Red ); - std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; - } - exit(1); - } - } - - virtual std::vector const& getAllTests() const { - return m_functionsInOrder; - } - - virtual std::vector const& getAllNonHiddenTests() const { - return m_nonHiddenFunctions; - } - - virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases ) const { - for( std::vector::const_iterator it = m_functionsInOrder.begin(), - itEnd = m_functionsInOrder.end(); - it != itEnd; - ++it ) { - if( testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ) ) - matchingTestCases.push_back( *it ); - } - } - - private: - - std::set m_functions; - std::vector m_functionsInOrder; - std::vector m_nonHiddenFunctions; - size_t m_unnamedCount; - }; - - /////////////////////////////////////////////////////////////////////////// - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; - }; - - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - AutoReg::~AutoReg() {} - - void AutoReg::registerTestCase( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - -} // end namespace Catch - -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() { - deleteAllValues( m_factories ); - } - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, IReporterFactory* factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - - FactoryMap const& getFactories() const { - return m_factories; - } - - private: - FactoryMap m_factories; - }; -} - -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" -#endif - -namespace Catch { - - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); - } - - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); - } - - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - throw; - } - @catch (NSException *exception) { - return toString( [exception description] ); - } -#else - throw; -#endif - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return tryTranslators( m_translators.begin() ); - } - } - - std::string tryTranslators( std::vector::const_iterator it ) const { - if( it == m_translators.end() ) - return "Unknown exception"; - - try { - return (*it)->translate(); - } - catch(...) { - return tryTranslators( it+1 ); - } - } - - private: - std::vector m_translators; - }; -} - -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); - - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerTest( TestCase const& testInfo ) { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } - } - - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } - -} // end namespace Catch - -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED - -#include - -namespace Catch { - - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } - - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } - -} // end namespace Catch - -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; - }; -} - -#include -#include - -namespace Catch { - - template - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - Stream::Stream() - : streamBuf( NULL ), isOwned( false ) - {} - - Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) - {} - - void Stream::release() { - if( isOwned ) { - delete streamBuf; - streamBuf = NULL; - isOwned = false; - } - } -} - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: // IContext - virtual IResultCapture& getResultCapture() { - return *m_resultCapture; - } - virtual IRunner& getRunner() { - return *m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture().getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture().getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; - }; - - namespace { - Context* currentContext = NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - Stream createStream( std::string const& streamName ) { - if( streamName == "stdout" ) return Stream( std::cout.rdbuf(), false ); - if( streamName == "stderr" ) return Stream( std::cerr.rdbuf(), false ); - if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); - - throw std::domain_error( "Unknown stream: " + streamName ); - } - - void cleanUpContext() { - delete currentContext; - currentContext = NULL; - } -} - -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED - -namespace Catch { namespace Detail { - struct IColourImpl { - virtual ~IColourImpl() {} - virtual void use( Colour::Code _colourCode ) = 0; - }; -}} - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -namespace Catch { -namespace { - - class Win32ColourImpl : public Detail::IColourImpl { - public: - Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) - { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); - originalAttributes = csbiInfo.wAttributes; - } - - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalAttributes ); - case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Red: return setTextAttribute( FOREGROUND_RED ); - case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); - case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); - case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); - case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); - case Colour::Grey: return setTextAttribute( 0 ); - - case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); - case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); - case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); - case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - - case Colour::Bright: throw std::logic_error( "not a colour" ); - } - } - - private: - void setTextAttribute( WORD _textAttribute ) { - SetConsoleTextAttribute( stdoutHandle, _textAttribute ); - } - HANDLE stdoutHandle; - WORD originalAttributes; - }; - - inline bool shouldUseColourForPlatform() { - return true; - } - - static Detail::IColourImpl* platformColourInstance() { - static Win32ColourImpl s_instance; - return &s_instance; - } - -} // end anon namespace -} // end namespace Catch - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -#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 Detail::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" ); - } - } - private: - void setColour( const char* _escapeCode ) { - std::cout << '\033' << _escapeCode; - } - }; - - inline bool shouldUseColourForPlatform() { - return isatty(STDOUT_FILENO); - } - - static Detail::IColourImpl* platformColourInstance() { - static PosixColourImpl s_instance; - return &s_instance; - } - -} // end anon namespace -} // end namespace Catch - -#endif // not Windows - -namespace Catch { - - namespace { - struct NoColourImpl : Detail::IColourImpl { - void use( Colour::Code ) {} - - static IColourImpl* instance() { - static NoColourImpl s_instance; - return &s_instance; - } - }; - static bool shouldUseColour() { - return shouldUseColourForPlatform() && !isDebuggerActive(); - } - } - - Colour::Colour( Code _colourCode ){ use( _colourCode ); } - Colour::~Colour(){ use( None ); } - void Colour::use( Code _colourCode ) { - impl()->use( _colourCode ); - } - - Detail::IColourImpl* Colour::impl() { - return shouldUseColour() - ? platformColourInstance() - : NoColourImpl::instance(); - } - -} // 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( shouldNegate( 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_expressionresult_builder.hpp -#define TWOBLUECUBES_CATCH_EXPRESSIONRESULT_BUILDER_HPP_INCLUDED - -#include - -namespace Catch { - - ExpressionResultBuilder::ExpressionResultBuilder( ResultWas::OfType resultType ) { - m_data.resultType = resultType; - } - ExpressionResultBuilder::ExpressionResultBuilder( ExpressionResultBuilder const& other ) - : m_data( other.m_data ), - m_exprComponents( other.m_exprComponents ) - { - m_stream << other.m_stream.str(); - } - ExpressionResultBuilder& ExpressionResultBuilder::operator=(ExpressionResultBuilder const& other ) { - m_data = other.m_data; - m_exprComponents = other.m_exprComponents; - m_stream.str(""); - m_stream << other.m_stream.str(); - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::endExpression( ResultDisposition::Flags resultDisposition ) { - m_exprComponents.shouldNegate = shouldNegate( resultDisposition ); - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ExpressionResultBuilder& ExpressionResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - AssertionResult ExpressionResultBuilder::buildResult( AssertionInfo const& info ) const - { - assert( m_data.resultType != ResultWas::Unknown ); - - AssertionResultData data = m_data; - - // Flip bool results if shouldNegate is set - if( m_exprComponents.shouldNegate && data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( m_exprComponents.shouldNegate && data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - - data.message = m_stream.str(); - data.reconstructedExpression = reconstructExpression( info ); - if( m_exprComponents.shouldNegate ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } - return AssertionResult( info, data ); - } - std::string ExpressionResultBuilder::reconstructExpression( AssertionInfo const& info ) const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? info.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 " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}"; - } - -} // end namespace Catch - -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED - -namespace Catch { - - inline bool isSpecialTag( std::string const& tag ) { - return tag == "." || - tag == "hide" || - tag == "!hide" || - tag == "!throws"; - } - inline bool isReservedTag( std::string const& tag ) { - return !isSpecialTag( tag ) && tag.size() > 0 && !isalnum( tag[0] ); - } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - std::cerr - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - std::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 == ']' ) { - enforceNotReservedTag( tag, _lineInfo ); - - inTag = false; - if( tag == "hide" || tag == "." ) { - tags.insert( "hide" ); - tags.insert( "." ); - isHidden = true; - } - else { - tags.insert( tag ); - } - tag.clear(); - } - else - tag += c; - } - } - TestCaseInfo info( _name, _className, desc, tags, isHidden, _lineInfo ); - return TestCase( _testCase, info ); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - bool _isHidden, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - tags( _tags ), - lineInfo( _lineInfo ), - isHidden( _isHidden ), - throws( false ) - { - std::ostringstream oss; - for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; - if( *it == "!throws" ) - throws = true; - } - tagsAsString = oss.str(); - } - - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - isHidden( other.isHidden ), - throws( other.throws ) - {} - - 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::invoke() const { - test->invoke(); - } - - bool TestCase::isHidden() const { - return TestCaseInfo::isHidden; - } - bool TestCase::throws() const { - return TestCaseInfo::throws; - } - - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - className.swap( other.className ); - name.swap( other.name ); - description.swap( other.description ); - std::swap( lineInfo, other.lineInfo ); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } - -} // end namespace Catch - -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED - -namespace Catch { - - // These numbers are maintained by a script - Version libraryVersion( 1, 0, 45, "master" ); -} - -// #included from: catch_message.hpp -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED - -namespace Catch { - - MessageInfo::MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++globalCount ) - {} - - // This may need protecting if threading support is added - unsigned int MessageInfo::globalCount = 0; - - //////////////////////////////////////////////////////////////////////////// - - ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) - { - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage( m_info ); - } - ScopedMessage::~ScopedMessage() { - 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 ); - - 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 ) { - ExpressionResultBuilder expressionBuilder( it->type ); - expressionBuilder << it->message; - AssertionInfo info( it->macroName, it->lineInfo, "", ResultDisposition::Normal ); - AssertionResult result = expressionBuilder.buildResult( info ); - 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 ); - } -} - -// #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((LARGE_INTEGER*)&hz); - QueryPerformanceCounter((LARGE_INTEGER*)&hzo); - } - uint64_t t; - QueryPerformanceCounter((LARGE_INTEGER*)&t); - return ((t-hzo)*1000000)/hz; - } -#else - uint64_t getCurrentTicks() { - timeval t; - gettimeofday(&t,NULL); - return (uint64_t)t.tv_sec * 1000000ull + (uint64_t)t.tv_usec; - } -#endif - } - - void Timer::start() { - m_ticks = getCurrentTicks(); - } - unsigned int Timer::getElapsedNanoseconds() const { - return (unsigned int)(getCurrentTicks() - m_ticks); - } - unsigned int Timer::getElapsedMilliseconds() const { - return (unsigned int)((getCurrentTicks() - m_ticks)/1000); - } - double Timer::getElapsedSeconds() const { - return (getCurrentTicks() - m_ticks)/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 ) : ""; - } - - 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; - } - - 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( isTrue( true )) - throw std::logic_error( oss.str() ); - } -} - -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED - -namespace Catch { - - Section::Section( SourceLineInfo const& lineInfo, - std::string const& name, - std::string const& description ) - : m_info( name, description, lineInfo ), - m_sectionIncluded( getCurrentContext().getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - - Section::~Section() { - if( m_sectionIncluded ) - getCurrentContext().getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); - } - - // This indicates whether the section should be executed or not - Section::operator bool() { - return m_sectionIncluded; - } - -} // end namespace Catch - -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED - -#include - -#ifdef CATCH_PLATFORM_MAC - - #include - #include - #include - #include - #include - - namespace Catch{ - - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ - - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - // Call sysctl. - - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { - std::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 - std::cout << text; - } - } -#endif // Platform - -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED - -namespace Catch { - -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 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( int value ) { - std::ostringstream oss; - oss << value; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - if( value > 8192 ) - oss << "0x" << std::hex << value; - else - oss << value; - return oss.str(); -} - -std::string toString( unsigned int value ) { - return toString( static_cast( value ) ); -} - -std::string toString( const double value ) { - std::ostringstream oss; - oss << std::setprecision( 10 ) - << 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( bool value ) { - return value ? "true" : "false"; -} - -std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); -} - -std::string toString( signed char value ) { - return toString( static_cast( value ) ); -} - -std::string toString( unsigned char value ) { - return toString( static_cast( value ) ); -} - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; -} -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return std::string( "@\"" ) + [nsstring UTF8String] + "\""; - } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return std::string( "@\"" ) + [nsstring UTF8String] + "\""; - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif - -} // 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 - -namespace Catch { - - struct StreamingReporterBase : SharedImpl { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - - virtual ~StreamingReporterBase(); - - virtual void noMatchingTestCases( std::string const& ) {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { - currentTestCaseInfo.reset(); - assert( m_sectionStack.empty() ); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - }; - - struct CumulativeReporterBase : SharedImpl { - template - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); - } - - SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; - } - private: - BySectionInfo& operator=( BySectionInfo const& other ); // = delete; - - SectionInfo const& m_other; - }; - - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - ~CumulativeReporterBase(); - - virtual void testRunStarting( TestRunInfo const& ) {} - virtual void testGroupStarting( GroupInfo const& ) {} - - virtual void testCaseStarting( TestCaseInfo const& ) {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - 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; - - }; - -} // end namespace Catch - -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED - -namespace Catch { - - template - class LegacyReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; -} - -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = NULL; - } - - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } - - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer; - }; - - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &std::cout ) - {} - - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &os ) - {} - - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } - -# ifndef CATCH_CPP11_OR_GREATER - XmlWriter& operator = ( XmlWriter const& other ) { - XmlWriter temp( other ); - swap( temp ); - return *this; - } -# else - XmlWriter( XmlWriter const& ) = default; - XmlWriter( XmlWriter && ) = default; - XmlWriter& operator = ( XmlWriter const& ) = default; - XmlWriter& operator = ( XmlWriter && ) = default; -# endif - - void swap( XmlWriter& other ) { - std::swap( m_tagIsOpen, other.m_tagIsOpen ); - std::swap( m_needsNewline, other.m_needsNewline ); - std::swap( m_tags, other.m_tags ); - std::swap( m_indent, other.m_indent ); - std::swap( m_os, other.m_os ); - } - - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - stream() << m_indent << "<" << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - stream() << "/>\n"; - m_tagIsOpen = false; - } - else { - stream() << m_indent << "\n"; - } - m_tags.pop_back(); - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) { - stream() << " " << name << "=\""; - writeEncodedText( attribute ); - stream() << "\""; - } - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; - return *this; - } - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - if( !name.empty() ) - stream() << " " << name << "=\"" << attribute << "\""; - return *this; - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - stream() << m_indent; - writeEncodedText( text ); - m_needsNewline = true; - } - return *this; - } - - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - stream() << m_indent << ""; - m_needsNewline = true; - return *this; - } - - XmlWriter& writeBlankLine() { - ensureTagClosed(); - stream() << "\n"; - return *this; - } - - private: - - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } - } - - void newlineIfNecessary() { - if( m_needsNewline ) { - stream() << "\n"; - m_needsNewline = false; - } - } - - void writeEncodedText( std::string const& text ) { - static const char* charsToEncode = "<&\""; - std::string mtext = text; - std::string::size_type pos = mtext.find_first_of( charsToEncode ); - while( pos != std::string::npos ) { - stream() << mtext.substr( 0, pos ); - - switch( mtext[pos] ) { - case '<': - stream() << "<"; - break; - case '&': - stream() << "&"; - break; - case '\"': - stream() << """; - break; - } - mtext = mtext.substr( pos+1 ); - pos = mtext.find_first_of( charsToEncode ); - } - stream() << mtext; - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream* m_os; - }; - -} -namespace Catch { - class XmlReporter : public SharedImpl { - public: - XmlReporter( ReporterConfig const& config ) : m_config( config ), m_sectionDepth( 0 ) {} - - static std::string getDescription() { - return "Reports test results as an XML document"; - } - virtual ~XmlReporter(); - - private: // IReporter - - virtual bool shouldRedirectStdout() const { - return true; - } - - virtual void StartTesting() { - m_xml = XmlWriter( m_config.stream() ); - m_xml.startElement( "Catch" ); - if( !m_config.fullConfig()->name().empty() ) - m_xml.writeAttribute( "name", m_config.fullConfig()->name() ); - } - - virtual void EndTesting( const Totals& totals ) { - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", totals.assertions.passed ) - .writeAttribute( "failures", totals.assertions.failed ); - m_xml.endElement(); - } - - virtual void StartGroup( const std::string& groupName ) { - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupName ); - } - - virtual void EndGroup( const std::string&, const Totals& totals ) { - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", totals.assertions.passed ) - .writeAttribute( "failures", totals.assertions.failed ); - m_xml.endElement(); - } - - virtual void StartSection( const std::string& sectionName, const std::string& description ) { - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionName ) ) - .writeAttribute( "description", description ); - } - } - virtual void NoAssertionsInSection( const std::string& ) {} - virtual void NoAssertionsInTestCase( const std::string& ) {} - - virtual void EndSection( const std::string& /*sectionName*/, const Counts& assertions ) { - if( --m_sectionDepth > 0 ) { - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", assertions.passed ) - .writeAttribute( "failures", assertions.failed ); - m_xml.endElement(); - } - } - - virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) { - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); - m_currentTestSuccess = true; - } - - virtual void Result( const Catch::AssertionResult& assertionResult ) { - if( !m_config.fullConfig()->includeSuccessfulResults() && assertionResult.getResultType() == ResultWas::Ok ) - return; - - if( assertionResult.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); - - m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); - m_currentTestSuccess &= assertionResult.succeeded(); - } - - switch( assertionResult.getResultType() ) { - case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - m_currentTestSuccess = false; - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Warning: - m_xml.scopedElement( "Warning" ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); - m_currentTestSuccess = false; - break; - case ResultWas::Unknown: - case ResultWas::Ok: - case ResultWas::FailureBit: - case ResultWas::ExpressionFailed: - case ResultWas::Exception: - case ResultWas::DidntThrowException: - break; - } - if( assertionResult.hasExpression() ) - m_xml.endElement(); - } - - virtual void Aborted() { - // !TBD - } - - virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) { - m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess ); - m_xml.endElement(); - } - - private: - ReporterConfig m_config; - bool m_currentTestSuccess; - XmlWriter m_xml; - int m_sectionDepth; - }; - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED - -#include - -namespace Catch { - - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), - xml( _config.stream() ) - {} - - ~JunitReporter(); - - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } - - virtual void noMatchingTestCases( std::string const& /*spec*/ ) {} - - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = true; - return prefs; - } - - virtual void testRunStarting( TestRunInfo const& runInfo ) { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() { - xml.endElement(); - } - - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD - - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); - } - - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; - } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + "/" + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", 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: - 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 - -#include - -namespace Catch { - - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ), - m_atLeastOneTestCasePrinted( false ) - {} - - virtual ~ConsoleReporter(); - static std::string getDescription() { - return "Reports test results as plain lines of text"; - } - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - lazyPrint(); - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - stream << std::endl; - return true; - } - - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) { - if( _sectionStats.missingAssertions ) { - lazyPrint(); - Colour colour( Colour::ResultError ); - if( m_sectionStack.size() > 1 ) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - m_headerPrinted = false; - } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - } - StreamingReporterBase::sectionEnded( _sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { - if( currentGroupInfo.used ) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; - } - StreamingReporterBase::testGroupEnded( _testGroupStats ); - } - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - if( m_atLeastOneTestCasePrinted ) - printTotalsDivider(); - 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 ), - 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::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; - } - m_atLeastOneTestCasePrinted = true; - } - void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; - Colour colour( Colour::SecondaryText ); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion.majorVersion << "." - << libraryVersion.minorVersion << " b" - << libraryVersion.buildNumber; - if( libraryVersion.branchName != std::string( "master" ) ) - stream << " (" << libraryVersion.branchName << ")"; - stream << " host application.\n" - << "Run with -? for options\n\n"; - - 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"; - } - - void printTotals( const Totals& totals ) { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran"; - } - else if( totals.assertions.total() == 0 ) { - Colour colour( Colour::Yellow ); - printCounts( "test case", totals.testCases ); - stream << " (no assertions)"; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - printCounts( "test case", totals.testCases ); - if( totals.testCases.failed > 0 ) { - stream << " ("; - printCounts( "assertion", totals.assertions ); - stream << ")"; - } - } - else { - Colour colour( Colour::ResultSuccess ); - stream << "All tests passed (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")"; - } - } - void printCounts( std::string const& label, Counts const& counts ) { - if( counts.total() == 1 ) { - stream << "1 " << label << " - "; - if( counts.failed ) - stream << "failed"; - else - stream << "passed"; - } - else { - stream << counts.total() << " " << label << "s "; - if( counts.passed ) { - if( counts.failed ) - stream << "- " << counts.failed << " failed"; - else if( counts.passed == 2 ) - stream << "- both passed"; - else - stream << "- all passed"; - } - else { - if( counts.failed == 2 ) - stream << "- both failed"; - else - stream << "- all failed"; - } - } - } - - void printTotalsDivider() { - stream << getLineOfChars<'='>() << "\n"; - } - void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; - } - template - static 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; - } - - private: - bool m_headerPrinted; - bool m_atLeastOneTestCasePrinted; - }; - - 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::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 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 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 colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); - - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; - } - - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - }; - - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; - } - } - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) - -} // end namespace Catch - -namespace Catch { - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} - - void Config::dummy() {} - - INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif - -#ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED - -#ifndef __OBJC__ - -// Standard C/C++ main entry point -int main (int argc, char * const argv[]) { - return Catch::Session().run( argc, argv ); -} - -#else // __OBJC__ - -// Objective-C entry point -int main (int argc, char * const argv[]) { -#if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; -#endif - - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); - -#if !CATCH_ARC_ENABLED - [pool drain]; -#endif - - return result; -} - -#endif // __OBJC__ - -#endif - -#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED -# undef CLARA_CONFIG_MAIN -#endif - -////// - -// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ -#ifdef CATCH_CONFIG_PREFIX_ALL - -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::NegateResult, "CATCH_REQUIRE_FALSE" ) - -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) - -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::NegateResult, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) - -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) - -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) - -// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required -#else - -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::NegateResult, "REQUIRE_FALSE" ) - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::Normal, "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) - -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::NegateResult, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) - -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) - -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) -#else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -#endif - -#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( " Given: " desc, "" ) -#define WHEN( desc ) SECTION( " When: " desc, "" ) -#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) -#define THEN( desc ) SECTION( " Then: " desc, "" ) -#define AND_THEN( desc ) SECTION( " And: " desc, "" ) - -using Catch::Detail::Approx; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - diff --git a/third_party/variant/test/compilation_failure/default_constructor.cpp b/third_party/variant/test/compilation_failure/default_constructor.cpp new file mode 100644 index 000000000..c75a8c16a --- /dev/null +++ b/third_party/variant/test/compilation_failure/default_constructor.cpp @@ -0,0 +1,22 @@ + +// @EXPECTED: First type in variant must be default constructible to allow default construction of variant + +#include + +// Checks that the first type in a variant must be default constructible to +// make the variant default constructible. + +struct no_def_constructor +{ + + int value; + + no_def_constructor() = delete; + + no_def_constructor(int v) : value(v) {} +}; + +int main() +{ + mapbox::util::variant x; +} diff --git a/third_party/variant/test/compilation_failure/empty_typelist.cpp b/third_party/variant/test/compilation_failure/empty_typelist.cpp new file mode 100644 index 000000000..69a631c7b --- /dev/null +++ b/third_party/variant/test/compilation_failure/empty_typelist.cpp @@ -0,0 +1,11 @@ + +// @EXPECTED: Template parameter type list of variant can not be empty + +#include + +// Empty type list should not work. + +int main() +{ + mapbox::util::variant<> x; +} diff --git a/third_party/variant/test/compilation_failure/equality.cpp b/third_party/variant/test/compilation_failure/equality.cpp new file mode 100644 index 000000000..b99a3086e --- /dev/null +++ b/third_party/variant/test/compilation_failure/equality.cpp @@ -0,0 +1,11 @@ + +// @EXPECTED: + +#include + +int main() +{ + mapbox::util::variant x; + mapbox::util::variant y; + x == y; +} diff --git a/third_party/variant/test/compilation_failure/get_type.cpp b/third_party/variant/test/compilation_failure/get_type.cpp new file mode 100644 index 000000000..5123eb10d --- /dev/null +++ b/third_party/variant/test/compilation_failure/get_type.cpp @@ -0,0 +1,10 @@ + +// @EXPECTED: enable_if + +#include + +int main() +{ + mapbox::util::variant x; + x.get(); +} diff --git a/third_party/variant/test/compilation_failure/is_type.cpp b/third_party/variant/test/compilation_failure/is_type.cpp new file mode 100644 index 000000000..a79638e8d --- /dev/null +++ b/third_party/variant/test/compilation_failure/is_type.cpp @@ -0,0 +1,10 @@ + +// @EXPECTED: invalid type in T in `is()` for this variant + +#include + +int main() +{ + mapbox::util::variant x; + x.is(); +} diff --git a/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp b/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp new file mode 100644 index 000000000..ee77b5614 --- /dev/null +++ b/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp @@ -0,0 +1,24 @@ + +// @EXPECTED: const int + +#include + +struct mutating_visitor +{ + mutating_visitor(int val) + : val_(val) {} + + void operator()(int& val) const + { + val = val_; + } + + int val_; +}; + +int main() +{ + const mapbox::util::variant var(123); + const mutating_visitor visitor(456); + mapbox::util::apply_visitor(visitor, var); +} diff --git a/third_party/variant/test/compilation_failure/no-reference.cpp b/third_party/variant/test/compilation_failure/no-reference.cpp new file mode 100644 index 000000000..17123ce8b --- /dev/null +++ b/third_party/variant/test/compilation_failure/no-reference.cpp @@ -0,0 +1,9 @@ + +// @EXPECTED: Variant can not hold reference types + +#include + +int main() +{ + mapbox::util::variant x{mapbox::util::no_init()}; +} diff --git a/third_party/variant/test/include/catch.hpp b/third_party/variant/test/include/catch.hpp new file mode 100644 index 000000000..dd6e3ede8 --- /dev/null +++ b/third_party/variant/test/include/catch.hpp @@ -0,0 +1,11613 @@ +/* + * Catch v1.3.2 + * Generated: 2015-12-28 15:07:07.166291 + * ---------------------------------------------------------- + * 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) +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) + +#define INTERNAL_CATCH_STRINGIFY2(expr) #expr +#define INTERNAL_CATCH_STRINGIFY(expr) INTERNAL_CATCH_STRINGIFY2(expr) + +#include +#include +#include + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// 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? + +// **************** +// 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 + +#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 + +#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 + +// - 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 + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define 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 + +// 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_TESTCASE(...) \ + static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)(); \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); \ + } \ + static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)() + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ + namespace { \ + struct INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____) : ClassName \ + { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test, #ClassName, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test() + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ + Catch::AutoReg(Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); + +#else +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE(Name, Desc) \ + static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)(); \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc)); \ + } \ + static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)() + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, Name, Desc) \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(Name, Desc), CATCH_INTERNAL_LINEINFO); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, TestName, Desc) \ + namespace { \ + struct INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____) : ClassName \ + { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test, #ClassName, Catch::NameAndDesc(TestName, Desc), CATCH_INTERNAL_LINEINFO); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test() + +/////////////////////////////////////////////////////////////////////////////// +#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> 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 m_data.adjustString(expr).find(m_data.m_str) == 0; + } + 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 m_data.adjustString(expr).find(m_data.m_str) == expr.size() - m_data.m_str.size(); + } + 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 opCast(lhs) == opCast(rhs); + } +}; +template +struct Evaluator +{ + static bool evaluate(T1 const& lhs, T2 const& rhs) + { + return opCast(lhs) != opCast(rhs); + } +}; +template +struct Evaluator +{ + static bool evaluate(T1 const& lhs, T2 const& rhs) + { + return opCast(lhs) < opCast(rhs); + } +}; +template +struct Evaluator +{ + static bool evaluate(T1 const& lhs, T2 const& rhs) + { + return opCast(lhs) > opCast(rhs); + } +}; +template +struct Evaluator +{ + static bool evaluate(T1 const& lhs, T2 const& rhs) + { + return opCast(lhs) >= opCast(rhs); + } +}; +template +struct Evaluator +{ + static bool evaluate(T1 const& lhs, T2 const& rhs) + { + return opCast(lhs) <= opCast(rhs); + } +}; + +template +bool applyEvaluator(T1 const& lhs, T2 const& rhs) +{ + return Evaluator::evaluate(lhs, rhs); +} + +// This level of indirection allows us to specialise for integer types +// to avoid signed/ unsigned warnings + +// "base" overload +template +bool compare(T1 const& lhs, T2 const& rhs) +{ + return Evaluator::evaluate(lhs, rhs); +} + +// unsigned X to int +template +bool compare(unsigned int lhs, int rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} +template +bool compare(unsigned long lhs, int rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} +template +bool compare(unsigned char lhs, int rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} + +// unsigned X to long +template +bool compare(unsigned int lhs, long rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} +template +bool compare(unsigned long lhs, long rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} +template +bool compare(unsigned char lhs, long rhs) +{ + return applyEvaluator(lhs, static_cast(rhs)); +} + +// int to unsigned X +template +bool compare(int lhs, unsigned int rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} +template +bool compare(int lhs, unsigned long rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} +template +bool compare(int lhs, unsigned char rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} + +// long to unsigned X +template +bool compare(long lhs, unsigned int rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} +template +bool compare(long lhs, unsigned long rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} +template +bool compare(long lhs, unsigned char rhs) +{ + return applyEvaluator(static_cast(lhs), rhs); +} + +// pointer to long (when comparing against NULL) +template +bool compare(long lhs, T* rhs) +{ + return Evaluator::evaluate(reinterpret_cast(lhs), rhs); +} +template +bool compare(T* lhs, long rhs) +{ + return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); +} + +// pointer to int (when comparing against NULL) +template +bool compare(int lhs, T* rhs) +{ + return Evaluator::evaluate(reinterpret_cast(lhs), rhs); +} +template +bool compare(T* lhs, int rhs) +{ + return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); +} + +#ifdef CATCH_CONFIG_CPP11_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 \ + { \ + (__catchResult <= expr).endExpression(); \ + } \ + catch (...) \ + { \ + __catchResult.useActiveException(Catch::ResultDisposition::Normal); \ + } \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::isTrue(false && (expr))) // expr here is never evaluated at runtime but it forces the compiler to give it a look + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF(expr, resultDisposition, macroName) \ + INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ + if (Catch::getResultCapture().getLastResult()->succeeded()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE(expr, resultDisposition, macroName) \ + INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ + if (!Catch::getResultCapture().getLastResult()->succeeded()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW(expr, resultDisposition, macroName) \ + do \ + { \ + Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ + try \ + { \ + expr; \ + __catchResult.captureResult(Catch::ResultWas::Ok); \ + } \ + catch (...) \ + { \ + __catchResult.useActiveException(resultDisposition); \ + } \ + INTERNAL_CATCH_REACT(__catchResult) \ + } while (Catch::alwaysFalse()) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS(expr, resultDisposition, 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_EXCEPTION(signature) \ + static std::string INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)(signature); \ + namespace { \ + Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)); \ + } \ + static std::string INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)(signature) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +namespace Catch { +namespace Detail { + +class Approx +{ + public: + explicit Approx(double value) + : m_epsilon(std::numeric_limits::epsilon() * 100), + m_scale(1.0), + m_value(value) + { + } + + Approx(Approx const& other) + : m_epsilon(other.m_epsilon), + m_scale(other.m_scale), + m_value(other.m_value) + { + } + + static Approx custom() + { + return Approx(0); + } + + Approx operator()(double value) + { + Approx approx(value); + approx.epsilon(m_epsilon); + approx.scale(m_scale); + return approx; + } + + friend bool operator==(double lhs, Approx const& rhs) + { + // Thanks to Richard Harris for his help refining this formula + return fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(fabs(lhs), fabs(rhs.m_value))); + } + + friend bool operator==(Approx const& lhs, double rhs) + { + return operator==(rhs, lhs); + } + + friend bool operator!=(double lhs, Approx const& rhs) + { + return !operator==(lhs, rhs); + } + + friend bool operator!=(Approx const& lhs, double rhs) + { + return !operator==(rhs, lhs); + } + + Approx& epsilon(double newEpsilon) + { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale(double newScale) + { + m_scale = newScale; + return *this; + } + + std::string toString() const + { + std::ostringstream oss; + oss << "Approx( " << Catch::toString(m_value) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; +}; +} + +template <> +inline std::string toString(Detail::Approx const& value) +{ + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_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 + }; +}; + +class TestSpec; + +struct IConfig : IShared +{ + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual bool forceColour() const = 0; +}; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +// #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), + forceColour(false), + filenamesAsTags(false), + abortAfter(-1), + rngSeed(0), + verbosity(Verbosity::Normal), + warnings(WarnAbout::Nothing), + showDurations(ShowDurations::DefaultForReporter), + runOrder(RunTests::InDeclarationOrder) + { + } + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool forceColour; + bool filenamesAsTags; + + int abortAfter; + unsigned int rngSeed; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + + 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 bool forceColour() const { return m_data.forceColour; } + + 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 + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH +const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else +const unsigned int consoleWidth = 80; +#endif + +struct TextAttributes +{ + TextAttributes() + : initialIndent(std::string::npos), + indent(0), + width(consoleWidth - 1), + tabChar('\t') + { + } + + TextAttributes& setInitialIndent(std::size_t _value) + { + initialIndent = _value; + return *this; + } + TextAttributes& setIndent(std::size_t _value) + { + indent = _value; + return *this; + } + TextAttributes& setWidth(std::size_t _value) + { + width = _value; + return *this; + } + TextAttributes& setTabChar(char _value) + { + tabChar = _value; + return *this; + } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos +}; + +class Text +{ + public: + Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) + : attr(_attr) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while (!remainder.empty()) + { + if (lines.size() >= 1000) + { + lines.push_back("... message truncated due to excessive size"); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)(remainder.size(), _attr.width - indent); + std::size_t pos = remainder.find_first_of('\n'); + if (pos <= width) + { + width = pos; + } + pos = remainder.find_last_of(_attr.tabChar, width); + if (pos != std::string::npos) + { + tabPos = pos; + if (remainder[width] == '\n') + width--; + remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); + } + + if (width == remainder.size()) + { + spliceLine(indent, remainder, width); + } + else if (remainder[width] == '\n') + { + spliceLine(indent, remainder, width); + if (width <= 1 || remainder.size() != 1) + remainder = remainder.substr(1); + indent = _attr.indent; + } + else + { + pos = remainder.find_last_of(wrappableChars, width); + if (pos != std::string::npos && pos > 0) + { + spliceLine(indent, remainder, pos); + if (remainder[0] == ' ') + remainder = remainder.substr(1); + } + else + { + spliceLine(indent, remainder, width - 1); + lines.back() += "-"; + } + if (lines.size() == 1) + indent = _attr.indent; + if (tabPos != std::string::npos) + indent += tabPos; + } + } + } + + void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) + { + lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); + _remainder = _remainder.substr(_pos); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[](std::size_t _index) const { return lines[_index]; } + std::string toString() const + { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) + { + for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it) + { + if (it != _text.begin()) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; +}; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + +struct UnpositionalTag +{ +}; + +extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN +UnpositionalTag _; +#endif + +namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH +const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else +const unsigned int consoleWidth = 80; +#endif + +using namespace Tbc; + +inline bool startsWith(std::string const& str, std::string const& prefix) +{ + return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; +} + +template +struct RemoveConstRef +{ + typedef T type; +}; +template +struct RemoveConstRef +{ + typedef T type; +}; +template +struct RemoveConstRef +{ + typedef T type; +}; +template +struct RemoveConstRef +{ + typedef T type; +}; + +template +struct IsBool +{ + static const bool value = false; +}; +template <> +struct IsBool +{ + static const bool value = true; +}; + +template +void convertInto(std::string const& _source, T& _dest) +{ + std::stringstream ss; + ss << _source; + ss >> _dest; + if (ss.fail()) + throw std::runtime_error("Unable to convert " + _source + " to destination type"); +} +inline void convertInto(std::string const& _source, std::string& _dest) +{ + _dest = _source; +} +inline void convertInto(std::string const& _source, bool& _dest) +{ + std::string sourceLC = _source; + std::transform(sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower); + if (sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on") + _dest = true; + else if (sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off") + _dest = false; + else + throw std::runtime_error("Expected a boolean value but did not recognise:\n '" + _source + "'"); +} +inline void convertInto(bool _source, bool& _dest) +{ + _dest = _source; +} +template +inline void convertInto(bool, T&) +{ + throw std::runtime_error("Invalid conversion"); +} + +template +struct IArgFunction +{ + virtual ~IArgFunction() {} +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction(IArgFunction const&) = default; +#endif + virtual void set(ConfigT& config, std::string const& value) const = 0; + virtual void setFlag(ConfigT& config) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; +}; + +template +class BoundArgFunction +{ + public: + BoundArgFunction() : functionObj(CATCH_NULL) {} + BoundArgFunction(IArgFunction* _functionObj) : functionObj(_functionObj) {} + BoundArgFunction(BoundArgFunction const& other) : functionObj(other.functionObj ? other.functionObj->clone() : CATCH_NULL) {} + BoundArgFunction& operator=(BoundArgFunction const& other) + { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set(ConfigT& config, std::string const& value) const + { + functionObj->set(config, value); + } + void setFlag(ConfigT& config) const + { + functionObj->setFlag(config); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const + { + return functionObj != CATCH_NULL; + } + + private: + IArgFunction* functionObj; +}; + +template +struct NullBinder : IArgFunction +{ + virtual void set(C&, std::string const&) const {} + virtual void setFlag(C&) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder(*this); } +}; + +template +struct BoundDataMember : IArgFunction +{ + BoundDataMember(M C::*_member) : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const + { + convertInto(stringValue, p.*member); + } + virtual void setFlag(C& p) const + { + convertInto(true, p.*member); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember(*this); } + M C::*member; +}; +template +struct BoundUnaryMethod : IArgFunction +{ + BoundUnaryMethod(void (C::*_member)(M)) : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const + { + typename RemoveConstRef::type value; + convertInto(stringValue, value); + (p.*member)(value); + } + virtual void setFlag(C& p) const + { + typename RemoveConstRef::type value; + convertInto(true, value); + (p.*member)(value); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod(*this); } + void (C::*member)(M); +}; +template +struct BoundNullaryMethod : IArgFunction +{ + BoundNullaryMethod(void (C::*_member)()) : member(_member) {} + virtual void set(C& p, std::string const& stringValue) const + { + bool value; + convertInto(stringValue, value); + if (value) + (p.*member)(); + } + virtual void setFlag(C& p) const + { + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod(*this); } + void (C::*member)(); +}; + +template +struct BoundUnaryFunction : IArgFunction +{ + BoundUnaryFunction(void (*_function)(C&)) : function(_function) {} + virtual void set(C& obj, std::string const& stringValue) const + { + bool value; + convertInto(stringValue, value); + if (value) + function(obj); + } + virtual void setFlag(C& p) const + { + function(p); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction(*this); } + void (*function)(C&); +}; + +template +struct BoundBinaryFunction : IArgFunction +{ + BoundBinaryFunction(void (*_function)(C&, T)) : function(_function) {} + virtual void set(C& obj, std::string const& stringValue) const + { + typename RemoveConstRef::type value; + convertInto(stringValue, value); + function(obj, value); + } + virtual void setFlag(C& obj) const + { + typename RemoveConstRef::type value; + convertInto(true, value); + function(obj, value); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction(*this); } + void (*function)(C&, T); +}; + +} // namespace Detail + +struct Parser +{ + Parser() : separators(" \t=:") {} + + struct Token + { + enum Type + { + Positional, + ShortOpt, + LongOpt + }; + Token(Type _type, std::string const& _data) : type(_type), data(_data) {} + Type type; + std::string data; + }; + + void parseIntoTokens(int argc, char const* const* argv, std::vector& tokens) const + { + const std::string doubleDash = "--"; + for (int i = 1; i < argc && argv[i] != doubleDash; ++i) + parseIntoTokens(argv[i], tokens); + } + void parseIntoTokens(std::string arg, std::vector& tokens) const + { + while (!arg.empty()) + { + Parser::Token token(Parser::Token::Positional, arg); + arg = ""; + if (token.data[0] == '-') + { + if (token.data.size() > 1 && token.data[1] == '-') + { + token = Parser::Token(Parser::Token::LongOpt, token.data.substr(2)); + } + else + { + token = Parser::Token(Parser::Token::ShortOpt, token.data.substr(1)); + if (token.data.size() > 1 && separators.find(token.data[1]) == std::string::npos) + { + arg = "-" + token.data.substr(1); + token.data = token.data.substr(0, 1); + } + } + } + if (token.type != Parser::Token::Positional) + { + std::size_t pos = token.data.find_first_of(separators); + if (pos != std::string::npos) + { + arg = token.data.substr(pos + 1); + token.data = token.data.substr(0, pos); + } + } + tokens.push_back(token); + } + } + std::string separators; +}; + +template +struct CommonArgProperties +{ + CommonArgProperties() {} + CommonArgProperties(Detail::BoundArgFunction const& _boundField) : boundField(_boundField) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const + { + return !placeholder.empty(); + } + void validate() const + { + if (!boundField.isSet()) + throw std::logic_error("option not bound"); + } +}; +struct OptionArgProperties +{ + std::vector shortNames; + std::string longName; + + bool hasShortName(std::string const& shortName) const + { + return std::find(shortNames.begin(), shortNames.end(), shortName) != shortNames.end(); + } + bool hasLongName(std::string const& _longName) const + { + return _longName == longName; + } +}; +struct PositionalArgProperties +{ + PositionalArgProperties() : position(-1) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const + { + return position != -1; + } +}; + +template +class CommandLine +{ + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties + { + Arg() {} + Arg(Detail::BoundArgFunction const& _boundField) : CommonArgProperties(_boundField) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const + { + if (!longName.empty()) + return "--" + longName; + if (!shortNames.empty()) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const + { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for (; it != itEnd; ++it) + { + if (first) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if (!longName.empty()) + { + if (!first) + oss << ", "; + oss << "--" << longName; + } + if (!placeholder.empty()) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CATCH_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 usageText(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)(usageText.size(), desc.size()); ++i) + { + std::string usageCol = i < usageText.size() ? usageText[i] : ""; + os << usageCol; + + if (i < desc.size() && !desc[i].empty()) + os << std::string(indent + 2 + maxWidth - usageCol.size(), ' ') + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const + { + std::ostringstream oss; + optUsage(oss); + return oss.str(); + } + + void argSynopsis(std::ostream& os) const + { + for (int i = 1; i <= m_highestSpecifiedArgPosition; ++i) + { + if (i > 1) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find(i); + if (it != m_positionalArgs.end()) + os << "<" << it->second.placeholder << ">"; + else if (m_floatingArg.get()) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error("non consecutive positional arguments with no floating args"); + } + // !TBD No indication of mandatory args + if (m_floatingArg.get()) + { + if (m_highestSpecifiedArgPosition > 1) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const + { + std::ostringstream oss; + argSynopsis(oss); + return oss.str(); + } + + void usage(std::ostream& os, std::string const& procName) const + { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis(os); + if (!m_options.empty()) + { + os << " [options]\n\nwhere options are: \n"; + optUsage(os, 2); + } + os << "\n"; + } + std::string usage(std::string const& procName) const + { + std::ostringstream oss; + usage(oss, procName); + return oss.str(); + } + + ConfigT parse(int argc, char const* const* argv) const + { + ConfigT config; + parseInto(argc, argv, config); + return config; + } + + std::vector parseInto(int argc, char const* const* argv, ConfigT& config) const + { + std::string processName = argv[0]; + std::size_t lastSlash = processName.find_last_of("/\\"); + if (lastSlash != std::string::npos) + processName = processName.substr(lastSlash + 1); + m_boundProcessName.set(config, processName); + std::vector tokens; + Parser parser; + parser.parseIntoTokens(argc, argv, tokens); + return populate(tokens, config); + } + + std::vector populate(std::vector const& tokens, ConfigT& config) const + { + validate(); + std::vector unusedTokens = populateOptions(tokens, config); + unusedTokens = populateFixedArgs(unusedTokens, config); + unusedTokens = populateFloatingArgs(unusedTokens, config); + return unusedTokens; + } + + std::vector populateOptions(std::vector const& tokens, ConfigT& config) const + { + std::vector unusedTokens; + std::vector errors; + for (std::size_t i = 0; i < tokens.size(); ++i) + { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for (; it != itEnd; ++it) + { + Arg const& arg = *it; + + try + { + if ((token.type == Parser::Token::ShortOpt && arg.hasShortName(token.data)) || + (token.type == Parser::Token::LongOpt && arg.hasLongName(token.data))) + { + if (arg.takesArg()) + { + if (i == tokens.size() - 1 || tokens[i + 1].type != Parser::Token::Positional) + errors.push_back("Expected argument to option: " + token.data); + else + arg.boundField.set(config, tokens[++i].data); + } + else + { + arg.boundField.setFlag(config); + } + break; + } + } + catch (std::exception& ex) + { + errors.push_back(std::string(ex.what()) + "\n- while parsing: (" + arg.commands() + ")"); + } + } + if (it == itEnd) + { + if (token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens) + unusedTokens.push_back(token); + else if (errors.empty() && m_throwOnUnrecognisedTokens) + errors.push_back("unrecognised option: " + token.data); + } + } + if (!errors.empty()) + { + std::ostringstream oss; + for (std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it) + { + if (it != errors.begin()) + oss << "\n"; + oss << *it; + } + throw std::runtime_error(oss.str()); + } + return unusedTokens; + } + std::vector populateFixedArgs(std::vector const& tokens, ConfigT& config) const + { + std::vector unusedTokens; + int position = 1; + for (std::size_t i = 0; i < tokens.size(); ++i) + { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find(position); + if (it != m_positionalArgs.end()) + it->second.boundField.set(config, token.data); + else + unusedTokens.push_back(token); + if (token.type == Parser::Token::Positional) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs(std::vector const& tokens, ConfigT& config) const + { + if (!m_floatingArg.get()) + return tokens; + std::vector unusedTokens; + for (std::size_t i = 0; i < tokens.size(); ++i) + { + Parser::Token const& token = tokens[i]; + if (token.type == Parser::Token::Positional) + m_floatingArg->boundField.set(config, token.data); + else + unusedTokens.push_back(token); + } + return unusedTokens; + } + + void validate() const + { + if (m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get()) + throw std::logic_error("No options or arguments specified"); + + for (typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it) + it->validate(); + } + + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; +}; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include + +namespace Catch { + +inline void abortAfterFirst(ConfigData& config) { config.abortAfter = 1; } +inline void abortAfterX(ConfigData& config, int x) +{ + if (x < 1) + throw std::runtime_error("Value after -x or --abortAfter must be greater than zero"); + config.abortAfter = x; +} +inline void addTestOrTags(ConfigData& config, std::string const& _testSpec) { config.testsOrTags.push_back(_testSpec); } +inline void 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 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") + .bind(&ConfigData::forceColour); + + return cli; +} + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#endif +#else +#define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +#endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH +const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else +const unsigned int consoleWidth = 80; +#endif + +struct TextAttributes +{ + TextAttributes() + : initialIndent(std::string::npos), + indent(0), + width(consoleWidth - 1), + tabChar('\t') + { + } + + TextAttributes& setInitialIndent(std::size_t _value) + { + initialIndent = _value; + return *this; + } + TextAttributes& setIndent(std::size_t _value) + { + indent = _value; + return *this; + } + TextAttributes& setWidth(std::size_t _value) + { + width = _value; + return *this; + } + TextAttributes& setTabChar(char _value) + { + tabChar = _value; + return *this; + } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos +}; + +class Text +{ + public: + Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) + : attr(_attr) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while (!remainder.empty()) + { + if (lines.size() >= 1000) + { + lines.push_back("... message truncated due to excessive size"); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)(remainder.size(), _attr.width - indent); + std::size_t pos = remainder.find_first_of('\n'); + if (pos <= width) + { + width = pos; + } + pos = remainder.find_last_of(_attr.tabChar, width); + if (pos != std::string::npos) + { + tabPos = pos; + if (remainder[width] == '\n') + width--; + remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); + } + + if (width == remainder.size()) + { + spliceLine(indent, remainder, width); + } + else if (remainder[width] == '\n') + { + spliceLine(indent, remainder, width); + if (width <= 1 || remainder.size() != 1) + remainder = remainder.substr(1); + indent = _attr.indent; + } + else + { + pos = remainder.find_last_of(wrappableChars, width); + if (pos != std::string::npos && pos > 0) + { + spliceLine(indent, remainder, pos); + if (remainder[0] == ' ') + remainder = remainder.substr(1); + } + else + { + spliceLine(indent, remainder, width - 1); + lines.back() += "-"; + } + if (lines.size() == 1) + indent = _attr.indent; + if (tabPos != std::string::npos) + indent += tabPos; + } + } + } + + void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) + { + lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); + _remainder = _remainder.substr(_pos); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[](std::size_t _index) const { return lines[_index]; } + std::string toString() const + { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) + { + for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it) + { + if (it != _text.begin()) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; +}; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { +using Tbc::Text; +using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + +struct Colour +{ + enum Code + { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour(Code _colourCode); + Colour(Colour const& other); + ~Colour(); + + // Use static method for one-shot changes + static void use(Code _colourCode); + + private: + bool m_moved; +}; + +inline std::ostream& operator<<(std::ostream& os, Colour const&) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include +#include + +namespace Catch { +struct ReporterConfig +{ + explicit ReporterConfig(Ptr const& _fullConfig) + : m_stream(&_fullConfig->stream()), m_fullConfig(_fullConfig) {} + + ReporterConfig(Ptr const& _fullConfig, std::ostream& _stream) + : m_stream(&_stream), m_fullConfig(_fullConfig) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr m_fullConfig; +}; + +struct ReporterPreferences +{ + ReporterPreferences() + : shouldRedirectStdOut(false) + { + } + + bool shouldRedirectStdOut; +}; + +template +struct LazyStat : Option +{ + LazyStat() : used(false) {} + LazyStat& operator=(T const& _value) + { + Option::operator=(_value); + used = false; + return *this; + } + void reset() + { + Option::reset(); + used = false; + } + bool used; +}; + +struct TestRunInfo +{ + TestRunInfo(std::string const& _name) : name(_name) {} + std::string name; +}; +struct GroupInfo +{ + GroupInfo(std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount) + : name(_name), + groupIndex(_groupIndex), + groupsCounts(_groupsCount) + { + } + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; +}; + +struct AssertionStats +{ + AssertionStats(AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals) + : assertionResult(_assertionResult), + infoMessages(_infoMessages), + totals(_totals) + { + if (assertionResult.hasMessage()) + { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back(builder.m_info); + } + } + virtual ~AssertionStats(); + +#ifdef CATCH_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); + 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 argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail) + { + try + { + m_cli.setThrowOnUnrecognisedTokens(unusedOptionBehaviour == OnUnusedOptions::Fail); + m_unusedTokens = m_cli.parseInto(argc, argv, m_configData); + if (m_configData.showHelp) + showHelp(m_configData.processName); + m_config.reset(); + } + catch (std::exception& ex) + { + { + Colour colourGuard(Colour::Red); + Catch::cerr() + << "\nError(s) in input:\n" + << Text(ex.what(), TextAttributes().setIndent(2)) + << "\n\n"; + } + m_cli.usage(Catch::cout(), m_configData.processName); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData(ConfigData const& _configData) + { + m_configData = _configData; + m_config.reset(); + } + + int run(int argc, char const* 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 < j); } +}; +struct RandomNumberGenerator +{ + int operator()(int n) const { return std::rand() % n; } +}; + +inline std::vector 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; + return &s_instance; +} + +} // end anon namespace +} // end namespace Catch + +#elif defined(CATCH_CONFIG_COLOUR_ANSI) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + +// use POSIX/ ANSI console terminal codes +// Thanks to Adam Strzelecki for original contribution +// (http://github.com/nanoant) +// https://github.com/philsquared/Catch/pull/131 +class PosixColourImpl : public IColourImpl +{ + public: + virtual void use(Colour::Code _colourCode) + { + switch (_colourCode) + { + case Colour::None: + case Colour::White: + return setColour("[0m"); + case Colour::Red: + return setColour("[0;31m"); + case Colour::Green: + return setColour("[0;32m"); + case Colour::Blue: + return setColour("[0:34m"); + case Colour::Cyan: + return setColour("[0;36m"); + case Colour::Yellow: + return setColour("[0;33m"); + case Colour::Grey: + return setColour("[1;30m"); + + case Colour::LightGrey: + return setColour("[0;37m"); + case Colour::BrightRed: + return setColour("[1;31m"); + case Colour::BrightGreen: + return setColour("[1;32m"); + case Colour::BrightWhite: + return setColour("[1;37m"); + + case Colour::Bright: + throw std::logic_error("not a colour"); + } + } + static IColourImpl* instance() + { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour(const char* _escapeCode) + { + Catch::cout() << '\033' << _escapeCode; + } +}; + +IColourImpl* platformColourInstance() +{ + Ptr config = getCurrentContext().getConfig(); + return (config && config->forceColour()) || isatty(STDOUT_FILENO) + ? PosixColourImpl::instance() + : NoColourImpl::instance(); +} + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + +static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + +Colour::Colour(Code _colourCode) : m_moved(false) { use(_colourCode); } +Colour::Colour(Colour const& _other) : m_moved(false) { const_cast(_other).m_moved = true; } +Colour::~Colour() +{ + if (!m_moved) use(None); +} + +void Colour::use(Code _colourCode) +{ + static IColourImpl* impl = isDebuggerActive() + ? NoColourImpl::instance() + : platformColourInstance(); + impl->use(_colourCode); +} + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + +struct GeneratorInfo : IGeneratorInfo +{ + + GeneratorInfo(std::size_t size) + : m_size(size), + m_currentIndex(0) + { + } + + bool moveNext() + { + if (++m_currentIndex == m_size) + { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const + { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; +}; + +/////////////////////////////////////////////////////////////////////////// + +class GeneratorsForTest : public IGeneratorsForTest +{ + + public: + ~GeneratorsForTest() + { + deleteAll(m_generatorsInOrder); + } + + IGeneratorInfo& getGeneratorInfo(std::string const& fileInfo, std::size_t size) + { + std::map::const_iterator it = m_generatorsByName.find(fileInfo); + if (it == m_generatorsByName.end()) + { + IGeneratorInfo* info = new GeneratorInfo(size); + m_generatorsByName.insert(std::make_pair(fileInfo, info)); + m_generatorsInOrder.push_back(info); + return *info; + } + return *it->second; + } + + bool moveNext() + { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for (; it != itEnd; ++it) + { + if ((*it)->moveNext()) + return true; + } + return false; + } + + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; +}; + +IGeneratorsForTest* createGeneratorsForTest() +{ + return new GeneratorsForTest(); +} + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + +AssertionInfo::AssertionInfo(std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition) + : macroName(_macroName), + lineInfo(_lineInfo), + capturedExpression(_capturedExpression), + resultDisposition(_resultDisposition) +{ +} + +AssertionResult::AssertionResult() {} + +AssertionResult::AssertionResult(AssertionInfo const& info, AssertionResultData const& data) + : m_info(info), + m_resultData(data) +{ +} + +AssertionResult::~AssertionResult() {} + +// Result was a success +bool AssertionResult::succeeded() const +{ + return Catch::isOk(m_resultData.resultType); +} + +// Result was a success, or failure is suppressed +bool AssertionResult::isOk() const +{ + return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); +} + +ResultWas::OfType AssertionResult::getResultType() const +{ + return m_resultData.resultType; +} + +bool AssertionResult::hasExpression() const +{ + return !m_info.capturedExpression.empty(); +} + +bool AssertionResult::hasMessage() const +{ + return !m_resultData.message.empty(); +} + +std::string AssertionResult::getExpression() const +{ + if (isFalseTest(m_info.resultDisposition)) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; +} +std::string AssertionResult::getExpressionInMacro() const +{ + if (m_info.macroName.empty()) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; +} + +bool AssertionResult::hasExpandedExpression() const +{ + return hasExpression() && getExpandedExpression() != getExpression(); +} + +std::string AssertionResult::getExpandedExpression() const +{ + return m_resultData.reconstructedExpression; +} + +std::string AssertionResult::getMessage() const +{ + return m_resultData.message; +} +SourceLineInfo AssertionResult::getSourceInfo() const +{ + return m_info.lineInfo; +} + +std::string AssertionResult::getTestMacroName() const +{ + return m_info.macroName; +} + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + +inline TestCaseInfo::SpecialProperties parseSpecialTag(std::string const& tag) +{ + if (startsWith(tag, ".") || + tag == "hide" || + tag == "!hide") + return TestCaseInfo::IsHidden; + else if (tag == "!throws") + return TestCaseInfo::Throws; + else if (tag == "!shouldfail") + return TestCaseInfo::ShouldFail; + else if (tag == "!mayfail") + return TestCaseInfo::MayFail; + else + return TestCaseInfo::None; +} +inline bool isReservedTag(std::string const& tag) +{ + return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 && !isalnum(tag[0]); +} +inline void enforceNotReservedTag(std::string const& tag, SourceLineInfo const& _lineInfo) +{ + if (isReservedTag(tag)) + { + { + Colour colourGuard(Colour::Red); + Catch::cerr() + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard(Colour::FileName); + Catch::cerr() << _lineInfo << std::endl; + } + exit(1); + } +} + +TestCase makeTestCase(ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo) +{ + bool isHidden(startsWith(_name, "./")); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for (std::size_t i = 0; i < _descOrTags.size(); ++i) + { + char c = _descOrTags[i]; + if (!inTag) + { + if (c == '[') + inTag = true; + else + desc += c; + } + else + { + if (c == ']') + { + TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); + if (prop == TestCaseInfo::IsHidden) + isHidden = true; + else if (prop == TestCaseInfo::None) + enforceNotReservedTag(tag, _lineInfo); + + tags.insert(tag); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if (isHidden) + { + tags.insert("hide"); + tags.insert("."); + } + + TestCaseInfo info(_name, _className, desc, tags, _lineInfo); + return TestCase(_testCase, info); +} + +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, 3, 2, "", 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) + { + 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.assertions.allPassed()) + { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ")" + << "\n"; + } + else + { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } + } + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) + { + for (std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it) + { + std::string value = it->rows[row]; + if (it->label.empty()) + { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } + else if (value != "0") + { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(it->colour) + << value << " " << it->label; + } + } + stream << "\n"; + } + + static std::size_t makeRatio(std::size_t number, std::size_t total) + { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; + } + static std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) + { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; + } + + void printTotalsDivider(Totals const& totals) + { + if (totals.testCases.total() > 0) + { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } + else + { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << "\n"; + } + void printSummaryDivider() + { + stream << getLineOfChars<'-'>() << "\n"; + } + + private: + bool m_headerPrinted; +}; + +INTERNAL_CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + +struct CompactReporter : StreamingReporterBase +{ + + CompactReporter(ReporterConfig const& _config) + : StreamingReporterBase(_config) + { + } + + virtual ~CompactReporter(); + + static std::string getDescription() + { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const + { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases(std::string const& spec) + { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting(AssertionInfo const&) + { + } + + virtual bool assertionEnded(AssertionStats const& _assertionStats) + { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) + { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded(TestRunStats const& _testRunStats) + { + printTotals(_testRunStats.totals); + stream << "\n" + << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } + + private: + class AssertionPrinter + { + void operator=(AssertionPrinter const&); + + public: + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), stats(_stats), result(_stats.assertionResult), messages(_stats.infoMessages), itMessage(_stats.infoMessages.begin()), printInfoMessages(_printInfoMessages) + { + } + + void print() + { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) + { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() + { + return "FAILED"; + } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() + { + return "failed"; + } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const + { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ":"; + } + + void printResultType(Colour::Code colour, std::string passOrFail) const + { + if (!passOrFail.empty()) + { + { + Colour colourGuard(colour); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue(std::string issue) const + { + stream << " " << issue; + } + + void printExpressionWas() + { + if (result.hasExpression()) + { + stream << ";"; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const + { + if (result.hasExpression()) + { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const + { + if (result.hasExpandedExpression()) + { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() + { + if (itMessage != messages.end()) + { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) + { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ":"; + } + + for (; itMessage != itEnd;) + { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) + { + stream << " '" << itMessage->message << "'"; + if (++itMessage != itEnd) + { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll(std::size_t count) const + { + return count == 1 ? "" : count == 2 ? "both " : "all "; + } + + void printTotals(const Totals& totals) const + { + if (totals.testCases.total() == 0) + { + stream << "No tests ran."; + } + else if (totals.testCases.failed == totals.testCases.total()) + { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? bothOrAll(totals.assertions.failed) : ""; + stream << "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " + << qualify_assertions_failed << pluralise(totals.assertions.failed, "assertion") << "."; + } + else if (totals.assertions.total() == 0) + { + stream << "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } + else if (totals.assertions.failed) + { + Colour colour(Colour::ResultError); + stream << "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " + << pluralise(totals.assertions.failed, "assertion") << "."; + } + else + { + Colour colour(Colour::ResultSuccess); + stream << "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << " with " << pluralise(totals.assertions.passed, "assertion") << "."; + } + } +}; + +INTERNAL_CATCH_REGISTER_REPORTER("compact", CompactReporter) + +} // end namespace Catch + +namespace Catch { +// 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(...) INTERNAL_CATCH_REGISTER_TESTCASE(__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(...) INTERNAL_CATCH_REGISTER_TESTCASE(__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/third_party/variant/test/optional_unit.cpp b/third_party/variant/test/optional_unit.cpp deleted file mode 100644 index a6573ca41..000000000 --- a/third_party/variant/test/optional_unit.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#define CATCH_CONFIG_RUNNER -#include "catch.hpp" - -#include "optional.hpp" - -using namespace mapbox; - -struct dummy { - dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {} - int m_1; - int m_2; - -}; - -int main (int argc, char* const argv[]) -{ - int result = Catch::Session().run(argc, argv); - if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n"); - return result; -} - -TEST_CASE( "optional can be instantiated with a POD type", "[optiona]" ) { - mapbox::util::optional dbl_opt; - - REQUIRE(!dbl_opt); - dbl_opt = 3.1415; - REQUIRE(dbl_opt); - - REQUIRE(dbl_opt.get() == 3.1415); - REQUIRE(*dbl_opt == 3.1415); -} - -TEST_CASE( "copy c'tor", "[optiona]" ) { - mapbox::util::optional dbl_opt; - - REQUIRE(!dbl_opt); - dbl_opt = 3.1415; - REQUIRE(dbl_opt); - - mapbox::util::optional other = dbl_opt; - - REQUIRE(other.get() == 3.1415); - REQUIRE(*other == 3.1415); -} - -TEST_CASE( "const operator*, const get()", "[optiona]" ) { - mapbox::util::optional dbl_opt = 3.1415; - - REQUIRE(dbl_opt); - - const double pi1 = dbl_opt.get(); - const double pi2 = *dbl_opt; - - REQUIRE(pi1 == 3.1415); - REQUIRE(pi2 == 3.1415); -} - -TEST_CASE( "emplace initialization, reset", "[optional]" ) { - mapbox::util::optional dummy_opt; - REQUIRE(!dummy_opt); - - // rvalues, baby! - dummy_opt.emplace(1, 2); - REQUIRE(dummy_opt); - REQUIRE(dummy_opt.get().m_1 == 1); - REQUIRE((*dummy_opt).m_2 == 2); - - dummy_opt.reset(); - REQUIRE(!dummy_opt); -} - -TEST_CASE( "assignment", "[optional]") { - mapbox::util::optional a; - mapbox::util::optional b; - - a = 1; b = 3; - REQUIRE(a.get() == 1); - REQUIRE(b.get() == 3); - b = a; - REQUIRE(a.get() == b.get()); - REQUIRE(b.get() == 1); -} diff --git a/third_party/variant/test/our_variant_hello_world.cpp b/third_party/variant/test/our_variant_hello_world.cpp new file mode 100644 index 000000000..be5996b59 --- /dev/null +++ b/third_party/variant/test/our_variant_hello_world.cpp @@ -0,0 +1,20 @@ +#include "variant.hpp" + +#include + +struct check +{ + template + void operator()(T const& val) const + { + if (val != 0) throw std::runtime_error("invalid"); + } +}; + +int main() +{ + typedef mapbox::util::variant variant_type; + variant_type v(0); + mapbox::util::apply_visitor(check(), v); + return 0; +} diff --git a/third_party/variant/test/recursive_wrapper_test.cpp b/third_party/variant/test/recursive_wrapper_test.cpp index 3cd79b5ce..0492af4c8 100644 --- a/third_party/variant/test/recursive_wrapper_test.cpp +++ b/third_party/variant/test/recursive_wrapper_test.cpp @@ -1,11 +1,12 @@ + +#include #include -#include -#include #include -#include +#include #include -#include + #include + #include "variant.hpp" using namespace mapbox; @@ -14,48 +15,48 @@ namespace test { struct add; struct sub; -template struct binary_op; -typedef util::variant +struct binary_op; + +typedef util::variant>, - util::recursive_wrapper> - > expression; + util::recursive_wrapper>> + expression; template struct binary_op { - expression left; // variant instantiated here... + expression left; // variant instantiated here... expression right; - binary_op(expression && lhs, expression && rhs) + binary_op(expression&& lhs, expression&& rhs) : left(std::move(lhs)), right(std::move(rhs)) { } }; -struct print : util::static_visitor +struct print { template - void operator() (T const& val) const + void operator()(T const& val) const { std::cerr << val << ":" << typeid(T).name() << std::endl; } }; - -struct test : util::static_visitor +struct test { template - std::string operator() (T const& obj) const + std::string operator()(T const& obj) const { return std::string("TYPE_ID=") + typeid(obj).name(); } }; -struct calculator : public util::static_visitor +struct calculator { -public: - + public: int operator()(int value) const { return value; @@ -63,21 +64,18 @@ public: int operator()(binary_op const& binary) const { - return util::apply_visitor(calculator(), binary.left) - + util::apply_visitor(calculator(), binary.right); + return util::apply_visitor(calculator(), binary.left) + util::apply_visitor(calculator(), binary.right); } int operator()(binary_op const& binary) const { - return util::apply_visitor(calculator(), binary.left) - - util::apply_visitor(calculator(), binary.right); + return util::apply_visitor(calculator(), binary.left) - util::apply_visitor(calculator(), binary.right); } }; -struct to_string : public util::static_visitor +struct to_string { -public: - + public: std::string operator()(int value) const { return std::to_string(value); @@ -85,23 +83,19 @@ public: std::string operator()(binary_op const& binary) const { - return util::apply_visitor(to_string(), binary.left) + std::string("+") - + util::apply_visitor(to_string(), binary.right); + return util::apply_visitor(to_string(), binary.left) + std::string("+") + util::apply_visitor(to_string(), binary.right); } std::string operator()(binary_op const& binary) const { - return util::apply_visitor(to_string(), binary.left) + std::string("-") - + util::apply_visitor(to_string(), binary.right); + return util::apply_visitor(to_string(), binary.left) + std::string("-") + util::apply_visitor(to_string(), binary.right); } - }; } // namespace test -int main (int argc, char** argv) +int main(int argc, char** argv) { - if (argc != 2) { std::cerr << "Usage" << argv[0] << " " << std::endl; @@ -110,21 +104,20 @@ int main (int argc, char** argv) const std::size_t NUM_ITER = static_cast(std::stol(argv[1])); - test::expression result( - test::binary_op( - test::binary_op(2, 3), 4)); + test::expression sum(test::binary_op(2, 3)); + test::expression result(test::binary_op(std::move(sum), 4)); std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; + int total = 0; { boost::timer::auto_cpu_timer t; - int total = 0; for (std::size_t i = 0; i < NUM_ITER; ++i) { total += util::apply_visitor(test::calculator(), result); } - std::cerr << "total=" << total << std::endl; } + std::cerr << "total=" << total << std::endl; std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; diff --git a/third_party/variant/test/reference_wrapper_test.cpp b/third_party/variant/test/reference_wrapper_test.cpp index 2abf02784..dc1209fe6 100644 --- a/third_party/variant/test/reference_wrapper_test.cpp +++ b/third_party/variant/test/reference_wrapper_test.cpp @@ -1,11 +1,11 @@ +#include +#include #include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include + #include "variant.hpp" using namespace mapbox; @@ -14,15 +14,19 @@ namespace test { struct point { -public: - point (double x_, double y_) + public: + point(double x_, double y_) : x(x_), y(y_) {} double x; double y; }; -struct line_string : std::vector {}; -struct polygon : std::vector {}; +struct line_string : std::vector +{ +}; +struct polygon : std::vector +{ +}; using variant = util::variant, std::reference_wrapper, std::reference_wrapper>; @@ -30,11 +34,11 @@ using variant = util::variant, struct print { using result_type = void; - void operator() (point const& pt) const + void operator()(point const& pt) const { std::cerr << "Point(" << pt.x << "," << pt.y << ")" << std::endl; } - void operator() (line_string const& line) const + void operator()(line_string const& line) const { std::cerr << "Line("; for (auto const& pt : line) @@ -44,27 +48,25 @@ struct print std::cerr << ")" << std::endl; } template - void operator() (T const& val) const + void operator()(T const& val) const { std::cerr << typeid(T).name() << std::endl; } }; - - } -int main (int argc, char** argv) +int main() { std::cerr << sizeof(test::polygon) << std::endl; std::cerr << sizeof(test::variant) << std::endl; - test::point pt(123,456); - test::variant var = std::move(std::cref(pt)); + test::point pt(123, 456); + test::variant var = std::cref(pt); util::apply_visitor(test::print(), var); test::line_string line; line.push_back(pt); line.push_back(pt); - line.push_back(test::point(999,333)); - var = std::move(std::cref(line)); + line.push_back(test::point(999, 333)); + var = std::cref(line); util::apply_visitor(test::print(), var); std::cerr << "Is line (cref) ? " << var.is>() << std::endl; auto const& line2 = var.get(); // accessing underlying type of std::reference_wrapper diff --git a/third_party/variant/test/t/binary_visitor_1.cpp b/third_party/variant/test/t/binary_visitor_1.cpp new file mode 100644 index 000000000..298a40bc0 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_1.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " i-d" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_2.cpp b/third_party/variant/test/t/binary_visitor_2.cpp new file mode 100644 index 000000000..33768b601 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_2.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " b-i-d" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_3.cpp b/third_party/variant/test/t/binary_visitor_3.cpp new file mode 100644 index 000000000..d35af4e33 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_3.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " i-d-b" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_4.cpp b/third_party/variant/test/t/binary_visitor_4.cpp new file mode 100644 index 000000000..daacc1bb8 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_4.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " b-i-d-c" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_5.cpp b/third_party/variant/test/t/binary_visitor_5.cpp new file mode 100644 index 000000000..28669be07 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_5.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " b-i-c-d-i" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_6.cpp b/third_party/variant/test/t/binary_visitor_6.cpp new file mode 100644 index 000000000..c881b0f36 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_6.cpp @@ -0,0 +1,7 @@ + +#include "variant.hpp" + +#define NAME_EXT " b-i-i-d-c-u" +using variant_type = mapbox::util::variant; + +#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_impl.hpp b/third_party/variant/test/t/binary_visitor_impl.hpp new file mode 100644 index 000000000..4d9a43fe8 --- /dev/null +++ b/third_party/variant/test/t/binary_visitor_impl.hpp @@ -0,0 +1,204 @@ + +#include + +#include "catch.hpp" + +#include "variant_io.hpp" + +struct add_visitor +{ + add_visitor() {} + + template + double operator()(A a, B b) const + { + return a + b; + } +}; + +TEST_CASE("const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") +{ + const variant_type a{7}; + const variant_type b = 3; + const variant_type c{7.1}; + const variant_type d = 2.9; + + const add_visitor v; + + REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); + + REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); +} + +TEST_CASE("non-const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") +{ + const variant_type a = 7; + const variant_type b = 3; + const variant_type c = 7.1; + const variant_type d = 2.9; + + add_visitor v; + + REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); + + REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); +} + +TEST_CASE("const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") +{ + variant_type a = 7; + variant_type b = 3; + variant_type c = 7.1; + variant_type d = 2.9; + + const add_visitor v; + + REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); + + REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); +} + +TEST_CASE("non-const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") +{ + variant_type a = 7; + variant_type b = 3; + variant_type c = 7.1; + variant_type d = 2.9; + + add_visitor v; + + REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); + + REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); +} + +TEST_CASE("rvalue binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") +{ + const variant_type a = 7; + const variant_type b = 3; + const variant_type c = 7.1; + const variant_type d = 2.9; + + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, b) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, d) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, c) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, d) == Approx(9.9)); + + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, b, a) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, c) == Approx(10)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, a) == Approx(14.1)); + REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, a) == Approx(9.9)); +} + +struct sum_mul_visitor +{ + double sum; + + sum_mul_visitor() : sum(0.0) {} + + template + double operator()(A a, B b) + { + double m = a * b; + sum += m; + return m; + } +}; + +TEST_CASE("mutable binary visitor works" NAME_EXT, "[visitor][binary visitor]") +{ + const variant_type a = 2; + const variant_type b = 3; + const variant_type c = 0.1; + const variant_type d = 0.2; + + sum_mul_visitor v; + + REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(6)); + REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(0.02)); + REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(0.2)); + REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(0.4)); + + REQUIRE(v.sum == Approx(6.62)); + + REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(6)); + REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(0.02)); + REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(0.2)); + REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(0.4)); +} + +struct swap_visitor +{ + swap_visitor(){}; + + template + void operator()(A& a, B& b) const + { + using T = typename std::common_type::type; + T tmp = a; + a = b; + b = tmp; + } +}; + +TEST_CASE("static mutating visitor on mutable variants works" NAME_EXT, "[visitor][binary visitor]") +{ + variant_type a = 2; + variant_type b = 3; + variant_type c = 0.1; + variant_type d = 0.2; + + const swap_visitor v; + + SECTION("swap a and b") + { + mapbox::util::apply_visitor(v, a, b); + REQUIRE(a.get() == 3); + REQUIRE(b.get() == 2); + } + + SECTION("swap c and d") + { + mapbox::util::apply_visitor(v, c, d); + REQUIRE(c.get() == Approx(0.2)); + REQUIRE(d.get() == Approx(0.1)); + } + + SECTION("swap a and c") + { + mapbox::util::apply_visitor(v, a, c); + REQUIRE(a.get() == 0); + REQUIRE(c.get() == Approx(2.0)); + } + + SECTION("swap c and a") + { + mapbox::util::apply_visitor(v, c, a); + REQUIRE(a.get() == 0); + REQUIRE(c.get() == Approx(2.0)); + } +} diff --git a/third_party/variant/test/t/issue21.cpp b/third_party/variant/test/t/issue21.cpp new file mode 100644 index 000000000..b95231396 --- /dev/null +++ b/third_party/variant/test/t/issue21.cpp @@ -0,0 +1,48 @@ + +#include "catch.hpp" + +#include "variant.hpp" +#include "variant_io.hpp" + +// https://github.com/mapbox/variant/issues/21 + +static int count; + +struct t1 +{ + int value; + t1(int v) : value(v) + { + ++count; + } + ~t1() + { + --count; + } +}; + +struct t2 +{ + int value; + t2(int v) : value(v) + { // constructor fails + throw std::runtime_error("fail"); + } +}; + +TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]") +{ + + using variant_type = mapbox::util::variant; + + count = 0; + { + variant_type v{42}; + REQUIRE(v.is()); + REQUIRE(v.get().value == 42); + REQUIRE_THROWS({ + v.set(13); + }); + } + REQUIRE(count == 0); +} diff --git a/third_party/variant/test/t/mutating_visitor.cpp b/third_party/variant/test/t/mutating_visitor.cpp new file mode 100644 index 000000000..f07afb82a --- /dev/null +++ b/third_party/variant/test/t/mutating_visitor.cpp @@ -0,0 +1,36 @@ + +#include "catch.hpp" + +#include "variant.hpp" +#include "variant_io.hpp" + +#include + +template +struct mutating_visitor +{ + mutating_visitor(T& val) + : val_(val) {} + + void operator()(T& val) const + { + val = val_; + } + + template + void operator()(T1&) const + { + } // no-op + + T& val_; +}; + +TEST_CASE("variant visitation", "[visitor][unary visitor]") +{ + mapbox::util::variant var(123); + REQUIRE(var.get() == 123); + int val = 456; + const mutating_visitor visitor(val); + mapbox::util::apply_visitor(visitor, var); + REQUIRE(var.get() == 456); +} diff --git a/third_party/variant/test/t/optional.cpp b/third_party/variant/test/t/optional.cpp new file mode 100644 index 000000000..b77bedaa4 --- /dev/null +++ b/third_party/variant/test/t/optional.cpp @@ -0,0 +1,102 @@ + +#include "catch.hpp" + +#include "optional.hpp" + +struct dummy +{ + dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {} + int m_1; + int m_2; +}; + +TEST_CASE("optional can be instantiated with a POD type", "[optional]") +{ + mapbox::util::optional dbl_opt; + + REQUIRE(!dbl_opt); + dbl_opt = 3; + REQUIRE(dbl_opt); + + REQUIRE(dbl_opt.get() == 3); + REQUIRE(*dbl_opt == 3); +} + +TEST_CASE("copy c'tor", "[optional]") +{ + mapbox::util::optional dbl_opt; + + REQUIRE(!dbl_opt); + dbl_opt = 3; + REQUIRE(dbl_opt); + + mapbox::util::optional other = dbl_opt; + + REQUIRE(other.get() == 3); + REQUIRE(*other == 3); +} + +TEST_CASE("const operator*, const get()", "[optional]") +{ + const mapbox::util::optional dbl_opt = 3; + + REQUIRE(dbl_opt); + + auto pi1 = dbl_opt.get(); + auto pi2 = *dbl_opt; + + REQUIRE(pi1 == 3); + REQUIRE(pi2 == 3); +} + +TEST_CASE("non-const operator*, non-const get()", "[optional]") +{ + mapbox::util::optional dbl_opt = 3; + + REQUIRE(dbl_opt); + + auto pi1 = dbl_opt.get(); + auto pi2 = *dbl_opt; + + REQUIRE(pi1 == 3); + REQUIRE(pi2 == 3); +} + +TEST_CASE("emplace initialization, reset", "[optional]") +{ + mapbox::util::optional dummy_opt; + REQUIRE(!dummy_opt); + + // rvalues, baby! + dummy_opt.emplace(1, 2); + REQUIRE(dummy_opt); + REQUIRE(dummy_opt.get().m_1 == 1); + REQUIRE((*dummy_opt).m_2 == 2); + + dummy_opt.reset(); + REQUIRE(!dummy_opt); +} + +TEST_CASE("assignment", "[optional]") +{ + mapbox::util::optional a; + mapbox::util::optional b; + + a = 1; + b = 3; + REQUIRE(a.get() == 1); + REQUIRE(b.get() == 3); + b = a; + REQUIRE(a.get() == b.get()); + REQUIRE(b.get() == 1); +} + +TEST_CASE("self assignment", "[optional]") +{ + mapbox::util::optional a; + + a = 1; + REQUIRE(a.get() == 1); + a = a; + REQUIRE(a.get() == 1); +} diff --git a/third_party/variant/test/t/recursive_wrapper.cpp b/third_party/variant/test/t/recursive_wrapper.cpp new file mode 100644 index 000000000..b2dec455f --- /dev/null +++ b/third_party/variant/test/t/recursive_wrapper.cpp @@ -0,0 +1,158 @@ + +#include "catch.hpp" + +#include "recursive_wrapper.hpp" + +#include +#include + +using rwi = mapbox::util::recursive_wrapper; +using rwp = mapbox::util::recursive_wrapper>; + +static_assert(std::is_same::value, "type check failed"); + +TEST_CASE("recursive wrapper of int") +{ + + SECTION("construct with value") + { + rwi a{7}; + + REQUIRE(a.get() == 7); + REQUIRE(*a.get_pointer() == 7); + + a = 8; + REQUIRE(a.get() == 8); + + rwi b{a}; + REQUIRE(b.get() == 8); + + rwi c; + c = b; + REQUIRE(b.get() == 8); + REQUIRE(c.get() == 8); + + c = 9; + REQUIRE(c.get() == 9); + + int x = 10; + c = x; + REQUIRE(c.get() == 10); + + b = std::move(c); + REQUIRE(b.get() == 10); + } + + SECTION("construct with const reference") + { + int i = 7; + rwi a{i}; + + REQUIRE(a.get() == 7); + } + + SECTION("implicit conversion to reference of underlying type") + { + + SECTION("const") + { + rwi const a{7}; + REQUIRE(a.get() == 7); + REQUIRE(*a.get_pointer() == 7); + + rwi::type const& underlying = a; + REQUIRE(underlying == 7); + } + + SECTION("non const") + { + rwi a{7}; + REQUIRE(a.get() == 7); + REQUIRE(*a.get_pointer() == 7); + + rwi::type& underlying = a; + REQUIRE(underlying == 7); + a = 8; + REQUIRE(underlying == 8); + } + } +} + +TEST_CASE("move of recursive wrapper") +{ + rwi a{1}; + + SECTION("move constructor") + { + rwi b{std::move(a)}; + REQUIRE(b.get() == 1); + } + + SECTION("operator= on rvalue") + { + rwi b{2}; + b = std::move(a); + REQUIRE(b.get() == 1); + } +} + +TEST_CASE("swap") +{ + rwi a{1}; + rwi b{2}; + + REQUIRE(a.get() == 1); + REQUIRE(b.get() == 2); + + using std::swap; + swap(a, b); + + REQUIRE(a.get() == 2); + REQUIRE(b.get() == 1); +} + +TEST_CASE("recursive wrapper of pair") +{ + + SECTION("default constructed") + { + rwp a; + REQUIRE(a.get().first == 0); + REQUIRE(a.get().second == 0); + } + + SECTION("construct with value") + { + rwp a{std::make_pair(1, 2)}; + + REQUIRE(a.get().first == 1); + REQUIRE(a.get().second == 2); + + REQUIRE(a.get_pointer()->first == 1); + REQUIRE(a.get_pointer()->second == 2); + + a = {3, 4}; + REQUIRE(a.get().first == 3); + REQUIRE(a.get().second == 4); + + rwp b{a}; + REQUIRE(b.get().first == 3); + REQUIRE(b.get().second == 4); + + rwp c; + c = b; + REQUIRE(b.get().first == 3); + REQUIRE(b.get().second == 4); + REQUIRE(c.get().first == 3); + REQUIRE(c.get().second == 4); + + c = {5, 6}; + REQUIRE(c.get().first == 5); + REQUIRE(c.get().second == 6); + + b = std::move(c); + REQUIRE(b.get().first == 5); + REQUIRE(b.get().second == 6); + // REQUIRE(c.get_pointer() == nullptr); + } +} diff --git a/third_party/variant/test/t/sizeof.cpp b/third_party/variant/test/t/sizeof.cpp new file mode 100644 index 000000000..0e74ce58e --- /dev/null +++ b/third_party/variant/test/t/sizeof.cpp @@ -0,0 +1,52 @@ + +#include +#include +#include + +#include "catch.hpp" + +#include "variant.hpp" +#include "variant_io.hpp" + +struct some_struct +{ + int a; + bool b; + std::string c; +}; + +using variant_internal_index_type = size_t; + +TEST_CASE("size of variants") +{ + constexpr const auto min_overhead = sizeof(variant_internal_index_type); + + using namespace std; // workaround for bug in GCC <= 4.8 where max_align_t is not in std + constexpr const auto max_overhead = alignof(max_align_t) + min_overhead; + + using v1 = mapbox::util::variant; + using v2 = mapbox::util::variant; + using v3 = mapbox::util::variant; + using v4 = mapbox::util::variant; + using v5 = mapbox::util::variant; + + constexpr const auto si = sizeof(int); + constexpr const auto sb = sizeof(bool); + constexpr const auto si64 = sizeof(int64_t); + constexpr const auto sd = sizeof(double); + constexpr const auto sstr = sizeof(std::string); + constexpr const auto spi = sizeof(std::pair); + constexpr const auto ss = sizeof(some_struct); + + REQUIRE(sizeof(v1) <= max_overhead + si); + REQUIRE(sizeof(v2) <= max_overhead + std::max({si, sb, si64})); + REQUIRE(sizeof(v3) <= max_overhead + std::max({si, sstr})); + REQUIRE(sizeof(v4) <= max_overhead + sstr); + REQUIRE(sizeof(v5) <= max_overhead + ss); + + REQUIRE(sizeof(v1) >= min_overhead + si); + REQUIRE(sizeof(v2) >= min_overhead + std::max({si, sb, si64})); + REQUIRE(sizeof(v3) >= min_overhead + std::max({si, sstr})); + REQUIRE(sizeof(v4) >= min_overhead + sstr); + REQUIRE(sizeof(v5) >= min_overhead + ss); +} diff --git a/third_party/variant/test/t/unary_visitor.cpp b/third_party/variant/test/t/unary_visitor.cpp new file mode 100644 index 000000000..8df61104f --- /dev/null +++ b/third_party/variant/test/t/unary_visitor.cpp @@ -0,0 +1,127 @@ + +#include "catch.hpp" + +#include "variant.hpp" +#include "variant_io.hpp" + +#include + +struct some_visitor +{ + int var_; + + some_visitor(int init) + : var_(init) {} + + int operator()(int val) const + { + return var_ + val; + } + + int operator()(double val) const + { + return var_ + int(val); + } + + int operator()(const std::string&) const + { + return 0; + } +}; + +TEST_CASE("non-const visitor works on const variants", "[visitor][unary visitor]") +{ + using variant_type = const mapbox::util::variant; + variant_type var1(123); + variant_type var2(3.2); + variant_type var3("foo"); + REQUIRE(var1.get() == 123); + REQUIRE(var2.get() == Approx(3.2)); + REQUIRE(var3.get() == "foo"); + + some_visitor visitor{1}; + + REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); + REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); + REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); +} + +TEST_CASE("const visitor works on const variants", "[visitor][unary visitor]") +{ + using variant_type = const mapbox::util::variant; + variant_type var1(123); + variant_type var2(3.2); + variant_type var3("foo"); + REQUIRE(var1.get() == 123); + REQUIRE(var2.get() == Approx(3.2)); + REQUIRE(var3.get() == "foo"); + + const some_visitor visitor{1}; + + REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); + REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); + REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); +} + +TEST_CASE("rvalue visitor works on const variants", "[visitor][unary visitor]") +{ + using variant_type = const mapbox::util::variant; + variant_type var1(123); + variant_type var2(3.2); + variant_type var3("foo"); + REQUIRE(var1.get() == 123); + REQUIRE(var2.get() == Approx(3.2)); + REQUIRE(var3.get() == "foo"); + + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var1) == 124); + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var2) == 4); + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var3) == 0); +} + +TEST_CASE("visitor works on rvalue variants", "[visitor][unary visitor]") +{ + using variant_type = const mapbox::util::variant; + + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{123}) == 124); + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{3.2}) == 4); + REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{"foo"}) == 0); +} + +struct total_sizeof +{ + total_sizeof() : total_(0) {} + + template + int operator()(const Value&) const + { + total_ += int(sizeof(Value)); + return total_; + } + + int result() const + { + return total_; + } + + mutable int total_; + +}; // total_sizeof + +TEST_CASE("changes in visitor should be visible", "[visitor][unary visitor]") +{ + using variant_type = mapbox::util::variant; + variant_type v; + total_sizeof ts; + v = 5.9; + REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(double)); + REQUIRE(ts.result() == sizeof(double)); +} + +TEST_CASE("changes in const visitor (with mutable internals) should be visible", "[visitor][unary visitor]") +{ + using variant_type = const mapbox::util::variant; + variant_type v{"foo"}; + const total_sizeof ts; + REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(std::string)); + REQUIRE(ts.result() == sizeof(std::string)); +} diff --git a/third_party/variant/test/t/variant.cpp b/third_party/variant/test/t/variant.cpp new file mode 100644 index 000000000..36655a501 --- /dev/null +++ b/third_party/variant/test/t/variant.cpp @@ -0,0 +1,570 @@ +#include "catch.hpp" + +#include "variant.hpp" +#include "variant_io.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Hack to make nullptr work with Catch +namespace std { + +template +std::basic_ostream& operator<<(std::basic_ostream& os, std::nullptr_t) +{ + return os << (void*)nullptr; +} +} + +TEST_CASE("variant can be moved into vector", "[variant]") +{ + using variant_type = mapbox::util::variant; + variant_type v(std::string("test")); + std::vector vec; + vec.emplace_back(std::move(v)); + REQUIRE(v.get() != std::string("test")); + REQUIRE(vec.at(0).get() == std::string("test")); +} + +TEST_CASE("variant should support built-in types", "[variant]") +{ + SECTION("bool") + { + mapbox::util::variant v(true); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == true); + v.set(false); + REQUIRE(v.get() == false); + v = true; + REQUIRE(v == mapbox::util::variant(true)); + } + SECTION("nullptr") + { + using value_type = std::nullptr_t; + mapbox::util::variant v(nullptr); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == nullptr); + REQUIRE(v == mapbox::util::variant(nullptr)); + } + SECTION("unique_ptr") + { + using value_type = std::unique_ptr; + mapbox::util::variant v(value_type(new std::string("hello"))); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(*v.get().get() == *value_type(new std::string("hello")).get()); + REQUIRE(*v.get() == "hello"); + } + SECTION("string") + { + using value_type = std::string; + mapbox::util::variant v(value_type("hello")); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == value_type("hello")); + v.set(value_type("there")); + REQUIRE(v.get() == value_type("there")); + v = value_type("variant"); + REQUIRE(v == mapbox::util::variant(value_type("variant"))); + } + SECTION("size_t") + { + using value_type = std::size_t; + mapbox::util::variant v(std::numeric_limits::max()); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == std::numeric_limits::max()); + v.set(value_type(0)); + REQUIRE(v.get() == value_type(0)); + v = value_type(1); + REQUIRE(v == mapbox::util::variant(value_type(1))); + } + SECTION("int8_t") + { + using value_type = std::int8_t; + mapbox::util::variant v(std::numeric_limits::max()); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == std::numeric_limits::max()); + v.set(0); + REQUIRE(v.get() == value_type(0)); + v = value_type(1); + REQUIRE(v == mapbox::util::variant(value_type(1))); + } + SECTION("int16_t") + { + using value_type = std::int16_t; + mapbox::util::variant v(std::numeric_limits::max()); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == std::numeric_limits::max()); + v.set(0); + REQUIRE(v.get() == value_type(0)); + v = value_type(1); + REQUIRE(v == mapbox::util::variant(value_type(1))); + } + SECTION("int32_t") + { + using value_type = std::int32_t; + mapbox::util::variant v(std::numeric_limits::max()); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == std::numeric_limits::max()); + v.set(0); + REQUIRE(v.get() == value_type(0)); + v = value_type(1); + REQUIRE(v == mapbox::util::variant(value_type(1))); + } + SECTION("int64_t") + { + using value_type = std::int64_t; + mapbox::util::variant v(std::numeric_limits::max()); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == std::numeric_limits::max()); + v.set(0); + REQUIRE(v.get() == value_type(0)); + v = value_type(1); + REQUIRE(v == mapbox::util::variant(value_type(1))); + } +} + +struct MissionInteger +{ + using value_type = uint64_t; + value_type val_; + + public: + MissionInteger(uint64_t val) : val_(val) {} + + bool operator==(MissionInteger const& rhs) const + { + return (val_ == rhs.get()); + } + + uint64_t get() const + { + return val_; + } +}; + +std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs) +{ + os << rhs.get(); + return os; +} + +TEST_CASE("variant should support custom types", "[variant]") +{ + // http://www.missionintegers.com/integer/34838300 + mapbox::util::variant v(MissionInteger(34838300)); + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.which() == 0); + REQUIRE(v.get() == MissionInteger(34838300)); + REQUIRE(v.get().get() == MissionInteger::value_type(34838300)); + // TODO: should both of the set usages below compile? + v.set(MissionInteger::value_type(0)); + v.set(MissionInteger(0)); + REQUIRE(v.get().get() == MissionInteger::value_type(0)); + v = MissionInteger(1); + REQUIRE(v == mapbox::util::variant(MissionInteger(1))); +} + +TEST_CASE("variant::which() returns zero based index of stored type", "[variant]") +{ + using variant_type = mapbox::util::variant; + // which() returns index in forward order + REQUIRE(0 == variant_type(true).which()); + REQUIRE(1 == variant_type(std::string("test")).which()); + REQUIRE(2 == variant_type(std::uint64_t(0)).which()); + REQUIRE(3 == variant_type(std::int64_t(0)).which()); + REQUIRE(4 == variant_type(double(0.0)).which()); + REQUIRE(5 == variant_type(float(0.0)).which()); +} + +TEST_CASE("get with wrong type (here: double) should throw", "[variant]") +{ + using variant_type = mapbox::util::variant; + variant_type var = 5; + REQUIRE(var.is()); + REQUIRE_FALSE(var.is()); + REQUIRE(var.get() == 5); + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("get with wrong type (here: int) should throw", "[variant]") +{ + using variant_type = mapbox::util::variant; + variant_type var = 5.0; + REQUIRE(var.is()); + REQUIRE_FALSE(var.is()); + REQUIRE(var.get() == 5.0); + REQUIRE(mapbox::util::get(var) == 5.0); + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(var); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("get on const varint with wrong type (here: int) should throw", "[variant]") +{ + using variant_type = mapbox::util::variant; + const variant_type var = 5.0; + REQUIRE(var.is()); + REQUIRE_FALSE(var.is()); + REQUIRE(var.get() == 5.0); + REQUIRE(mapbox::util::get(var) == 5.0); + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(var); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("get with any type should throw if not initialized", "[variant]") +{ + mapbox::util::variant var{mapbox::util::no_init()}; + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("no_init variant can be copied and moved from", "[variant]") +{ + using variant_type = mapbox::util::variant; + + variant_type v1{mapbox::util::no_init()}; + variant_type v2{42}; + variant_type v3{23}; + + REQUIRE(v2.get() == 42); + v2 = v1; + REQUIRE_THROWS_AS({ + v2.get(); + }, + mapbox::util::bad_variant_access&); + + REQUIRE(v3.get() == 23); + v3 = std::move(v1); + REQUIRE_THROWS_AS({ + v3.get(); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("no_init variant can be copied and moved to", "[variant]") +{ + using variant_type = mapbox::util::variant; + + variant_type v1{42}; + variant_type v2{mapbox::util::no_init()}; + variant_type v3{mapbox::util::no_init()}; + + REQUIRE_THROWS_AS({ + v2.get(); + }, + mapbox::util::bad_variant_access&); + + REQUIRE(v1.get() == 42); + v2 = v1; + REQUIRE(v2.get() == 42); + REQUIRE(v1.get() == 42); + + REQUIRE_THROWS_AS({ + v3.get(); + }, + mapbox::util::bad_variant_access&); + + v3 = std::move(v1); + REQUIRE(v3.get() == 42); +} + +TEST_CASE("implicit conversion", "[variant][implicit conversion]") +{ + using variant_type = mapbox::util::variant; + variant_type var(5.0); // converted to int + REQUIRE(var.get() == 5); + var = 6.0; // works for operator=, too + REQUIRE(var.get() == 6); +} + +TEST_CASE("implicit conversion to first type in variant type list", "[variant][implicit conversion]") +{ + using variant_type = mapbox::util::variant; + variant_type var = 5.0; // converted to long + REQUIRE(var.get() == 5); + REQUIRE_THROWS_AS({ + var.get(); + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]") +{ + using variant_type = mapbox::util::variant; + variant_type var = 100.0; + CHECK(var.get() == static_cast(100.0)); + CHECK(var.get() == static_cast(static_cast(100.0))); +} + +struct dummy +{ +}; + +TEST_CASE("implicit conversion to a suitable type", "[variant][implicit conversion]") +{ + using mapbox::util::variant; + CHECK_NOTHROW((variant(123)).get()); + CHECK_NOTHROW((variant("foo")).get()); +} + +TEST_CASE("value_traits for non-convertible type", "[variant::detail]") +{ + namespace detail = mapbox::util::detail; + using target_type = detail::value_traits::target_type; + CHECK((std::is_same::value) == true); +} + +TEST_CASE("Type indexing should work with variants with duplicated types", "[variant::detail]") +{ + // Index is in reverse order + REQUIRE((mapbox::util::detail::value_traits::index == 3)); + REQUIRE((mapbox::util::detail::value_traits::index == 3)); + REQUIRE((mapbox::util::detail::value_traits::index == 2)); + REQUIRE((mapbox::util::detail::value_traits::index == 2)); + REQUIRE((mapbox::util::detail::value_traits::index == 1)); + REQUIRE((mapbox::util::detail::value_traits::index == 3)); + REQUIRE((mapbox::util::detail::value_traits::index == 0)); + REQUIRE((mapbox::util::detail::value_traits::index == mapbox::util::detail::invalid_value)); + REQUIRE((mapbox::util::detail::value_traits, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value)); +} + +TEST_CASE("variant default constructor", "[variant][default constructor]") +{ + // By default variant is initialised with (default constructed) first type in template parameters pack + // As a result first type in Types... must be default constructable + // NOTE: index in reverse order -> index = N - 1 + using variant_type = mapbox::util::variant; + REQUIRE(variant_type{}.which() == 0); + REQUIRE(variant_type{}.valid()); + REQUIRE_FALSE(variant_type{mapbox::util::no_init()}.valid()); +} + +TEST_CASE("variant printer", "[visitor][unary visitor][printer]") +{ + using variant_type = mapbox::util::variant; + std::vector var = {2.1, 123, "foo", 456}; + std::stringstream out; + std::copy(var.begin(), var.end(), std::ostream_iterator(out, ",")); + out << var[2]; + REQUIRE(out.str() == "2.1,123,foo,456,foo"); +} + +TEST_CASE("swapping variants should do the right thing", "[variant]") +{ + using variant_type = mapbox::util::variant; + variant_type a = 7; + variant_type b = 3; + variant_type c = 3.141; + variant_type d = "foo"; + variant_type e = "a long string that is longer than small string optimization"; + + using std::swap; + swap(a, b); + REQUIRE(a.get() == 3); + REQUIRE(a.which() == 0); + REQUIRE(b.get() == 7); + REQUIRE(b.which() == 0); + + swap(b, c); + REQUIRE(b.get() == Approx(3.141)); + REQUIRE(b.which() == 1); + REQUIRE(c.get() == 7); + REQUIRE(c.which() == 0); + + swap(b, d); + REQUIRE(b.get() == "foo"); + REQUIRE(b.which() == 2); + REQUIRE(d.get() == Approx(3.141)); + REQUIRE(d.which() == 1); + + swap(b, e); + REQUIRE(b.get() == "a long string that is longer than small string optimization"); + REQUIRE(b.which() == 2); + REQUIRE(e.get() == "foo"); + REQUIRE(e.which() == 2); +} + +TEST_CASE("variant should work with equality operators") +{ + using variant_type = mapbox::util::variant; + + variant_type a{1}; + variant_type b{1}; + variant_type c{2}; + variant_type s{"foo"}; + + REQUIRE(a == a); + REQUIRE(a == b); + REQUIRE_FALSE(a == c); + REQUIRE_FALSE(a == s); + REQUIRE_FALSE(c == s); + + REQUIRE_FALSE(a != a); + REQUIRE_FALSE(a != b); + REQUIRE(a != c); + REQUIRE(a != s); + REQUIRE(c != s); +} + +TEST_CASE("variant should work with comparison operators") +{ + using variant_type = mapbox::util::variant; + + variant_type a{1}; + variant_type b{1}; + variant_type c{2}; + variant_type s{"foo"}; + variant_type t{"bar"}; + + REQUIRE_FALSE(a < a); + REQUIRE_FALSE(a < b); + REQUIRE(a < c); + REQUIRE(a < s); + REQUIRE(c < s); + REQUIRE(t < s); + + REQUIRE_FALSE(a > a); + REQUIRE_FALSE(a > b); + REQUIRE_FALSE(a > c); + REQUIRE_FALSE(a > s); + REQUIRE_FALSE(c > s); + REQUIRE_FALSE(t > s); + + REQUIRE(a <= a); + REQUIRE(a <= b); + REQUIRE(a <= c); + REQUIRE(a <= s); + REQUIRE(c <= s); + REQUIRE(t <= s); + + REQUIRE(a >= a); + REQUIRE(a >= b); + REQUIRE_FALSE(a >= c); + REQUIRE_FALSE(a >= s); + REQUIRE_FALSE(c >= s); + REQUIRE_FALSE(t >= s); +} + +TEST_CASE("storing reference wrappers works") +{ + using variant_type = mapbox::util::variant, std::reference_wrapper>; + + int a = 1; + variant_type v{std::ref(a)}; + REQUIRE(v.get() == 1); + REQUIRE(mapbox::util::get(v) == 1); + REQUIRE_THROWS_AS({ + v.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(v); + }, + mapbox::util::bad_variant_access&); + a = 2; + REQUIRE(v.get() == 2); + v.get() = 3; + REQUIRE(a == 3); + + double b = 3.141; + v = std::ref(b); + REQUIRE(v.get() == Approx(3.141)); + REQUIRE(mapbox::util::get(v) == Approx(3.141)); + REQUIRE_THROWS_AS({ + v.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(v); + }, + mapbox::util::bad_variant_access&); + b = 2.718; + REQUIRE(v.get() == Approx(2.718)); + a = 3; + REQUIRE(v.get() == Approx(2.718)); + v.get() = 4.1; + REQUIRE(b == Approx(4.1)); + + REQUIRE_THROWS_AS({ + v.get() = 4; + }, + mapbox::util::bad_variant_access&); +} + +TEST_CASE("storing reference wrappers to consts works") +{ + using variant_type = mapbox::util::variant, std::reference_wrapper>; + + int a = 1; + variant_type v{std::cref(a)}; + REQUIRE(v.get() == 1); + REQUIRE(v.get() == 1); // this works (see #82) + REQUIRE(mapbox::util::get(v) == 1); + // REQUIRE(mapbox::util::get(v) == 1); // this doesn't work (see #82) + REQUIRE_THROWS_AS({ + v.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(v); + }, + mapbox::util::bad_variant_access&); + + double b = 3.141; + v = std::cref(b); + REQUIRE(v.get() == Approx(3.141)); + REQUIRE(mapbox::util::get(v) == Approx(3.141)); + REQUIRE_THROWS_AS({ + v.get(); + }, + mapbox::util::bad_variant_access&); + REQUIRE_THROWS_AS({ + mapbox::util::get(v); + }, + mapbox::util::bad_variant_access&); +} diff --git a/third_party/variant/test/unique_ptr_test.cpp b/third_party/variant/test/unique_ptr_test.cpp index 2a51efef0..6578991ca 100644 --- a/third_party/variant/test/unique_ptr_test.cpp +++ b/third_party/variant/test/unique_ptr_test.cpp @@ -1,12 +1,13 @@ + +#include #include -#include -#include +#include #include -#include +#include #include -#include -#include + #include + #include "variant.hpp" using namespace mapbox; @@ -15,46 +16,48 @@ namespace test { struct add; struct sub; -template struct binary_op; -typedef util::variant +struct binary_op; + +typedef util::variant>, - std::unique_ptr> - > expression; + std::unique_ptr>> + expression; template struct binary_op { - expression left; // variant instantiated here... + expression left; // variant instantiated here... expression right; - binary_op(expression && lhs, expression && rhs) - : left(std::move(lhs)), right(std::move(rhs)) {} + binary_op(expression&& lhs, expression&& rhs) + : left(std::move(lhs)), right(std::move(rhs)) + { + } }; -struct print : util::static_visitor +struct print { template - void operator() (T const& val) const + void operator()(T const& val) const { std::cerr << val << ":" << typeid(T).name() << std::endl; } }; - -struct test : util::static_visitor +struct test { template - std::string operator() (T const& obj) const + std::string operator()(T const& obj) const { return std::string("TYPE_ID=") + typeid(obj).name(); } }; -struct calculator : public util::static_visitor +struct calculator { -public: - + public: int operator()(int value) const { return value; @@ -62,21 +65,18 @@ public: int operator()(std::unique_ptr> const& binary) const { - return util::apply_visitor(calculator(), binary->left) - + util::apply_visitor(calculator(), binary->right); + return util::apply_visitor(calculator(), binary->left) + util::apply_visitor(calculator(), binary->right); } int operator()(std::unique_ptr> const& binary) const { - return util::apply_visitor(calculator(), binary->left) - - util::apply_visitor(calculator(), binary->right); + return util::apply_visitor(calculator(), binary->left) - util::apply_visitor(calculator(), binary->right); } }; -struct to_string : public util::static_visitor +struct to_string { -public: - + public: std::string operator()(int value) const { return std::to_string(value); @@ -84,21 +84,18 @@ public: std::string operator()(std::unique_ptr> const& binary) const { - return util::apply_visitor(to_string(), binary->left) + std::string("+") - + util::apply_visitor(to_string(), binary->right); + return util::apply_visitor(to_string(), binary->left) + std::string("+") + util::apply_visitor(to_string(), binary->right); } std::string operator()(std::unique_ptr> const& binary) const { - return util::apply_visitor(to_string(), binary->left) + std::string("-") - + util::apply_visitor(to_string(), binary->right); + return util::apply_visitor(to_string(), binary->left) + std::string("-") + util::apply_visitor(to_string(), binary->right); } - }; } // namespace test -int main (int argc, char** argv) +int main(int argc, char** argv) { if (argc != 2) { @@ -110,17 +107,18 @@ int main (int argc, char** argv) test::expression sum(std::unique_ptr>(new test::binary_op(2, 3))); test::expression result(std::unique_ptr>(new test::binary_op(std::move(sum), 4))); + std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; + int total = 0; { boost::timer::auto_cpu_timer t; - int total = 0; for (std::size_t i = 0; i < NUM_ITER; ++i) { total += util::apply_visitor(test::calculator(), result); } - std::cerr << "total=" << total << std::endl; } + std::cerr << "total=" << total << std::endl; std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; diff --git a/third_party/variant/test/unit.cpp b/third_party/variant/test/unit.cpp index 9b874c707..0c7c351f4 100644 --- a/third_party/variant/test/unit.cpp +++ b/third_party/variant/test/unit.cpp @@ -1,314 +1,2 @@ -#define CATCH_CONFIG_RUNNER +#define CATCH_CONFIG_MAIN #include "catch.hpp" - -#include "variant.hpp" -#include "variant_io.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace mapbox; - -template -struct mutating_visitor -{ - mutating_visitor(T & val) - : val_(val) {} - - void operator() (T & val) const - { - val = val_; - } - - template - void operator() (T1& ) const {} // no-op - - T & val_; -}; - - - -TEST_CASE( "variant version", "[variant]" ) { - unsigned int version = VARIANT_VERSION; - REQUIRE(version == 100); - #if VARIANT_VERSION == 100 - REQUIRE(true); - #else - REQUIRE(false); - #endif -} - -TEST_CASE( "variant can be moved into vector", "[variant]" ) { - typedef util::variant variant_type; - variant_type v(std::string("test")); - std::vector vec; - vec.emplace_back(std::move(v)); - REQUIRE(v.get() != std::string("test")); - REQUIRE(vec.at(0).get() == std::string("test")); -} - -TEST_CASE( "variant should support built-in types", "[variant]" ) { - SECTION( "bool" ) { - util::variant v(true); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == true); - v.set(false); - REQUIRE(v.get() == false); - v = true; - REQUIRE(v == util::variant(true)); - } - SECTION( "nullptr" ) { - typedef std::nullptr_t value_type; - util::variant v(nullptr); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - // TODO: commented since it breaks on windows: 'operator << is ambiguous' - //REQUIRE(v.get() == nullptr); - // FIXME: does not compile: ./variant.hpp:340:14: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::__1::basic_ostream' and 'const nullptr_t') - // https://github.com/mapbox/variant/issues/14 - //REQUIRE(v == util::variant(nullptr)); - } - SECTION( "unique_ptr" ) { - typedef std::unique_ptr value_type; - util::variant v(value_type(new std::string("hello"))); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(*v.get().get() == *value_type(new std::string("hello")).get()); - } - SECTION( "string" ) { - typedef std::string value_type; - util::variant v(value_type("hello")); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == value_type("hello")); - v.set(value_type("there")); - REQUIRE(v.get() == value_type("there")); - v = value_type("variant"); - REQUIRE(v == util::variant(value_type("variant"))); - } - SECTION( "size_t" ) { - typedef std::size_t value_type; - util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(value_type(0)); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == util::variant(value_type(1))); - } - SECTION( "int8_t" ) { - typedef std::int8_t value_type; - util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == util::variant(value_type(1))); - } - SECTION( "int16_t" ) { - typedef std::int16_t value_type; - util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == util::variant(value_type(1))); - } - SECTION( "int32_t" ) { - typedef std::int32_t value_type; - util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == util::variant(value_type(1))); - } - SECTION( "int64_t" ) { - typedef std::int64_t value_type; - util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == util::variant(value_type(1))); - } -} - -struct MissionInteger -{ - typedef uint64_t value_type; - value_type val_; - public: - MissionInteger(uint64_t val) : - val_(val) {} - - bool operator==(MissionInteger const& rhs) const - { - return (val_ == rhs.get()); - } - - uint64_t get() const - { - return val_; - } -}; - -// TODO - remove after https://github.com/mapbox/variant/issues/14 -std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs) -{ - os << rhs.get(); - return os; -} - -TEST_CASE( "variant should support custom types", "[variant]" ) { - // http://www.missionintegers.com/integer/34838300 - util::variant v(MissionInteger(34838300)); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.get_type_index() == 0); - REQUIRE(v.get() == MissionInteger(34838300)); - REQUIRE(v.get().get() == MissionInteger::value_type(34838300)); - // TODO: should both of the set usages below compile? - v.set(MissionInteger::value_type(0)); - v.set(MissionInteger(0)); - REQUIRE(v.get().get() == MissionInteger::value_type(0)); - v = MissionInteger(1); - REQUIRE(v == util::variant(MissionInteger(1))); -} - -// Test internal api -TEST_CASE( "variant should correctly index types", "[variant]" ) { - typedef util::variant variant_type; - // Index is in reverse order - REQUIRE(variant_type(true).get_type_index() == 5); - REQUIRE(variant_type(std::string("test")).get_type_index() == 4); - REQUIRE(variant_type(std::uint64_t(0)).get_type_index() == 3); - REQUIRE(variant_type(std::int64_t(0)).get_type_index() == 2); - REQUIRE(variant_type(double(0.0)).get_type_index() == 1); - REQUIRE(variant_type(float(0.0)).get_type_index() == 0); -} - -// Test internal api -TEST_CASE( "variant::which() returns zero based index of stored type", "[variant]" ) { - typedef util::variant variant_type; - // Index is in reverse order - REQUIRE(variant_type(true).which() == 0); - REQUIRE(variant_type(std::string("test")).which() == 1); - REQUIRE(variant_type(std::uint64_t(0)).which() == 2); - REQUIRE(variant_type(std::int64_t(0)).which() == 3); - REQUIRE(variant_type(double(0.0)).which() == 4); - REQUIRE(variant_type(float(0.0)).which() == 5); -} - -TEST_CASE( "get with type not in variant type list should throw", "[variant]" ) { - typedef util::variant variant_type; - variant_type var = 5; - REQUIRE(var.get() == 5); -} - -TEST_CASE( "get with wrong type (here: double) should throw", "[variant]" ) { - typedef util::variant variant_type; - variant_type var = 5; - REQUIRE(var.get() == 5); - REQUIRE_THROWS(var.get()); -} - -TEST_CASE( "get with wrong type (here: int) should throw", "[variant]" ) { - typedef util::variant variant_type; - variant_type var = 5.0; - REQUIRE(var.get() == 5.0); - REQUIRE_THROWS(var.get()); -} - -TEST_CASE( "implicit conversion", "[variant][implicit conversion]" ) { - typedef util::variant variant_type; - variant_type var(5.0); // converted to int - REQUIRE(var.get() == 5); - var = 6.0; // works for operator=, too - REQUIRE(var.get() == 6); -} - -TEST_CASE( "implicit conversion to first type in variant type list", "[variant][implicit conversion]" ) { - typedef util::variant variant_type; - variant_type var = 5.0; // converted to long - REQUIRE(var.get() == 5); - REQUIRE_THROWS(var.get()); -} - -TEST_CASE( "implicit conversion to unsigned char", "[variant][implicit conversion]" ) { - typedef util::variant variant_type; - variant_type var = 100.0; - CHECK(var.get() == static_cast(100.0)); - CHECK(var.get() == static_cast(static_cast(100.0))); -} - -struct dummy {}; - -TEST_CASE( "variant value traits", "[variant::detail]" ) { - // Users should not create variants with duplicated types - // however our type indexing should still work - // Index is in reverse order - REQUIRE((util::detail::value_traits::index == 3)); - REQUIRE((util::detail::value_traits::index == 2)); - REQUIRE((util::detail::value_traits::index == 1)); - REQUIRE((util::detail::value_traits::index == 0)); - REQUIRE((util::detail::value_traits::index == util::detail::invalid_value)); - REQUIRE((util::detail::value_traits, bool, int, double, std::string>::index == util::detail::invalid_value)); -} - -TEST_CASE( "variant default constructor", "[variant][default constructor]" ) { - // By default variant is initialised with (default constructed) first type in template parameters pack - // As a result first type in Types... must be default constructable - // NOTE: index in reverse order -> index = N - 1 - REQUIRE((util::variant().get_type_index() == 2)); - REQUIRE((util::variant(util::no_init()).get_type_index() == util::detail::invalid_value)); -} - -TEST_CASE( "variant visitation", "[visitor][unary visitor]" ) { - util::variant var(123); - REQUIRE(var.get() == 123); - int val = 456; - mutating_visitor visitor(val); - util::apply_visitor(visitor, var); - REQUIRE(var.get() == 456); -} - -TEST_CASE( "variant printer", "[visitor][unary visitor][printer]" ) { - typedef util::variant variant_type; - std::vector var = {2.1, 123, "foo", 456}; - std::stringstream out; - std::copy(var.begin(), var.end(), std::ostream_iterator(out, ",")); - out << var[2]; - REQUIRE(out.str() == "2.1,123,foo,456,foo"); -} - -int main (int argc, char* const argv[]) -{ - int result = Catch::Session().run(argc, argv); - if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n"); - return result; -} diff --git a/third_party/variant/test/variant_hello_world.cpp b/third_party/variant/test/variant_hello_world.cpp deleted file mode 100644 index b445fdbab..000000000 --- a/third_party/variant/test/variant_hello_world.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "variant.hpp" -#include -#include - -using namespace mapbox; - -struct check : util::static_visitor<> -{ - template - void operator() (T const& val) const - { - if (val != 0) throw std::runtime_error("invalid"); - } -}; - - -int main() { - typedef util::variant variant_type; - variant_type v(0); - util::apply_visitor(check(), v); - return 0; -} diff --git a/third_party/variant/variant.gyp b/third_party/variant/variant.gyp index 712cafed1..b1f3801f2 100644 --- a/third_party/variant/variant.gyp +++ b/third_party/variant/variant.gyp @@ -7,15 +7,29 @@ "target_name": "tests", "type": "executable", "sources": [ - "test/unit.cpp" + "test/unit.cpp", + "test/t/binary_visitor_1.cpp", + "test/t/binary_visitor_2.cpp", + "test/t/binary_visitor_3.cpp", + "test/t/binary_visitor_4.cpp", + "test/t/binary_visitor_5.cpp", + "test/t/binary_visitor_6.cpp", + "test/t/issue21.cpp", + "test/t/mutating_visitor.cpp", + "test/t/optional.cpp", + "test/t/recursive_wrapper.cpp", + "test/t/sizeof.cpp", + "test/t/unary_visitor.cpp", + "test/t/variant.cpp" ], "xcode_settings": { "SDKROOT": "macosx", "SUPPORTED_PLATFORMS":["macosx"] }, "include_dirs": [ - "./" + "./", + "test/include" ] } ] -} \ No newline at end of file +} diff --git a/third_party/variant/variant.hpp b/third_party/variant/variant.hpp index 90aacae00..db5d3c86b 100644 --- a/third_party/variant/variant.hpp +++ b/third_party/variant/variant.hpp @@ -1,19 +1,35 @@ #ifndef MAPBOX_UTIL_VARIANT_HPP #define MAPBOX_UTIL_VARIANT_HPP -#include -#include -#include +#include +#include // size_t +#include // operator new #include // runtime_error -#include // operator new -#include // size_t -#include #include +#include +#include +#include +#include #include "recursive_wrapper.hpp" +// clang-format off +// [[deprecated]] is only available in C++14, use this for the time being +#if __cplusplus <= 201103L +# ifdef __GNUC__ +# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated) +# else +# define MAPBOX_VARIANT_DEPRECATED +# endif +#else +# define MAPBOX_VARIANT_DEPRECATED [[deprecated]] +#endif + + #ifdef _MSC_VER - // http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx + // https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx #ifdef NDEBUG #define VARIANT_INLINE __forceinline #else @@ -26,22 +42,37 @@ #define VARIANT_INLINE __attribute__((noinline)) #endif #endif +// clang-format on -#define VARIANT_MAJOR_VERSION 0 +#define VARIANT_MAJOR_VERSION 1 #define VARIANT_MINOR_VERSION 1 #define VARIANT_PATCH_VERSION 0 -// translates to 100 -#define VARIANT_VERSION (VARIANT_MAJOR_VERSION*100000) + (VARIANT_MINOR_VERSION*100) + (VARIANT_PATCH_VERSION) +#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION) -namespace mapbox { namespace util { +namespace mapbox { +namespace util { + +// XXX This should derive from std::logic_error instead of std::runtime_error. +// See https://github.com/mapbox/variant/issues/48 for details. +class bad_variant_access : public std::runtime_error +{ + + public: + explicit bad_variant_access(const std::string& what_arg) + : runtime_error(what_arg) {} + + explicit bad_variant_access(const char* what_arg) + : runtime_error(what_arg) {} + +}; // class bad_variant_access -// static visitor template -struct static_visitor +struct MAPBOX_VARIANT_DEPRECATED static_visitor { using result_type = R; -protected: + + protected: static_visitor() {} ~static_visitor() {} }; @@ -50,14 +81,15 @@ namespace detail { static constexpr std::size_t invalid_value = std::size_t(-1); -template +template struct direct_type; -template +template struct direct_type { static constexpr std::size_t index = std::is_same::value - ? sizeof...(Types) : direct_type::index; + ? sizeof...(Types) + : direct_type::index; }; template @@ -66,14 +98,15 @@ struct direct_type static constexpr std::size_t index = invalid_value; }; -template +template struct convertible_type; -template +template struct convertible_type { static constexpr std::size_t index = std::is_convertible::value - ? sizeof...(Types) : convertible_type::index; + ? sizeof...(Types) + : convertible_type::index; }; template @@ -82,63 +115,52 @@ struct convertible_type static constexpr std::size_t index = invalid_value; }; -template +template struct value_traits { - static constexpr std::size_t direct_index = direct_type::index; - static constexpr std::size_t index = - (direct_index == invalid_value) ? convertible_type::index : direct_index; + using value_type = typename std::remove_reference::type; + static constexpr std::size_t direct_index = direct_type::index; + static constexpr bool is_direct = direct_index != invalid_value; + static constexpr std::size_t index = is_direct ? direct_index : convertible_type::index; + static constexpr bool is_valid = index != invalid_value; + static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0; + using target_type = typename std::tuple_element>::type; }; // check if T is in Types... -template +template struct has_type; template struct has_type { - static constexpr bool value = std::is_same::value - || has_type::value; + static constexpr bool value = std::is_same::value || has_type::value; }; template -struct has_type : std::false_type {}; -// +struct has_type : std::false_type +{ +}; -template +template struct is_valid_type; template struct is_valid_type { - static constexpr bool value = std::is_convertible::value - || is_valid_type::value; + static constexpr bool value = std::is_convertible::value || is_valid_type::value; }; template -struct is_valid_type : std::false_type {}; - -template -struct select_type +struct is_valid_type : std::false_type { - static_assert(N < sizeof...(Types), "index out of bounds"); }; -template -struct select_type -{ - using type = typename select_type::type; -}; - -template -struct select_type<0, T, Types...> -{ - using type = T; -}; - - template -struct enable_if_type { using type = R; }; +struct enable_if_type +{ + using type = R; +}; template struct result_of_unary_visit @@ -147,29 +169,24 @@ struct result_of_unary_visit }; template -struct result_of_unary_visit::type > +struct result_of_unary_visit::type> { using type = typename F::result_type; }; -template +template struct result_of_binary_visit { - using type = typename std::result_of::type; + using type = typename std::result_of::type; }; - template -struct result_of_binary_visit::type > +struct result_of_binary_visit::type> { using type = typename F::result_type; }; - -} // namespace detail - - -template +template struct static_max; template @@ -178,326 +195,305 @@ struct static_max static const std::size_t value = arg; }; -template +template struct static_max { - static const std::size_t value = arg1 >= arg2 ? static_max::value : - static_max::value; + static const std::size_t value = arg1 >= arg2 ? static_max::value : static_max::value; }; -template +template struct variant_helper; -template +template struct variant_helper { - VARIANT_INLINE static void destroy(const std::size_t id, void * data) + VARIANT_INLINE static void destroy(const std::size_t type_index, void* data) { - if (id == sizeof...(Types)) + if (type_index == sizeof...(Types)) { reinterpret_cast(data)->~T(); } else { - variant_helper::destroy(id, data); + variant_helper::destroy(type_index, data); } } - VARIANT_INLINE static void move(const std::size_t old_id, void * old_value, void * new_value) + VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value) { - if (old_id == sizeof...(Types)) + if (old_type_index == sizeof...(Types)) { new (new_value) T(std::move(*reinterpret_cast(old_value))); - //std::memcpy(new_value, old_value, sizeof(T)); - // ^^ DANGER: this should only be considered for relocatable types e.g built-in types - // Also, I don't see any measurable performance benefit just yet } else { - variant_helper::move(old_id, old_value, new_value); + variant_helper::move(old_type_index, old_value, new_value); } } - VARIANT_INLINE static void copy(const std::size_t old_id, const void * old_value, void * new_value) + VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value) { - if (old_id == sizeof...(Types)) + if (old_type_index == sizeof...(Types)) { new (new_value) T(*reinterpret_cast(old_value)); } else { - variant_helper::copy(old_id, old_value, new_value); - } - } - - VARIANT_INLINE static void direct_swap(const std::size_t id, void * lhs, void * rhs) - { - using std::swap; //enable ADL - if (id == sizeof...(Types)) - { - // both lhs and rhs hold T - swap(*reinterpret_cast(lhs), *reinterpret_cast(rhs)); - } - else - { - variant_helper::direct_swap(id, lhs, rhs); + variant_helper::copy(old_type_index, old_value, new_value); } } }; -template<> struct variant_helper<> +template <> +struct variant_helper<> { - VARIANT_INLINE static void destroy(const std::size_t, void *) {} - VARIANT_INLINE static void move(const std::size_t, void *, void *) {} - VARIANT_INLINE static void copy(const std::size_t, const void *, void *) {} - VARIANT_INLINE static void direct_swap(const std::size_t, void *, void *) {} + VARIANT_INLINE static void destroy(const std::size_t, void*) {} + VARIANT_INLINE static void move(const std::size_t, void*, void*) {} + VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {} }; -namespace detail { - template struct unwrapper { - T const& operator() (T const& obj) const - { - return obj; - } - - T& operator() (T & obj) const - { - return obj; - } + static T const& apply_const(T const& obj) { return obj; } + static T& apply(T& obj) { return obj; } }; - template struct unwrapper> { - auto operator() (recursive_wrapper const& obj) const + static auto apply_const(recursive_wrapper const& obj) -> typename recursive_wrapper::type const& { return obj.get(); } + static auto apply(recursive_wrapper& obj) + -> typename recursive_wrapper::type& + { + return obj.get(); + } }; template struct unwrapper> { - auto operator() (std::reference_wrapper const& obj) const + static auto apply_const(std::reference_wrapper const& obj) -> typename std::reference_wrapper::type const& { return obj.get(); } - + static auto apply(std::reference_wrapper& obj) + -> typename std::reference_wrapper::type& + { + return obj.get(); + } }; - -template +template struct dispatcher; -template +template struct dispatcher { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const& v, F f) + VARIANT_INLINE static R apply_const(V const& v, F&& f) { - if (v.get_type_index() == sizeof...(Types)) + if (v.template is()) { - return f(unwrapper()(v. template get())); + return f(unwrapper::apply_const(v.template get())); } else { - return dispatcher::apply_const(v, f); + return dispatcher::apply_const(v, std::forward(f)); } } - VARIANT_INLINE static result_type apply(V & v, F f) + VARIANT_INLINE static R apply(V& v, F&& f) { - if (v.get_type_index() == sizeof...(Types)) + if (v.template is()) { - return f(unwrapper()(v. template get())); + return f(unwrapper::apply(v.template get())); } else { - return dispatcher::apply(v, f); + return dispatcher::apply(v, std::forward(f)); } } }; -template -struct dispatcher +template +struct dispatcher { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const&, F) + VARIANT_INLINE static R apply_const(V const& v, F&& f) { - throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name()); + return f(unwrapper::apply_const(v.template get())); } - VARIANT_INLINE static result_type apply(V &, F) + VARIANT_INLINE static R apply(V& v, F&& f) { - throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name()); + return f(unwrapper::apply(v.template get())); } }; - -template +template struct binary_dispatcher_rhs; -template +template struct binary_dispatcher_rhs { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) + VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { - if (rhs.get_type_index() == sizeof...(Types)) // call binary functor + if (rhs.template is()) // call binary functor { - return f(unwrapper()(lhs. template get()), - unwrapper()(rhs. template get())); + return f(unwrapper::apply_const(lhs.template get()), + unwrapper::apply_const(rhs.template get())); } else { - return binary_dispatcher_rhs::apply_const(lhs, rhs, f); + return binary_dispatcher_rhs::apply_const(lhs, rhs, std::forward(f)); } } - VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f) + VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { - if (rhs.get_type_index() == sizeof...(Types)) // call binary functor + if (rhs.template is()) // call binary functor { - return f(unwrapper()(lhs. template get()), - unwrapper()(rhs. template get())); + return f(unwrapper::apply(lhs.template get()), + unwrapper::apply(rhs.template get())); } else { - return binary_dispatcher_rhs::apply(lhs, rhs, f); + return binary_dispatcher_rhs::apply(lhs, rhs, std::forward(f)); } } - }; -template -struct binary_dispatcher_rhs +template +struct binary_dispatcher_rhs { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const&, V const&, F) + VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply_const(lhs.template get()), + unwrapper::apply_const(rhs.template get())); } - VARIANT_INLINE static result_type apply(V &, V &, F) + + VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply(lhs.template get()), + unwrapper::apply(rhs.template get())); } }; - -template +template struct binary_dispatcher_lhs; -template +template struct binary_dispatcher_lhs { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) + VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { - if (lhs.get_type_index() == sizeof...(Types)) // call binary functor + if (lhs.template is()) // call binary functor { - return f(lhs. template get(), rhs. template get()); + return f(unwrapper::apply_const(lhs.template get()), + unwrapper::apply_const(rhs.template get())); } else { - return binary_dispatcher_lhs::apply_const(lhs, rhs, f); + return binary_dispatcher_lhs::apply_const(lhs, rhs, std::forward(f)); } } - VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f) + VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { - if (lhs.get_type_index() == sizeof...(Types)) // call binary functor + if (lhs.template is()) // call binary functor { - return f(lhs. template get(), rhs. template get()); + return f(unwrapper::apply(lhs.template get()), + unwrapper::apply(rhs.template get())); } else { - return binary_dispatcher_lhs::apply(lhs, rhs, f); + return binary_dispatcher_lhs::apply(lhs, rhs, std::forward(f)); } } - }; -template -struct binary_dispatcher_lhs +template +struct binary_dispatcher_lhs { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const&, V const&, F) + VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply_const(lhs.template get()), + unwrapper::apply_const(rhs.template get())); } - VARIANT_INLINE static result_type apply(V &, V &, F) + VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply(lhs.template get()), + unwrapper::apply(rhs.template get())); } }; -template +template struct binary_dispatcher; -template +template struct binary_dispatcher { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f) + VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) { - if (v0.get_type_index() == sizeof...(Types)) + if (v0.template is()) { - if (v0.get_type_index() == v1.get_type_index()) + if (v1.template is()) { - return f(v0. template get(), v1. template get()); // call binary functor + return f(unwrapper::apply_const(v0.template get()), + unwrapper::apply_const(v1.template get())); // call binary functor } else { - return binary_dispatcher_rhs::apply_const(v0, v1, f); + return binary_dispatcher_rhs::apply_const(v0, v1, std::forward(f)); } } - else if (v1.get_type_index() == sizeof...(Types)) + else if (v1.template is()) { - return binary_dispatcher_lhs::apply_const(v0, v1, f); + return binary_dispatcher_lhs::apply_const(v0, v1, std::forward(f)); } - return binary_dispatcher::apply_const(v0, v1, f); + return binary_dispatcher::apply_const(v0, v1, std::forward(f)); } - VARIANT_INLINE static result_type apply(V & v0, V & v1, F f) + VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) { - if (v0.get_type_index() == sizeof...(Types)) + if (v0.template is()) { - if (v0.get_type_index() == v1.get_type_index()) + if (v1.template is()) { - return f(v0. template get(), v1. template get()); // call binary functor + return f(unwrapper::apply(v0.template get()), + unwrapper::apply(v1.template get())); // call binary functor } else { - return binary_dispatcher_rhs::apply(v0, v1, f); + return binary_dispatcher_rhs::apply(v0, v1, std::forward(f)); } } - else if (v1.get_type_index() == sizeof...(Types)) + else if (v1.template is()) { - return binary_dispatcher_lhs::apply(v0, v1, f); + return binary_dispatcher_lhs::apply(v0, v1, std::forward(f)); } - return binary_dispatcher::apply(v0, v1, f); + return binary_dispatcher::apply(v0, v1, std::forward(f)); } }; -template -struct binary_dispatcher +template +struct binary_dispatcher { - using result_type = R; - VARIANT_INLINE static result_type apply_const(V const&, V const&, F) + VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply_const(v0.template get()), + unwrapper::apply_const(v1.template get())); // call binary functor } - VARIANT_INLINE static result_type apply(V &, V &, F) + VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) { - throw std::runtime_error("binary dispatch: FAIL"); + return f(unwrapper::apply(v0.template get()), + unwrapper::apply(v1.template get())); // call binary functor } }; @@ -523,60 +519,77 @@ struct less_comp template class comparer { -public: + public: explicit comparer(Variant const& lhs) noexcept : lhs_(lhs) {} comparer& operator=(comparer const&) = delete; // visitor - template + template bool operator()(T const& rhs_content) const { T const& lhs_content = lhs_.template get(); return Comp()(lhs_content, rhs_content); } -private: + + private: Variant const& lhs_; }; +// True if Predicate matches for all of the types Ts +template