diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index bb9fc1a00..a20ca5dce 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -10,7 +10,7 @@ set -o nounset # http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html OSMIUM_REPO="https://github.com/osmcode/libosmium.git" -OSMIUM_TAG=v2.11.3 +OSMIUM_TAG=v2.13.1 VARIANT_REPO="https://github.com/mapbox/variant.git" VARIANT_TAG=v1.1.3 diff --git a/third_party/libosmium/.codecov.yml b/third_party/libosmium/.codecov.yml new file mode 100644 index 000000000..79204cdb3 --- /dev/null +++ b/third_party/libosmium/.codecov.yml @@ -0,0 +1,7 @@ + +ignore: + - "include/gdalcpp.hpp" + - "include/protozero" + - "include/utf8" + - "test/include/catch.hpp" + diff --git a/third_party/libosmium/.gitignore b/third_party/libosmium/.gitignore index 79e5e65d5..bfc4cebb0 100644 --- a/third_party/libosmium/.gitignore +++ b/third_party/libosmium/.gitignore @@ -1,4 +1,6 @@ *.swp .ycm_extra_conf.pyc +/_build* /build /libosmium-deps +/.vs* diff --git a/third_party/libosmium/.travis.yml b/third_party/libosmium/.travis.yml index 91ee84293..332396308 100644 --- a/third_party/libosmium/.travis.yml +++ b/third_party/libosmium/.travis.yml @@ -141,6 +141,14 @@ matrix: packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] env: COMPILER='g++-6' BUILD_TYPE='Dev' + - os: linux + compiler: linux-gcc6-coverage + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='g++-6' BUILD_TYPE='Coverage' + # 3/ OSX Clang Builds - os: osx @@ -166,12 +174,12 @@ matrix: - os: osx - osx_image: xcode8 + osx_image: xcode8.3 compiler: xcode8-clang-release env: COMPILER='clang++' BUILD_TYPE='Release' - os: osx - osx_image: xcode8 + osx_image: xcode8.3 compiler: xcode8-clang-dev env: COMPILER='clang++' BUILD_TYPE='Dev' @@ -195,3 +203,16 @@ before_script: script: - make VERBOSE=1 && ctest --output-on-failure +after_success: + - | + if [ "${BUILD_TYPE}" = "Coverage" ]; then + curl -S -f https://codecov.io/bash -o codecov + chmod +x codecov + gcov-${COMPILER#g++-} -p $(find test/CMakeFiles -name '*.o') + ./codecov -Z -c -F unit_tests + gcov-${COMPILER#g++-} -p $(find test/data-tests -name '*.o') + ./codecov -Z -c -F data_tests + gcov-${COMPILER#g++-} -p $(find examples -name '*.o') + ./codecov -Z -c -F examples + fi + diff --git a/third_party/libosmium/CHANGELOG.md b/third_party/libosmium/CHANGELOG.md index cc1fdcc8d..2493e0af8 100644 --- a/third_party/libosmium/CHANGELOG.md +++ b/third_party/libosmium/CHANGELOG.md @@ -6,9 +6,130 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] - +### Added + +### Changed + ### Fixed -## [2.11.3] - 2017-05-03 + +## [2.13.1] - 2017-08-25 + +### Added + +- New "blackhole" file format which throws away all data written into it. + Used for benchmarking. + +### Changed + +- When reading OPL files, CRLF file endings are now handled correctly. +- Reduce the max number of threads allowed for the `Pool` to 32. This should + still be plenty and might help with test failures on some architectures. + +### Fixed + +- Tests now run correctly independent of git `core.autocrlf` setting. +- Set binary mode for all files on Windows in example code. +- Low-level file functions now set an invalid parameter handler on Windows + to properly handle errors. +- Restore earlier behaviour allowing zero-length mmap. It is important to + allow zero-length memory mapping, because it is possible that such an index + is empty, for instance when one type of object is missing from an input + file as in https://github.com/osmcode/osmium-tool/issues/65. Drawback is + that files must be opened read-write for this to work, even if we only + want to read from them. +- Use Approx() to compare floating point values in tests. +- Fix broken `Item` test on 32 bit platforms. + + +## [2.13.0] - 2017-08-15 + +### Added + +- New `RelationsManager` class superseeds the `relations::Collector` class. + The new class is much more modular and easier to extend. If you are using + the Collector class, you are encouraged to switch. +- New `MultipolygonManager` based on the `RelationsManager` class superseeds + the `MultipolygonCollector` class. The examples have been changed to use the + new class and all users are encouraged to switch. There is also a + `MultipolygonManagerLegacy` class if you still need old-style multipolygon + support (see below). +- New `FlexMem` index class that works with input files of any size and + stores the index in memory. This should now be used as the default index + for node location stores. Several example programs now use this index. +- New `CallbackBuffer` class, basically a convenient wrapper around the + `Buffer` class with an additional callback function that is called whenever + the buffer is full. +- Introduce new `ItemStash` class for storing OSM objects in memory. +- New `osmium::geom::overlaps()` function to check if two `Box` objects + overlap. +- Add function `IdSet::used_memory()` to get estimate of memory used in the + set. +- New `is_defined()` and `is_undefined()` methods on `Location` class. +- Tests for all provided example programs. (Some tests currently fail + on Windows for the `osmium_index_lookup` program.) + +### Changed + +- The area `Assembler` now doesn't work with old-style multipolygons (those + are multipolygon relations with the tags on the outer ways(s) instead of + on the relation) any more. Because old-style multipolygons are now (mostly) + gone from the OSM database this is usually what you want. The new + `AssemblerLegacy` class can be used if you actually need support for + old-style multipolygons, for instance if you are working with historical + data. (In that case you also need to use the `MultipolygonManagerLegacy` + class instead of the `MultipolygonManager` class.) +- Changes for consistent ordering of OSM data: OSM data can come in any order, + but usual OSM files are ordered by type, ID, and version. These changes + extend this ordering to negative IDs which are sometimes used for objects + that have not been uploaded to the OSM server yet. The negative IDs are + ordered now before the positive ones, both in order of their absolute value. + This is the same ordering as JOSM uses. +- Multipolygon assembler now checks for three or more overlapping segments + which are always an error and can report them. +- Enable use of user-provided `thread::Pool` instances in `Reader` and + `Writer` for special use cases. +- Growing a `Buffer` will now work with any capacity parameter, it is + always rounded up for proper alignment. Buffer constructor with three + arguments will now check that commmitted is not larger than capacity. +- Updated embedded protozero to 1.5.2. +- Update version of Catch unit test framework to 1.9.7. +- And, as always, lots of small code cleanups and more tests. + +### Fixed + +- Buffers larger than 2^32 bytes do now work. +- Output coordinate with value of -2^31 correctly. +- Changeset comments with more than 2^16 characters are now allowed. The new + maximum size is 2^32. +- `ChangesetDiscussionBuilder::add_comment_text()` could fail silently instead + of throwing an exception. +- Changeset bounding boxes are now always output to OSM files (any format) + if at least one of the corners is defined. This is needed to handle broken + data from the main OSM database which contains such cases. The OPL reader + has also been fixed to handle this case. +- In the example `osmium_location_cache_create`, the index file written is + always truncated first. + + +## [2.12.2] - 2017-05-03 + +### Added + +- Add two argument (key, value) overload of `TagMatcher::operator()`. + +### Changed + +- Detect, report, and remove duplicate ways in multipolygon relations. +- Change EOF behaviour of Reader: The `Reader::read()` function will now + always return an invalid buffer exactly once to signal EOF. +- Update QGIS multipolygon project that is part of the test suite to show + more problem types. +- Copy multipolygon QGIS file for tests to build dir in cmake step. +- Some code cleanups and improved debug output in multipolygon code. +- Refactor I/O code to simplify code. +- Disable some warnings on MSVC. +- Various small code and build script changes. ### Fixed @@ -16,25 +137,94 @@ This project adheres to [Semantic Versioning](http://semver.org/). multipolygons with overlapping or nearly overlapping lines. - Invalid use of iterators leading to undefined behaviour in area assembler code. +- Area assembler stats were not correctly counting inner rings that are + areas in their own right. +- Fix a thread problem valgrind found that might or might not be real. - Read OPL file correctly even if trailing newline in file is missing. +- Include order for `osmium/index/map` headers and + `osmium/index/node_locations_map.hpp` (or + `osmium/handler/node_locations_for_ways.hpp`) doesn't matter any more. -## [2.11.2] - 2017-04-10 +## [2.12.1] - 2017-04-10 + +### Added + +- New `TagsFilter::set_default_result()` function. + +### Changed + +- Use larger capacity for `Buffer` if necessary for alignment instead of + throwing an exception. Minimum buffer size is now 64 bytes. +- Check order of input data in relations collector. The relations collector + can not deal with history data or a changes file. This was documented as a + requirement, but often lead to problems, because this was ignored by users. + So it now checks that the input data it gets is ordered and throws an + exception otherwise. +- When writing an OSM file, set generator to libosmium if not set by app. ### Fixed -- Use minimum size of 64 bytes for buffers. This fixes an infinite loop - when buffer size is zero. +- Infinite loop in `Buffer::reserve_space()`. (Issue #202.) +- `ObjectPointerCollection::unique()` now removes elements at end. +- Tests comparing double using `==` operator. +- Build on Cygwin. -## [2.11.1] - 2017-03-07 +## [2.12.0] - 2017-03-07 + +### Added + +- `TagMatcher` and `TagsFilter` classes for more flexibly matching tags and + selecting objects based on tags. This obsoletes the less flexible classes + based on `osmium::tags::Filter` classes. +- Extended `index::RelationsMap(Stash|Index)` classes to also allow + parent-to-member lookups. +- New `nrw_array` helper class. +- `ObjectPointerCollection::unique()` function. + +### Changed + +- Area assembler can now detect invalid locations and report them in the + stats and through the problem reporter. If the new config option + `ignore_invalid_locations` is set, the Assembler will pretend they weren't + even referenced in the ways. (Issue #195.) +- `osmium::area::Assembler::operator()` will now return a boolean reporting + whether building of the area(s) was successful. +- Split up area `Assembler` class into three classes: The + `detail::BasicAssembler` is now the parent class. `Assembler` is the child + class for usual use. The new `GeomAssembler` also derives from + `BasicAssembler` and builds areas without taking tags into account at all. + This is to support osm2pgsql which does tag handling itself. (Issue #194.) +- The `Projection` class can do any projection supported by the Proj.4 + library. As a special case it now uses our own Mercator projection + functions when the web mercator projection (EPSG 3857) is used. This is + much faster than going through Proj.4. +- Better error messages for low-level file utility functions. +- Mark `build_tag_list*` functions in `builder_helper.hpp` as deprecated. You + should use the functions from `osmium/builder/attr.hpp` instead. +- Improved performance of the `osmium::tags::match_(any|all|none)_of` + functions. +- Improved performance of string comparison in `tags::Filter`. +- Update version of Catch unit test framework to 1.8.1. This meant some + tests had to be updated. +- Use `get_noexcept()` in `NodeLocationsForWays` handler. +- And lots of code and test cleanups... ### Fixed - Terminate called on full non-auto-growing buffer. (Issue #189.) - When file formats were used that were not compiled into the binary, it terminated instead of throwing. (Issue #197.) +- Windows build problem related to including two different winsock versions. +- Windows build problem related to forced build for old Windows versions. + (Issue #196.) +- Clear stream contents in ProblemReporterException correctly. +- Add `-pthread` compiler and linker options on Linux/OSX. This should fix + a problem where some linker versions will not link binaries correctly when + the `--as-needed` option is used. - The `Filter::count()` method didn't compile at all. +- XML reader doesn't fail on relation member ref=0 any more. ## [2.11.0] - 2017-01-14 @@ -549,10 +739,12 @@ 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.11.3...HEAD -[2.11.3]: https://github.com/osmcode/libosmium/compare/v2.11.2...v2.11.3 -[2.11.2]: https://github.com/osmcode/libosmium/compare/v2.11.1...v2.11.2 -[2.11.1]: https://github.com/osmcode/libosmium/compare/v2.11.0...v2.11.1 +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.13.1...HEAD +[2.13.1]: https://github.com/osmcode/libosmium/compare/v2.13.0...v2.13.1 +[2.13.0]: https://github.com/osmcode/libosmium/compare/v2.12.2...v2.13.0 +[2.12.2]: https://github.com/osmcode/libosmium/compare/v2.12.1...v2.12.2 +[2.12.1]: https://github.com/osmcode/libosmium/compare/v2.12.0...v2.12.1 +[2.12.0]: https://github.com/osmcode/libosmium/compare/v2.11.0...v2.12.0 [2.11.0]: https://github.com/osmcode/libosmium/compare/v2.10.3...v2.11.0 [2.10.3]: https://github.com/osmcode/libosmium/compare/v2.10.2...v2.10.3 [2.10.2]: https://github.com/osmcode/libosmium/compare/v2.10.1...v2.10.2 diff --git a/third_party/libosmium/CMakeLists.txt b/third_party/libosmium/CMakeLists.txt index e536c3469..e27887590 100644 --- a/third_party/libosmium/CMakeLists.txt +++ b/third_party/libosmium/CMakeLists.txt @@ -24,8 +24,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 11) -set(LIBOSMIUM_VERSION_PATCH 3) +set(LIBOSMIUM_VERSION_MINOR 13) +set(LIBOSMIUM_VERSION_PATCH 1) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") @@ -43,8 +43,14 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(CMAKE_BUILD_TYPE STREQUAL "Dev") set(dev_build ON) + set(data_test_build ON) else() set(dev_build OFF) + set(data_test_build OFF) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Coverage") + set(data_test_build ON) endif() option(BUILD_EXAMPLES "compile example programs" ON) @@ -52,7 +58,8 @@ option(BUILD_TESTING "compile unit tests, please run them with ctest" ON) 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(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${data_test_build}) option(INSTALL_GDALCPP "also install gdalcpp headers" OFF) option(INSTALL_PROTOZERO "also install protozero headers" OFF) @@ -88,11 +95,12 @@ endif() # #----------------------------------------------------------------------------- -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag("-fkeep-inline-functions" HAS_KEEP_INLINE_FUNCTIONS) -if(HAS_KEEP_INLINE_FUNCTIONS) - set(extra_coverage_flags_ "-fkeep-inline-functions") -endif() +## This leads to all sorts of compile problems, so disable for now +#include(CheckCXXCompilerFlag) +#check_cxx_compiler_flag("-fkeep-inline-functions" HAS_KEEP_INLINE_FUNCTIONS) +#if(HAS_KEEP_INLINE_FUNCTIONS) +# set(extra_coverage_flags_ "-fkeep-inline-functions") +#endif() set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 -fno-inline-functions -fno-inline --coverage ${extra_coverage_flags_}" @@ -103,8 +111,8 @@ set(CMAKE_EXE_LINKER_FLAGS_COVERAGE CACHE STRING "Flags used by the linker during coverage builds.") if(CMAKE_BUILD_TYPE STREQUAL "Coverage") - if(BUILD_EXAMPLES OR BUILD_HEADERS OR BUILD_BENCHMARKS OR BUILD_DATA_TESTS) - message(WARNING "Coverage builds don't work for anything but the unit tests") + if(BUILD_EXAMPLES OR BUILD_HEADERS OR BUILD_BENCHMARKS) + message(WARNING "Coverage builds don't work for anything but the tests") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -184,7 +192,11 @@ endif() #----------------------------------------------------------------------------- if(NOT MSVC) if(NOT USE_CPP_VERSION) - set(USE_CPP_VERSION c++11) + if(CYGWIN) + set(USE_CPP_VERSION gnu++11) + else() + set(USE_CPP_VERSION c++11) + endif() endif() message(STATUS "Use C++ version: ${USE_CPP_VERSION}") # following only available from cmake 2.8.12: @@ -201,15 +213,15 @@ endif() #----------------------------------------------------------------------------- if(MSVC) set(USUAL_COMPILE_OPTIONS "/Ox") - set(USUAL_LINK_OPTIONS "/debug") + # do not show warnings caused by missing .pdb files for libraries + set(USUAL_LINK_OPTIONS "/debug /ignore:4099") else() set(USUAL_COMPILE_OPTIONS "-O3 -g") set(USUAL_LINK_OPTIONS "") endif() if(WIN32) - add_definitions(-DWIN32 -D_WIN32 -DMSWIN32 -DBGDWIN32 - -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0600) + add_definitions(-DWIN32 -D_WIN32 -DMSWIN32 -DBGDWIN32) endif() set(CMAKE_CXX_FLAGS_DEV "${USUAL_COMPILE_OPTIONS}" @@ -228,6 +240,15 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}" CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds." FORCE) +set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${USUAL_LINK_OPTIONS}" + CACHE STRING "Flags used by the linker during RELWITHDEBINFO builds." + FORCE) + +mark_as_advanced( + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +) + if(WITH_PROFILING) add_definitions(-fno-omit-frame-pointer) endif() @@ -301,7 +322,13 @@ find_program(CPPCHECK cppcheck) if(CPPCHECK) message(STATUS "Looking for cppcheck - found") set(CPPCHECK_OPTIONS - --enable=warning,style,performance,portability,information,missingInclude --force -Uassert) + --language=c++ + --quiet + -j4 + --inline-suppr + --enable=warning,style,performance,portability,information,missingInclude + --force + -Uassert -DPROTOZERO_STRICT_API -DPROTOZERO_USE_BUILTIN_BSWAP -UPROTOZERO_USE_VIEW) # cpp doesn't find system includes for some reason, suppress that report set(CPPCHECK_OPTIONS ${CPPCHECK_OPTIONS} --suppress=missingIncludeSystem) @@ -460,10 +487,12 @@ if(CLANG_TIDY) list(APPEND CT_CHECKS "misc-*" "-misc-argument-comment") - list(APPEND CT_CHECKS "modernize-*") + list(APPEND CT_CHECKS "modernize-*" + "-modernize-make-unique") # not available in C++11 list(APPEND CT_CHECKS "readability-*" "-readability-identifier-naming" + "-readability-implicit-bool-cast" "-readability-named-parameter") string(REPLACE ";" "," ALL_CHECKS "${CT_CHECKS}") diff --git a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md index 5f84494f5..4bd9d68cf 100644 --- a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md +++ b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md @@ -12,7 +12,7 @@ All Osmium code MUST be in the `osmium` namespace or one of its sub-namespaces. ## Include-Only Osmium is a include-only library. You can't compile the library itself. There -is no libosmium.so. +is no `libosmium.so` or `libosmium.dll`. One drawback ist that you can't have static data in classes, because there is no place to put this data. @@ -69,7 +69,7 @@ different. * 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 +* The ellipsis in a 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. @@ -92,6 +92,19 @@ between compilers due to different C++11 support. Usually all code must work on Linux, OSX, and Windows. Execptions are allowed for some minor functionality, but please discuss this first. +When writing code and tests, care must be taken that everything works with the +CR line ending convention used on Linux and OSX and the CRLF line ending used +on Windows. Note that `git` can be run with different settings regarding line +ending rewritings on different machines making things more difficult. Some +files have been "forced" to LF line endings using `.gitattributes` files. + + +## 32bit systems + +Libosmium tries to work on 32bit systems whereever possible. But there are +some parts which will not work on 32bit systems, mainly because the amount +of main memory available is not enough for it to work anyway. + ## Checking your code @@ -118,9 +131,20 @@ used to define mappings for iwyu. See the IWYU tool at ## Testing -There are a unit tests using the Catch Unit Test Framework in the `test` -directory and some data tests in `test/osm-testdata`. They are built by the -default cmake config. Run `ctest` to run them. Many more tests are needed. +There are unit tests using the Catch Unit Test Framework in the `test` +directory, some data tests in `test/osm-testdata` and tests of the examples in +`test/examples`. They are built by the default cmake config. Run `ctest` to +run them. We can always use more tests. + +Tests are run automatically using the Travis (Linux/Mac) and Appveyor (Windows) +services. We automatically create coverage reports on Codevoc.io. Note that +the coverage percentages reported are not always accurate, because code that +is not used in tests at all will not necessarily end up in the binary and +the code coverage tool will not know it is there. + +[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) +[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) +[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium) ## Documenting the code diff --git a/third_party/libosmium/README.md b/third_party/libosmium/README.md index ce27fead3..b4fc4dcdb 100644 --- a/third_party/libosmium/README.md +++ b/third_party/libosmium/README.md @@ -6,8 +6,9 @@ A fast and flexible C++ library for working with OpenStreetMap data. Libosmium works on Linux, Mac OSX and Windows. -[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) -[![Build status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) +[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) +[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) +[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium) Please see the [Libosmium manual](http://osmcode.org/libosmium/manual.html) for more details than this README can provide. diff --git a/third_party/libosmium/appveyor.yml b/third_party/libosmium/appveyor.yml index 7e6060a7f..ca77ae647 100644 --- a/third_party/libosmium/appveyor.yml +++ b/third_party/libosmium/appveyor.yml @@ -7,7 +7,13 @@ environment: matrix: - config: Dev + autocrlf: true + - config: Dev + autocrlf: false - config: RelWithDebInfo + autocrlf: true + - config: RelWithDebInfo + autocrlf: false shallow_clone: true @@ -16,6 +22,8 @@ os: Visual Studio 2015 # scripts that are called at very beginning, before repo cloning init: + - git config --global core.autocrlf %autocrlf% + - git config --get core.autocrlf # clone directory clone_folder: c:\projects\libosmium @@ -24,3 +32,8 @@ platform: x64 build_script: - build-appveyor.bat + +# remove garbage VS messages +# http://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored +before_build: + - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets" diff --git a/third_party/libosmium/benchmarks/CMakeLists.txt b/third_party/libosmium/benchmarks/CMakeLists.txt index 4b76fcbc1..5d8b3c1e2 100644 --- a/third_party/libosmium/benchmarks/CMakeLists.txt +++ b/third_party/libosmium/benchmarks/CMakeLists.txt @@ -33,6 +33,7 @@ foreach(benchmark ${BENCHMARKS}) target_link_libraries(osmium_benchmark_${benchmark} ${OSMIUM_IO_LIBRARIES} ${BENCHMARK_LIBS_${benchmark}}) + set_pthread_on_target(osmium_benchmark_${benchmark}) configure_file(run_benchmark_${benchmark}.sh ${CMAKE_CURRENT_BINARY_DIR}/run_benchmark_${benchmark}.sh @ONLY) diff --git a/third_party/libosmium/build-appveyor.bat b/third_party/libosmium/build-appveyor.bat index 27d387c33..ef9da5ffd 100644 --- a/third_party/libosmium/build-appveyor.bat +++ b/third_party/libosmium/build-appveyor.bat @@ -97,11 +97,14 @@ ECHO calling^: %CMAKE_CMD% %CMAKE_CMD% IF %ERRORLEVEL% NEQ 0 GOTO ERROR +SET avlogger= +IF /I "%APPVEYOR%"=="True" SET avlogger=/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + msbuild libosmium.sln ^ /p:Configuration=%config% ^ /toolsversion:14.0 ^ /p:Platform=x64 ^ -/p:PlatformToolset=v140 +/p:PlatformToolset=v140 %avlogger% IF %ERRORLEVEL% NEQ 0 GOTO ERROR ctest --output-on-failure ^ diff --git a/third_party/libosmium/cmake/FindOsmium.cmake b/third_party/libosmium/cmake/FindOsmium.cmake index 2224e18dc..adc457f9a 100644 --- a/third_party/libosmium/cmake/FindOsmium.cmake +++ b/third_party/libosmium/cmake/FindOsmium.cmake @@ -296,6 +296,20 @@ find_package_handle_standard_args(Osmium VERSION_VAR _libosmium_version) unset(OSMIUM_EXTRA_FIND_VARS) +#---------------------------------------------------------------------- +# +# A function for setting the -pthread option in compilers/linkers +# +#---------------------------------------------------------------------- +function(set_pthread_on_target _target) + if(NOT MSVC) + set_target_properties(${_target} PROPERTIES COMPILE_FLAGS "-pthread") + if(NOT APPLE) + set_target_properties(${_target} PROPERTIES LINK_FLAGS "-pthread") + endif() + endif() +endfunction() + #---------------------------------------------------------------------- # # Add compiler flags @@ -319,6 +333,10 @@ if(MSVC) # old compilers anyway. add_definitions(-wd4351) + # Disable warning C4503: "decorated name length exceeded, name was truncated" + # there are more than 150 of generated names in libosmium longer than 4096 symbols supported in MSVC + add_definitions(-wd4503) + add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS) endif() diff --git a/third_party/libosmium/examples/CMakeLists.txt b/third_party/libosmium/examples/CMakeLists.txt index 442fda7e3..c4f875f43 100644 --- a/third_party/libosmium/examples/CMakeLists.txt +++ b/third_party/libosmium/examples/CMakeLists.txt @@ -58,7 +58,12 @@ message(STATUS "Configuring examples - Building these examples:") foreach(example ${EXAMPLES}) message(STATUS " - osmium_${example}") add_executable(osmium_${example} "osmium_${example}.cpp") + set_pthread_on_target(osmium_${example}) target_link_libraries(osmium_${example} ${OSMIUM_IO_LIBRARIES} ${EXAMPLE_LIBS_${example}}) + add_test(NAME examples_usage_${example} COMMAND osmium_${example}) + set_tests_properties(examples_usage_${example} PROPERTIES + PASS_REGULAR_EXPRESSION "^Usage: " + ) endforeach() diff --git a/third_party/libosmium/examples/osmium_amenity_list.cpp b/third_party/libosmium/examples/osmium_amenity_list.cpp index 502b44b81..7e8d79a9e 100644 --- a/third_party/libosmium/examples/osmium_amenity_list.cpp +++ b/third_party/libosmium/examples/osmium_amenity_list.cpp @@ -9,7 +9,7 @@ DEMONSTRATES USE OF: * file input * location indexes and the NodeLocationsForWays handler - * the MultipolygonCollector and Assembler to assemble areas (multipolygons) + * the MultipolygonManager and Assembler to assemble areas (multipolygons) * your own handler that works with areas (multipolygons) * accessing tags * osmium::geom::Coordinates @@ -29,17 +29,22 @@ #include // for std::cerr #include // for std::string -// For memory based sparse index -#include +// For the location index. There are different types of indexes available. +// This will work for all input files keeping the index in memory. +#include // For the NodeLocationForWays handler #include -using index_type = osmium::index::map::SparseMemArray; + +// The type of index used. This must match the include file above +using index_type = osmium::index::map::FlexMem; + +// The location handler always depends on the index type using location_handler_type = osmium::handler::NodeLocationsForWays; // For assembling multipolygons #include -#include +#include // Allow any format of input files (XML, PBF, ...) #include @@ -108,8 +113,8 @@ int main(int argc, char* argv[]) { std::exit(1); } - // The input file name - const std::string input_file_name{argv[1]}; + // The input file + const osmium::io::File input_file{argv[1]}; // Configuration for the multipolygon assembler. We disable the option to // create empty areas when invalid multipolygons are encountered. This @@ -118,21 +123,16 @@ int main(int argc, char* argv[]) { osmium::area::Assembler::config_type assembler_config; assembler_config.create_empty_areas = false; - // Initialize the MultipolygonCollector. Its job is to collect all + // Initialize the MultipolygonManager. Its job is to collect all // relations and member ways needed for each area. It then calls an // instance of the osmium::area::Assembler class (with the given config) // to actually assemble one area. - osmium::area::MultipolygonCollector collector{assembler_config}; + osmium::area::MultipolygonManager mp_manager{assembler_config}; // We read the input file twice. In the first pass, only relations are - // read and fed into the multipolygon collector. The read_meta::no option - // disables reading of meta data (such as version numbers, timestamps, etc.) - // which are not needed in this case. Disabling this can speed up your - // program. + // read and fed into the multipolygon manager. std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1{input_file_name, osmium::osm_entity_bits::relation, osmium::io::read_meta::no}; - collector.read_relations(reader1); - reader1.close(); + osmium::relations::read_relations(input_file, mp_manager); std::cerr << "Pass 1 done\n"; // The index storing all node locations. @@ -151,17 +151,21 @@ int main(int argc, char* argv[]) { AmenityHandler data_handler; // On the second pass we read all objects and run them first through the - // node location handler and then the multipolygon collector. The collector + // node location handler and then the multipolygon manager. The manager // will put the areas it has created into the "buffer" which are then // fed through our handler. + // + // The read_meta::no option disables reading of meta data (such as version + // numbers, timestamps, etc.) which are not needed in this case. Disabling + // this can speed up your program. std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2{input_file_name, osmium::io::read_meta::no}; + osmium::io::Reader reader{input_file, osmium::io::read_meta::no}; - osmium::apply(reader2, location_handler, data_handler, collector.handler([&data_handler](const osmium::memory::Buffer& area_buffer) { + osmium::apply(reader, location_handler, data_handler, mp_manager.handler([&data_handler](const osmium::memory::Buffer& area_buffer) { osmium::apply(area_buffer, data_handler); })); - reader2.close(); + reader.close(); std::cerr << "Pass 2 done\n"; } diff --git a/third_party/libosmium/examples/osmium_area_test.cpp b/third_party/libosmium/examples/osmium_area_test.cpp index 4bfff72f2..948c032ed 100644 --- a/third_party/libosmium/examples/osmium_area_test.cpp +++ b/third_party/libosmium/examples/osmium_area_test.cpp @@ -8,7 +8,7 @@ DEMONSTRATES USE OF: * file input * location indexes and the NodeLocationsForWays handler - * the MultipolygonCollector and Assembler to assemble areas (multipolygons) + * the MultipolygonManager and Assembler to assemble areas (multipolygons) * your own handler that works with areas (multipolygons) * the WKTFactory to write geometries in WKT format * the Dump handler @@ -31,7 +31,7 @@ // For assembling multipolygons #include -#include +#include // For the DynamicHandler class #include @@ -52,11 +52,11 @@ #include // For the location index. There are different types of indexes available. -// This will work for small and medium sized input files. -#include +// This will work for all input files keeping the index in memory. +#include // The type of index used. This must match the include file above -using index_type = osmium::index::map::SparseMemArray; +using index_type = osmium::index::map::FlexMem; // The location handler always depends on the index type using location_handler_type = osmium::handler::NodeLocationsForWays; @@ -93,10 +93,10 @@ void print_help() { int main(int argc, char* argv[]) { static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"dump-wkt", no_argument, 0, 'w'}, - {"dump-objects", no_argument, 0, 'o'}, - {0, 0, 0, 0} + {"help", no_argument, nullptr, 'h'}, + {"dump-wkt", no_argument, nullptr, 'w'}, + {"dump-objects", no_argument, nullptr, 'o'}, + {nullptr, 0, nullptr, 0} }; // Initialize an empty DynamicHandler. Later it will be associated @@ -107,7 +107,7 @@ int main(int argc, char* argv[]) { // Read options from command line. while (true) { - const int c = getopt_long(argc, argv, "hwo", long_options, 0); + const int c = getopt_long(argc, argv, "hwo", long_options, nullptr); if (c == -1) { break; } @@ -139,24 +139,29 @@ int main(int argc, char* argv[]) { // are used, but you could change multiple settings. osmium::area::Assembler::config_type assembler_config; - // Initialize the MultipolygonCollector. Its job is to collect all + // Set up a filter matching only forests. This will be used to only build + // areas with matching tags. + osmium::TagsFilter filter{false}; + filter.add_rule(true, "landuse", "forest"); + filter.add_rule(true, "natural", "wood"); + + // Initialize the MultipolygonManager. Its job is to collect all // relations and member ways needed for each area. It then calls an // instance of the osmium::area::Assembler class (with the given config) - // to actually assemble one area. - osmium::area::MultipolygonCollector collector{assembler_config}; + // to actually assemble one area. The filter parameter is optional, if + // it is not set, all areas will be built. + osmium::area::MultipolygonManager mp_manager{assembler_config, filter}; // We read the input file twice. In the first pass, only relations are - // read and fed into the multipolygon collector. + // read and fed into the multipolygon manager. std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1{input_file, osmium::osm_entity_bits::relation}; - collector.read_relations(reader1); - reader1.close(); + osmium::relations::read_relations(input_file, mp_manager); std::cerr << "Pass 1 done\n"; // Output the amount of main memory used so far. All multipolygon relations // are in memory now. std::cerr << "Memory:\n"; - collector.used_memory(); + osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); // The index storing all node locations. index_type index; @@ -175,26 +180,29 @@ int main(int argc, char* argv[]) { // will put the areas it has created into the "buffer" which are then // fed through our "handler". std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2{input_file}; - osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) { + osmium::io::Reader reader{input_file}; + osmium::apply(reader, location_handler, mp_manager.handler([&handler](osmium::memory::Buffer&& buffer) { osmium::apply(buffer, handler); })); - reader2.close(); + reader.close(); std::cerr << "Pass 2 done\n"; // Output the amount of main memory used so far. All complete multipolygon // relations have been cleaned up. std::cerr << "Memory:\n"; - collector.used_memory(); + osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); // If there were multipolgyon relations in the input, but some of their // members are not in the input file (which often happens for extracts) // this will write the IDs of the incomplete relations to stderr. - std::vector incomplete_relations = collector.get_incomplete_relations(); - if (!incomplete_relations.empty()) { + std::vector incomplete_relations_ids; + mp_manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){ + incomplete_relations_ids.push_back(handle->id()); + }); + if (!incomplete_relations_ids.empty()) { std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; - for (const auto* relation : incomplete_relations) { - std::cerr << " " << relation->id(); + for (const auto id : incomplete_relations_ids) { + std::cerr << " " << id; } std::cerr << "\n"; } diff --git a/third_party/libosmium/examples/osmium_change_tags.cpp b/third_party/libosmium/examples/osmium_change_tags.cpp index a7c19047d..ae2e99e98 100644 --- a/third_party/libosmium/examples/osmium_change_tags.cpp +++ b/third_party/libosmium/examples/osmium_change_tags.cpp @@ -91,7 +91,7 @@ class RewriteHandler : public osmium::handler::Handler { public: // Constructor. New data will be added to the given buffer. - RewriteHandler(osmium::memory::Buffer& buffer) : + explicit RewriteHandler(osmium::memory::Buffer& buffer) : m_buffer(buffer) { } @@ -119,7 +119,7 @@ public: m_buffer.commit(); } - // The way handler is called for each node in the input data. + // The way handler is called for each way in the input data. void way(const osmium::Way& way) { { osmium::builder::WayBuilder builder{m_buffer}; @@ -132,7 +132,7 @@ public: m_buffer.commit(); } - // The relation handler is called for each node in the input data. + // The relation handler is called for each relation in the input data. void relation(const osmium::Relation& relation) { { osmium::builder::RelationBuilder builder{m_buffer}; diff --git a/third_party/libosmium/examples/osmium_convert.cpp b/third_party/libosmium/examples/osmium_convert.cpp index 0ced82c1c..dfa0abd4a 100644 --- a/third_party/libosmium/examples/osmium_convert.cpp +++ b/third_party/libosmium/examples/osmium_convert.cpp @@ -53,10 +53,10 @@ void print_help() { int main(int argc, char* argv[]) { static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"from-format", required_argument, 0, 'f'}, - {"to-format", required_argument, 0, 't'}, - {0, 0, 0, 0} + {"help", no_argument, nullptr, 'h'}, + {"from-format", required_argument, nullptr, 'f'}, + {"to-format", required_argument, nullptr, 't'}, + {nullptr, 0, nullptr, 0} }; // Input and output format are empty by default. Later this will mean that @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) { // Read options from command line. while (true) { - const int c = getopt_long(argc, argv, "dhf:t:", long_options, 0); + const int c = getopt_long(argc, argv, "dhf:t:", long_options, nullptr); if (c == -1) { break; } @@ -88,7 +88,7 @@ int main(int argc, char* argv[]) { } const int remaining_args = argc - optind; - if (remaining_args > 2) { + if (remaining_args == 0 || remaining_args > 2) { std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n"; std::exit(1); } @@ -108,8 +108,8 @@ int main(int argc, char* argv[]) { // This declares the input and output files using either the suffix of // the file names or the format in the 2nd argument. It does not yet open // the files. - osmium::io::File input_file{input_file_name, input_format}; - osmium::io::File output_file{output_file_name, output_format}; + const osmium::io::File input_file{input_file_name, input_format}; + const osmium::io::File output_file{output_file_name, output_format}; // Input and output files can be OSM data files (without history) or // OSM history files. History files are detected if they use the '.osh' diff --git a/third_party/libosmium/examples/osmium_create_pois.cpp b/third_party/libosmium/examples/osmium_create_pois.cpp index ff79cbbd5..df27bfeff 100644 --- a/third_party/libosmium/examples/osmium_create_pois.cpp +++ b/third_party/libosmium/examples/osmium_create_pois.cpp @@ -46,6 +46,10 @@ int main(int argc, char* argv[]) { // Get output file name from command line. std::string output_file_name{argv[1]}; + // If output file name is "-", this means STDOUT. Set the OPL file type + // in this case. Otherwise take the file type from the file name suffix. + osmium::io::File output_file{output_file_name, output_file_name == "-" ? ".opl" : ""}; + try { // Create a buffer where all objects will live. Use a sensible initial // buffer size and set the buffer to automatically grow if needed. @@ -78,7 +82,7 @@ int main(int argc, char* argv[]) { // Initialize Writer using the header from above and tell it that it // is allowed to overwrite a possibly existing file. - osmium::io::Writer writer{output_file_name, header, osmium::io::overwrite::allow}; + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; // Write out the contents of the output buffer. writer(std::move(buffer)); diff --git a/third_party/libosmium/examples/osmium_debug.cpp b/third_party/libosmium/examples/osmium_debug.cpp index 813349190..ed6859612 100644 --- a/third_party/libosmium/examples/osmium_debug.cpp +++ b/third_party/libosmium/examples/osmium_debug.cpp @@ -44,10 +44,18 @@ int main(int argc, char* argv[]) { if (argc == 3) { read_types = osmium::osm_entity_bits::nothing; std::string types = argv[2]; - if (types.find('n') != std::string::npos) read_types |= osmium::osm_entity_bits::node; - if (types.find('w') != std::string::npos) read_types |= osmium::osm_entity_bits::way; - if (types.find('r') != std::string::npos) read_types |= osmium::osm_entity_bits::relation; - if (types.find('c') != std::string::npos) read_types |= osmium::osm_entity_bits::changeset; + if (types.find('n') != std::string::npos) { + read_types |= osmium::osm_entity_bits::node; + } + if (types.find('w') != std::string::npos) { + read_types |= osmium::osm_entity_bits::way; + } + if (types.find('r') != std::string::npos) { + read_types |= osmium::osm_entity_bits::relation; + } + if (types.find('c') != std::string::npos) { + read_types |= osmium::osm_entity_bits::changeset; + } } // Initialize Reader with file name and the types of entities we want to diff --git a/third_party/libosmium/examples/osmium_dump_internal.cpp b/third_party/libosmium/examples/osmium_dump_internal.cpp index dbc50db9a..78229dadc 100644 --- a/third_party/libosmium/examples/osmium_dump_internal.cpp +++ b/third_party/libosmium/examples/osmium_dump_internal.cpp @@ -6,7 +6,7 @@ indexes to find objects and object relations. Note that this example programm will only work with small and medium sized - OSM file, not with the planet. + OSM files, not with the planet. You can use the osmium_index example program to inspect the indexes. @@ -36,6 +36,10 @@ #include // for open #include // for open +#ifdef _WIN32 +# include // for _setmode +#endif + #ifdef _MSC_VER # include #endif @@ -66,12 +70,15 @@ class IndexFile { public: - IndexFile(const std::string& filename) : + explicit IndexFile(const std::string& filename) : m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) { if (m_fd < 0) { std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n"; std::exit(2); } +#ifdef _WIN32 + _setmode(m_fd, _O_BINARY); +#endif } ~IndexFile() { @@ -114,6 +121,10 @@ int main(int argc, char* argv[]) { std::exit(2); } +#ifdef _WIN32 + _setmode(data_fd, _O_BINARY); +#endif + // These indexes store the offset in the data file where each node, way, // or relation is stored. offset_index_type node_index; diff --git a/third_party/libosmium/examples/osmium_index_lookup.cpp b/third_party/libosmium/examples/osmium_index_lookup.cpp index 01d7e36b3..2af237833 100644 --- a/third_party/libosmium/examples/osmium_index_lookup.cpp +++ b/third_party/libosmium/examples/osmium_index_lookup.cpp @@ -33,6 +33,10 @@ #include // for open #include // for std::vector +#ifdef _WIN32 +# include // for _setmode +#endif + // Disk-based indexes #include #include @@ -52,7 +56,7 @@ class IndexAccess { public: - IndexAccess(int fd) : + explicit IndexAccess(int fd) : m_fd(fd) { } @@ -83,7 +87,7 @@ class IndexAccessDense : public IndexAccess { public: - IndexAccessDense(int fd) : + explicit IndexAccessDense(int fd) : IndexAccess(fd) { } @@ -122,7 +126,7 @@ class IndexAccessSparse : public IndexAccess { public: - IndexAccessSparse(int fd) : + explicit IndexAccessSparse(int fd) : IndexAccess(fd) { } @@ -192,17 +196,17 @@ public: } static struct option long_options[] = { - {"array", required_argument, 0, 'a'}, - {"dump", no_argument, 0, 'd'}, - {"help", no_argument, 0, 'h'}, - {"list", required_argument, 0, 'l'}, - {"search", required_argument, 0, 's'}, - {"type", required_argument, 0, 't'}, - {0, 0, 0, 0} + {"array", required_argument, nullptr, 'a'}, + {"dump", no_argument, nullptr, 'd'}, + {"help", no_argument, nullptr, 'h'}, + {"list", required_argument, nullptr, 'l'}, + {"search", required_argument, nullptr, 's'}, + {"type", required_argument, nullptr, 't'}, + {nullptr, 0, nullptr, 0} }; while (true) { - const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0); + const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, nullptr); if (c == -1) { break; } @@ -243,7 +247,7 @@ public: std::exit(2); } - if (m_dump && !m_ids.empty()) { + if (m_dump == !m_ids.empty()) { std::cerr << "Need option --dump or --search, but not both\n"; std::exit(2); } @@ -308,26 +312,35 @@ int main(int argc, char* argv[]) { Options options{argc, argv}; // Open the index file. - const int fd = open(options.filename(), O_RDWR); + const int fd = ::open(options.filename(), O_RDWR); if (fd < 0) { std::cerr << "Can not open file '" << options.filename() << "': " << std::strerror(errno) << '\n'; std::exit(2); } - // Depending on the type of index, we have different implementations. - if (options.type_is("location")) { - // index id -> location - const auto index = create(options.dense_format(), fd); - return run(*index, options); - } else if (options.type_is("id")) { - // index id -> id - const auto index = create(options.dense_format(), fd); - return run(*index, options); - } else { - // index id -> offset - const auto index = create(options.dense_format(), fd); - return run(*index, options); +#ifdef _WIN32 + _setmode(fd, _O_BINARY); +#endif + + try { + // Depending on the type of index, we have different implementations. + if (options.type_is("location")) { + // index id -> location + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } else if (options.type_is("id")) { + // index id -> id + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } else { + // index id -> offset + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } + } catch(const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + std::exit(1); } } diff --git a/third_party/libosmium/examples/osmium_location_cache_create.cpp b/third_party/libosmium/examples/osmium_location_cache_create.cpp index 9de41d12f..a7119df60 100644 --- a/third_party/libosmium/examples/osmium_location_cache_create.cpp +++ b/third_party/libosmium/examples/osmium_location_cache_create.cpp @@ -33,6 +33,10 @@ #include // for open #include // for open +#ifdef _WIN32 +# include // for _setmode +#endif + // Allow any format of input files (XML, PBF, ...) #include @@ -68,11 +72,14 @@ int main(int argc, char* argv[]) { osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node}; // Initialize location index on disk creating a new file. - const int fd = open(cache_filename.c_str(), O_RDWR | O_CREAT, 0666); + const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd == -1) { std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; std::exit(1); } +#ifdef _WIN32 + _setmode(fd, _O_BINARY); +#endif index_type index{fd}; // The handler that stores all node locations in the index. diff --git a/third_party/libosmium/examples/osmium_location_cache_use.cpp b/third_party/libosmium/examples/osmium_location_cache_use.cpp index f5db0becf..6af0fe67a 100644 --- a/third_party/libosmium/examples/osmium_location_cache_use.cpp +++ b/third_party/libosmium/examples/osmium_location_cache_use.cpp @@ -33,6 +33,10 @@ #include // for open #include // for open +#ifdef _WIN32 +# include // for _setmode +#endif + // Allow any format of input files (XML, PBF, ...) #include @@ -81,11 +85,14 @@ int main(int argc, char* argv[]) { osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way}; // Initialize location index on disk using an existing file - const int fd = open(cache_filename.c_str(), O_RDWR); + const int fd = ::open(cache_filename.c_str(), O_RDWR); if (fd == -1) { std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; return 1; } +#ifdef _WIN32 + _setmode(fd, _O_BINARY); +#endif index_type index{fd}; // The handler that adds node locations from the index to the ways. diff --git a/third_party/libosmium/examples/osmium_road_length.cpp b/third_party/libosmium/examples/osmium_road_length.cpp index 2e1be9080..4aced95f8 100644 --- a/third_party/libosmium/examples/osmium_road_length.cpp +++ b/third_party/libosmium/examples/osmium_road_length.cpp @@ -33,14 +33,14 @@ #include // For the location index. There are different types of indexes available. -// This will work for small and medium sized input files. -#include +// This will work for all input files keeping the index in memory. +#include // For the NodeLocationForWays handler #include // The type of index used. This must match the include file above -using index_type = osmium::index::map::SparseMemArray; +using index_type = osmium::index::map::FlexMem; // The location handler always depends on the index type using location_handler_type = osmium::handler::NodeLocationsForWays; diff --git a/third_party/libosmium/include/osmium/area/assembler.hpp b/third_party/libosmium/include/osmium/area/assembler.hpp index 78a9d09d7..461a18376 100644 --- a/third_party/libosmium/include/osmium/area/assembler.hpp +++ b/third_party/libosmium/include/osmium/area/assembler.hpp @@ -33,1363 +33,64 @@ 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 -#include -#include - -#include -#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include namespace osmium { namespace area { - /** - * Configuration for osmium::area::Assembler objects. Create this - * once, set the options you want and then re-use it every time you - * create an Assembler object. - */ - struct AssemblerConfig { - - /** - * Optional pointer to problem reporter. - */ - osmium::area::ProblemReporter* problem_reporter = nullptr; - - /** - * Debug level. If this is greater than zero, debug messages will - * be printed to stderr. Available levels are 1 to 3. Note that - * level 2 and above will generate a lot of messages! - */ - int debug_level = 0; - - /** - * The roles of multipolygon members are ignored when assembling - * multipolygons, because they are often missing or wrong. If this - * is set, the roles are checked after the multipolygons are built - * against what the assembly process decided where the inner and - * outer rings are. This slows down the processing, so it only - * makes sense if you want to get the problem reports. - */ - bool check_roles = false; - - /** - * When the assembler can't create an area, usually because its - * geometry would be invalid, it will create an "empty" area object - * without rings. This allows you to detect where an area was - * invalid. - * - * If this is set to false, invalid areas will simply be discarded. - */ - bool create_empty_areas = true; - - /** - * Create areas for (multi)polygons where the tags are on the - * relation. - * - * If this is set to false, those areas will simply be discarded. - */ - bool create_new_style_polygons = true; - - /** - * Create areas for (multi)polygons where the tags are on the - * outer way(s). - * - * If this is set to false, those areas will simply be discarded. - */ - bool create_old_style_polygons = true; - - /** - * Create areas for polygons created from ways. - * - * If this is set to false, those areas will simply be discarded. - */ - bool create_way_polygons = true; - - /** - * Keep the type tag from multipolygon relations on the area - * object. By default this is false, and the type tag will be - * removed. - */ - bool keep_type_tag = false; - - AssemblerConfig() noexcept = default; - - /** - * Constructor - * @deprecated Use default constructor and set values afterwards. - */ - explicit AssemblerConfig(osmium::area::ProblemReporter* pr, bool d = false) : - problem_reporter(pr), - debug_level(d) { - } - - /** - * Enable or disable debug output to stderr. This is for Osmium - * developers only. - * - * @deprecated Set debug_level directly. - */ - OSMIUM_DEPRECATED void enable_debug_output(bool d = true) { - debug_level = d; - } - - }; // struct AssemblerConfig - - namespace detail { - - using open_ring_its_type = std::list::iterator>; - - struct location_to_ring_map { - osmium::Location location; - open_ring_its_type::iterator ring_it; - bool start; - - location_to_ring_map(const osmium::Location& l, const open_ring_its_type::iterator& r, bool s) noexcept : - location(l), - ring_it(r), - start(s) { - } - - explicit location_to_ring_map(const osmium::Location& l) noexcept : - location(l), - ring_it(), - start(false) { - } - - const detail::ProtoRing& ring() const noexcept { - return **ring_it; - } - - }; // struct location_to_ring_map - - inline bool operator==(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { - return lhs.location == rhs.location; - } - - inline bool operator<(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { - return lhs.location < rhs.location; - } - - } // namespace detail - /** * Assembles area objects from closed ways or multipolygon relations * and their members. */ - class Assembler { - - using open_ring_its_type = detail::open_ring_its_type; - using location_to_ring_map = detail::location_to_ring_map; - - struct slocation { - - static constexpr const uint32_t invalid_item = 1 << 30; - - uint32_t item : 31; - uint32_t reverse : 1; - - slocation() noexcept : - item(invalid_item), - reverse(false) { - } - - explicit slocation(uint32_t n, bool r = false) noexcept : - item(n), - reverse(r) { - } - - osmium::Location location(const detail::SegmentList& segment_list) const noexcept { - const auto& segment = segment_list[item]; - return reverse ? segment.second().location() : segment.first().location(); - } - - const osmium::NodeRef& node_ref(const detail::SegmentList& segment_list) const noexcept { - const auto& segment = segment_list[item]; - return reverse ? segment.second() : segment.first(); - } - - osmium::Location location(const detail::SegmentList& segment_list, const osmium::Location& default_location) const noexcept { - if (item == invalid_item) { - return default_location; - } - return location(segment_list); - } - - }; // struct slocation - - // Configuration settings for this Assembler - const AssemblerConfig& m_config; - - // List of segments (connection between two nodes) - osmium::area::detail::SegmentList m_segment_list; - - // The rings we are building from the segments - std::list m_rings; - - // All node locations - std::vector m_locations; - - // All locations where more than two segments start/end - std::vector m_split_locations; - - // Statistics - area_stats m_stats; - - // The number of members the multipolygon relation has - size_t m_num_members = 0; - - bool debug() const noexcept { - return m_config.debug_level > 1; - } - - bool report_ways() const noexcept { - if (!m_config.problem_reporter) { - return false; - } - return m_stats.duplicate_nodes || - m_stats.duplicate_segments || - m_stats.intersections || - m_stats.open_rings || - m_stats.short_ways || - m_stats.touching_rings || - m_stats.ways_in_multiple_rings || - m_stats.wrong_role; - } - - void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const { - builder.add_item(way.tags()); - } - - void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set& ways) const { - std::map counter; - for (const osmium::Way* way : ways) { - for (const auto& tag : way->tags()) { - std::string kv {tag.key()}; - kv.append(1, '\0'); - kv.append(tag.value()); - ++counter[kv]; - } - } - - const size_t num_ways = ways.size(); - for (const auto& t_c : counter) { - if (debug()) { - std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n"; - } - if (t_c.second == num_ways) { - const size_t len = std::strlen(t_c.first.c_str()); - tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1); - } - } - } - - struct MPFilter : public osmium::tags::KeyFilter { - - MPFilter() : osmium::tags::KeyFilter(true) { - add(false, "type"); - add(false, "created_by"); - add(false, "source"); - add(false, "note"); - add(false, "test:id"); - add(false, "test:section"); - } - - }; // struct MPFilter - - static const MPFilter& filter() noexcept { - static const MPFilter filter; - return filter; - } - - static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { - osmium::builder::TagListBuilder tl_builder{builder}; - for (const osmium::Tag& tag : tags) { - if (std::strcmp(tag.key(), "type")) { - tl_builder.add_tag(tag.key(), tag.value()); - } - } - } - - void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) { - const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), filter()); - - if (debug()) { - std::cerr << " found " << count << " tags on relation (without ignored ones)\n"; - } - - if (count > 0) { - if (debug()) { - std::cerr << " use tags from relation\n"; - } - - if (m_config.keep_type_tag) { - builder.add_item(relation.tags()); - } else { - copy_tags_without_type(builder, relation.tags()); - } - } else { - ++m_stats.no_tags_on_relation; - if (debug()) { - std::cerr << " use tags from outer ways\n"; - } - std::set ways; - for (const auto& ring : m_rings) { - if (ring.is_outer()) { - ring.get_ways(ways); - } - } - if (ways.size() == 1) { - if (debug()) { - std::cerr << " only one outer way\n"; - } - builder.add_item((*ways.cbegin())->tags()); - } else { - if (debug()) { - std::cerr << " multiple outer ways, get common tags\n"; - } - osmium::builder::TagListBuilder tl_builder{builder}; - add_common_tags(tl_builder, ways); - } - } - } - - template - static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) { - TBuilder ring_builder{builder}; - ring_builder.add_node_ref(ring.get_node_ref_start()); - for (const auto& segment : ring.segments()) { - ring_builder.add_node_ref(segment->stop()); - } - } - - /** - * Append each outer ring together with its inner rings to the - * area in the buffer. - */ - void add_rings_to_area(osmium::builder::AreaBuilder& builder) const { - for (const detail::ProtoRing& ring : m_rings) { - if (ring.is_outer()) { - build_ring_from_proto_ring(builder, ring); - for (const detail::ProtoRing* inner : ring.inner_rings()) { - build_ring_from_proto_ring(builder, *inner); - } - } - } - } - - void check_inner_outer_roles() { - if (debug()) { - std::cerr << " Checking inner/outer roles\n"; - } - - std::unordered_map way_rings; - std::unordered_set ways_in_multiple_rings; - - for (const detail::ProtoRing& ring : m_rings) { - for (const auto& segment : ring.segments()) { - assert(segment->way()); - - if (!segment->role_empty() && (ring.is_outer() ? !segment->role_outer() : !segment->role_inner())) { - ++m_stats.wrong_role; - if (debug()) { - std::cerr << " Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name() - << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n"; - } - if (m_config.problem_reporter) { - if (ring.is_outer()) { - m_config.problem_reporter->report_role_should_be_outer(segment->way()->id(), segment->first().location(), segment->second().location()); - } else { - m_config.problem_reporter->report_role_should_be_inner(segment->way()->id(), segment->first().location(), segment->second().location()); - } - } - } - - auto& r = way_rings[segment->way()]; - if (!r) { - r = ˚ - } else if (r != &ring) { - ways_in_multiple_rings.insert(segment->way()); - } - - } - } - - for (const osmium::Way* way : ways_in_multiple_rings) { - ++m_stats.ways_in_multiple_rings; - if (debug()) { - std::cerr << " Way " << way->id() << " is in multiple rings\n"; - } - if (m_config.problem_reporter) { - m_config.problem_reporter->report_way_in_multiple_rings(*way); - } - } - - } - - detail::NodeRefSegment* get_next_segment(const osmium::Location& location) { - auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { - return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); - }); - - assert(it != m_locations.end()); - if (m_segment_list[it->item].is_done()) { - ++it; - } - assert(it != m_locations.end()); - - assert(!m_segment_list[it->item].is_done()); - return &m_segment_list[it->item]; - } - - class rings_stack_element { - - double m_y; - detail::ProtoRing* m_ring_ptr; - - public: - - rings_stack_element(double y, detail::ProtoRing* ring_ptr) : - m_y(y), - m_ring_ptr(ring_ptr) { - } - - double y() const noexcept { - return m_y; - } - - const detail::ProtoRing& ring() const noexcept { - return *m_ring_ptr; - } - - detail::ProtoRing* ring_ptr() noexcept { - return m_ring_ptr; - } - - bool operator==(const rings_stack_element& rhs) const noexcept { - return m_ring_ptr == rhs.m_ring_ptr; - } - - bool operator<(const rings_stack_element& rhs) const noexcept { - return m_y < rhs.m_y; - } - - }; // class rings_stack_element - - using rings_stack = std::vector; - - void remove_duplicates(rings_stack& outer_rings) { - while (true) { - const auto it = std::adjacent_find(outer_rings.begin(), outer_rings.end()); - if (it == outer_rings.end()) { - return; - } - outer_rings.erase(it, std::next(it, 2)); - } - } - - detail::ProtoRing* find_enclosing_ring(detail::NodeRefSegment* segment) { - if (debug()) { - std::cerr << " Looking for ring enclosing " << *segment << "\n"; - } - - const auto location = segment->first().location(); - const auto end_location = segment->second().location(); - - while (segment->first().location() == location) { - if (segment == &m_segment_list.back()) { - break; - } - ++segment; - } - - int nesting = 0; - - rings_stack outer_rings; - while (segment >= &m_segment_list.front()) { - if (!segment->is_direction_done()) { - --segment; - continue; - } - if (debug()) { - std::cerr << " Checking against " << *segment << "\n"; - } - const osmium::Location& a = segment->first().location(); - const osmium::Location& b = segment->second().location(); - - if (segment->first().location() == location) { - const int64_t ax = a.x(); - const int64_t bx = b.x(); - const int64_t lx = end_location.x(); - const int64_t ay = a.y(); - const int64_t by = b.y(); - const int64_t ly = end_location.y(); - const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); - if (debug()) { - std::cerr << " Segment XXXX z=" << z << "\n"; - } - if (z > 0) { - nesting += segment->is_reverse() ? -1 : 1; - if (debug()) { - std::cerr << " Segment is below (nesting=" << nesting << ")\n"; - } - if (segment->ring()->is_outer()) { - if (debug()) { - std::cerr << " Segment belongs to outer ring\n"; - } - outer_rings.emplace_back(a.y(), segment->ring()); - } - } - } else if (a.x() <= location.x() && location.x() < b.x()) { - if (debug()) { - std::cerr << " Is in x range\n"; - } - - const int64_t ax = a.x(); - const int64_t bx = b.x(); - const int64_t lx = location.x(); - const int64_t ay = a.y(); - const int64_t by = b.y(); - const int64_t ly = location.y(); - const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); - - if (z >= 0) { - nesting += segment->is_reverse() ? -1 : 1; - if (debug()) { - std::cerr << " Segment is below (nesting=" << nesting << ")\n"; - } - if (segment->ring()->is_outer()) { - if (debug()) { - std::cerr << " Segment belongs to outer ring\n"; - } - const double y = ay + (by - ay) * (lx - ax) / double(bx - ax); - outer_rings.emplace_back(y, segment->ring()); - } - } - } - --segment; - } - - if (nesting % 2 == 0) { - if (debug()) { - std::cerr << " Decided that this is an outer ring\n"; - } - return nullptr; - } else { - if (debug()) { - std::cerr << " Decided that this is an inner ring\n"; - } - assert(!outer_rings.empty()); - - std::sort(outer_rings.rbegin(), outer_rings.rend()); - if (debug()) { - for (const auto& o : outer_rings) { - std::cerr << " y=" << o.y() << " " << o.ring() << "\n"; - } - } - - remove_duplicates(outer_rings); - if (debug()) { - std::cerr << " after remove duplicates:\n"; - for (const auto& o : outer_rings) { - std::cerr << " y=" << o.y() << " " << o.ring() << "\n"; - } - } - - assert(!outer_rings.empty()); - return outer_rings.front().ring_ptr(); - } - } - - bool is_split_location(const osmium::Location& location) const noexcept { - return std::find(m_split_locations.cbegin(), m_split_locations.cend(), location) != m_split_locations.cend(); - } - - uint32_t add_new_ring(slocation& node) { - detail::NodeRefSegment* segment = &m_segment_list[node.item]; - assert(!segment->is_done()); - - if (debug()) { - std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; - } - - if (node.reverse) { - segment->reverse(); - } - - detail::ProtoRing* outer_ring = nullptr; - - if (segment != &m_segment_list.front()) { - outer_ring = find_enclosing_ring(segment); - } - segment->mark_direction_done(); - - m_rings.emplace_back(segment); - detail::ProtoRing* ring = &m_rings.back(); - if (outer_ring) { - if (debug()) { - std::cerr << " This is an inner ring. Outer ring is " << *outer_ring << "\n"; - } - outer_ring->add_inner_ring(ring); - ring->set_outer_ring(outer_ring); - } else if (debug()) { - std::cerr << " This is an outer ring\n"; - } - - const osmium::Location& first_location = node.location(m_segment_list); - osmium::Location last_location = segment->stop().location(); - - uint32_t nodes = 1; - while (first_location != last_location) { - ++nodes; - detail::NodeRefSegment* next_segment = get_next_segment(last_location); - next_segment->mark_direction_done(); - if (next_segment->start().location() != last_location) { - next_segment->reverse(); - } - ring->add_segment_back(next_segment); - if (debug()) { - std::cerr << " Next segment is " << *next_segment << "\n"; - } - last_location = next_segment->stop().location(); - } - - ring->fix_direction(); - - if (debug()) { - std::cerr << " Completed ring: " << *ring << "\n"; - } - - return nodes; - } - - uint32_t add_new_ring_complex(slocation& node) { - detail::NodeRefSegment* segment = &m_segment_list[node.item]; - assert(!segment->is_done()); - - if (debug()) { - std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; - } - - if (node.reverse) { - segment->reverse(); - } - - m_rings.emplace_back(segment); - detail::ProtoRing* ring = &m_rings.back(); - - const osmium::Location& first_location = node.location(m_segment_list); - osmium::Location last_location = segment->stop().location(); - - uint32_t nodes = 1; - while (first_location != last_location && !is_split_location(last_location)) { - ++nodes; - detail::NodeRefSegment* next_segment = get_next_segment(last_location); - if (next_segment->start().location() != last_location) { - next_segment->reverse(); - } - ring->add_segment_back(next_segment); - if (debug()) { - std::cerr << " Next segment is " << *next_segment << "\n"; - } - last_location = next_segment->stop().location(); - } - - if (debug()) { - if (first_location == last_location) { - std::cerr << " Completed ring: " << *ring << "\n"; - } else { - std::cerr << " Completed partial ring: " << *ring << "\n"; - } - } - - return nodes; - } - - void create_locations_list() { - m_locations.reserve(m_segment_list.size() * 2); - - for (uint32_t n = 0; n < m_segment_list.size(); ++n) { - m_locations.emplace_back(n, false); - m_locations.emplace_back(n, true); - } - - std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& lhs, const slocation& rhs) { - return lhs.location(m_segment_list) < rhs.location(m_segment_list); - }); - } - - void find_inner_outer_complex(detail::ProtoRing* ring) { - detail::ProtoRing* outer_ring = find_enclosing_ring(ring->min_segment()); - if (outer_ring) { - outer_ring->add_inner_ring(ring); - ring->set_outer_ring(outer_ring); - } - ring->fix_direction(); - ring->mark_direction_done(); - } - - void find_inner_outer_complex() { - if (debug()) { - std::cerr << " Finding inner/outer rings\n"; - } - std::vector rings; - rings.reserve(m_rings.size()); - for (auto& ring : m_rings) { - if (ring.closed()) { - rings.push_back(&ring); - } - } - - if (rings.empty()) { - return; - } - - std::sort(rings.begin(), rings.end(), [](detail::ProtoRing* a, detail::ProtoRing* b) { - return a->min_segment() < b->min_segment(); - }); - - rings.front()->fix_direction(); - rings.front()->mark_direction_done(); - if (debug()) { - std::cerr << " First ring is outer: " << *rings.front() << "\n"; - } - for (auto it = std::next(rings.begin()); it != rings.end(); ++it) { - if (debug()) { - std::cerr << " Checking (at min segment " << *((*it)->min_segment()) << ") ring " << **it << "\n"; - } - find_inner_outer_complex(*it); - if (debug()) { - std::cerr << " Ring is " << ((*it)->is_outer() ? "OUTER: " : "INNER: ") << **it << "\n"; - } - } - } - - /** - * Finds all locations where more than two segments meet. If there - * are any open rings found along the way, they are reported - * and the function returns false. - */ - bool find_split_locations() { - osmium::Location previous_location; - for (auto it = m_locations.cbegin(); it != m_locations.cend(); ++it) { - const osmium::NodeRef& nr = it->node_ref(m_segment_list); - const osmium::Location& loc = nr.location(); - if (std::next(it) == m_locations.cend() || loc != std::next(it)->location(m_segment_list)) { - if (debug()) { - std::cerr << " Found open ring at " << nr << "\n"; - } - if (m_config.problem_reporter) { - const auto& segment = m_segment_list[it->item]; - m_config.problem_reporter->report_ring_not_closed(nr, segment.way()); - } - ++m_stats.open_rings; - } else { - if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location )) { - m_split_locations.push_back(previous_location); - } - ++it; - if (it == m_locations.end()) { - break; - } - } - previous_location = loc; - } - return m_stats.open_rings == 0; - } - - void create_rings_simple_case() { - auto count_remaining = m_segment_list.size(); - for (slocation& sl : m_locations) { - const detail::NodeRefSegment& segment = m_segment_list[sl.item]; - if (!segment.is_done()) { - count_remaining -= add_new_ring(sl); - if (count_remaining == 0) { - return; - } - } - } - } - - std::vector create_location_to_ring_map(open_ring_its_type& open_ring_its) { - std::vector xrings; - xrings.reserve(open_ring_its.size() * 2); - - for (auto it = open_ring_its.begin(); it != open_ring_its.end(); ++it) { - if (debug()) { - std::cerr << " Ring: " << **it << "\n"; - } - xrings.emplace_back((*it)->get_node_ref_start().location(), it, true); - xrings.emplace_back((*it)->get_node_ref_stop().location(), it, false); - } - - std::sort(xrings.begin(), xrings.end()); - - return xrings; - } - - void merge_two_rings(open_ring_its_type& open_ring_its, const location_to_ring_map& m1, const location_to_ring_map& m2) { - std::list::iterator r1 = *m1.ring_it; - std::list::iterator r2 = *m2.ring_it; - - if (r1->get_node_ref_stop().location() == r2->get_node_ref_start().location()) { - r1->join_forward(*r2); - } else if (r1->get_node_ref_stop().location() == r2->get_node_ref_stop().location()) { - r1->join_backward(*r2); - } else if (r1->get_node_ref_start().location() == r2->get_node_ref_start().location()) { - r1->reverse(); - r1->join_forward(*r2); - } else if (r1->get_node_ref_start().location() == r2->get_node_ref_stop().location()) { - r1->reverse(); - r1->join_backward(*r2); - } else { - assert(false); - } - - open_ring_its.erase(std::find(open_ring_its.begin(), open_ring_its.end(), r2)); - m_rings.erase(r2); - - if (r1->closed()) { - open_ring_its.erase(std::find(open_ring_its.begin(), open_ring_its.end(), r1)); - } - } - - bool try_to_merge(open_ring_its_type& open_ring_its) { - if (open_ring_its.empty()) { - return false; - } - - if (debug()) { - std::cerr << " Trying to merge " << open_ring_its.size() << " open rings\n"; - } - - std::vector xrings = create_location_to_ring_map(open_ring_its); - - auto it = xrings.cbegin(); - while (it != xrings.cend()) { - it = std::adjacent_find(it, xrings.cend()); - if (it == xrings.cend()) { - return false; - } - auto after = std::next(it, 2); - if (after == xrings.cend() || after->location != it->location) { - if (debug()) { - std::cerr << " Merging two rings\n"; - } - merge_two_rings(open_ring_its, *it, *std::next(it)); - return true; - } - while (it != xrings.cend() && it->location == after->location) { - ++it; - } - } - - return false; - } - - bool there_are_open_rings() const noexcept { - return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){ - return !ring.closed(); - }); - } - - struct candidate { - int64_t sum; - std::vector> rings; - osmium::Location start_location; - osmium::Location stop_location; - - explicit candidate(location_to_ring_map& ring, bool reverse) : - sum(ring.ring().sum()), - rings(), - start_location(ring.ring().get_node_ref_start().location()), - stop_location(ring.ring().get_node_ref_stop().location()) { - rings.emplace_back(ring, reverse); - } - - bool closed() const noexcept { - return start_location == stop_location; - } - - }; - - void find_candidates(std::vector& candidates, std::unordered_set& loc_done, const std::vector& xrings, candidate& cand) { - if (debug()) { - std::cerr << " find_candidates sum=" << cand.sum << " start=" << cand.start_location << " stop=" << cand.stop_location << "\n"; - for (const auto& ring : cand.rings) { - std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; - } - } - - const auto connections = make_range(std::equal_range(xrings.cbegin(), - xrings.cend(), - location_to_ring_map{cand.stop_location})); - - assert(connections.begin() != connections.end()); - - assert(!cand.rings.empty()); - const detail::ProtoRing* ring_leading_here = &cand.rings.back().first.ring(); - for (const location_to_ring_map& m : connections) { - const detail::ProtoRing& ring = m.ring(); - - if (&ring != ring_leading_here) { - if (debug()) { - std::cerr << " next possible connection: " << ring << (m.start ? "" : " reverse") << "\n"; - } - - candidate c = cand; - if (m.start) { - c.rings.emplace_back(m, false); - c.stop_location = ring.get_node_ref_stop().location(); - c.sum += ring.sum(); - } else { - c.rings.emplace_back(m, true); - c.stop_location = ring.get_node_ref_start().location(); - c.sum -= ring.sum(); - } - if (c.closed()) { - if (debug()) { - std::cerr << " found candidate\n"; - } - candidates.push_back(c); - } else if (loc_done.count(c.stop_location) == 0) { - if (debug()) { - std::cerr << " recurse...\n"; - } - loc_done.insert(c.stop_location); - find_candidates(candidates, loc_done, xrings, c); - loc_done.erase(c.stop_location); - if (debug()) { - std::cerr << " ...back\n"; - } - } else if (debug()) { - std::cerr << " loop found\n"; - } - } - } - } - - /** - * If there are multiple open rings and mltiple ways to join them, - * this function is called. It will take the first open ring and - * try recursively all ways of closing it. Of all the candidates - * the one with the smallest area is chosen and closed. If it - * can't close this ring, an error is reported and the function - * returns false. - */ - bool join_connected_rings(open_ring_its_type& open_ring_its) { - assert(!open_ring_its.empty()); - - if (debug()) { - std::cerr << " Trying to merge " << open_ring_its.size() << " open rings\n"; - } - - std::vector xrings = create_location_to_ring_map(open_ring_its); - - const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& lhs, const location_to_ring_map& rhs) { - return lhs.ring().min_segment() < rhs.ring().min_segment(); - }); - - find_inner_outer_complex(); - detail::ProtoRing* outer_ring = find_enclosing_ring(ring_min->ring().min_segment()); - bool ring_min_is_outer = !outer_ring; - if (debug()) { - std::cerr << " Open ring is " << (ring_min_is_outer ? "outer" : "inner") << " ring\n"; - } - for (auto& ring : m_rings) { - ring.reset(); - } - - candidate cand{*ring_min, false}; - - // Locations we have visited while finding candidates, used - // to detect loops. - std::unordered_set loc_done; - - loc_done.insert(cand.stop_location); - - std::vector candidates; - find_candidates(candidates, loc_done, xrings, cand); - - if (candidates.empty()) { - if (debug()) { - std::cerr << " Found no candidates\n"; - } - if (!open_ring_its.empty()) { - ++m_stats.open_rings; - if (m_config.problem_reporter) { - for (auto& it : open_ring_its) { - m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start()); - m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop()); - } - } - } - return false; - } - - if (debug()) { - std::cerr << " Found candidates:\n"; - for (const auto& cand : candidates) { - std::cerr << " sum=" << cand.sum << "\n"; - for (const auto& ring : cand.rings) { - std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; - } - } - } - - // Find the candidate with the smallest/largest area - const auto chosen_cand = ring_min_is_outer ? - std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { - return std::abs(lhs.sum) < std::abs(rhs.sum); - }) : - std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { - return std::abs(lhs.sum) < std::abs(rhs.sum); - }); - - if (debug()) { - std::cerr << " Decided on: sum=" << chosen_cand->sum << "\n"; - for (const auto& ring : chosen_cand->rings) { - std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; - } - } - - // Join all (open) rings in the candidate to get one closed ring. - assert(chosen_cand->rings.size() > 1); - const auto& first_ring = chosen_cand->rings.front().first; - const detail::ProtoRing& remaining_ring = first_ring.ring(); - for (auto it = std::next(chosen_cand->rings.begin()); it != chosen_cand->rings.end(); ++it) { - merge_two_rings(open_ring_its, first_ring, it->first); - } - - if (debug()) { - std::cerr << " Merged to " << remaining_ring << "\n"; - } - - return true; - } - - bool create_rings_complex_case() { - // First create all the (partial) rings starting at the split locations - auto count_remaining = m_segment_list.size(); - for (const osmium::Location& location : m_split_locations) { - const auto locs = make_range(std::equal_range(m_locations.begin(), - m_locations.end(), - slocation{}, - [this, &location](const slocation& lhs, const slocation& rhs) { - return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); - })); - for (auto& loc : locs) { - if (!m_segment_list[loc.item].is_done()) { - count_remaining -= add_new_ring_complex(loc); - if (count_remaining == 0) { - break; - } - } - } - } - - // Now find all the rest of the rings (ie not starting at split locations) - if (count_remaining > 0) { - for (slocation& sl : m_locations) { - const detail::NodeRefSegment& segment = m_segment_list[sl.item]; - if (!segment.is_done()) { - count_remaining -= add_new_ring_complex(sl); - if (count_remaining == 0) { - break; - } - } - } - } - - // Now all segments are in exactly one (partial) ring. - - // If there are open rings, try to join them to create closed - // rings. - if (there_are_open_rings()) { - ++m_stats.area_really_complex_case; - - open_ring_its_type open_ring_its; - for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { - if (!it->closed()) { - open_ring_its.push_back(it); - } - } - - while (!open_ring_its.empty()) { - if (debug()) { - std::cerr << " There are " << open_ring_its.size() << " open rings\n"; - } - while (try_to_merge(open_ring_its)); - - if (!open_ring_its.empty()) { - if (debug()) { - std::cerr << " After joining obvious cases there are still " << open_ring_its.size() << " open rings\n"; - } - if (!join_connected_rings(open_ring_its)) { - return false; - } - } - } - - if (debug()) { - std::cerr << " Joined all open rings\n"; - } - } - - // Now all rings are complete. - - find_inner_outer_complex(); - - return true; - } - - /** - * Checks if any ways were completely removed in the - * erase_duplicate_segments step. - */ - bool ways_were_lost() { - std::unordered_set ways_in_segments; - - for (const auto& segment : m_segment_list) { - ways_in_segments.insert(segment.way()); - } - - return ways_in_segments.size() < m_num_members; - } - - /** - * Create rings from segments. - */ - bool create_rings() { - m_stats.nodes += m_segment_list.size(); - - // Sort the list of segments (from left to right and bottom - // to top). - osmium::Timer timer_sort; - m_segment_list.sort(); - timer_sort.stop(); - - // Remove duplicate segments. Removal is in pairs, so if there - // are two identical segments, they will both be removed. If - // there are three, two will be removed and one remains. - osmium::Timer timer_dupl; - m_stats.duplicate_segments = m_segment_list.erase_duplicate_segments(m_config.problem_reporter); - timer_dupl.stop(); - - // If there are no segments left at this point, this isn't - // a valid area. - if (m_segment_list.empty()) { - if (debug()) { - std::cerr << " No segments left\n"; - } - return false; - } - - // If one or more complete ways was removed because of - // duplicate segments, this isn't a valid area. - if (ways_were_lost()) { - if (debug()) { - std::cerr << " Complete ways removed because of duplicate segments\n"; - } - return false; - } - - if (m_config.debug_level >= 3) { - std::cerr << "Sorted de-duplicated segment list:\n"; - for (const auto& s : m_segment_list) { - std::cerr << " " << s << "\n"; - } - } - - // Now we look for segments crossing each other. If there are - // any, the multipolygon is invalid. - // In the future this could be improved by trying to fix those - // cases. - osmium::Timer timer_intersection; - m_stats.intersections = m_segment_list.find_intersections(m_config.problem_reporter); - timer_intersection.stop(); - - if (m_stats.intersections) { - return false; - } - - // This creates an ordered list of locations of both endpoints - // of all segments with pointers back to the segments. We will - // use this list later to quickly find which segment(s) fits - // onto a known segment. - osmium::Timer timer_locations_list; - create_locations_list(); - timer_locations_list.stop(); - - // Find all locations where more than two segments start or - // end. We call those "split" locations. If there are any - // "spike" segments found while doing this, we know the area - // geometry isn't valid and return. - osmium::Timer timer_split; - if (!find_split_locations()) { - return false; - } - timer_split.stop(); - - // Now report all split locations to the problem reporter. - m_stats.touching_rings += m_split_locations.size(); - if (!m_split_locations.empty()) { - if (debug()) { - std::cerr << " Found split locations:\n"; - } - for (const auto& location : m_split_locations) { - if (m_config.problem_reporter) { - auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { - return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); - }); - assert(it != m_locations.cend()); - const osmium::object_id_type id = it->node_ref(m_segment_list).ref(); - m_config.problem_reporter->report_touching_ring(id, location); - } - if (debug()) { - std::cerr << " " << location << "\n"; - } - } - } - - // From here on we use two different algorithms depending on - // whether there were any split locations or not. If there - // are no splits, we use the faster "simple algorithm", if - // there are, we use the slower "complex algorithm". - osmium::Timer timer_simple_case; - osmium::Timer timer_complex_case; - if (m_split_locations.empty()) { - if (debug()) { - std::cerr << " No split locations -> using simple algorithm\n"; - } - ++m_stats.area_simple_case; - - timer_simple_case.start(); - create_rings_simple_case(); - timer_simple_case.stop(); - } else { - if (debug()) { - std::cerr << " Found split locations -> using complex algorithm\n"; - } - ++m_stats.area_touching_rings_case; - - timer_complex_case.start(); - if (!create_rings_complex_case()) { - return false; - } - timer_complex_case.stop(); - } - - // If the assembler was so configured, now check whether the - // member roles are correctly tagged. - if (m_config.check_roles && m_stats.from_relations) { - osmium::Timer timer_roles; - check_inner_outer_roles(); - timer_roles.stop(); - } - - m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){ - return ring.is_outer(); - }); - m_stats.inner_rings = m_rings.size() - m_stats.outer_rings; - -#ifdef OSMIUM_WITH_TIMER - std::cout << m_stats.nodes << ' ' << m_stats.outer_rings << ' ' << m_stats.inner_rings << - ' ' << timer_sort.elapsed_microseconds() << - ' ' << timer_dupl.elapsed_microseconds() << - ' ' << timer_intersection.elapsed_microseconds() << - ' ' << timer_locations_list.elapsed_microseconds() << - ' ' << timer_split.elapsed_microseconds(); - - if (m_split_locations.empty()) { - std::cout << ' ' << timer_simple_case.elapsed_microseconds() << - " 0"; - } else { - std::cout << " 0" << - ' ' << timer_complex_case.elapsed_microseconds(); - } - - std::cout << -# ifdef OSMIUM_AREA_CHECK_INNER_OUTER_ROLES - ' ' << timer_roles.elapsed_microseconds() << -# else - " 0" << -# endif - '\n'; -#endif - - return true; - } - -#ifdef OSMIUM_WITH_TIMER - static bool print_header() { - std::cout << "nodes outer_rings inner_rings sort dupl intersection locations split simple_case complex_case roles_check\n"; - return true; - } - - static bool init_header() { - static bool printed_print_header = print_header(); - return printed_print_header; - } -#endif + class Assembler : public detail::BasicAssemblerWithTags { bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) { osmium::builder::AreaBuilder builder{out_buffer}; builder.initialize_from_object(way); const bool area_okay = create_rings(); - if (area_okay || m_config.create_empty_areas) { - add_tags_to_area(builder, way); + if (area_okay || config().create_empty_areas) { + builder.add_item(way.tags()); } if (area_okay) { add_rings_to_area(builder); } if (report_ways()) { - m_config.problem_reporter->report_way(way); + config().problem_reporter->report_way(way); } - return area_okay || m_config.create_empty_areas; + return area_okay || config().create_empty_areas; } bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector& members) { - m_num_members = members.size(); + set_num_members(members.size()); osmium::builder::AreaBuilder builder{out_buffer}; builder.initialize_from_object(relation); const bool area_okay = create_rings(); - if (area_okay || m_config.create_empty_areas) { - add_tags_to_area(builder, relation); + if (area_okay || config().create_empty_areas) { + if (config().keep_type_tag) { + builder.add_item(relation.tags()); + } else { + copy_tags_without_type(builder, relation.tags()); + } } if (area_okay) { add_rings_to_area(builder); @@ -1397,23 +98,17 @@ namespace osmium { if (report_ways()) { for (const osmium::Way* way : members) { - m_config.problem_reporter->report_way(*way); + config().problem_reporter->report_way(*way); } } - return area_okay || m_config.create_empty_areas; + return area_okay || config().create_empty_areas; } public: - using config_type = osmium::area::AssemblerConfig; - explicit Assembler(const config_type& config) : - m_config(config), - m_segment_list(config.debug_level > 1) { -#ifdef OSMIUM_WITH_TIMER - init_header(); -#endif + detail::BasicAssemblerWithTags(config) { } ~Assembler() noexcept = default; @@ -1421,165 +116,113 @@ namespace osmium { /** * Assemble an area from the given way. * The resulting area is put into the out_buffer. + * + * @returns false if there was some kind of error building the + * area, true otherwise. */ - void operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) { - if (!m_config.create_way_polygons) { - return; + bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) { + if (!config().create_way_polygons) { + return true; } - if (way.tags().has_tag("area", "no")) { - return; - } - - if (m_config.problem_reporter) { - m_config.problem_reporter->set_object(osmium::item_type::way, way.id()); - m_config.problem_reporter->set_nodes(way.nodes().size()); + if (config().problem_reporter) { + config().problem_reporter->set_object(osmium::item_type::way, way.id()); + config().problem_reporter->set_nodes(way.nodes().size()); } // Ignore (but count) ways without segments. if (way.nodes().size() < 2) { - ++m_stats.short_ways; - return; + ++stats().short_ways; + return false; } if (!way.ends_have_same_id()) { - ++m_stats.duplicate_nodes; - if (m_config.problem_reporter) { - m_config.problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location()); + ++stats().duplicate_nodes; + if (config().problem_reporter) { + config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location()); } } - ++m_stats.from_ways; - m_stats.duplicate_nodes += m_segment_list.extract_segments_from_way(m_config.problem_reporter, way); + ++stats().from_ways; + stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter, + stats().duplicate_nodes, + way); + if (!config().ignore_invalid_locations && stats().invalid_locations > 0) { + return false; + } - if (m_config.debug_level > 0) { - std::cerr << "\nAssembling way " << way.id() << " containing " << m_segment_list.size() << " nodes\n"; + if (config().debug_level > 0) { + std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n"; } // Now create the Area object and add the attributes and tags // from the way. - if (create_area(out_buffer, way)) { + const bool okay = create_area(out_buffer, way); + if (okay) { out_buffer.commit(); } else { out_buffer.rollback(); } if (debug()) { - std::cerr << "Done: " << m_stats << "\n"; + std::cerr << "Done: " << stats() << "\n"; } + + return okay; } /** * Assemble an area from the given relation and its members. - * All members are to be found in the in_buffer at the offsets - * given by the members parameter. * The resulting area is put into the out_buffer. * - * @deprecated - * This function is deprecated. Use the other form of the function - * instead. + * @returns false if there was some kind of error building the + * area(s), true otherwise. */ - OSMIUM_DEPRECATED void operator()(const osmium::Relation& relation, const std::vector& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) { - std::vector ways; - for (size_t offset : members) { - const osmium::Way& way = in_buffer.get(offset); - ways.push_back(&way); + bool operator()(const osmium::Relation& relation, const std::vector& members, osmium::memory::Buffer& out_buffer) { + if (!config().create_new_style_polygons) { + return true; } - operator()(relation, ways, out_buffer); - } - /** - * Assemble an area from the given relation and its members. - * The resulting area is put into the out_buffer. - */ - void operator()(const osmium::Relation& relation, const std::vector& members, osmium::memory::Buffer& out_buffer) { - assert(relation.members().size() >= members.size()); + assert(relation.cmembers().size() >= members.size()); - if (m_config.problem_reporter) { - m_config.problem_reporter->set_object(osmium::item_type::relation, relation.id()); + if (config().problem_reporter) { + config().problem_reporter->set_object(osmium::item_type::relation, relation.id()); } if (relation.members().empty()) { - ++m_stats.no_way_in_mp_relation; - return; + ++stats().no_way_in_mp_relation; + return false; } - ++m_stats.from_relations; - m_stats.duplicate_nodes += m_segment_list.extract_segments_from_ways(m_config.problem_reporter, relation, members); - m_stats.member_ways = members.size(); + ++stats().from_relations; + stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter, + stats().duplicate_nodes, + stats().duplicate_ways, + relation, + members); + if (!config().ignore_invalid_locations && stats().invalid_locations > 0) { + return false; + } + stats().member_ways = members.size(); - if (m_stats.member_ways == 1) { - ++m_stats.single_way_in_mp_relation; + if (stats().member_ways == 1) { + ++stats().single_way_in_mp_relation; } - if (m_config.debug_level > 0) { - std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << m_segment_list.size() << " nodes\n"; + if (config().debug_level > 0) { + std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n"; } - const size_t area_offset = out_buffer.committed(); - // Now create the Area object and add the attributes and tags // from the relation. - if (create_area(out_buffer, relation, members)) { - if ((m_config.create_new_style_polygons && m_stats.no_tags_on_relation == 0) || - (m_config.create_old_style_polygons && m_stats.no_tags_on_relation != 0)) { - out_buffer.commit(); - } else { - out_buffer.rollback(); - } + bool okay = create_area(out_buffer, relation, members); + if (okay) { + out_buffer.commit(); } else { out_buffer.rollback(); } - const osmium::TagList& area_tags = out_buffer.get(area_offset).tags(); // tags of the area we just built - - // Find all closed ways that are inner rings and check their - // tags. If they are not the same as the tags of the area we - // just built, add them to a list and later build areas for - // them, too. - std::vector ways_that_should_be_areas; - if (m_stats.wrong_role == 0) { - detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) { - if (!std::strcmp(member.role(), "inner")) { - if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) { - const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), filter()); - if (d > 0) { - osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().cbegin(), way.tags().cend()); - osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().cend(), way.tags().cend()); - osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.cbegin(), area_tags.cend()); - osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.cend(), area_tags.cend()); - - if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) { - ways_that_should_be_areas.push_back(&way); - } else { - ++m_stats.inner_with_same_tags; - if (m_config.problem_reporter) { - m_config.problem_reporter->report_inner_with_same_tags(way); - } - } - } - } - } - }); - } - - if (debug()) { - std::cerr << "Done: " << m_stats << "\n"; - } - - // Now build areas for all ways found in the last step. - for (const osmium::Way* way : ways_that_should_be_areas) { - Assembler assembler(m_config); - assembler(*way, out_buffer); - } - } - - /** - * Get statistics from assembler. Call this after running the - * assembler to get statistics and data about errors. - */ - const osmium::area::area_stats& stats() const noexcept { - return m_stats; + return okay; } }; // class Assembler diff --git a/third_party/libosmium/include/osmium/area/assembler_config.hpp b/third_party/libosmium/include/osmium/area/assembler_config.hpp new file mode 100644 index 000000000..24bd60a99 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/assembler_config.hpp @@ -0,0 +1,154 @@ +#ifndef OSMIUM_AREA_ASSEMBLER_CONFIG_HPP +#define OSMIUM_AREA_ASSEMBLER_CONFIG_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace area { + + class ProblemReporter; + + /** + * Configuration for osmium::area::Assembler objects. Create this + * once, set the options you want and then re-use it every time you + * create an Assembler object. + */ + struct AssemblerConfig { + + /** + * Optional pointer to problem reporter. + */ + ProblemReporter* problem_reporter = nullptr; + + /** + * Debug level. If this is greater than zero, debug messages will + * be printed to stderr. Available levels are 1 to 3. Note that + * level 2 and above will generate a lot of messages! + */ + int debug_level = 0; + + /** + * The roles of multipolygon members are ignored when assembling + * multipolygons, because they are often missing or wrong. If this + * is set, the roles are checked after the multipolygons are built + * against what the assembly process decided where the inner and + * outer rings are. This slows down the processing, so it only + * makes sense if you want to get the problem reports. + */ + bool check_roles = false; + + /** + * When the assembler can't create an area, usually because its + * geometry would be invalid, it will create an "empty" area object + * without rings. This allows you to detect where an area was + * invalid. + * + * If this is set to false, invalid areas will simply be discarded. + */ + bool create_empty_areas = true; + + /** + * Create areas for (multi)polygons where the tags are on the + * relation. + * + * If this is set to false, those areas will simply be discarded. + */ + bool create_new_style_polygons = true; + + /** + * Create areas for (multi)polygons where the tags are on the + * outer way(s). This is ignored by the area::Assembler which + * doesn't support old-style multipolygons any more. Use the + * area::AssemblerLegacy if you need this. + * + * If this is set to false, those areas will simply be discarded. + */ + bool create_old_style_polygons = true; + + /** + * Create areas for polygons created from ways. + * + * If this is set to false, those areas will simply be discarded. + */ + bool create_way_polygons = true; + + /** + * Keep the type tag from multipolygon relations on the area + * object. By default this is false, and the type tag will be + * removed. + */ + bool keep_type_tag = false; + + /** + * If there is an invalid location in any of the ways needed for + * assembling the multipolygon, the assembler will normally fail. + * If this is set, the assembler will silently ignore the invalid + * locations pretending them to be not referenced from the ways. + * This will allow some areas to be built, others will now be + * incorrect. This can sometimes be useful to assemble areas + * crossing the boundary of an extract, but you will also get + * geometrically valid but wrong (multi)polygons. + */ + bool ignore_invalid_locations = false; + + AssemblerConfig() noexcept = default; + + /** + * Constructor + * @deprecated Use default constructor and set values afterwards. + */ + explicit AssemblerConfig(ProblemReporter* pr, bool d = false) : + problem_reporter(pr), + debug_level(d) { + } + + /** + * Enable or disable debug output to stderr. This is for Osmium + * developers only. + * + * @deprecated Set debug_level directly. + */ + OSMIUM_DEPRECATED void enable_debug_output(bool d = true) { + debug_level = d; + } + + }; // struct AssemblerConfig + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_ASSEMBLER_CONFIG_HPP diff --git a/third_party/libosmium/include/osmium/area/assembler_legacy.hpp b/third_party/libosmium/include/osmium/area/assembler_legacy.hpp new file mode 100644 index 000000000..e616c947c --- /dev/null +++ b/third_party/libosmium/include/osmium/area/assembler_legacy.hpp @@ -0,0 +1,382 @@ +#ifndef OSMIUM_AREA_ASSEMBLER_LEGACY_HPP +#define OSMIUM_AREA_ASSEMBLER_LEGACY_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 { + + namespace area { + + /** + * Assembles area objects from closed ways or multipolygon relations + * and their members. + */ + class AssemblerLegacy : public detail::BasicAssemblerWithTags { + + void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const { + builder.add_item(way.tags()); + } + + void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set& ways) const { + std::map counter; + for (const osmium::Way* way : ways) { + for (const auto& tag : way->tags()) { + std::string kv{tag.key()}; + kv.append(1, '\0'); + kv.append(tag.value()); + ++counter[kv]; + } + } + + const std::size_t num_ways = ways.size(); + for (const auto& t_c : counter) { + if (debug()) { + std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n"; + } + if (t_c.second == num_ways) { + const std::size_t len = std::strlen(t_c.first.c_str()); + tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1); + } + } + } + + struct MPFilter : public osmium::tags::KeyFilter { + + MPFilter() : osmium::tags::KeyFilter(true) { + add(false, "type"); + add(false, "created_by"); + add(false, "source"); + add(false, "note"); + add(false, "test:id"); + add(false, "test:section"); + } + + }; // struct MPFilter + + static const MPFilter& filter() noexcept { + static const MPFilter filter; + return filter; + } + + void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) { + const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), std::cref(filter())); + + if (debug()) { + std::cerr << " found " << count << " tags on relation (without ignored ones)\n"; + } + + if (count > 0) { + if (debug()) { + std::cerr << " use tags from relation\n"; + } + + if (config().keep_type_tag) { + builder.add_item(relation.tags()); + } else { + copy_tags_without_type(builder, relation.tags()); + } + } else { + ++stats().no_tags_on_relation; + if (debug()) { + std::cerr << " use tags from outer ways\n"; + } + std::set ways; + for (const auto& ring : rings()) { + if (ring.is_outer()) { + ring.get_ways(ways); + } + } + if (ways.size() == 1) { + if (debug()) { + std::cerr << " only one outer way\n"; + } + builder.add_item((*ways.cbegin())->tags()); + } else { + if (debug()) { + std::cerr << " multiple outer ways, get common tags\n"; + } + osmium::builder::TagListBuilder tl_builder{builder}; + add_common_tags(tl_builder, ways); + } + } + } + + bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) { + osmium::builder::AreaBuilder builder{out_buffer}; + builder.initialize_from_object(way); + + const bool area_okay = create_rings(); + if (area_okay || config().create_empty_areas) { + add_tags_to_area(builder, way); + } + if (area_okay) { + add_rings_to_area(builder); + } + + if (report_ways()) { + config().problem_reporter->report_way(way); + } + + return area_okay || config().create_empty_areas; + } + + bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector& members) { + set_num_members(members.size()); + osmium::builder::AreaBuilder builder{out_buffer}; + builder.initialize_from_object(relation); + + const bool area_okay = create_rings(); + if (area_okay || config().create_empty_areas) { + add_tags_to_area(builder, relation); + } + if (area_okay) { + add_rings_to_area(builder); + } + + if (report_ways()) { + for (const osmium::Way* way : members) { + config().problem_reporter->report_way(*way); + } + } + + return area_okay || config().create_empty_areas; + } + + public: + + explicit AssemblerLegacy(const config_type& config) : + detail::BasicAssemblerWithTags(config) { + } + + ~AssemblerLegacy() noexcept = default; + + /** + * Assemble an area from the given way. + * The resulting area is put into the out_buffer. + * + * @returns false if there was some kind of error building the + * area, true otherwise. + */ + bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) { + if (!config().create_way_polygons) { + return true; + } + + if (way.tags().has_tag("area", "no")) { + return true; + } + + if (config().problem_reporter) { + config().problem_reporter->set_object(osmium::item_type::way, way.id()); + config().problem_reporter->set_nodes(way.nodes().size()); + } + + // Ignore (but count) ways without segments. + if (way.nodes().size() < 2) { + ++stats().short_ways; + return false; + } + + if (!way.ends_have_same_id()) { + ++stats().duplicate_nodes; + if (config().problem_reporter) { + config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location()); + } + } + + ++stats().from_ways; + stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter, + stats().duplicate_nodes, + way); + if (!config().ignore_invalid_locations && stats().invalid_locations > 0) { + return false; + } + + if (config().debug_level > 0) { + std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n"; + } + + // Now create the Area object and add the attributes and tags + // from the way. + const bool okay = create_area(out_buffer, way); + if (okay) { + out_buffer.commit(); + } else { + out_buffer.rollback(); + } + + if (debug()) { + std::cerr << "Done: " << stats() << "\n"; + } + + return okay; + } + + /** + * Assemble an area from the given relation and its members. + * The resulting area is put into the out_buffer. + * + * @returns false if there was some kind of error building the + * area(s), true otherwise. + */ + bool operator()(const osmium::Relation& relation, const std::vector& members, osmium::memory::Buffer& out_buffer) { + assert(relation.members().size() >= members.size()); + + if (config().problem_reporter) { + config().problem_reporter->set_object(osmium::item_type::relation, relation.id()); + } + + if (relation.members().empty()) { + ++stats().no_way_in_mp_relation; + return false; + } + + ++stats().from_relations; + stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter, + stats().duplicate_nodes, + stats().duplicate_ways, + relation, + members); + if (!config().ignore_invalid_locations && stats().invalid_locations > 0) { + return false; + } + stats().member_ways = members.size(); + + if (stats().member_ways == 1) { + ++stats().single_way_in_mp_relation; + } + + if (config().debug_level > 0) { + std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n"; + } + + const std::size_t area_offset = out_buffer.committed(); + + // Now create the Area object and add the attributes and tags + // from the relation. + bool okay = create_area(out_buffer, relation, members); + if (okay) { + if ((config().create_new_style_polygons && stats().no_tags_on_relation == 0) || + (config().create_old_style_polygons && stats().no_tags_on_relation != 0)) { + out_buffer.commit(); + } else { + out_buffer.rollback(); + } + } else { + out_buffer.rollback(); + } + + const osmium::TagList& area_tags = out_buffer.get(area_offset).tags(); // tags of the area we just built + + // Find all closed ways that are inner rings and check their + // tags. If they are not the same as the tags of the area we + // just built, add them to a list and later build areas for + // them, too. + std::vector ways_that_should_be_areas; + if (stats().wrong_role == 0) { + detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) { + if (!std::strcmp(member.role(), "inner")) { + if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) { + const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter())); + if (d > 0) { + osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend()); + osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend()); + osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend()); + osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend()); + + if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) { + ways_that_should_be_areas.push_back(&way); + } else { + ++stats().inner_with_same_tags; + if (config().problem_reporter) { + config().problem_reporter->report_inner_with_same_tags(way); + } + } + } + } + } + }); + } + + if (debug()) { + std::cerr << "Done: " << stats() << "\n"; + } + + // Now build areas for all ways found in the last step. + for (const osmium::Way* way : ways_that_should_be_areas) { + AssemblerLegacy assembler{config()}; + if (!assembler(*way, out_buffer)) { + okay = false; + } + stats() += assembler.stats(); + } + + return okay; + } + + }; // class AssemblerLegacy + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_ASSEMBLER_LEGACY_HPP diff --git a/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp b/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp new file mode 100644 index 000000000..94168a12d --- /dev/null +++ b/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp @@ -0,0 +1,1203 @@ +#ifndef OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_HPP +#define OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace area { + + namespace detail { + + using open_ring_its_type = std::list::iterator>; + + struct location_to_ring_map { + osmium::Location location; + open_ring_its_type::iterator ring_it; + bool start; + + location_to_ring_map(const osmium::Location& l, const open_ring_its_type::iterator& r, bool s) noexcept : + location(l), + ring_it(r), + start(s) { + } + + explicit location_to_ring_map(const osmium::Location& l) noexcept : + location(l), + ring_it(), + start(false) { + } + + const ProtoRing& ring() const noexcept { + return **ring_it; + } + + }; // struct location_to_ring_map + + inline bool operator==(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { + return lhs.location == rhs.location; + } + + inline bool operator<(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { + return lhs.location < rhs.location; + } + + /** + * Class for assembling ways and relations into multipolygons + * (areas). Contains the basic functionality needed but is not + * used directly. Use the osmium::area::Assembler class instead. + */ + class BasicAssembler { + + struct slocation { + + static constexpr const uint32_t invalid_item = 1 << 30; + + uint32_t item : 31; + uint32_t reverse : 1; + + slocation() noexcept : + item(invalid_item), + reverse(false) { + } + + explicit slocation(uint32_t n, bool r = false) noexcept : + item(n), + reverse(r) { + } + + osmium::Location location(const SegmentList& segment_list) const noexcept { + const auto& segment = segment_list[item]; + return reverse ? segment.second().location() : segment.first().location(); + } + + const osmium::NodeRef& node_ref(const SegmentList& segment_list) const noexcept { + const auto& segment = segment_list[item]; + return reverse ? segment.second() : segment.first(); + } + + osmium::Location location(const SegmentList& segment_list, const osmium::Location& default_location) const noexcept { + if (item == invalid_item) { + return default_location; + } + return location(segment_list); + } + + }; // struct slocation + + // Configuration settings for this Assembler + const AssemblerConfig& m_config; + + // List of segments (connection between two nodes) + SegmentList m_segment_list; + + // The rings we are building from the segments + std::list m_rings; + + // All node locations + std::vector m_locations; + + // All locations where more than two segments start/end + std::vector m_split_locations; + + // Statistics + area_stats m_stats; + + // The number of members the multipolygon relation has + std::size_t m_num_members = 0; + + template + static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const ProtoRing& ring) { + TBuilder ring_builder{builder}; + ring_builder.add_node_ref(ring.get_node_ref_start()); + for (const auto& segment : ring.segments()) { + ring_builder.add_node_ref(segment->stop()); + } + } + + void check_inner_outer_roles() { + if (debug()) { + std::cerr << " Checking inner/outer roles\n"; + } + + std::unordered_map way_rings; + std::unordered_set ways_in_multiple_rings; + + for (const ProtoRing& ring : m_rings) { + for (const auto& segment : ring.segments()) { + assert(segment->way()); + + if (!segment->role_empty() && (ring.is_outer() ? !segment->role_outer() : !segment->role_inner())) { + ++m_stats.wrong_role; + if (debug()) { + std::cerr << " Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name() + << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n"; + } + if (m_config.problem_reporter) { + if (ring.is_outer()) { + m_config.problem_reporter->report_role_should_be_outer(segment->way()->id(), segment->first().location(), segment->second().location()); + } else { + m_config.problem_reporter->report_role_should_be_inner(segment->way()->id(), segment->first().location(), segment->second().location()); + } + } + } + + auto& r = way_rings[segment->way()]; + if (!r) { + r = ˚ + } else if (r != &ring) { + ways_in_multiple_rings.insert(segment->way()); + } + + } + } + + for (const osmium::Way* way : ways_in_multiple_rings) { + ++m_stats.ways_in_multiple_rings; + if (debug()) { + std::cerr << " Way " << way->id() << " is in multiple rings\n"; + } + if (m_config.problem_reporter) { + m_config.problem_reporter->report_way_in_multiple_rings(*way); + } + } + + } + + NodeRefSegment* get_next_segment(const osmium::Location& location) { + auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); + }); + + assert(it != m_locations.end()); + if (m_segment_list[it->item].is_done()) { + ++it; + } + assert(it != m_locations.end()); + + assert(!m_segment_list[it->item].is_done()); + return &m_segment_list[it->item]; + } + + class rings_stack_element { + + double m_y; + ProtoRing* m_ring_ptr; + + public: + + rings_stack_element(double y, ProtoRing* ring_ptr) : + m_y(y), + m_ring_ptr(ring_ptr) { + } + + double y() const noexcept { + return m_y; + } + + const ProtoRing& ring() const noexcept { + return *m_ring_ptr; + } + + ProtoRing* ring_ptr() noexcept { + return m_ring_ptr; + } + + bool operator==(const rings_stack_element& rhs) const noexcept { + return m_ring_ptr == rhs.m_ring_ptr; + } + + bool operator<(const rings_stack_element& rhs) const noexcept { + return m_y < rhs.m_y; + } + + }; // class rings_stack_element + + using rings_stack = std::vector; + + void remove_duplicates(rings_stack& outer_rings) { + while (true) { + const auto it = std::adjacent_find(outer_rings.begin(), outer_rings.end()); + if (it == outer_rings.end()) { + return; + } + outer_rings.erase(it, std::next(it, 2)); + } + } + + ProtoRing* find_enclosing_ring(NodeRefSegment* segment) { + if (debug()) { + std::cerr << " Looking for ring enclosing " << *segment << "\n"; + } + + const auto location = segment->first().location(); + const auto end_location = segment->second().location(); + + while (segment->first().location() == location) { + if (segment == &m_segment_list.back()) { + break; + } + ++segment; + } + + int nesting = 0; + + rings_stack outer_rings; + while (segment >= &m_segment_list.front()) { + if (!segment->is_direction_done()) { + --segment; + continue; + } + if (debug()) { + std::cerr << " Checking against " << *segment << "\n"; + } + const osmium::Location& a = segment->first().location(); + const osmium::Location& b = segment->second().location(); + + if (segment->first().location() == location) { + const int64_t ax = a.x(); + const int64_t bx = b.x(); + const int64_t lx = end_location.x(); + const int64_t ay = a.y(); + const int64_t by = b.y(); + const int64_t ly = end_location.y(); + const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); + if (debug()) { + std::cerr << " Segment XXXX z=" << z << "\n"; + } + if (z > 0) { + nesting += segment->is_reverse() ? -1 : 1; + if (debug()) { + std::cerr << " Segment is below (nesting=" << nesting << ")\n"; + } + if (segment->ring()->is_outer()) { + if (debug()) { + std::cerr << " Segment belongs to outer ring (y=" << a.y() << " ring=" << *segment->ring() << ")\n"; + } + outer_rings.emplace_back(a.y(), segment->ring()); + } + } + } else if (a.x() <= location.x() && location.x() < b.x()) { + if (debug()) { + std::cerr << " Is in x range\n"; + } + + const int64_t ax = a.x(); + const int64_t bx = b.x(); + const int64_t lx = location.x(); + const int64_t ay = a.y(); + const int64_t by = b.y(); + const int64_t ly = location.y(); + const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); + + if (z >= 0) { + nesting += segment->is_reverse() ? -1 : 1; + if (debug()) { + std::cerr << " Segment is below (nesting=" << nesting << ")\n"; + } + if (segment->ring()->is_outer()) { + const double y = ay + (by - ay) * (lx - ax) / double(bx - ax); + if (debug()) { + std::cerr << " Segment belongs to outer ring (y=" << y << " ring=" << *segment->ring() << ")\n"; + } + outer_rings.emplace_back(y, segment->ring()); + } + } + } + --segment; + } + + if (nesting % 2 == 0) { + if (debug()) { + std::cerr << " Decided that this is an outer ring\n"; + } + return nullptr; + } + + if (debug()) { + std::cerr << " Decided that this is an inner ring\n"; + } + assert(!outer_rings.empty()); + + std::sort(outer_rings.rbegin(), outer_rings.rend()); + if (debug()) { + for (const auto& o : outer_rings) { + std::cerr << " y=" << o.y() << " " << o.ring() << "\n"; + } + } + + remove_duplicates(outer_rings); + if (debug()) { + std::cerr << " after remove duplicates:\n"; + for (const auto& o : outer_rings) { + std::cerr << " y=" << o.y() << " " << o.ring() << "\n"; + } + } + + assert(!outer_rings.empty()); + return outer_rings.front().ring_ptr(); + } + + bool is_split_location(const osmium::Location& location) const noexcept { + return std::find(m_split_locations.cbegin(), m_split_locations.cend(), location) != m_split_locations.cend(); + } + + uint32_t add_new_ring(slocation& node) { + NodeRefSegment* segment = &m_segment_list[node.item]; + assert(!segment->is_done()); + + if (debug()) { + std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; + } + + if (node.reverse) { + segment->reverse(); + } + + ProtoRing* outer_ring = nullptr; + + if (segment != &m_segment_list.front()) { + outer_ring = find_enclosing_ring(segment); + } + segment->mark_direction_done(); + + m_rings.emplace_back(segment); + ProtoRing* ring = &m_rings.back(); + if (outer_ring) { + if (debug()) { + std::cerr << " This is an inner ring. Outer ring is " << *outer_ring << "\n"; + } + outer_ring->add_inner_ring(ring); + ring->set_outer_ring(outer_ring); + } else if (debug()) { + std::cerr << " This is an outer ring\n"; + } + + const osmium::Location& first_location = node.location(m_segment_list); + osmium::Location last_location = segment->stop().location(); + + uint32_t nodes = 1; + while (first_location != last_location) { + ++nodes; + NodeRefSegment* next_segment = get_next_segment(last_location); + next_segment->mark_direction_done(); + if (next_segment->start().location() != last_location) { + next_segment->reverse(); + } + ring->add_segment_back(next_segment); + if (debug()) { + std::cerr << " Next segment is " << *next_segment << "\n"; + } + last_location = next_segment->stop().location(); + } + + ring->fix_direction(); + + if (debug()) { + std::cerr << " Completed ring: " << *ring << "\n"; + } + + return nodes; + } + + uint32_t add_new_ring_complex(slocation& node) { + NodeRefSegment* segment = &m_segment_list[node.item]; + assert(!segment->is_done()); + + if (debug()) { + std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; + } + + if (node.reverse) { + segment->reverse(); + } + + m_rings.emplace_back(segment); + ProtoRing* ring = &m_rings.back(); + + const osmium::Location& first_location = node.location(m_segment_list); + osmium::Location last_location = segment->stop().location(); + + uint32_t nodes = 1; + while (first_location != last_location && !is_split_location(last_location)) { + ++nodes; + NodeRefSegment* next_segment = get_next_segment(last_location); + if (next_segment->start().location() != last_location) { + next_segment->reverse(); + } + ring->add_segment_back(next_segment); + if (debug()) { + std::cerr << " Next segment is " << *next_segment << "\n"; + } + last_location = next_segment->stop().location(); + } + + if (debug()) { + if (first_location == last_location) { + std::cerr << " Completed ring: " << *ring << "\n"; + } else { + std::cerr << " Completed partial ring: " << *ring << "\n"; + } + } + + return nodes; + } + + void create_locations_list() { + m_locations.reserve(m_segment_list.size() * 2); + + for (uint32_t n = 0; n < m_segment_list.size(); ++n) { + m_locations.emplace_back(n, false); + m_locations.emplace_back(n, true); + } + + std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list) < rhs.location(m_segment_list); + }); + } + + void find_inner_outer_complex(ProtoRing* ring) { + ProtoRing* outer_ring = find_enclosing_ring(ring->min_segment()); + if (outer_ring) { + outer_ring->add_inner_ring(ring); + ring->set_outer_ring(outer_ring); + } + ring->fix_direction(); + ring->mark_direction_done(); + } + + void find_inner_outer_complex() { + if (debug()) { + std::cerr << " Finding inner/outer rings\n"; + } + std::vector rings; + rings.reserve(m_rings.size()); + for (auto& ring : m_rings) { + if (ring.closed()) { + rings.push_back(&ring); + } + } + + if (rings.empty()) { + return; + } + + std::sort(rings.begin(), rings.end(), [](ProtoRing* a, ProtoRing* b) { + return a->min_segment() < b->min_segment(); + }); + + rings.front()->fix_direction(); + rings.front()->mark_direction_done(); + if (debug()) { + std::cerr << " First ring is outer: " << *rings.front() << "\n"; + } + for (auto it = std::next(rings.begin()); it != rings.end(); ++it) { + if (debug()) { + std::cerr << " Checking (at min segment " << *((*it)->min_segment()) << ") ring " << **it << "\n"; + } + find_inner_outer_complex(*it); + if (debug()) { + std::cerr << " Ring is " << ((*it)->is_outer() ? "OUTER: " : "INNER: ") << **it << "\n"; + } + } + } + + /** + * Finds all locations where more than two segments meet. If there + * are any open rings found along the way, they are reported + * and the function returns false. + */ + bool find_split_locations() { + osmium::Location previous_location; + for (auto it = m_locations.cbegin(); it != m_locations.cend(); ++it) { + const osmium::NodeRef& nr = it->node_ref(m_segment_list); + const osmium::Location& loc = nr.location(); + if (std::next(it) == m_locations.cend() || loc != std::next(it)->location(m_segment_list)) { + if (debug()) { + std::cerr << " Found open ring at " << nr << "\n"; + } + if (m_config.problem_reporter) { + const auto& segment = m_segment_list[it->item]; + m_config.problem_reporter->report_ring_not_closed(nr, segment.way()); + } + ++m_stats.open_rings; + } else { + if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location )) { + m_split_locations.push_back(previous_location); + } + ++it; + if (it == m_locations.end()) { + break; + } + } + previous_location = loc; + } + return m_stats.open_rings == 0; + } + + void create_rings_simple_case() { + auto count_remaining = m_segment_list.size(); + for (slocation& sl : m_locations) { + const NodeRefSegment& segment = m_segment_list[sl.item]; + if (!segment.is_done()) { + count_remaining -= add_new_ring(sl); + if (count_remaining == 0) { + return; + } + } + } + } + + std::vector create_location_to_ring_map(open_ring_its_type& open_ring_its) { + std::vector xrings; + xrings.reserve(open_ring_its.size() * 2); + + for (auto it = open_ring_its.begin(); it != open_ring_its.end(); ++it) { + if (debug()) { + std::cerr << " " << **it << '\n'; + } + xrings.emplace_back((*it)->get_node_ref_start().location(), it, true); + xrings.emplace_back((*it)->get_node_ref_stop().location(), it, false); + } + + std::sort(xrings.begin(), xrings.end()); + + return xrings; + } + + void merge_two_rings(open_ring_its_type& open_ring_its, const location_to_ring_map& m1, const location_to_ring_map& m2) { + std::list::iterator r1 = *m1.ring_it; + std::list::iterator r2 = *m2.ring_it; + + if (r1->get_node_ref_stop().location() == r2->get_node_ref_start().location()) { + r1->join_forward(*r2); + } else if (r1->get_node_ref_stop().location() == r2->get_node_ref_stop().location()) { + r1->join_backward(*r2); + } else if (r1->get_node_ref_start().location() == r2->get_node_ref_start().location()) { + r1->reverse(); + r1->join_forward(*r2); + } else if (r1->get_node_ref_start().location() == r2->get_node_ref_stop().location()) { + r1->reverse(); + r1->join_backward(*r2); + } else { + assert(false); + } + + open_ring_its.erase(std::find(open_ring_its.begin(), open_ring_its.end(), r2)); + m_rings.erase(r2); + + if (r1->closed()) { + open_ring_its.erase(std::find(open_ring_its.begin(), open_ring_its.end(), r1)); + } + } + + bool try_to_merge(open_ring_its_type& open_ring_its) { + if (open_ring_its.empty()) { + return false; + } + + if (debug()) { + std::cerr << " Trying to merge " << open_ring_its.size() << " open rings (try_to_merge)\n"; + } + + std::vector xrings = create_location_to_ring_map(open_ring_its); + + auto it = xrings.cbegin(); + while (it != xrings.cend()) { + it = std::adjacent_find(it, xrings.cend()); + if (it == xrings.cend()) { + return false; + } + auto after = std::next(it, 2); + if (after == xrings.cend() || after->location != it->location) { + if (debug()) { + std::cerr << " Merging two rings\n"; + } + merge_two_rings(open_ring_its, *it, *std::next(it)); + return true; + } + while (it != xrings.cend() && it->location == after->location) { + ++it; + } + } + + return false; + } + + bool there_are_open_rings() const noexcept { + return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring){ + return !ring.closed(); + }); + } + + struct candidate { + int64_t sum; + std::vector> rings; + osmium::Location start_location; + osmium::Location stop_location; + + explicit candidate(location_to_ring_map& ring, bool reverse) : + sum(ring.ring().sum()), + rings(), + start_location(ring.ring().get_node_ref_start().location()), + stop_location(ring.ring().get_node_ref_stop().location()) { + rings.emplace_back(ring, reverse); + } + + bool closed() const noexcept { + return start_location == stop_location; + } + + }; + + void find_candidates(std::vector& candidates, std::unordered_set& loc_done, const std::vector& xrings, candidate& cand) { + if (debug()) { + std::cerr << " find_candidates sum=" << cand.sum << " start=" << cand.start_location << " stop=" << cand.stop_location << "\n"; + for (const auto& ring : cand.rings) { + std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; + } + } + + const auto connections = make_range(std::equal_range(xrings.cbegin(), + xrings.cend(), + location_to_ring_map{cand.stop_location})); + + assert(connections.begin() != connections.end()); + + assert(!cand.rings.empty()); + const ProtoRing* ring_leading_here = &cand.rings.back().first.ring(); + for (const location_to_ring_map& m : connections) { + const ProtoRing& ring = m.ring(); + + if (&ring != ring_leading_here) { + if (debug()) { + std::cerr << " next possible connection: " << ring << (m.start ? "" : " reverse") << "\n"; + } + + candidate c = cand; + if (m.start) { + c.rings.emplace_back(m, false); + c.stop_location = ring.get_node_ref_stop().location(); + c.sum += ring.sum(); + } else { + c.rings.emplace_back(m, true); + c.stop_location = ring.get_node_ref_start().location(); + c.sum -= ring.sum(); + } + if (c.closed()) { + if (debug()) { + std::cerr << " found candidate\n"; + } + candidates.push_back(c); + } else if (loc_done.count(c.stop_location) == 0) { + if (debug()) { + std::cerr << " recurse...\n"; + } + loc_done.insert(c.stop_location); + find_candidates(candidates, loc_done, xrings, c); + loc_done.erase(c.stop_location); + if (debug()) { + std::cerr << " ...back\n"; + } + } else if (debug()) { + std::cerr << " loop found\n"; + } + } + } + } + + /** + * If there are multiple open rings and multiple ways to join them, + * this function is called. It will take the first open ring and + * try recursively all ways of closing it. Of all the candidates + * the one with the smallest area is chosen and closed. If it + * can't close this ring, an error is reported and the function + * returns false. + */ + bool join_connected_rings(open_ring_its_type& open_ring_its) { + assert(!open_ring_its.empty()); + + if (debug()) { + std::cerr << " Trying to merge " << open_ring_its.size() << " open rings (join_connected_rings)\n"; + } + + std::vector xrings = create_location_to_ring_map(open_ring_its); + + const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& lhs, const location_to_ring_map& rhs) { + return lhs.ring().min_segment() < rhs.ring().min_segment(); + }); + + find_inner_outer_complex(); + ProtoRing* outer_ring = find_enclosing_ring(ring_min->ring().min_segment()); + bool ring_min_is_outer = !outer_ring; + if (debug()) { + std::cerr << " Open ring is " << (ring_min_is_outer ? "outer" : "inner") << " ring\n"; + } + for (auto& ring : m_rings) { + ring.reset(); + } + + candidate cand{*ring_min, false}; + + // Locations we have visited while finding candidates, used + // to detect loops. + std::unordered_set loc_done; + + loc_done.insert(cand.stop_location); + + std::vector candidates; + find_candidates(candidates, loc_done, xrings, cand); + + if (candidates.empty()) { + if (debug()) { + std::cerr << " Found no candidates\n"; + } + if (!open_ring_its.empty()) { + ++m_stats.open_rings; + if (m_config.problem_reporter) { + for (auto& it : open_ring_its) { + m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start()); + m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop()); + } + } + } + return false; + } + + if (debug()) { + std::cerr << " Found candidates:\n"; + for (const auto& cand : candidates) { + std::cerr << " sum=" << cand.sum << "\n"; + for (const auto& ring : cand.rings) { + std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; + } + } + } + + // Find the candidate with the smallest/largest area + const auto chosen_cand = ring_min_is_outer ? + std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { + return std::abs(lhs.sum) < std::abs(rhs.sum); + }) : + std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { + return std::abs(lhs.sum) < std::abs(rhs.sum); + }); + + if (debug()) { + std::cerr << " Decided on: sum=" << chosen_cand->sum << "\n"; + for (const auto& ring : chosen_cand->rings) { + std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; + } + } + + // Join all (open) rings in the candidate to get one closed ring. + assert(chosen_cand->rings.size() > 1); + const auto& first_ring = chosen_cand->rings.front().first; + const ProtoRing& remaining_ring = first_ring.ring(); + for (auto it = std::next(chosen_cand->rings.begin()); it != chosen_cand->rings.end(); ++it) { + merge_two_rings(open_ring_its, first_ring, it->first); + } + + if (debug()) { + std::cerr << " Merged to " << remaining_ring << '\n'; + } + + return true; + } + + bool create_rings_complex_case() { + // First create all the (partial) rings starting at the split locations + auto count_remaining = m_segment_list.size(); + for (const osmium::Location& location : m_split_locations) { + const auto locs = make_range(std::equal_range(m_locations.begin(), + m_locations.end(), + slocation{}, + [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); + })); + for (auto& loc : locs) { + if (!m_segment_list[loc.item].is_done()) { + count_remaining -= add_new_ring_complex(loc); + if (count_remaining == 0) { + break; + } + } + } + } + + // Now find all the rest of the rings (ie not starting at split locations) + if (count_remaining > 0) { + for (slocation& sl : m_locations) { + const NodeRefSegment& segment = m_segment_list[sl.item]; + if (!segment.is_done()) { + count_remaining -= add_new_ring_complex(sl); + if (count_remaining == 0) { + break; + } + } + } + } + + // Now all segments are in exactly one (partial) ring. + + // If there are open rings, try to join them to create closed + // rings. + if (there_are_open_rings()) { + ++m_stats.area_really_complex_case; + + open_ring_its_type open_ring_its; + for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { + if (!it->closed()) { + open_ring_its.push_back(it); + } + } + + while (!open_ring_its.empty()) { + if (debug()) { + std::cerr << " There are " << open_ring_its.size() << " open rings\n"; + } + while (try_to_merge(open_ring_its)) { + // intentionally left blank + } + + if (!open_ring_its.empty()) { + if (debug()) { + std::cerr << " After joining obvious cases there are still " << open_ring_its.size() << " open rings\n"; + } + if (!join_connected_rings(open_ring_its)) { + return false; + } + } + } + + if (debug()) { + std::cerr << " Joined all open rings\n"; + } + } + + // Now all rings are complete. + + find_inner_outer_complex(); + + return true; + } + + /** + * Checks if any ways were completely removed in the + * erase_duplicate_segments step. + */ + bool ways_were_lost() { + std::unordered_set ways_in_segments; + + for (const auto& segment : m_segment_list) { + ways_in_segments.insert(segment.way()); + } + + return ways_in_segments.size() < m_num_members; + } + +#ifdef OSMIUM_WITH_TIMER + static bool print_header() { + std::cout << "nodes outer_rings inner_rings sort dupl intersection locations split simple_case complex_case roles_check\n"; + return true; + } + + static bool init_header() { + static bool printed_print_header = print_header(); + return printed_print_header; + } +#endif + + protected: + + const std::list& rings() const noexcept { + return m_rings; + } + + void set_num_members(std::size_t size) noexcept { + m_num_members = size; + } + + SegmentList& segment_list() noexcept { + return m_segment_list; + } + + /** + * Append each outer ring together with its inner rings to the + * area in the buffer. + */ + void add_rings_to_area(osmium::builder::AreaBuilder& builder) const { + for (const ProtoRing& ring : m_rings) { + if (ring.is_outer()) { + build_ring_from_proto_ring(builder, ring); + for (const ProtoRing* inner : ring.inner_rings()) { + build_ring_from_proto_ring(builder, *inner); + } + } + } + } + + /** + * Create rings from segments. + */ + bool create_rings() { + m_stats.nodes += m_segment_list.size(); + + // Sort the list of segments (from left to right and bottom + // to top). + osmium::Timer timer_sort; + m_segment_list.sort(); + timer_sort.stop(); + + // Remove duplicate segments. Removal is in pairs, so if there + // are two identical segments, they will both be removed. If + // there are three, two will be removed and one remains. + osmium::Timer timer_dupl; + m_segment_list.erase_duplicate_segments(m_config.problem_reporter, m_stats.duplicate_segments, m_stats.overlapping_segments); + timer_dupl.stop(); + + // If there are no segments left at this point, this isn't + // a valid area. + if (m_segment_list.empty()) { + if (debug()) { + std::cerr << " No segments left\n"; + } + return false; + } + + // If one or more complete ways was removed because of + // duplicate segments, this isn't a valid area. + if (ways_were_lost()) { + if (debug()) { + std::cerr << " Complete ways removed because of duplicate segments\n"; + } + return false; + } + + if (m_config.debug_level >= 3) { + std::cerr << "Sorted de-duplicated segment list:\n"; + for (const auto& s : m_segment_list) { + std::cerr << " " << s << "\n"; + } + } + + // Now we look for segments crossing each other. If there are + // any, the multipolygon is invalid. + // In the future this could be improved by trying to fix those + // cases. + osmium::Timer timer_intersection; + m_stats.intersections = m_segment_list.find_intersections(m_config.problem_reporter); + timer_intersection.stop(); + + if (m_stats.intersections) { + return false; + } + + // This creates an ordered list of locations of both endpoints + // of all segments with pointers back to the segments. We will + // use this list later to quickly find which segment(s) fits + // onto a known segment. + osmium::Timer timer_locations_list; + create_locations_list(); + timer_locations_list.stop(); + + // Find all locations where more than two segments start or + // end. We call those "split" locations. If there are any + // "spike" segments found while doing this, we know the area + // geometry isn't valid and return. + osmium::Timer timer_split; + if (!find_split_locations()) { + return false; + } + timer_split.stop(); + + // Now report all split locations to the problem reporter. + m_stats.touching_rings += m_split_locations.size(); + if (!m_split_locations.empty()) { + if (debug()) { + std::cerr << " Found split locations:\n"; + } + for (const auto& location : m_split_locations) { + if (m_config.problem_reporter) { + auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); + }); + assert(it != m_locations.cend()); + const osmium::object_id_type id = it->node_ref(m_segment_list).ref(); + m_config.problem_reporter->report_touching_ring(id, location); + } + if (debug()) { + std::cerr << " " << location << "\n"; + } + } + } + + // From here on we use two different algorithms depending on + // whether there were any split locations or not. If there + // are no splits, we use the faster "simple algorithm", if + // there are, we use the slower "complex algorithm". + osmium::Timer timer_simple_case; + osmium::Timer timer_complex_case; + if (m_split_locations.empty()) { + if (debug()) { + std::cerr << " No split locations -> using simple algorithm\n"; + } + ++m_stats.area_simple_case; + + timer_simple_case.start(); + create_rings_simple_case(); + timer_simple_case.stop(); + } else { + if (debug()) { + std::cerr << " Found split locations -> using complex algorithm\n"; + } + ++m_stats.area_touching_rings_case; + + timer_complex_case.start(); + if (!create_rings_complex_case()) { + return false; + } + timer_complex_case.stop(); + } + + // If the assembler was so configured, now check whether the + // member roles are correctly tagged. + if (m_config.check_roles && m_stats.from_relations) { + osmium::Timer timer_roles; + check_inner_outer_roles(); + timer_roles.stop(); + } + + m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring){ + return ring.is_outer(); + }); + m_stats.inner_rings = m_rings.size() - m_stats.outer_rings; + +#ifdef OSMIUM_WITH_TIMER + std::cout << m_stats.nodes << ' ' << m_stats.outer_rings << ' ' << m_stats.inner_rings << + ' ' << timer_sort.elapsed_microseconds() << + ' ' << timer_dupl.elapsed_microseconds() << + ' ' << timer_intersection.elapsed_microseconds() << + ' ' << timer_locations_list.elapsed_microseconds() << + ' ' << timer_split.elapsed_microseconds(); + + if (m_split_locations.empty()) { + std::cout << ' ' << timer_simple_case.elapsed_microseconds() << + " 0"; + } else { + std::cout << " 0" << + ' ' << timer_complex_case.elapsed_microseconds(); + } + + std::cout << +# ifdef OSMIUM_AREA_CHECK_INNER_OUTER_ROLES + ' ' << timer_roles.elapsed_microseconds() << +# else + " 0" << +# endif + '\n'; +#endif + + return true; + } + + public: + + using config_type = osmium::area::AssemblerConfig; + + explicit BasicAssembler(const config_type& config) : + m_config(config), + m_segment_list(config.debug_level > 1) { +#ifdef OSMIUM_WITH_TIMER + init_header(); +#endif + } + + ~BasicAssembler() noexcept = default; + + const AssemblerConfig& config() const noexcept { + return m_config; + } + + bool debug() const noexcept { + return m_config.debug_level > 1; + } + + /** + * Get statistics from assembler. Call this after running the + * assembler to get statistics and data about errors. + */ + const osmium::area::area_stats& stats() const noexcept { + return m_stats; + } + + osmium::area::area_stats& stats() noexcept { + return m_stats; + } + + }; // class BasicAssembler + + } // namespace detail + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_HPP diff --git a/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp b/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp new file mode 100644 index 000000000..1e946805c --- /dev/null +++ b/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp @@ -0,0 +1,93 @@ +#ifndef OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP +#define OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 area { + + namespace detail { + + class BasicAssemblerWithTags : public detail::BasicAssembler { + + protected: + + bool report_ways() const noexcept { + if (!config().problem_reporter) { + return false; + } + return stats().duplicate_nodes || + stats().duplicate_segments || + stats().intersections || + stats().open_rings || + stats().short_ways || + stats().touching_rings || + stats().ways_in_multiple_rings || + stats().wrong_role; + } + + static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { + osmium::builder::TagListBuilder tl_builder{builder}; + for (const osmium::Tag& tag : tags) { + if (std::strcmp(tag.key(), "type")) { + tl_builder.add_tag(tag.key(), tag.value()); + } + } + } + + public: + + using config_type = osmium::area::AssemblerConfig; + + explicit BasicAssemblerWithTags(const config_type& config) : + BasicAssembler(config) { + } + + }; // class BasicAssemblerWithTags + + } // namespace detail + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP 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 585ac9514..8039579b2 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 @@ -106,7 +106,7 @@ namespace osmium { m_role(role_type::unknown) { } - NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role = role_type::unknown, const osmium::Way* way = nullptr) noexcept : + NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role, const osmium::Way* way) noexcept : m_first(nr1), m_second(nr2), m_way(way), @@ -262,18 +262,6 @@ namespace osmium { return lhs.first().location() < rhs.first().location(); } - inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return rhs < lhs; - } - - inline bool operator<=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return ! (rhs < lhs); - } - - inline bool operator>=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return ! (lhs < rhs); - } - template inline std::basic_ostream& operator<<(std::basic_ostream& out, const NodeRefSegment& segment) { return out << segment.start() << "--" << segment.stop() @@ -283,19 +271,13 @@ namespace osmium { } inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { - if (s1.first().location().x() > s2.second().location().x()) { - return true; - } - return false; + return s1.first().location().x() > s2.second().location().x(); } inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { 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; - } - return true; + return !(m1.first > m2.second || m2.first > m1.second); } /** @@ -331,7 +313,7 @@ namespace osmium { if ((p0 == q0 && p1 == q1) || (p0 == q1 && p1 == q0)) { // segments are the same - return osmium::Location(); + return osmium::Location{}; } const vec pd = p1 - p0; @@ -342,7 +324,7 @@ namespace osmium { if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) { // touching at an end point - return osmium::Location(); + return osmium::Location{}; } // intersection in a point @@ -357,10 +339,10 @@ namespace osmium { (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) { const double ua = double(na) / d; const vec i = p0 + ua * (p1 - p0); - return osmium::Location(int32_t(i.x), int32_t(i.y)); + return osmium::Location{int32_t(i.x), int32_t(i.y)}; } - return osmium::Location(); + return osmium::Location{}; } // segments are collinear @@ -390,13 +372,12 @@ namespace osmium { if (sl[0].segment != sl[1].segment) { if (sl[0].location == sl[1].location) { return sl[2].location; - } else { - return sl[1].location; } + return sl[1].location; } } - return osmium::Location(); + return osmium::Location{}; } } // namespace detail 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 e1ad79641..7ab353b88 100644 --- a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp +++ b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp @@ -77,6 +77,15 @@ namespace osmium { // If this is an inner ring, points to the outer ring. ProtoRing* m_outer_ring; +#ifdef OSMIUM_DEBUG_RING_NO + static int64_t next_num() noexcept { + static int64_t counter = 0; + return ++counter; + } + + int64_t m_num; +#endif + int64_t m_sum; public: @@ -86,6 +95,9 @@ namespace osmium { m_inner(), m_min_segment(segment), m_outer_ring(nullptr), +#ifdef OSMIUM_DEBUG_RING_NO + m_num(next_num()), +#endif m_sum(0) { add_segment_back(segment); } @@ -200,7 +212,11 @@ namespace osmium { } void print(std::ostream& out) const { - out << "["; +#ifdef OSMIUM_DEBUG_RING_NO + out << "Ring #" << m_num << " ["; +#else + out << "Ring ["; +#endif if (!m_segments.empty()) { out << m_segments.front()->start().ref(); } 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 64b2b4e5a..39e16abae 100644 --- a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp +++ b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp @@ -40,6 +40,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include @@ -48,6 +49,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include namespace osmium { @@ -89,9 +91,11 @@ namespace osmium { static role_type parse_role(const char* role) noexcept { if (role[0] == '\0') { return role_type::empty; - } else if (!std::strcmp(role, "outer")) { + } + if (!std::strcmp(role, "outer")) { return role_type::outer; - } else if (!std::strcmp(role, "inner")) { + } + if (!std::strcmp(role, "inner")) { return role_type::inner; } return role_type::unknown; @@ -100,21 +104,27 @@ namespace osmium { /** * Calculate the number of segments in all the ways together. */ - static size_t get_num_segments(const std::vector& members) noexcept { - return std::accumulate(members.cbegin(), members.cend(), static_cast(0), [](size_t sum, const osmium::Way* way) { + static std::size_t get_num_segments(const std::vector& members) noexcept { + return std::accumulate(members.cbegin(), members.cend(), static_cast(0), [](std::size_t sum, const osmium::Way* way) { if (way->nodes().empty()) { return sum; - } else { - return sum + way->nodes().size() - 1; } + return sum + way->nodes().size() - 1; }); } - uint32_t extract_segments_from_way_impl(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way, role_type role) { - uint32_t duplicate_nodes = 0; + uint32_t extract_segments_from_way_impl(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way, role_type role) { + uint32_t invalid_locations = 0; osmium::NodeRef previous_nr; for (const osmium::NodeRef& nr : way.nodes()) { + if (!nr.location().valid()) { + ++invalid_locations; + if (problem_reporter) { + problem_reporter->report_invalid_location(way.id(), nr.ref()); + } + continue; + } if (previous_nr.location()) { if (previous_nr.location() != nr.location()) { m_segments.emplace_back(previous_nr, nr, role, &way); @@ -128,7 +138,7 @@ namespace osmium { previous_nr = nr; } - return duplicate_nodes; + return invalid_locations; } public: @@ -147,7 +157,7 @@ namespace osmium { SegmentList& operator=(SegmentList&&) = delete; /// The number of segments in the list. - size_t size() const noexcept { + std::size_t size() const noexcept { return m_segments.size(); } @@ -167,12 +177,12 @@ namespace osmium { return m_segments.back(); } - const NodeRefSegment& operator[](size_t n) const noexcept { + const NodeRefSegment& operator[](std::size_t n) const noexcept { assert(n < m_segments.size()); return m_segments[n]; } - NodeRefSegment& operator[](size_t n) noexcept { + NodeRefSegment& operator[](std::size_t n) noexcept { assert(n < m_segments.size()); return m_segments[n]; } @@ -213,33 +223,48 @@ namespace osmium { * same node or different nodes with same location) are * removed after reporting the duplicate node. */ - uint32_t extract_segments_from_way(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way) { + uint32_t extract_segments_from_way(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way) { if (way.nodes().empty()) { return 0; } m_segments.reserve(way.nodes().size() - 1); - return extract_segments_from_way_impl(problem_reporter, way, role_type::outer); + return extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role_type::outer); } /** * Extract all segments from all ways that make up this * multipolygon relation and add them to the list. */ - uint32_t extract_segments_from_ways(osmium::area::ProblemReporter* problem_reporter, const osmium::Relation& relation, const std::vector& members) { - assert(relation.members().size() >= members.size()); + uint32_t extract_segments_from_ways(ProblemReporter* problem_reporter, + uint64_t& duplicate_nodes, + uint64_t& duplicate_ways, + const osmium::Relation& relation, + const std::vector& members) { + assert(relation.cmembers().size() >= members.size()); - const size_t num_segments = get_num_segments(members); + const std::size_t num_segments = get_num_segments(members); if (problem_reporter) { problem_reporter->set_nodes(num_segments); } m_segments.reserve(num_segments); - uint32_t duplicate_nodes = 0; - for_each_member(relation, members, [this, &problem_reporter, &duplicate_nodes](const osmium::RelationMember& member, const osmium::Way& way) { - duplicate_nodes += extract_segments_from_way_impl(problem_reporter, way, parse_role(member.role())); + std::unordered_set ids; + ids.reserve(members.size()); + uint32_t invalid_locations = 0; + for_each_member(relation, members, [&](const osmium::RelationMember& member, const osmium::Way& way) { + if (ids.count(way.id()) == 0) { + ids.insert(way.id()); + const auto role = parse_role(member.role()); + invalid_locations += extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role); + } else { + ++duplicate_ways; + if (problem_reporter) { + problem_reporter->report_duplicate_way(way); + } + } }); - return duplicate_nodes; + return invalid_locations; } /** @@ -248,9 +273,7 @@ namespace osmium { * same segment. So if there are three, for instance, two will * be removed and one will be left. */ - uint32_t erase_duplicate_segments(osmium::area::ProblemReporter* problem_reporter) { - uint32_t duplicate_segments = 0; - + void erase_duplicate_segments(ProblemReporter* problem_reporter, uint64_t& duplicate_segments, uint64_t& overlapping_segments) { while (true) { auto it = std::adjacent_find(m_segments.begin(), m_segments.end()); if (it == m_segments.end()) { @@ -273,10 +296,16 @@ namespace osmium { problem_reporter->report_duplicate_segment(it->first(), it->second()); } } + + if (it+2 != m_segments.end() && *it == *(it+2)) { + ++overlapping_segments; + if (problem_reporter) { + problem_reporter->report_overlapping_segment(it->first(), it->second()); + } + } + m_segments.erase(it, it+2); } - - return duplicate_segments; } /** @@ -286,14 +315,14 @@ namespace osmium { * reported to this object. * @returns true if there are intersections. */ - uint32_t find_intersections(osmium::area::ProblemReporter* problem_reporter) const { + uint32_t find_intersections(ProblemReporter* problem_reporter) const { if (m_segments.empty()) { return 0; } uint32_t found_intersections = 0; - for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend()-1; ++it1) { + for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend() - 1; ++it1) { const NodeRefSegment& s1 = *it1; for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) { const NodeRefSegment& s2 = *it2; @@ -305,7 +334,7 @@ namespace osmium { } if (y_range_overlap(s1, s2)) { - osmium::Location intersection = calculate_intersection(s1, s2); + osmium::Location intersection{calculate_intersection(s1, s2)}; if (intersection) { ++found_intersections; if (m_debug) { diff --git a/third_party/libosmium/include/osmium/area/detail/vector.hpp b/third_party/libosmium/include/osmium/area/detail/vector.hpp index 14bf0cb7b..e8ff0a501 100644 --- a/third_party/libosmium/include/osmium/area/detail/vector.hpp +++ b/third_party/libosmium/include/osmium/area/detail/vector.hpp @@ -118,4 +118,4 @@ namespace osmium { } // namespace osmium -#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP +#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP diff --git a/third_party/libosmium/include/osmium/area/geom_assembler.hpp b/third_party/libosmium/include/osmium/area/geom_assembler.hpp new file mode 100644 index 000000000..8fb3b3ac5 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/geom_assembler.hpp @@ -0,0 +1,132 @@ +#ifndef OSMIUM_AREA_GEOM_ASSEMBLER_HPP +#define OSMIUM_AREA_GEOM_ASSEMBLER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 { + + namespace area { + + /** + * Assembles area objects from closed ways or multipolygon relations + * and their members. Unlike the Assembler, this one doesn't take + * tags into account at all. And it doesn't interpret all the config + * settings and doesn't do all the checks and error reporting the + * Assembler does. + * + * This class was developed specifically for the need of osm2pgsql. + * Unless you know what you are doing, use the Assembler class instead + * of this class. Contact the Libosmium developers if you want to use + * this class. + */ + class GeomAssembler : public detail::BasicAssembler { + + public: + + using config_type = osmium::area::AssemblerConfig; + + explicit GeomAssembler(const config_type& config) : + detail::BasicAssembler(config) { + } + + ~GeomAssembler() noexcept = default; + + /** + * Assemble an area from the given way. + * + * The resulting area is put into the out_buffer. + * + * @returns false if there was some kind of error building the + * area, true otherwise. + */ + bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) { + segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way); + + if (!create_rings()) { + return false; + } + + { + osmium::builder::AreaBuilder builder{out_buffer}; + builder.initialize_from_object(way); + add_rings_to_area(builder); + } + out_buffer.commit(); + + return true; + } + + /** + * Assemble an area from the given relation and its member ways + * which are in the ways_buffer. + * + * The resulting area is put into the out_buffer. + * + * @returns false if there was some kind of error building the + * area, true otherwise. + */ + bool operator()(const osmium::Relation& relation, const osmium::memory::Buffer& ways_buffer, osmium::memory::Buffer& out_buffer) { + for (const auto& way : ways_buffer.select()) { + segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way); + } + + if (!create_rings()) { + return false; + } + + { + osmium::builder::AreaBuilder builder{out_buffer}; + builder.initialize_from_object(relation); + add_rings_to_area(builder); + } + out_buffer.commit(); + + return true; + } + + }; // class GeomAssembler + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_GEOM_ASSEMBLER_HPP diff --git a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp index cc790e9c7..9ad68e166 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp @@ -81,7 +81,7 @@ namespace osmium { osmium::memory::Buffer m_output_buffer; - osmium::area::area_stats m_stats; + area_stats m_stats; static constexpr size_t initial_output_buffer_size = 1024 * 1024; static constexpr size_t max_buffer_size_for_flush = 100 * 1024; @@ -109,7 +109,7 @@ namespace osmium { m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) { } - const osmium::area::area_stats& stats() const noexcept { + const area_stats& stats() const noexcept { return m_stats; } @@ -127,11 +127,7 @@ namespace osmium { return false; } - if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) { - return true; - } - - return false; + return (!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary")); } /** @@ -155,11 +151,11 @@ namespace osmium { } try { if (!way.nodes().front().location() || !way.nodes().back().location()) { - throw osmium::invalid_location("invalid location"); + throw osmium::invalid_location{"invalid location"}; } if (way.ends_have_same_location()) { // way is closed and has enough nodes, build simple multipolygon - TAssembler assembler(m_assembler_config); + TAssembler assembler{m_assembler_config}; assembler(way, m_output_buffer); m_stats += assembler.stats(); possibly_flush_output_buffer(); @@ -174,6 +170,7 @@ namespace osmium { const osmium::memory::Buffer& buffer = this->members_buffer(); std::vector ways; + ways.reserve(relation.members().size()); for (const auto& member : relation.members()) { if (member.ref() != 0) { const size_t offset = this->get_offset(member.type(), member.ref()); @@ -182,7 +179,7 @@ namespace osmium { } try { - TAssembler assembler(m_assembler_config); + TAssembler assembler{m_assembler_config}; assembler(relation, ways, m_output_buffer); m_stats += assembler.stats(); possibly_flush_output_buffer(); diff --git a/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp b/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp new file mode 100644 index 000000000..e5ee2c8f1 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp @@ -0,0 +1,189 @@ +#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP +#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + /** + * @brief Code related to the building of areas (multipolygons) from relations. + */ + namespace area { + + /** + * This class collects all data needed for creating areas from + * relations tagged with type=multipolygon or type=boundary. + * Most of its functionality is derived from the parent class + * osmium::relations::Collector. + * + * The actual assembling of the areas is done by the assembler + * class given as template argument. + * + * @tparam TAssembler Multipolygon Assembler class. + * @pre The Ids of all objects must be unique in the input data. + */ + template + class MultipolygonManager : public osmium::relations::RelationsManager, false, true, false> { + + using assembler_config_type = typename TAssembler::config_type; + const assembler_config_type m_assembler_config; + + area_stats m_stats; + + osmium::TagsFilter m_filter; + + public: + + /** + * Construct a MultipolygonManager. + * + * @param assembler_config The configuration that will be given to + * any newly constructed area assembler. + * @param filter An optional filter specifying what tags are + * needed on closed ways or multipolygon relations + * to build the area. + */ + explicit MultipolygonManager(const assembler_config_type& assembler_config, const osmium::TagsFilter& filter = osmium::TagsFilter{true}) : + m_assembler_config(assembler_config), + m_filter(filter) { + } + + /** + * Access the aggregated statistics generated by the assemblers + * called from the manager. + */ + const area_stats& stats() const noexcept { + return m_stats; + } + + /** + * We are interested in all relations tagged with type=multipolygon + * or type=boundary with at least one way member. + */ + bool new_relation(const osmium::Relation& relation) const { + const char* type = relation.tags().get_value_by_key("type"); + + // ignore relations without "type" tag + if (type == nullptr) { + return false; + } + + if (((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) && osmium::tags::match_any_of(relation.tags(), m_filter)) { + return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) { + return member.type() == osmium::item_type::way; + }); + } + + return false; + } + + /** + * This is called when a relation is complete, ie. all members + * were found in the input. It will build the area using the + * assembler. + */ + void complete_relation(const osmium::Relation& relation) { + std::vector ways; + ways.reserve(relation.members().size()); + for (const auto& member : relation.members()) { + if (member.ref() != 0) { + ways.push_back(this->get_member_way(member.ref())); + assert(ways.back() != nullptr); + } + } + + try { + TAssembler assembler{m_assembler_config}; + assembler(relation, ways, this->buffer()); + m_stats += assembler.stats(); + } catch (const osmium::invalid_location&) { + // XXX ignore + } + } + + void after_way(const osmium::Way& way) { + // you need at least 4 nodes to make up a polygon + if (way.nodes().size() <= 3) { + return; + } + + try { + if (!way.nodes().front().location() || !way.nodes().back().location()) { + throw osmium::invalid_location{"invalid location"}; + } + if (way.ends_have_same_location()) { + if (way.tags().has_tag("area", "no")) { + return; + } + + if (osmium::tags::match_none_of(way.tags(), m_filter)) { + return; + } + + TAssembler assembler{m_assembler_config}; + assembler(way, this->buffer()); + m_stats += assembler.stats(); + this->possibly_flush(); + } + } catch (const osmium::invalid_location&) { + // XXX ignore + } + } + + }; // class MultipolygonManager + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP diff --git a/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp b/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp new file mode 100644 index 000000000..491557aa8 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp @@ -0,0 +1,180 @@ +#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP +#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + /** + * @brief Code related to the building of areas (multipolygons) from relations. + */ + namespace area { + + /** + * This class collects all data needed for creating areas from + * relations tagged with type=multipolygon or type=boundary. + * Most of its functionality is derived from the parent class + * osmium::relations::Collector. + * + * The actual assembling of the areas is done by the assembler + * class given as template argument. + * + * @tparam TAssembler Multipolygon Assembler class. + * @pre The Ids of all objects must be unique in the input data. + */ + template + class MultipolygonManagerLegacy : public osmium::relations::RelationsManager, false, true, false> { + + using assembler_config_type = typename TAssembler::config_type; + const assembler_config_type m_assembler_config; + + area_stats m_stats; + + public: + + /** + * Construct a MultipolygonManagerLegacy. + * + * @param assembler_config The configuration that will be given to + * any newly constructed area assembler. + */ + explicit MultipolygonManagerLegacy(const assembler_config_type& assembler_config) : + m_assembler_config(assembler_config) { + } + + /** + * Access the aggregated statistics generated by the assemblers + * called from the manager. + */ + const area_stats& stats() const noexcept { + return m_stats; + } + + /** + * We are interested in all relations tagged with type=multipolygon + * or type=boundary with at least one way member. + */ + bool new_relation(const osmium::Relation& relation) const { + const char* type = relation.tags().get_value_by_key("type"); + + // ignore relations without "type" tag + if (!type) { + return false; + } + + if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) { + return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) { + return member.type() == osmium::item_type::way; + }); + } + + return false; + } + + /** + * This is called when a relation is complete, ie. all members + * were found in the input. It will build the area using the + * assembler. + */ + void complete_relation(const osmium::Relation& relation) { + std::vector ways; + ways.reserve(relation.members().size()); + for (const auto& member : relation.members()) { + if (member.ref() != 0) { + ways.push_back(this->get_member_way(member.ref())); + assert(ways.back() != nullptr); + } + } + + try { + TAssembler assembler{m_assembler_config}; + assembler(relation, ways, this->buffer()); + m_stats += assembler.stats(); + } catch (const osmium::invalid_location&) { + // XXX ignore + } + } + + /** + * This is called when a way is not in any multipolygon + * relation. + */ + void way_not_in_any_relation(const osmium::Way& way) { + // you need at least 4 nodes to make up a polygon + if (way.nodes().size() <= 3) { + return; + } + try { + if (!way.nodes().front().location() || !way.nodes().back().location()) { + throw osmium::invalid_location{"invalid location"}; + } + if (way.ends_have_same_location()) { + // way is closed and has enough nodes, build simple multipolygon + TAssembler assembler{m_assembler_config}; + assembler(way, this->buffer()); + m_stats += assembler.stats(); + } + } catch (const osmium::invalid_location&) { + // XXX ignore + } + } + + }; // class MultipolygonManagerLegacy + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP diff --git a/third_party/libosmium/include/osmium/area/problem_reporter.hpp b/third_party/libosmium/include/osmium/area/problem_reporter.hpp index 8a72c1ad9..181d2d1ee 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter.hpp @@ -143,6 +143,18 @@ namespace osmium { virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) { } + /** + * Report a duplicate segments. Two or more segments are directly + * on top of each other. This can be a problem, if there is a + * spike for instance, or it could be okay, if there are touching + * inner rings. + * + * @param nr1 NodeRef of one end of the segment. + * @param nr2 NodeRef of the other end of the segment. + */ + virtual void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) { + } + /** * Report an open ring. * @@ -189,6 +201,23 @@ namespace osmium { virtual void report_inner_with_same_tags(const osmium::Way& way) { } + /** + * Report an invalid location in a way. + * + * @param way_id ID of the way the node is in. + * @param node_id ID of the node with the invalid location. + */ + virtual void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) { + } + + /** + * Report a way that is more than once in a relation. + * + * @param way The way + */ + virtual void report_duplicate_way(const osmium::Way& way) { + } + /** * In addition to reporting specific problems, this is used to * report all ways belonging to areas having problems. 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 152be40a9..cfc2b5428 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp @@ -60,58 +60,76 @@ namespace osmium { ~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(); + m_sstream.str(""); ProblemReporterStream::report_duplicate_node(node_id1, node_id2, location); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_touching_ring(node_id, location); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end, osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_intersection(way1_id, way1_seg_start, way1_seg_end, way2_id, way2_seg_start, way2_seg_end, intersection); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_duplicate_segment(nr1, nr2); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; + } + + void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + m_sstream.str(""); + ProblemReporterStream::report_overlapping_segment(nr1, nr2); + throw std::runtime_error{m_sstream.str()}; } void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_ring_not_closed(nr, way); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_role_should_be_outer(way_id, seg_start, seg_end); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_role_should_be_inner(way_id, seg_start, seg_end); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_way_in_multiple_rings(const osmium::Way& way) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_way_in_multiple_rings(way); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; } void report_inner_with_same_tags(const osmium::Way& way) override { - m_sstream.str(); + m_sstream.str(""); ProblemReporterStream::report_inner_with_same_tags(way); - throw std::runtime_error(m_sstream.str()); + throw std::runtime_error{m_sstream.str()}; + } + + void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override { + m_sstream.str(""); + ProblemReporterStream::report_invalid_location(way_id, node_id); + throw std::runtime_error{m_sstream.str()}; + } + + void report_duplicate_way(const osmium::Way& way) override { + m_sstream.str(""); + ProblemReporterStream::report_duplicate_way(way); + throw std::runtime_error{m_sstream.str()}; } }; // class ProblemReporterException 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 914c8bad8..5f531c8f4 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp @@ -73,14 +73,14 @@ namespace osmium { gdalcpp::Layer m_layer_ways; void set_object(gdalcpp::Feature& feature) { - const char t[2] = { osmium::item_type_to_char(m_object_type), '\0' }; + const char t[2] = {osmium::item_type_to_char(m_object_type), '\0'}; feature.set_field("obj_type", t); feature.set_field("obj_id", int32_t(m_object_id)); feature.set_field("nodes", int32_t(m_nodes)); } void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) { - gdalcpp::Feature feature(m_layer_perror, m_ogr_factory.create_point(location)); + gdalcpp::Feature feature{m_layer_perror, m_ogr_factory.create_point(location)}; set_object(feature); feature.set_field("id1", double(id1)); feature.set_field("id2", double(id2)); @@ -93,7 +93,7 @@ namespace osmium { ogr_linestring->addPoint(loc1.lon(), loc1.lat()); ogr_linestring->addPoint(loc2.lon(), loc2.lat()); - gdalcpp::Feature feature(m_layer_lerror, std::move(ogr_linestring)); + gdalcpp::Feature feature{m_layer_lerror, std::move(ogr_linestring)}; set_object(feature); feature.set_field("id1", static_cast(id1)); feature.set_field("id2", static_cast(id2)); @@ -158,6 +158,10 @@ namespace osmium { write_line("duplicate_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location()); } + void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + write_line("overlapping_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location()); + } + void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location()); } @@ -175,7 +179,7 @@ namespace osmium { return; } try { - gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way)); + gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; set_object(feature); feature.set_field("id1", int32_t(way.id())); feature.set_field("id2", 0); @@ -191,7 +195,7 @@ namespace osmium { return; } try { - gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way)); + gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; set_object(feature); feature.set_field("id1", int32_t(way.id())); feature.set_field("id2", 0); @@ -202,6 +206,22 @@ namespace osmium { } } + void report_duplicate_way(const osmium::Way& way) override { + if (way.nodes().size() < 2) { + return; + } + try { + gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; + set_object(feature); + feature.set_field("id1", int32_t(way.id())); + feature.set_field("id2", 0); + feature.set_field("problem", "duplicate_way"); + feature.add_to_layer(); + } catch (const osmium::geometry_error&) { + // XXX + } + } + void report_way(const osmium::Way& way) override { if (way.nodes().empty()) { return; @@ -212,7 +232,7 @@ namespace osmium { return; } try { - gdalcpp::Feature feature(m_layer_ways, m_ogr_factory.create_linestring(way)); + gdalcpp::Feature feature{m_layer_ways, m_ogr_factory.create_linestring(way)}; set_object(feature); feature.set_field("way_id", int32_t(way.id())); feature.add_to_layer(); 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 0c008f131..774e9c3b4 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp @@ -85,6 +85,12 @@ namespace osmium { << " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n"; } + void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + header("overlapping segment"); + *m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location() + << " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n"; + } + void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { header("ring not closed"); *m_out << "node_id=" << nr.ref() << " location=" << nr.location(); @@ -114,6 +120,16 @@ namespace osmium { *m_out << "way_id=" << way.id() << '\n'; } + void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override { + header("invalid location"); + *m_out << "way_id=" << way_id << " node_id=" << node_id << '\n'; + } + + void report_duplicate_way(const osmium::Way& way) override { + header("duplicate way"); + *m_out << "way_id=" << way.id() << '\n'; + } + }; // class ProblemReporterStream } // namespace area diff --git a/third_party/libosmium/include/osmium/area/stats.hpp b/third_party/libosmium/include/osmium/area/stats.hpp index 07be78376..6133da847 100644 --- a/third_party/libosmium/include/osmium/area/stats.hpp +++ b/third_party/libosmium/include/osmium/area/stats.hpp @@ -52,6 +52,7 @@ namespace osmium { uint64_t area_touching_rings_case = 0; ///< More difficult case with touching rings uint64_t duplicate_nodes = 0; ///< Consecutive identical nodes or consecutive nodes with same location uint64_t duplicate_segments = 0; ///< Segments duplicated (going back and forth) + uint64_t duplicate_ways = 0; ///< Ways that are in relation more than once uint64_t from_relations = 0; ///< Area created from multipolygon relation uint64_t from_ways = 0; ///< Area created from way uint64_t inner_rings = 0; ///< Number of inner rings @@ -63,11 +64,13 @@ namespace osmium { uint64_t nodes = 0; ///< Number of nodes in the area uint64_t open_rings = 0; ///< Number of open rings in the area uint64_t outer_rings = 0; ///< Number of outer rings in the area + uint64_t overlapping_segments = 0; ///< Three or more segments with same end points uint64_t short_ways = 0; ///< Number of ways with less than two nodes uint64_t single_way_in_mp_relation = 0; ///< Multipolygon relation containing a single way uint64_t touching_rings = 0; ///< Rings touching in a node uint64_t ways_in_multiple_rings = 0; ///< Different segments of a way ended up in different rings uint64_t wrong_role = 0; ///< Member has wrong role (not "outer", "inner", or empty) + uint64_t invalid_locations = 0; ///< Invalid location found area_stats& operator+=(const area_stats& other) noexcept { area_really_complex_case += other.area_really_complex_case; @@ -75,6 +78,7 @@ namespace osmium { area_touching_rings_case += other.area_touching_rings_case; duplicate_nodes += other.duplicate_nodes; duplicate_segments += other.duplicate_segments; + duplicate_ways += other.duplicate_ways; from_relations += other.from_relations; from_ways += other.from_ways; inner_rings += other.inner_rings; @@ -91,6 +95,7 @@ namespace osmium { touching_rings += other.touching_rings; ways_in_multiple_rings += other.ways_in_multiple_rings; wrong_role += other.wrong_role; + invalid_locations += invalid_locations; return *this; } @@ -103,6 +108,7 @@ namespace osmium { << " area_touching_rings_case=" << s.area_touching_rings_case << " duplicate_nodes=" << s.duplicate_nodes << " duplicate_segments=" << s.duplicate_segments + << " duplicate_ways=" << s.duplicate_ways << " from_relations=" << s.from_relations << " from_ways=" << s.from_ways << " inner_rings=" << s.inner_rings @@ -118,7 +124,8 @@ namespace osmium { << " single_way_in_mp_relation=" << s.single_way_in_mp_relation << " touching_rings=" << s.touching_rings << " ways_in_multiple_rings=" << s.ways_in_multiple_rings - << " wrong_role=" << s.wrong_role; + << " wrong_role=" << s.wrong_role + << " invalid_locations=" << s.invalid_locations; } } // namespace area diff --git a/third_party/libosmium/include/osmium/builder/builder.hpp b/third_party/libosmium/include/osmium/builder/builder.hpp index d4358843b..4351588d8 100644 --- a/third_party/libosmium/include/osmium/builder/builder.hpp +++ b/third_party/libosmium/include/osmium/builder/builder.hpp @@ -35,16 +35,12 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include -#include -#include -#include #include #include -#include -#include #include namespace osmium { @@ -62,7 +58,7 @@ namespace osmium { osmium::memory::Buffer& m_buffer; Builder* m_parent; - size_t m_item_offset; + std::size_t m_item_offset; Builder(const Builder&) = delete; Builder(Builder&&) = delete; @@ -101,7 +97,7 @@ namespace osmium { return *reinterpret_cast(m_buffer.data() + m_item_offset); } - unsigned char* reserve_space(size_t size) { + unsigned char* reserve_space(std::size_t size) { return m_buffer.reserve_space(size); } @@ -119,7 +115,9 @@ namespace osmium { * */ void add_padding(bool self = false) { - const auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); + // We know the padding is only a very small number, so it will + // always fit. + const auto padding = static_cast(osmium::memory::align_bytes - (size() % osmium::memory::align_bytes)); if (padding != osmium::memory::align_bytes) { std::fill_n(reserve_space(padding), padding, 0); if (self) { @@ -131,7 +129,7 @@ namespace osmium { } } - void add_size(uint32_t size) { + void add_size(osmium::memory::item_size_type size) { item().add_size(size); if (m_parent) { m_parent->add_size(size); diff --git a/third_party/libosmium/include/osmium/builder/builder_helper.hpp b/third_party/libosmium/include/osmium/builder/builder_helper.hpp index 8bfa21e24..a273e5041 100644 --- a/third_party/libosmium/include/osmium/builder/builder_helper.hpp +++ b/third_party/libosmium/include/osmium/builder/builder_helper.hpp @@ -71,7 +71,7 @@ namespace osmium { * @deprecated * Use osmium::builder::add_tag_list() instead. */ - inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { + OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); @@ -87,7 +87,7 @@ namespace osmium { * @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) { + OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map& tags) { const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); @@ -103,7 +103,7 @@ namespace osmium { * @deprecated * Use osmium::builder::add_tag_list() instead. */ - inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function func) { + OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function func) { const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); 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 d4483b192..47063140e 100644 --- a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp +++ b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp @@ -33,7 +33,10 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include +#include #include #include #include @@ -43,25 +46,25 @@ 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 namespace osmium { - class Node; - namespace memory { class Buffer; } // namespace memory @@ -74,12 +77,12 @@ namespace osmium { explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(TagList)) { - new (&item()) TagList(); + new (&item()) TagList{}; } explicit TagListBuilder(Builder& parent) : Builder(parent.buffer(), &parent, sizeof(TagList)) { - new (&item()) TagList(); + new (&item()) TagList{}; } ~TagListBuilder() { @@ -94,10 +97,10 @@ namespace osmium { */ 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"); + 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"); + throw std::length_error{"OSM tag value is too long"}; } add_size(append(key)); add_size(append(value)); @@ -111,12 +114,12 @@ 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 size_t key_length, const char* value, const size_t value_length) { + void add_tag(const char* key, const std::size_t key_length, const char* value, const std::size_t value_length) { if (key_length > osmium::max_osm_string_length) { - throw std::length_error("OSM tag key is too long"); + 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"); + throw std::length_error{"OSM tag value is too long"}; } add_size(append_with_zero(key, osmium::memory::item_size_type(key_length))); add_size(append_with_zero(value, osmium::memory::item_size_type(value_length))); @@ -130,10 +133,10 @@ namespace osmium { */ void add_tag(const std::string& key, const std::string& value) { if (key.size() > osmium::max_osm_string_length) { - throw std::length_error("OSM tag key is too long"); + 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"); + throw std::length_error{"OSM tag value is too long"}; } add_size(append(key.data(), osmium::memory::item_size_type(key.size()) + 1)); add_size(append(value.data(), osmium::memory::item_size_type(value.size()) + 1)); @@ -185,12 +188,12 @@ namespace osmium { explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(T)) { - new (&item()) T(); + new (&item()) T{}; } explicit NodeRefListBuilder(Builder& parent) : Builder(parent.buffer(), &parent, sizeof(T)) { - new (&item()) T(); + new (&item()) T{}; } ~NodeRefListBuilder() { @@ -198,12 +201,12 @@ namespace osmium { } void add_node_ref(const NodeRef& node_ref) { - new (reserve_space_for()) osmium::NodeRef(node_ref); + new (reserve_space_for()) osmium::NodeRef{node_ref}; add_size(sizeof(osmium::NodeRef)); } void add_node_ref(const object_id_type ref, const osmium::Location& location = Location{}) { - add_node_ref(NodeRef(ref, location)); + add_node_ref(NodeRef{ref, location}); } }; // class NodeRefListBuilder @@ -223,9 +226,9 @@ namespace osmium { * @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 size_t length) { + void add_role(osmium::RelationMember& member, const char* role, const std::size_t length) { if (length > osmium::max_osm_string_length) { - throw std::length_error("OSM relation member role is too long"); + throw std::length_error{"OSM relation member role is too long"}; } member.set_role_size(osmium::string_size_type(length) + 1); add_size(append_with_zero(role, osmium::memory::item_size_type(length))); @@ -236,12 +239,12 @@ namespace osmium { explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(RelationMemberList)) { - new (&item()) RelationMemberList(); + new (&item()) RelationMemberList{}; } explicit RelationMemberListBuilder(Builder& parent) : Builder(parent.buffer(), &parent, sizeof(RelationMemberList)) { - new (&item()) RelationMemberList(); + new (&item()) RelationMemberList{}; } ~RelationMemberListBuilder() { @@ -261,9 +264,9 @@ namespace osmium { * @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 size_t role_length, const osmium::OSMObject* full_member = nullptr) { + void add_member(osmium::item_type type, object_id_type ref, const char* role, const std::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); + new (member) osmium::RelationMember{ref, type, full_member != nullptr}; add_size(sizeof(RelationMember)); add_role(*member, role, role_length); if (full_member) { @@ -307,23 +310,19 @@ namespace osmium { osmium::ChangesetComment* m_comment = nullptr; - void add_user(osmium::ChangesetComment& comment, const char* user, const size_t length) { + void add_user(osmium::ChangesetComment& comment, const char* user, const std::size_t length) { if (length > osmium::max_osm_string_length) { - throw std::length_error("OSM user name is too long"); + throw std::length_error{"OSM user name is too long"}; } comment.set_user_size(osmium::string_size_type(length) + 1); add_size(append_with_zero(user, osmium::memory::item_size_type(length))); } - 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"); + void add_text(osmium::ChangesetComment& comment, const char* text, const std::size_t length) { + 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); + comment.set_text_size(osmium::changeset_comment_size_type(length) + 1); add_size(append_with_zero(text, osmium::memory::item_size_type(length))); add_padding(true); } @@ -332,12 +331,12 @@ namespace osmium { explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(ChangesetDiscussion)) { - new (&item()) ChangesetDiscussion(); + new (&item()) ChangesetDiscussion{}; } explicit ChangesetDiscussionBuilder(Builder& parent) : Builder(parent.buffer(), &parent, sizeof(ChangesetDiscussion)) { - new (&item()) ChangesetDiscussion(); + new (&item()) ChangesetDiscussion{}; } ~ChangesetDiscussionBuilder() { @@ -348,21 +347,23 @@ namespace osmium { 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); + 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)); + osmium::ChangesetComment& comment = *m_comment; m_comment = nullptr; + add_text(comment, text, std::strlen(text)); } 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()); + osmium::ChangesetComment& comment = *m_comment; m_comment = nullptr; + add_text(comment, text.c_str(), text.size()); } }; // class ChangesetDiscussionBuilder @@ -379,13 +380,13 @@ namespace osmium { using type = TDerived; - constexpr static const size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1); + constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1); public: explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(T) + min_size_for_user) { - new (&item()) T(); + new (&item()) T{}; add_size(min_size_for_user); std::fill_n(object().data() + sizeof(T), min_size_for_user, 0); object().set_user_size(1); @@ -402,6 +403,17 @@ namespace osmium { return static_cast(item()); } + /** + * Get a const reference to the object buing built. + * + * Note that this reference will be invalidated by every action + * on the builder that might make the buffer grow. This includes + * calls to set_user() and any time a new sub-builder is created. + */ + const T& cobject() const noexcept { + return static_cast(item()); + } + /** * Set user name. * @@ -410,7 +422,7 @@ namespace osmium { */ TDerived& set_user(const char* user, const string_size_type length) { const auto size_of_object = sizeof(T) + sizeof(string_size_type); - assert(object().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1)) + assert(cobject().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1)) && "set_user() must be called at most once and before any sub-builders"); const auto available_space = min_size_for_user - sizeof(string_size_type) - 1; if (length > available_space) { @@ -558,13 +570,13 @@ namespace osmium { using type = ChangesetBuilder; - constexpr static const size_t min_size_for_user = osmium::memory::padded_length(1); + constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(1); public: explicit ChangesetBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(Changeset) + min_size_for_user) { - new (&item()) Changeset(); + new (&item()) Changeset{}; add_size(min_size_for_user); std::fill_n(object().data() + sizeof(Changeset), min_size_for_user, 0); object().set_user_size(1); @@ -581,6 +593,17 @@ namespace osmium { return static_cast(item()); } + /** + * Get a const reference to the changeset buing built. + * + * Note that this reference will be invalidated by every action + * on the builder that might make the buffer grow. This includes + * calls to set_user() and any time a new sub-builder is created. + */ + const Changeset& cobject() const noexcept { + return static_cast(item()); + } + OSMIUM_FORWARD(set_id) OSMIUM_FORWARD(set_uid) OSMIUM_FORWARD(set_uid_from_signed) @@ -608,7 +631,7 @@ namespace osmium { * @param length Length of user name (without \0 termination). */ ChangesetBuilder& set_user(const char* user, const string_size_type length) { - assert(object().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1)) + assert(cobject().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1)) && "set_user() must be called at most once and before any sub-builders"); const auto available_space = min_size_for_user - 1; if (length > available_space) { diff --git a/third_party/libosmium/include/osmium/diff_handler.hpp b/third_party/libosmium/include/osmium/diff_handler.hpp index 19c75b826..d042edd8a 100644 --- a/third_party/libosmium/include/osmium/diff_handler.hpp +++ b/third_party/libosmium/include/osmium/diff_handler.hpp @@ -48,13 +48,13 @@ namespace osmium { DiffHandler() = default; - void node(const osmium::DiffNode&) const { + void node(const osmium::DiffNode&) const noexcept { } - void way(const osmium::DiffWay&) const { + void way(const osmium::DiffWay&) const noexcept { } - void relation(const osmium::DiffRelation&) const { + void relation(const osmium::DiffRelation&) const noexcept { } }; // class DiffHandler diff --git a/third_party/libosmium/include/osmium/diff_iterator.hpp b/third_party/libosmium/include/osmium/diff_iterator.hpp index 9a703992e..c65085884 100644 --- a/third_party/libosmium/include/osmium/diff_iterator.hpp +++ b/third_party/libosmium/include/osmium/diff_iterator.hpp @@ -66,8 +66,8 @@ namespace osmium { void set_diff() const noexcept { assert(m_curr != m_end); - 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(); + const bool use_curr_for_prev = m_prev->type() != m_curr->type() || m_prev->id() != m_curr->id(); + const bool use_curr_for_next = m_next == m_end || m_next->type() != m_curr->type() || m_next->id() != m_curr->id(); m_diff = std::move(osmium::DiffObject{ *(use_curr_for_prev ? m_curr : m_prev), @@ -104,7 +104,7 @@ namespace osmium { } DiffIterator operator++(int) { - DiffIterator tmp(*this); + DiffIterator tmp{*this}; operator++(); return tmp; } diff --git a/third_party/libosmium/include/osmium/diff_visitor.hpp b/third_party/libosmium/include/osmium/diff_visitor.hpp index f796ac69f..7ede0eeb1 100644 --- a/third_party/libosmium/include/osmium/diff_visitor.hpp +++ b/third_party/libosmium/include/osmium/diff_visitor.hpp @@ -56,7 +56,7 @@ namespace osmium { handler.relation(static_cast(diff)); break; default: - throw osmium::unknown_type(); + throw osmium::unknown_type{}; } } @@ -72,8 +72,8 @@ namespace osmium { inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) { using diff_iterator = osmium::DiffIterator; - diff_iterator dit(it, end); - diff_iterator dend(end, end); + diff_iterator dit{it, end}; + diff_iterator dend{end, end}; for (; dit != dend; ++dit) { detail::apply_diff_iterator_recurse(*dit, handlers...); diff --git a/third_party/libosmium/include/osmium/dynamic_handler.hpp b/third_party/libosmium/include/osmium/dynamic_handler.hpp index 9219ade45..8e51852c3 100644 --- a/third_party/libosmium/include/osmium/dynamic_handler.hpp +++ b/third_party/libosmium/include/osmium/dynamic_handler.hpp @@ -54,8 +54,7 @@ namespace osmium { public: - virtual ~HandlerWrapperBase() { - } + virtual ~HandlerWrapperBase() = default; virtual void node(const osmium::Node&) { } @@ -115,7 +114,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> public: template - HandlerWrapper(TArgs&&... args) : + explicit HandlerWrapper(TArgs&&... args) : m_handler(std::forward(args)...) { } @@ -155,12 +154,12 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> public: DynamicHandler() : - m_impl(impl_ptr(new osmium::handler::detail::HandlerWrapperBase)) { + m_impl(new osmium::handler::detail::HandlerWrapperBase) { } template void set(TArgs&&... args) { - m_impl = impl_ptr(new osmium::handler::detail::HandlerWrapper(std::forward(args)...)); + m_impl.reset(new osmium::handler::detail::HandlerWrapper{std::forward(args)...}); } void node(const osmium::Node& node) { diff --git a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp index 08f426cf0..2f66ba456 100644 --- a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp +++ b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp @@ -78,7 +78,7 @@ namespace osmium { { m_location_handler.ignore_errors(); if (m_with_areas) { - osmium::io::Reader reader(file, osmium::osm_entity_bits::relation); + osmium::io::Reader reader{file, osmium::osm_entity_bits::relation}; m_collector.read_relations(reader); reader.close(); } diff --git a/third_party/libosmium/include/osmium/geom/factory.hpp b/third_party/libosmium/include/osmium/geom/factory.hpp index 6f8d954b0..85d0abd4a 100644 --- a/third_party/libosmium/include/osmium/geom/factory.hpp +++ b/third_party/libosmium/include/osmium/geom/factory.hpp @@ -165,6 +165,11 @@ namespace osmium { public: + GeometryFactory() : + m_projection(), + m_impl(m_projection.epsg()) { + } + /** * Constructor for default initialized projection. */ @@ -192,7 +197,7 @@ namespace osmium { using multipolygon_type = typename TGeomImpl::multipolygon_type; using ring_type = typename TGeomImpl::ring_type; - int epsg() const { + int epsg() const noexcept { return m_projection.epsg(); } @@ -380,9 +385,9 @@ namespace osmium { size_t num_rings = 0; m_impl.multipolygon_start(); - for (auto it = area.cbegin(); it != area.cend(); ++it) { - if (it->type() == osmium::item_type::outer_ring) { - auto& ring = static_cast(*it); + for (const auto& item : area) { + if (item.type() == osmium::item_type::outer_ring) { + auto& ring = static_cast(item); if (num_polygons > 0) { m_impl.multipolygon_polygon_finish(); } @@ -392,8 +397,8 @@ namespace osmium { m_impl.multipolygon_outer_ring_finish(); ++num_rings; ++num_polygons; - } else if (it->type() == osmium::item_type::inner_ring) { - auto& ring = static_cast(*it); + } else if (item.type() == osmium::item_type::inner_ring) { + auto& ring = static_cast(item); m_impl.multipolygon_inner_ring_start(); add_points(ring); m_impl.multipolygon_inner_ring_finish(); diff --git a/third_party/libosmium/include/osmium/geom/geos.hpp b/third_party/libosmium/include/osmium/geom/geos.hpp index b472a3886..2367b9761 100644 --- a/third_party/libosmium/include/osmium/geom/geos.hpp +++ b/third_party/libosmium/include/osmium/geom/geos.hpp @@ -129,13 +129,13 @@ namespace osmium { */ OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) : m_precision_model(new geos::geom::PrecisionModel), - m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)), + m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}), m_geos_factory(m_our_geos_factory.get()) { } explicit GEOSFactoryImpl(int srid) : m_precision_model(new geos::geom::PrecisionModel), - m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)), + m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}), m_geos_factory(m_our_geos_factory.get()) { } @@ -143,7 +143,7 @@ namespace osmium { point_type make_point(const osmium::geom::Coordinates& xy) const { try { - return point_type(m_geos_factory->createPoint(geos::geom::Coordinate(xy.x, xy.y))); + return point_type{m_geos_factory->createPoint(geos::geom::Coordinate{xy.x, xy.y})}; } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -153,7 +153,7 @@ namespace osmium { void linestring_start() { try { - m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); + m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -161,15 +161,15 @@ namespace osmium { void linestring_add_location(const osmium::geom::Coordinates& xy) { try { - m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); + m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y}); } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } - linestring_type linestring_finish(size_t /* num_points */) { + linestring_type linestring_finish(std::size_t /* num_points */) { try { - return linestring_type(m_geos_factory->createLineString(m_coordinate_sequence.release())); + return linestring_type{m_geos_factory->createLineString(m_coordinate_sequence.release())}; } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -201,7 +201,7 @@ namespace osmium { void multipolygon_outer_ring_start() { try { - m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); + m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -217,7 +217,7 @@ namespace osmium { void multipolygon_inner_ring_start() { try { - m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); + m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -233,7 +233,7 @@ namespace osmium { void multipolygon_add_location(const osmium::geom::Coordinates& xy) { try { - m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); + m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y}); } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } @@ -246,7 +246,7 @@ namespace osmium { return p.release(); }); m_polygons.clear(); - return multipolygon_type(m_geos_factory->createMultiPolygon(polygons)); + return multipolygon_type{m_geos_factory->createMultiPolygon(polygons)}; } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } diff --git a/third_party/libosmium/include/osmium/geom/mercator_projection.hpp b/third_party/libosmium/include/osmium/geom/mercator_projection.hpp index 814bce632..5be84a465 100644 --- a/third_party/libosmium/include/osmium/geom/mercator_projection.hpp +++ b/third_party/libosmium/include/osmium/geom/mercator_projection.hpp @@ -118,7 +118,7 @@ namespace osmium { * @pre @code c.valid() @endcode */ inline Coordinates lonlat_to_mercator(const Coordinates& c) { - return Coordinates(detail::lon_to_x(c.x), detail::lat_to_y(c.y)); + return Coordinates{detail::lon_to_x(c.x), detail::lat_to_y(c.y)}; } /** @@ -127,7 +127,7 @@ namespace osmium { * @pre @code c.valid() @endcode */ inline Coordinates mercator_to_lonlat(const Coordinates& c) { - return Coordinates(detail::x_to_lon(c.x), detail::y_to_lat(c.y)); + return Coordinates{detail::x_to_lon(c.x), detail::y_to_lat(c.y)}; } /** @@ -138,8 +138,11 @@ namespace osmium { public: + MercatorProjection() { + } + Coordinates operator()(osmium::Location location) const { - return Coordinates {detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())}; + return Coordinates{detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())}; } int epsg() const noexcept { diff --git a/third_party/libosmium/include/osmium/geom/projection.hpp b/third_party/libosmium/include/osmium/geom/projection.hpp index 60e0ecf3d..de3bf9003 100644 --- a/third_party/libosmium/include/osmium/geom/projection.hpp +++ b/third_party/libosmium/include/osmium/geom/projection.hpp @@ -48,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include @@ -70,15 +71,15 @@ namespace osmium { public: - explicit CRS(const std::string& crs) : - m_crs(pj_init_plus(crs.c_str()), ProjCRSDeleter()) { + explicit CRS(const char* crs) : + m_crs(pj_init_plus(crs), ProjCRSDeleter()) { if (!m_crs) { - throw osmium::projection_error(std::string{"creation of CRS failed: "} + pj_strerrno(*pj_get_errno_ref())); + throw osmium::projection_error{std::string{"creation of CRS failed: "} + pj_strerrno(*pj_get_errno_ref())}; } } - explicit CRS(const char* crs) : - CRS(std::string{crs}) { + explicit CRS(const std::string& crs) : + CRS(crs.c_str()) { } explicit CRS(int epsg) : @@ -88,15 +89,15 @@ namespace osmium { /** * Get underlying projPJ handle from proj library. */ - projPJ get() const { + projPJ get() const noexcept { return m_crs.get(); } - bool is_latlong() const { + bool is_latlong() const noexcept { return pj_is_latlong(m_crs.get()) != 0; } - bool is_geocent() const { + bool is_geocent() const noexcept { return pj_is_geocent(m_crs.get()) != 0; } @@ -108,12 +109,13 @@ namespace osmium { * * Coordinates have to be in radians and are produced in radians. * - * @throws osmmium::projection_error if the projection fails + * @throws osmium::projection_error if the projection fails */ + // cppcheck-suppress passedByValue (because c is small and we want to change it) 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); + const 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)); + throw osmium::projection_error{std::string{"projection failed: "} + pj_strerrno(result)}; } return c; } @@ -121,12 +123,19 @@ namespace osmium { /** * Functor that does projection from WGS84 (EPSG:4326) to the given * CRS. + * + * If this Projection is initialized with the constructor taking + * an integer with the epsg code 4326, no projection is done. If it + * is initialized with epsg code 3857 the Osmium-internal + * implementation of the Mercator projection is used, otherwise this + * falls back to using the proj.4 library. Note that this "magic" does + * not work if you use any of the constructors taking a string. */ class Projection { int m_epsg; std::string m_proj_string; - CRS m_crs_wgs84 {4326}; + CRS m_crs_wgs84{4326}; CRS m_crs_user; public: @@ -145,19 +154,23 @@ namespace osmium { explicit Projection(int epsg) : m_epsg(epsg), - m_proj_string(std::string("+init=epsg:") + std::to_string(epsg)), + m_proj_string(std::string{"+init=epsg:"} + std::to_string(epsg)), m_crs_user(epsg) { } Coordinates operator()(osmium::Location location) const { - Coordinates c {location.lon(), location.lat()}; + if (m_epsg == 4326) { + return Coordinates{location.lon(), location.lat()}; + } else if (m_epsg == 3857) { + return Coordinates{detail::lon_to_x(location.lon()), + detail::lat_to_y(location.lat())}; + } - if (m_epsg != 4326) { - c = transform(m_crs_wgs84, m_crs_user, Coordinates(deg_to_rad(location.lon()), deg_to_rad(location.lat()))); - if (m_crs_user.is_latlong()) { - c.x = rad_to_deg(c.x); - c.y = rad_to_deg(c.y); - } + Coordinates c{transform(m_crs_wgs84, m_crs_user, Coordinates{deg_to_rad(location.lon()), + deg_to_rad(location.lat())})}; + if (m_crs_user.is_latlong()) { + c.x = rad_to_deg(c.x); + c.y = rad_to_deg(c.y); } return c; diff --git a/third_party/libosmium/include/osmium/geom/relations.hpp b/third_party/libosmium/include/osmium/geom/relations.hpp index f11ba511b..c8d5caf83 100644 --- a/third_party/libosmium/include/osmium/geom/relations.hpp +++ b/third_party/libosmium/include/osmium/geom/relations.hpp @@ -43,13 +43,23 @@ namespace osmium { /** * Check whether one geometry contains another. */ - inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) { + inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) noexcept { return ((lhs.bottom_left().x() >= rhs.bottom_left().x()) && (lhs.top_right().x() <= rhs.top_right().x()) && (lhs.bottom_left().y() >= rhs.bottom_left().y()) && (lhs.top_right().y() <= rhs.top_right().y())); } + /** + * Check whether one geometry overlaps another. + */ + inline bool overlaps(const osmium::Box& lhs, const osmium::Box& rhs) noexcept { + return ((lhs.bottom_left().x() <= rhs.top_right().x()) && + (lhs.bottom_left().y() <= rhs.top_right().y()) && + (rhs.bottom_left().x() <= lhs.top_right().x()) && + (rhs.bottom_left().y() <= lhs.top_right().y())); + } + } // namespace geom } // namespace osmium diff --git a/third_party/libosmium/include/osmium/geom/tile.hpp b/third_party/libosmium/include/osmium/geom/tile.hpp index 55ea87b2e..6f07d8c72 100644 --- a/third_party/libosmium/include/osmium/geom/tile.hpp +++ b/third_party/libosmium/include/osmium/geom/tile.hpp @@ -62,7 +62,7 @@ namespace osmium { } /** - * Returns the width or hight of a tile in web mercator coordinates for + * Returns the width or height of a tile in web mercator coordinates for * the given zoom level. */ inline constexpr double tile_extent_in_zoom(uint32_t zoom) noexcept { @@ -108,7 +108,7 @@ namespace osmium { uint32_t z; /** - * Create a tile with the given zoom level and x any y tile + * Create a tile with the given zoom level and x and y tile * coordinates. * * The values are not checked for validity. @@ -172,22 +172,30 @@ namespace osmium { }; // struct Tile /// Tiles are equal if all their attributes are equal. - inline bool operator==(const Tile& lhs, const Tile& rhs) { + inline bool operator==(const Tile& lhs, const Tile& rhs) noexcept { return lhs.z == rhs.z && lhs.x == rhs.x && lhs.y == rhs.y; } - inline bool operator!=(const Tile& lhs, const Tile& rhs) { + inline bool operator!=(const Tile& lhs, const Tile& rhs) noexcept { return ! (lhs == rhs); } /** * This defines an arbitrary order on tiles for use in std::map etc. */ - inline bool operator<(const Tile& lhs, const Tile& rhs) { - if (lhs.z < rhs.z) return true; - if (lhs.z > rhs.z) return false; - if (lhs.x < rhs.x) return true; - if (lhs.x > rhs.x) return false; + inline bool operator<(const Tile& lhs, const Tile& rhs) noexcept { + if (lhs.z < rhs.z) { + return true; + } + if (lhs.z > rhs.z) { + return false; + } + if (lhs.x < rhs.x) { + return true; + } + if (lhs.x > rhs.x) { + return false; + } return lhs.y < rhs.y; } diff --git a/third_party/libosmium/include/osmium/geom/wkb.hpp b/third_party/libosmium/include/osmium/geom/wkb.hpp index 39105fc47..5299d4587 100644 --- a/third_party/libosmium/include/osmium/geom/wkb.hpp +++ b/third_party/libosmium/include/osmium/geom/wkb.hpp @@ -108,19 +108,19 @@ namespace osmium { }; // enum class wkb_byte_order_type std::string m_data; - uint32_t m_points {0}; + uint32_t m_points = 0; int m_srid; wkb_type m_wkb_type; out_type m_out_type; - size_t m_linestring_size_offset = 0; - size_t m_polygons = 0; - size_t m_rings = 0; - size_t m_multipolygon_size_offset = 0; - size_t m_polygon_size_offset = 0; - size_t m_ring_size_offset = 0; + std::size_t m_linestring_size_offset = 0; + std::size_t m_polygons = 0; + std::size_t m_rings = 0; + std::size_t m_multipolygon_size_offset = 0; + std::size_t m_polygon_size_offset = 0; + std::size_t m_ring_size_offset = 0; - size_t header(std::string& str, wkbGeometryType type, bool add_length) const { + std::size_t header(std::string& str, wkbGeometryType type, bool add_length) const { #if __BYTE_ORDER == __LITTLE_ENDIAN str_push(str, wkb_byte_order_type::NDR); #else @@ -132,14 +132,14 @@ namespace osmium { } else { str_push(str, type); } - const size_t offset = str.size(); + const std::size_t offset = str.size(); if (add_length) { str_push(str, static_cast(0)); } return offset; } - void set_size(const size_t offset, const size_t size) { + void set_size(const std::size_t offset, const std::size_t size) { uint32_t s = static_cast_with_assert(size); std::copy_n(reinterpret_cast(&s), sizeof(uint32_t), &m_data[offset]); } @@ -185,7 +185,7 @@ namespace osmium { str_push(m_data, xy.y); } - linestring_type linestring_finish(size_t num_points) { + linestring_type linestring_finish(std::size_t num_points) { set_size(m_linestring_size_offset, num_points); std::string data; diff --git a/third_party/libosmium/include/osmium/handler/check_order.hpp b/third_party/libosmium/include/osmium/handler/check_order.hpp index 11995d7c1..48a673b74 100644 --- a/third_party/libosmium/include/osmium/handler/check_order.hpp +++ b/third_party/libosmium/include/osmium/handler/check_order.hpp @@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -51,12 +52,16 @@ namespace osmium { */ struct out_of_order_error : public std::runtime_error { - explicit out_of_order_error(const std::string& what) : - std::runtime_error(what) { + osmium::object_id_type object_id; + + explicit out_of_order_error(const std::string& what, osmium::object_id_type id) : + std::runtime_error(what), + object_id(id) { } - explicit out_of_order_error(const char* what) : - std::runtime_error(what) { + explicit out_of_order_error(const char* what, osmium::object_id_type id) : + std::runtime_error(what), + object_id(id) { } }; // struct out_of_order_error @@ -67,12 +72,14 @@ namespace osmium { * 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. + * of their IDs, then relations in the order or their IDs. Negative + * IDs are ordered first then positive IDs, both ordered by absolute + * value. * * IDs have to be unique for each type. This check will fail for * history files. * - * To use this add a CheckOrder member variable to your handler and + * 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. @@ -86,33 +93,42 @@ namespace osmium { 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_way_id > std::numeric_limits::min()) { + throw out_of_order_error{"Found a node after a way.", node.id()}; } - if (m_max_relation_id > 0) { - throw out_of_order_error("Found a node after a relation."); + if (m_max_relation_id > std::numeric_limits::min()) { + throw out_of_order_error{"Found a node after a relation.", node.id()}; } - if (m_max_node_id >= node.id()) { - throw out_of_order_error("Node IDs out of order."); + if (m_max_node_id == node.id()) { + throw out_of_order_error{"Node ID twice in input. Maybe you are using a history or change file?", node.id()}; + } + if (id_order{}(node.id(), m_max_node_id)) { + throw out_of_order_error{"Node IDs out of order.", node.id()}; } 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_relation_id > std::numeric_limits::min()) { + throw out_of_order_error{"Found a way after a relation.", way.id()}; } - if (m_max_way_id >= way.id()) { - throw out_of_order_error("Way IDs out of order."); + if (m_max_way_id == way.id()) { + throw out_of_order_error{"Way ID twice in input. Maybe you are using a history or change file?", way.id()}; + } + if (id_order{}(way.id(), m_max_way_id)) { + throw out_of_order_error{"Way IDs out of order.", way.id()}; } 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."); + if (m_max_relation_id == relation.id()) { + throw out_of_order_error{"Relation ID twice in input. Maybe you are using a history or change file?", relation.id()}; + } + if (id_order{}(relation.id(), m_max_relation_id)) { + throw out_of_order_error{"Relation IDs out of order.", relation.id()}; } m_max_relation_id = relation.id(); } diff --git a/third_party/libosmium/include/osmium/handler/disk_store.hpp b/third_party/libosmium/include/osmium/handler/disk_store.hpp index 882d7a17c..da1119dde 100644 --- a/third_party/libosmium/include/osmium/handler/disk_store.hpp +++ b/third_party/libosmium/include/osmium/handler/disk_store.hpp @@ -60,9 +60,9 @@ namespace osmium { */ class DiskStore : public osmium::handler::Handler { - using offset_index_type = osmium::index::map::Map; + using offset_index_type = osmium::index::map::Map; - size_t m_offset = 0; + std::size_t m_offset = 0; int m_data_fd; offset_index_type& m_node_index; diff --git a/third_party/libosmium/include/osmium/handler/dump.hpp b/third_party/libosmium/include/osmium/handler/dump.hpp index b3f2ed7aa..9c2c5c66e 100644 --- a/third_party/libosmium/include/osmium/handler/dump.hpp +++ b/third_party/libosmium/include/osmium/handler/dump.hpp @@ -108,7 +108,7 @@ namespace osmium { << (object.visible() ? "yes" : "no") << "\n"; - Dump dump(*m_out, m_with_size, m_prefix + " "); + Dump dump{*m_out, m_with_size, m_prefix + " "}; osmium::apply(object.cbegin(), object.cend(), dump); } @@ -281,7 +281,7 @@ namespace osmium { *m_out << "\n"; - Dump dump(*m_out, m_with_size, m_prefix + " "); + Dump dump{*m_out, m_with_size, m_prefix + " "}; osmium::apply(changeset.cbegin(), changeset.cend(), dump); } 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 12c2b5f6c..ff3fbe9c9 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 @@ -64,9 +64,11 @@ namespace osmium { 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"); + template + using based_on_map = std::is_base_of, T>; - static_assert(std::is_base_of, TStorageNegIDs>::value, "Index class must be derived from osmium::index::map::Map"); + static_assert(based_on_map::value, "Index class must be derived from osmium::index::map::Map"); + static_assert(based_on_map::value, "Index class must be derived from osmium::index::map::Map"); public: @@ -81,11 +83,11 @@ namespace osmium { /// Object that handles the actual storage of the node locations (with negative IDs). TStorageNegIDs& m_storage_neg; - osmium::unsigned_object_id_type m_last_id{0}; + osmium::unsigned_object_id_type m_last_id = 0; - bool m_ignore_errors{false}; + bool m_ignore_errors = false; - bool m_must_sort{false}; + bool m_must_sort = false; // It is okay to have this static dummy instance, even when using several threads, // because it is read-only. @@ -123,7 +125,7 @@ namespace osmium { } m_last_id = node.positive_id(); - const osmium::object_id_type id = node.id(); + const auto id = node.id(); if (id >= 0) { m_storage_pos.set(static_cast( id), node.location()); } else { @@ -136,9 +138,9 @@ namespace osmium { */ osmium::Location get_node_location(const osmium::object_id_type id) const { if (id >= 0) { - return m_storage_pos.get(static_cast( id)); + return m_storage_pos.get_noexcept(static_cast( id)); } else { - return m_storage_neg.get(static_cast(-id)); + return m_storage_neg.get_noexcept(static_cast(-id)); } } @@ -155,16 +157,12 @@ namespace osmium { } bool error = false; for (auto& node_ref : way.nodes()) { - try { - node_ref.set_location(get_node_location(node_ref.ref())); - if (!node_ref.location()) { - error = true; - } - } catch (const osmium::not_found&) { + node_ref.set_location(get_node_location(node_ref.ref())); + if (!node_ref.location()) { error = true; } } - if (error && !m_ignore_errors) { + if (!m_ignore_errors && error) { throw osmium::not_found{"location for one or more nodes not found in node location index"}; } } 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 e724f1835..745a35805 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 @@ -50,15 +50,15 @@ namespace osmium { template inline T* create_map_with_fd(const std::vector& config) { if (config.size() == 1) { - return new T(); + return new T{}; } assert(config.size() > 1); const std::string& filename = config[1]; const 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 + "': " + std::strerror(errno)); + throw std::runtime_error{std::string{"can't open file '"} + filename + "': " + std::strerror(errno)}; } - return new T(fd); + return new T{fd}; } } // namespace detail 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 6ba7e2e45..d853992a4 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 @@ -53,11 +53,11 @@ namespace osmium { template class mmap_vector_file : public mmap_vector_base { - size_t filesize(int fd) const { - const size_t size = osmium::util::file_size(fd); + static std::size_t filesize(int fd) { + const auto size = osmium::util::file_size(fd); if (size % sizeof(T) != 0) { - throw std::runtime_error("Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ")."); + throw std::runtime_error{"Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ")."}; } return size / sizeof(T); diff --git a/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp b/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp index 9da7b7033..ce9b194d5 100644 --- a/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp +++ b/third_party/libosmium/include/osmium/index/detail/tmpfile.hpp @@ -50,7 +50,7 @@ namespace osmium { inline int create_tmp_file() { FILE* file = ::tmpfile(); if (!file) { - throw std::system_error(errno, std::system_category(), "tempfile failed"); + throw std::system_error{errno, std::system_category(), "tempfile failed"}; } return fileno(file); } 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 ed0f760ba..0eb49d596 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_map.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_map.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include @@ -70,7 +69,7 @@ namespace osmium { ~VectorBasedDenseMap() noexcept final = default; - void reserve(const size_t size) final { + void reserve(const std::size_t size) final { m_vector.reserve(size); } @@ -99,15 +98,15 @@ namespace osmium { return m_vector[id]; } - size_t size() const final { + std::size_t size() const final { return m_vector.size(); } - size_t byte_size() const { + std::size_t byte_size() const { return m_vector.size() * sizeof(element_type); } - size_t used_memory() const final { + std::size_t used_memory() const final { return sizeof(TValue) * size(); } @@ -205,15 +204,15 @@ namespace osmium { return result->second; } - size_t size() const final { + std::size_t size() const final { return m_vector.size(); } - size_t byte_size() const { + std::size_t byte_size() const { return m_vector.size() * sizeof(element_type); } - size_t used_memory() const final { + std::size_t used_memory() const final { return sizeof(element_type) * size(); } diff --git a/third_party/libosmium/include/osmium/index/id_set.hpp b/third_party/libosmium/include/osmium/index/id_set.hpp index 50426b006..d86973d85 100644 --- a/third_party/libosmium/include/osmium/index/id_set.hpp +++ b/third_party/libosmium/include/osmium/index/id_set.hpp @@ -35,10 +35,11 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include -#include #include #include @@ -57,8 +58,7 @@ namespace osmium { public: - virtual ~IdSet() { - } + virtual ~IdSet() = default; /** * Add the given Id to the set. @@ -80,6 +80,11 @@ namespace osmium { */ virtual void clear() = 0; + /** + * Get an estimate of the amount of memory used for the set. + */ + virtual std::size_t used_memory() const noexcept = 0; + }; // class IdSet template @@ -139,7 +144,7 @@ namespace osmium { } IdSetDenseIterator operator++(int) noexcept { - IdSetDenseIterator tmp(*this); + IdSetDenseIterator tmp{*this}; operator++(); return tmp; } @@ -178,17 +183,17 @@ namespace osmium { // which would mean less (but larger) memory allocations. For // relations Ids it could be smaller, because they would all fit // into a smaller allocation. - constexpr static const size_t chunk_bits = 22; - constexpr static const size_t chunk_size = 1 << chunk_bits; + constexpr static const std::size_t chunk_bits = 22; + constexpr static const std::size_t chunk_size = 1 << chunk_bits; std::vector> m_data; T m_size = 0; - static size_t chunk_id(T id) noexcept { + static std::size_t chunk_id(T id) noexcept { return id >> (chunk_bits + 3); } - static size_t offset(T id) noexcept { + static std::size_t offset(T id) noexcept { return (id >> 3) & ((1 << chunk_bits) - 1); } @@ -244,7 +249,7 @@ namespace osmium { * * @param id The Id to set. */ - void set(T id) override final { + void set(T id) final { (void)check_and_set(id); } @@ -267,7 +272,7 @@ namespace osmium { * * @param id The Id to check. */ - bool get(T id) const noexcept override final { + bool get(T id) const noexcept final { if (chunk_id(id) >= m_data.size()) { return false; } @@ -281,7 +286,7 @@ namespace osmium { /** * Is the set empty? */ - bool empty() const noexcept override final { + bool empty() const noexcept final { return m_size == 0; } @@ -295,17 +300,21 @@ namespace osmium { /** * Clear the set. */ - void clear() override final { + void clear() final { m_data.clear(); m_size = 0; } + std::size_t used_memory() const noexcept final { + return m_data.size() * chunk_size; + } + IdSetDenseIterator begin() const { - return IdSetDenseIterator{this, 0, last()}; + return {this, 0, last()}; } IdSetDenseIterator end() const { - return IdSetDenseIterator{this, last(), last()}; + return {this, last(), last()}; } }; // class IdSetDense @@ -324,7 +333,7 @@ namespace osmium { /** * Add the given Id to the set. */ - void set(T id) override final { + void set(T id) final { m_data.push_back(id); } @@ -333,7 +342,7 @@ namespace osmium { * * @param id The Id to check. */ - bool get(T id) const noexcept override final { + bool get(T id) const noexcept final { const auto it = std::find(m_data.cbegin(), m_data.cend(), id); return it != m_data.cend(); } @@ -355,14 +364,14 @@ namespace osmium { /** * Is the set empty? */ - bool empty() const noexcept override final { + bool empty() const noexcept final { return m_data.empty(); } /** * Clear the set. */ - void clear() override final { + void clear() final { m_data.clear(); } @@ -383,10 +392,14 @@ namespace osmium { * @pre You must have called sort_unique() before calling this * or be sure there are no duplicates. */ - size_t size() const noexcept { + std::size_t size() const noexcept { return m_data.size(); } + std::size_t used_memory() const noexcept final { + return m_data.capacity() * sizeof(T); + } + /// Iterator type. There is no non-const iterator. using const_iterator = typename std::vector::const_iterator; @@ -408,6 +421,7 @@ namespace osmium { }; // class IdSetSmall + /// @deprecated Use nwr_array helper class instead. template class IdSetType> class NWRIdSet { diff --git a/third_party/libosmium/include/osmium/index/index.hpp b/third_party/libosmium/include/osmium/index/index.hpp index 565009c12..325b42f93 100644 --- a/third_party/libosmium/include/osmium/index/index.hpp +++ b/third_party/libosmium/include/osmium/index/index.hpp @@ -34,12 +34,11 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include -#include - namespace osmium { /** diff --git a/third_party/libosmium/include/osmium/index/map.hpp b/third_party/libosmium/include/osmium/index/map.hpp index bcda5894a..e0f9aecef 100644 --- a/third_party/libosmium/include/osmium/index/map.hpp +++ b/third_party/libosmium/include/osmium/index/map.hpp @@ -43,7 +43,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include namespace osmium { @@ -181,14 +180,14 @@ namespace osmium { // 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"); + throw std::runtime_error{"can't dump as list"}; } // This function can 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"); + throw std::runtime_error{"can't dump as array"}; } }; // class Map @@ -245,13 +244,13 @@ namespace osmium { } std::unique_ptr create_map(const std::string& config_string) const { - std::vector config = osmium::split_string(config_string, ','); + std::vector config{osmium::split_string(config_string, ',')}; if (config.empty()) { throw map_factory_error{"Need non-empty map type name"}; } - auto it = m_callbacks.find(config[0]); + const auto it = m_callbacks.find(config[0]); if (it != m_callbacks.end()) { return std::unique_ptr((it->second)(config)); } diff --git a/third_party/libosmium/include/osmium/index/map/all.hpp b/third_party/libosmium/include/osmium/index/map/all.hpp index 1e4827f89..f196ffe17 100644 --- a/third_party/libosmium/include/osmium/index/map/all.hpp +++ b/third_party/libosmium/include/osmium/index/map/all.hpp @@ -37,6 +37,7 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep +#include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep 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 cdde37371..c567612a1 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 @@ -64,4 +64,8 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseFileArray, dense_file_array) +#endif + #endif // OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP 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 af610a805..839825873 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 @@ -54,4 +54,8 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMemArray, dense_mem_array) +#endif + #endif // OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP 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 85057dc28..8f51cc5d3 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 @@ -55,6 +55,10 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMmapArray, dense_mmap_array) +#endif + #endif // __linux__ #endif // OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP diff --git a/third_party/libosmium/include/osmium/index/map/flex_mem.hpp b/third_party/libosmium/include/osmium/index/map/flex_mem.hpp new file mode 100644 index 000000000..db474e75d --- /dev/null +++ b/third_party/libosmium/include/osmium/index/map/flex_mem.hpp @@ -0,0 +1,285 @@ +#ifndef OSMIUM_INDEX_MAP_FLEX_MEM_HPP +#define OSMIUM_INDEX_MAP_FLEX_MEM_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +#define OSMIUM_HAS_INDEX_MAP_FLEX_MEM + +namespace osmium { + + namespace index { + + namespace map { + + /** + * This is an autoscaling index that works well with small and + * large input data. All data will be held in memory. For small + * input data a sparse array will be used, if this becomes + * inefficient, the class will switch automatically to a dense + * index. + */ + template + class FlexMem : public osmium::index::map::Map { + + // This value is based on benchmarks with a planet file and + // some smaller files. + enum constant_bits { + bits = 16 + }; + + enum constant_block_size : uint64_t { + block_size = 1ll << bits + }; + + // Minimum number of entries in the sparse index before we + // are considering switching to a dense index. + enum constant_min_dense_entries : int64_t { + min_dense_entries = 0xffffff + }; + + // When more than a third of all Ids are in the index, we + // switch to the dense index. This is a compromise between + // the best memory efficiency (which we would get at a factor + // of 2) and the performance (dense index is much faster then + // the sparse index). + enum constant_density_factor { + density_factor = 3 + }; + + // An entry in the sparse index + struct entry { + uint64_t id; + TValue value; + + entry(uint64_t i, TValue v) : + id(i), + value(v) { + } + + bool operator<(const entry other) const noexcept { + return id < other.id; + } + }; + + std::vector m_sparse_entries; + + std::vector> m_dense_blocks; + + // The maximum Id that was seen yet. Only set in sparse mode. + uint64_t m_max_id = 0; + + // Set to false in sparse mode and to true in dense mode. + bool m_dense; + + static uint64_t block(const uint64_t id) noexcept { + return id >> bits; + } + + static uint64_t offset(const uint64_t id) noexcept { + return id & (block_size - 1); + } + + // Assure that the block with the given number exists. Create + // it if needed. + void assure_block(const uint64_t num) { + if (num >= m_dense_blocks.size()) { + m_dense_blocks.resize(num + 1); + } + if (m_dense_blocks[num].empty()) { + m_dense_blocks[num].assign(block_size, osmium::index::empty_value()); + } + } + + void set_sparse(const uint64_t id, const TValue value) { + m_sparse_entries.emplace_back(id, value); + if (id > m_max_id) { + m_max_id = id; + + if (m_sparse_entries.size() >= min_dense_entries) { + if (m_max_id < m_sparse_entries.size() * density_factor) { + switch_to_dense(); + } + } + } + } + + TValue get_sparse(const uint64_t id) const noexcept { + const auto it = std::lower_bound(m_sparse_entries.begin(), + m_sparse_entries.end(), + entry{id, osmium::index::empty_value()}); + if (it == m_sparse_entries.end() || it->id != id) { + return osmium::index::empty_value(); + } + return it->value; + } + + void set_dense(const uint64_t id, const TValue value) { + assure_block(block(id)); + m_dense_blocks[block(id)][offset(id)] = value; + } + + TValue get_dense(const uint64_t id) const noexcept { + if (m_dense_blocks.size() <= block(id) || m_dense_blocks[block(id)].empty()) { + return osmium::index::empty_value(); + } + return m_dense_blocks[block(id)][offset(id)]; + } + + public: + + /** + * Create FlexMem index. + * + * @param use_dense Usually FlexMem indexes start out as sparse + * indexes and will switch to dense when they + * think it is better. Set this to force dense + * indexing from the start. This is usually + * only useful for testing. + */ + explicit FlexMem(bool use_dense = false) : + m_dense(use_dense) { + } + + ~FlexMem() noexcept final = default; + + bool is_dense() const noexcept { + return m_dense; + } + + std::size_t size() const noexcept final { + if (m_dense) { + return m_dense_blocks.size() * block_size; + } + return m_sparse_entries.size(); + } + + std::size_t used_memory() const noexcept final { + return sizeof(FlexMem) + + m_sparse_entries.size() * sizeof(entry) + + m_dense_blocks.size() * (block_size * sizeof(TValue) + sizeof(std::vector)); + } + + void set(const TId id, const TValue value) final { + if (m_dense) { + set_dense(id, value); + } else { + set_sparse(id, value); + } + } + + TValue get_noexcept(const TId id) const noexcept final { + if (m_dense) { + return get_dense(id); + } + return get_sparse(id); + } + + TValue get(const TId id) const final { + const auto value = get_noexcept(id); + if (value == osmium::index::empty_value()) { + throw osmium::not_found{id}; + } + return value; + } + + void clear() final { + m_sparse_entries.clear(); + m_sparse_entries.shrink_to_fit(); + m_dense_blocks.clear(); + m_dense_blocks.shrink_to_fit(); + m_max_id = 0; + m_dense = false; + } + + void sort() final { + std::sort(m_sparse_entries.begin(), m_sparse_entries.end()); + } + + /** + * Switch from using a sparse to a dense index. Usually you + * do not need to call this, because the FlexMem class will + * do this automatically if it thinks the dense index is more + * efficient. + * + * Does nothing if the index is already in dense mode. + */ + void switch_to_dense() { + if (m_dense) { + return; + } + for (const auto entry : m_sparse_entries) { + set_dense(entry.id, entry.value); + } + m_sparse_entries.clear(); + m_sparse_entries.shrink_to_fit(); + m_max_id = 0; + m_dense = true; + } + + std::pair stats() const noexcept { + std::size_t used_blocks = 0; + std::size_t empty_blocks = 0; + + for (const auto& block : m_dense_blocks) { + if (block.empty()) { + ++empty_blocks; + } else { + ++used_blocks; + } + } + + return std::make_pair(used_blocks, empty_blocks); + } + + }; // class FlexMem + + } // namespace map + + } // namespace index + +} // namespace osmium + +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::FlexMem, flex_mem) +#endif + +#endif // OSMIUM_INDEX_MAP_FLEX_MEM_HPP 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 778b01095..8898dd0a2 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 @@ -64,4 +64,8 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseFileArray, sparse_file_array) +#endif + #endif // OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP 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 752e3b008..28255a582 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 @@ -57,4 +57,8 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemArray, sparse_mem_array) +#endif + #endif // OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP 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 f57df0971..3287c333c 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 @@ -120,4 +120,8 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemMap, sparse_mem_map) +#endif + #endif // OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP 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 f6a5fa34f..b08384b67 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 @@ -54,16 +54,16 @@ namespace osmium { namespace map { /** - * The SparseMemTable index stores elements in a Google sparsetable, - * a data structure that can hold sparsly filled tables in a - * space efficient way. It will resize automatically. - * - * Use this index if the ID space is only sparsly - * populated, such as when working with smaller OSM files (like - * country extracts). - * - * This will only work on 64 bit machines. - */ + * The SparseMemTable index stores elements in a Google sparsetable, + * a data structure that can hold sparsly filled tables in a + * space efficient way. It will resize automatically. + * + * Use this index if the ID space is only sparsly + * populated, such as when working with smaller OSM files (like + * country extracts). + * + * This will only work on 64 bit machines. + */ template class SparseMemTable : public osmium::index::map::Map { @@ -150,6 +150,10 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemTable, sparse_mem_table) +#endif + #endif // OSMIUM_WITH_SPARSEHASH -#endif // OSMIUM_INDEX_BYID_SPARSE_MEM_TABLE_HPP +#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP 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 1c15abcea..03b49a5e7 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 @@ -55,6 +55,10 @@ namespace osmium { } // namespace osmium +#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMmapArray, sparse_mmap_array) +#endif + #endif // __linux__ #endif // OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP 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 0642875a5..0e78de133 100644 --- a/third_party/libosmium/include/osmium/index/node_locations_map.hpp +++ b/third_party/libosmium/include/osmium/index/node_locations_map.hpp @@ -35,6 +35,8 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: keep +#define OSMIUM_WANT_NODE_LOCATION_MAPS + #ifdef OSMIUM_HAS_INDEX_MAP_DENSE_FILE_ARRAY REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseFileArray, dense_file_array) #endif @@ -67,4 +69,8 @@ DEALINGS IN THE SOFTWARE. REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMmapArray, sparse_mmap_array) #endif +#ifdef OSMIUM_HAS_INDEX_MAP_FLEX_MEM + REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::FlexMem, flex_mem) +#endif + #endif // OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP diff --git a/third_party/libosmium/include/osmium/index/nwr_array.hpp b/third_party/libosmium/include/osmium/index/nwr_array.hpp new file mode 100644 index 000000000..2c52c7e8f --- /dev/null +++ b/third_party/libosmium/include/osmium/index/nwr_array.hpp @@ -0,0 +1,59 @@ +#ifndef OSMIUM_INDEX_NWR_ARRAY_HPP +#define OSMIUM_INDEX_NWR_ARRAY_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + template + class nwr_array { + + T m_sets[3]; + + public: + + T& operator()(osmium::item_type type) noexcept { + return m_sets[osmium::item_type_to_nwr_index(type)]; + } + + const T& operator()(osmium::item_type type) const noexcept { + return m_sets[osmium::item_type_to_nwr_index(type)]; + } + + }; // class nwr_array + +} // namespace osmium + +#endif // OSMIUM_INDEX_NWR_ARRAY_HPP diff --git a/third_party/libosmium/include/osmium/index/relations_map.hpp b/third_party/libosmium/include/osmium/index/relations_map.hpp index 5f46e6609..bc2671959 100644 --- a/third_party/libosmium/include/osmium/index/relations_map.hpp +++ b/third_party/libosmium/include/osmium/index/relations_map.hpp @@ -35,10 +35,14 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include +#include +#include #include +#include #include #include @@ -91,6 +95,24 @@ namespace osmium { m_map.emplace_back(key, value); } + typename std::enable_if::value>::type flip_in_place() { + for (auto& p : m_map) { + using std::swap; + swap(p.key, p.value); + } + } + + flat_map flip_copy() { + flat_map map; + map.reserve(m_map.size()); + + for (const auto& p : m_map) { + map.set(p.value, p.key); + } + + return map; + } + void sort_unique() { std::sort(m_map.begin(), m_map.end()); const auto last = std::unique(m_map.begin(), m_map.end()); @@ -107,47 +129,54 @@ namespace osmium { return m_map.empty(); } - size_t size() const noexcept { + std::size_t size() const noexcept { return m_map.size(); } + void reserve(std::size_t size) { + m_map.reserve(size); + } + }; // class flat_map } // namespace detail /** - * Index for looking up parent relation IDs given a member relation ID. - * You can not instantiate such an index yourself, instead you need to - * instantiate a RelationsMapStash, fill it and then create an index from - * it: - * - * @code - * RelationsMapStash stash; - * ... - * for_each_relation(const osmium::Relation& relation) { - * stash.add_members(relation); - * } - * ... - * const auto index = stash.build_index(); - * ... - * osmium::unsigned_object_id_type member_id = ...; - * index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) { - * ... - * }); - * ... - * @endcode - * - */ + * Index for looking up parent relation IDs given a member relation ID + * or the other way around. + * + * You can not instantiate such an index yourself, instead you need to + * instantiate a RelationsMapStash, fill it and then create an index + * from it: + * + * @code + * RelationsMapStash stash; + * ... + * for_each_relation(const osmium::Relation& relation) { + * stash.add_members(relation); + * } + * ... + * const auto index = stash.build_member_to_parent_index(); + * ... + * osmium::unsigned_object_id_type member_id = ...; + * index.for_each(member_id, [](osmium::unsigned_object_id_type parent_id) { + * ... + * }); + * ... + * @endcode + * + */ class RelationsMapIndex { friend class RelationsMapStash; + friend class RelationsMapIndexes; using map_type = detail::flat_map; + osmium::unsigned_object_id_type, uint32_t>; map_type m_map; - RelationsMapIndex(map_type&& map) : + explicit RelationsMapIndex(map_type&& map) : m_map(std::move(map)) { } @@ -162,56 +191,122 @@ namespace osmium { RelationsMapIndex& operator=(RelationsMapIndex&&) = default; /** - * Find the given relation id in the index and call the given function - * with all parent relation ids. - * - * @code - * osmium::unsigned_object_id_type member_id = 17; - * index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) { - * ... - * }); - * @endcode - * - * Complexity: Logarithmic in the number of elements in the index. - * (Lookup uses binary search.) - */ - template - void for_each_parent(osmium::unsigned_object_id_type member_id, Func&& func) const { + * Find the given relation id in the index and call the given + * function with all parent relation ids. + * + * @code + * osmium::unsigned_object_id_type member_id = 17; + * index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) { + * ... + * }); + * @endcode + * + * @deprecated Use for_each() instead. + * + * Complexity: Logarithmic in the number of elements in the index. + * (Lookup uses binary search.) + */ + template + void for_each_parent(osmium::unsigned_object_id_type member_id, TFunc&& func) const { const auto parents = m_map.get(member_id); for (auto it = parents.first; it != parents.second; ++it) { - std::forward(func)(it->value); + std::forward(func)(it->value); } } /** - * Is this index empty? - * - * Complexity: Constant. - */ + * Find the given relation id in the index and call the given + * function with all related relation ids. + * + * @code + * osmium::unsigned_object_id_type id = 17; + * index.for_each(id, [](osmium::unsigned_object_id_type rid) { + * ... + * }); + * @endcode + * + * Complexity: Logarithmic in the number of elements in the index. + * (Lookup uses binary search.) + */ + template + void for_each(osmium::unsigned_object_id_type id, TFunc&& func) const { + const auto parents = m_map.get(id); + for (auto it = parents.first; it != parents.second; ++it) { + std::forward(func)(it->value); + } + } + + /** + * Is this index empty? + * + * Complexity: Constant. + */ bool empty() const noexcept { return m_map.empty(); } /** - * How many entries are in this index? - * - * Complexity: Constant. - */ - size_t size() const noexcept { + * How many entries are in this index? + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { return m_map.size(); } - }; // RelationsMapIndex + }; // class RelationsMapIndex + + class RelationsMapIndexes { + + friend class RelationsMapStash; + + RelationsMapIndex m_member_to_parent; + RelationsMapIndex m_parent_to_member; + + RelationsMapIndexes(RelationsMapIndex::map_type&& map1, RelationsMapIndex::map_type&& map2) : + m_member_to_parent(std::move(map1)), + m_parent_to_member(std::move(map2)) { + } + + public: + + const RelationsMapIndex& member_to_parent() const noexcept { + return m_member_to_parent; + } + + const RelationsMapIndex& parent_to_member() const noexcept { + return m_parent_to_member; + } + + /** + * Is this index empty? + * + * Complexity: Constant. + */ + bool empty() const noexcept { + return m_member_to_parent.empty(); + } + + /** + * How many entries are in this index? + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { + return m_member_to_parent.size(); + } + + }; // class RelationsMapIndexes /** - * The RelationsMapStash is used to build up the data needed to create - * an index of member relation ID to parent relation ID. See the - * RelationsMapIndex class for more. - */ + * The RelationsMapStash is used to build up the data needed to create + * an index of member relation ID to parent relation ID or the other + * way around. See the RelationsMapIndex class for more. + */ class RelationsMapStash { using map_type = detail::flat_map; + osmium::unsigned_object_id_type, uint32_t>; map_type m_map; @@ -230,16 +325,16 @@ namespace osmium { RelationsMapStash& operator=(RelationsMapStash&&) = default; /** - * Add mapping from member to parent relation in the stash. - */ + * Add mapping from member to parent relation in the stash. + */ void add(osmium::unsigned_object_id_type member_id, osmium::unsigned_object_id_type relation_id) { assert(m_valid && "You can't use the RelationsMap any more after calling build_index()"); m_map.set(member_id, relation_id); } /** - * Add mapping from all members to given parent relation in the stash. - */ + * Add mapping from all members to given parent relation in the stash. + */ void add_members(const osmium::Relation& relation) { assert(m_valid && "You can't use the RelationsMap any more after calling build_index()"); for (const auto& member : relation.members()) { @@ -250,30 +345,33 @@ namespace osmium { } /** - * Is this stash empty? - * - * Complexity: Constant. - */ + * Is this stash empty? + * + * Complexity: Constant. + */ bool empty() const noexcept { assert(m_valid && "You can't use the RelationsMap any more after calling build_index()"); return m_map.empty(); } /** - * How many entries are in this stash? - * - * Complexity: Constant. - */ - size_t size() const noexcept { + * How many entries are in this stash? + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { assert(m_valid && "You can't use the RelationsMap any more after calling build_index()"); return m_map.size(); } /** - * Build an index with the contents of this stash and return it. - * - * After you get the index you can not use the stash any more! - */ + * Build an index for member to parent lookups from the contents + * of this stash and return it. + * + * After you get the index you can not use the stash any more! + * + * @deprecated Use build_member_to_parent_index() instead. + */ RelationsMapIndex build_index() { assert(m_valid && "You can't use the RelationsMap any more after calling build_index()"); m_map.sort_unique(); @@ -283,6 +381,54 @@ namespace osmium { return RelationsMapIndex{std::move(m_map)}; } + /** + * Build an index for member to parent lookups from the contents + * of this stash and return it. + * + * After you get the index you can not use the stash any more! + */ + RelationsMapIndex build_member_to_parent_index() { + assert(m_valid && "You can't use the RelationsMap any more after calling build_member_to_parent_index()"); + m_map.sort_unique(); +#ifndef NDEBUG + m_valid = false; +#endif + return RelationsMapIndex{std::move(m_map)}; + } + + /** + * Build an index for parent to member lookups from the contents + * of this stash and return it. + * + * After you get the index you can not use the stash any more! + */ + RelationsMapIndex build_parent_to_member_index() { + assert(m_valid && "You can't use the RelationsMap any more after calling build_parent_to_member_index()"); + m_map.flip_in_place(); + m_map.sort_unique(); +#ifndef NDEBUG + m_valid = false; +#endif + return RelationsMapIndex{std::move(m_map)}; + } + + /** + * Build indexes for member-to-parent and parent-to-member lookups + * from the contents of this stash and return them. + * + * After you get the index you can not use the stash any more! + */ + RelationsMapIndexes build_indexes() { + assert(m_valid && "You can't use the RelationsMap any more after calling build_indexes()"); + auto reverse_map = m_map.flip_copy(); + reverse_map.sort_unique(); + m_map.sort_unique(); +#ifndef NDEBUG + m_valid = false; +#endif + return RelationsMapIndexes{std::move(m_map), std::move(reverse_map)}; + } + }; // class RelationsMapStash } // namespace index diff --git a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp index 84b2825be..094062c80 100644 --- a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp +++ b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp @@ -85,7 +85,7 @@ namespace osmium { namespace detail { OSMIUM_NORETURN inline void throw_bzip2_error(BZFILE* bzfile, const char* msg, int bzlib_error = 0) { - std::string error("bzip2 error: "); + std::string error{"bzip2 error: "}; error += msg; error += ": "; int errnum = bzlib_error; @@ -94,7 +94,7 @@ namespace osmium { } else { error += ::BZ2_bzerror(bzfile, &errnum); } - throw osmium::bzip2_error(error, errnum); + throw osmium::bzip2_error{error, errnum}; } } // namespace detail @@ -143,7 +143,7 @@ namespace osmium { osmium::io::detail::reliable_fsync(::fileno(m_file)); } if (fclose(m_file) != 0) { - throw std::system_error(errno, std::system_category(), "Close failed"); + throw std::system_error{errno, std::system_category(), "Close failed"}; } } if (error != BZ_OK) { @@ -187,7 +187,7 @@ namespace osmium { if (!m_stream_end) { buffer.resize(osmium::io::Decompressor::input_buffer_size); int error; - int nread = ::BZ2_bzRead(&error, m_bzfile, const_cast(buffer.data()), static_cast_with_assert(buffer.size())); + const int nread = ::BZ2_bzRead(&error, m_bzfile, const_cast(buffer.data()), static_cast_with_assert(buffer.size())); if (error != BZ_OK && error != BZ_STREAM_END) { detail::throw_bzip2_error(m_bzfile, "read failed", error); } @@ -227,7 +227,7 @@ namespace osmium { m_bzfile = nullptr; if (m_file) { if (fclose(m_file) != 0) { - throw std::system_error(errno, std::system_category(), "Close failed"); + throw std::system_error{errno, std::system_category(), "Close failed"}; } } if (error != BZ_OK) { @@ -252,10 +252,10 @@ namespace osmium { m_bzstream() { m_bzstream.next_in = const_cast(buffer); m_bzstream.avail_in = static_cast_with_assert(size); - int result = BZ2_bzDecompressInit(&m_bzstream, 0, 0); + const int result = BZ2_bzDecompressInit(&m_bzstream, 0, 0); if (result != BZ_OK) { - std::string message("bzip2 error: decompression init failed: "); - throw bzip2_error(message, result); + std::string message{"bzip2 error: decompression init failed: "}; + throw bzip2_error{message, result}; } } @@ -275,7 +275,7 @@ namespace osmium { output.resize(buffer_size); m_bzstream.next_out = const_cast(output.data()); m_bzstream.avail_out = buffer_size; - int result = BZ2_bzDecompress(&m_bzstream); + const int result = BZ2_bzDecompress(&m_bzstream); if (result != BZ_OK) { m_buffer = nullptr; @@ -283,8 +283,8 @@ namespace osmium { } if (result != BZ_OK && result != BZ_STREAM_END) { - std::string message("bzip2 error: decompress failed: "); - throw bzip2_error(message, result); + std::string message{"bzip2 error: decompress failed: "}; + throw bzip2_error{message, result}; } output.resize(static_cast(m_bzstream.next_out - output.data())); @@ -304,9 +304,9 @@ namespace osmium { // 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, 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); } + [](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}; } ); // dummy function to silence the unused variable warning from above diff --git a/third_party/libosmium/include/osmium/io/compression.hpp b/third_party/libosmium/include/osmium/io/compression.hpp index 68011e810..c3b8706a4 100644 --- a/third_party/libosmium/include/osmium/io/compression.hpp +++ b/third_party/libosmium/include/osmium/io/compression.hpp @@ -54,7 +54,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include namespace osmium { @@ -77,8 +76,7 @@ namespace osmium { m_fsync(sync) { } - virtual ~Compressor() noexcept { - } + virtual ~Compressor() noexcept = default; virtual void write(const std::string& data) = 0; @@ -88,8 +86,8 @@ namespace osmium { class Decompressor { - std::atomic m_file_size {0}; - std::atomic m_offset {0}; + std::atomic m_file_size{0}; + std::atomic m_offset{0}; public: @@ -103,26 +101,25 @@ namespace osmium { Decompressor(Decompressor&&) = delete; Decompressor& operator=(Decompressor&&) = delete; - virtual ~Decompressor() noexcept { - } + virtual ~Decompressor() noexcept = default; virtual std::string read() = 0; virtual void close() = 0; - size_t file_size() const noexcept { + std::size_t file_size() const noexcept { return m_file_size; } - void set_file_size(size_t size) noexcept { + void set_file_size(std::size_t size) noexcept { m_file_size = size; } - size_t offset() const noexcept { + std::size_t offset() const noexcept { return m_offset; } - void set_offset(size_t offset) noexcept { + void set_offset(std::size_t offset) noexcept { m_offset = offset; } @@ -141,7 +138,7 @@ namespace osmium { using create_compressor_type = std::function; using create_decompressor_type_fd = std::function; - using create_decompressor_type_buffer = std::function; + using create_decompressor_type_buffer = std::function; private: @@ -187,10 +184,10 @@ namespace osmium { create_decompressor_type_fd create_decompressor_fd, create_decompressor_type_buffer create_decompressor_buffer) { - compression_map_type::value_type cc(compression, + compression_map_type::value_type cc{compression, std::make_tuple(create_compressor, create_decompressor_fd, - create_decompressor_buffer)); + create_decompressor_buffer)}; return m_callbacks.insert(cc).second; } @@ -208,7 +205,7 @@ namespace osmium { return p; } - std::unique_ptr create_decompressor(osmium::io::file_compression compression, const char* buffer, size_t size) const { + std::unique_ptr create_decompressor(osmium::io::file_compression compression, const char* buffer, std::size_t size) const { const auto callbacks = find_callbacks(compression); return std::unique_ptr(std::get<2>(callbacks)(buffer, size)); } @@ -240,7 +237,7 @@ namespace osmium { void close() final { if (m_fd >= 0) { - int fd = m_fd; + const int fd = m_fd; m_fd = -1; if (do_fsync()) { osmium::io::detail::reliable_fsync(fd); @@ -255,8 +252,8 @@ namespace osmium { int m_fd; const char *m_buffer; - size_t m_buffer_size; - size_t m_offset = 0; + std::size_t m_buffer_size; + std::size_t m_offset = 0; public: @@ -267,7 +264,7 @@ namespace osmium { m_buffer_size(0) { } - NoDecompressor(const char* buffer, size_t size) : + NoDecompressor(const char* buffer, std::size_t size) : Decompressor(), m_fd(-1), m_buffer(buffer), @@ -287,15 +284,15 @@ namespace osmium { if (m_buffer) { if (m_buffer_size != 0) { - size_t size = m_buffer_size; + const std::size_t size = m_buffer_size; m_buffer_size = 0; buffer.append(m_buffer, size); } } else { buffer.resize(osmium::io::Decompressor::input_buffer_size); - auto nread = ::read(m_fd, const_cast(buffer.data()), osmium::io::Decompressor::input_buffer_size); + const auto nread = ::read(m_fd, const_cast(buffer.data()), osmium::io::Decompressor::input_buffer_size); if (nread < 0) { - throw std::system_error(errno, std::system_category(), "Read failed"); + throw std::system_error{errno, std::system_category(), "Read failed"}; } buffer.resize(std::string::size_type(nread)); } @@ -308,7 +305,7 @@ namespace osmium { void close() final { if (m_fd >= 0) { - int fd = m_fd; + const int fd = m_fd; m_fd = -1; osmium::io::detail::reliable_close(fd); } @@ -321,9 +318,9 @@ namespace osmium { // 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, 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); } + [](int fd, fsync sync) { return new osmium::io::NoCompressor{fd, sync}; }, + [](int fd) { return new osmium::io::NoDecompressor{fd}; }, + [](const char* buffer, std::size_t size) { return new osmium::io::NoDecompressor{buffer, size}; } ); // dummy function to silence the unused variable warning from above 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 7e2f07ecd..f7ce7c838 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 @@ -282,15 +282,16 @@ namespace osmium { void write_box(const osmium::Box& box) { write_fieldname("box l/b/r/t"); - if (!box) { + if (box.bottom_left().is_undefined() && + box.top_right().is_undefined()) { write_error("BOX NOT SET!\n"); return; } const auto& bl = box.bottom_left(); const auto& tr = box.top_right(); - bl.as_string(std::back_inserter(*m_out)); + bl.as_string_without_check(std::back_inserter(*m_out)); *m_out += ' '; - tr.as_string(std::back_inserter(*m_out)); + tr.as_string_without_check(std::back_inserter(*m_out)); if (!box.valid()) { write_error(" INVALID BOX!"); } @@ -520,8 +521,8 @@ namespace osmium { public: - DebugOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : - OutputFormat(output_queue), + DebugOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(pool, output_queue), m_options() { m_options.add_metadata = file.is_not_false("add_metadata"); m_options.use_color = file.is_true("color"); @@ -576,7 +577,7 @@ namespace osmium { } void write_buffer(osmium::memory::Buffer&& buffer) final { - m_output_queue.push(osmium::thread::Pool::instance().submit(DebugOutputBlock{std::move(buffer), m_options})); + m_output_queue.push(m_pool.submit(DebugOutputBlock{std::move(buffer), m_options})); } }; // class DebugOutputFormat @@ -584,8 +585,8 @@ namespace osmium { // 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); + [](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::DebugOutputFormat(pool, file, output_queue); }); // dummy function to silence the unused variable warning from above 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 beb5b685c..7ba6677b1 100644 --- a/third_party/libosmium/include/osmium/io/detail/input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/input_format.hpp @@ -48,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -55,35 +56,37 @@ namespace osmium { namespace detail { - struct reader_options { - osmium::osm_entity_bits::type read_which_entities = osm_entity_bits::all; - osmium::io::read_meta read_metadata = read_meta::yes; + struct parser_arguments { + osmium::thread::Pool& pool; + future_string_queue_type& input_queue; + future_buffer_queue_type& output_queue; + std::promise& header_promise; + osmium::osm_entity_bits::type read_which_entities; + osmium::io::read_meta read_metadata; }; class Parser { + osmium::thread::Pool& m_pool; future_buffer_queue_type& m_output_queue; std::promise& m_header_promise; queue_wrapper m_input_queue; - reader_options m_options; + osmium::osm_entity_bits::type m_read_which_entities; + osmium::io::read_meta m_read_metadata; bool m_header_is_done; protected: - std::string get_input() { - return m_input_queue.pop(); - } - - bool input_done() const { - return m_input_queue.has_reached_end_of_data(); + osmium::thread::Pool& get_pool() { + return m_pool; } osmium::osm_entity_bits::type read_types() const noexcept { - return m_options.read_which_entities; + return m_read_which_entities; } osmium::io::read_meta read_metadata() const noexcept { - return m_options.read_metadata; + return m_read_metadata; } bool header_is_done() const noexcept { @@ -117,14 +120,13 @@ namespace osmium { public: - Parser(future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) : - m_output_queue(output_queue), - m_header_promise(header_promise), - m_input_queue(input_queue), - m_options(options), + explicit Parser(parser_arguments& args) : + m_pool(args.pool), + m_output_queue(args.output_queue), + m_header_promise(args.header_promise), + m_input_queue(args.input_queue), + m_read_which_entities(args.read_which_entities), + m_read_metadata(args.read_metadata), m_header_is_done(false) { } @@ -138,6 +140,14 @@ namespace osmium { virtual void run() = 0; + std::string get_input() { + return m_input_queue.pop(); + } + + bool input_done() const { + return m_input_queue.has_reached_end_of_data(); + } + void parse() { try { run(); @@ -163,10 +173,7 @@ namespace osmium { public: - using create_parser_type = std::function(future_string_queue_type&, - future_buffer_queue_type&, - std::promise& header_promise, - osmium::io::detail::reader_options options)>; + using create_parser_type = std::function(parser_arguments&)>; private: @@ -185,22 +192,20 @@ namespace osmium { return factory; } - 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; + bool register_parser(osmium::io::file_format format, create_parser_type&& create_function) { + const auto result = m_callbacks.emplace(format, std::forward(create_function)); + return result.second; } - create_parser_type get_creator_function(const osmium::io::File& file) { - auto it = m_callbacks.find(file.format()); + create_parser_type get_creator_function(const osmium::io::File& file) const { + const auto it = m_callbacks.find(file.format()); if (it == m_callbacks.end()) { - throw unsupported_file_format_error( - std::string("Can not open file '") + + 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."); + "'. No support for reading this format in this program."}; } return it->second; } 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 index 36db4e418..e0b3fa82e 100644 --- a/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp @@ -121,7 +121,7 @@ namespace osmium { current_entry = 0; } - void add(const char* string, size_t size) { + void add(const char* string, std::size_t size) { if (m_table.empty()) { m_table.resize(entry_size * number_of_entries); } @@ -162,7 +162,7 @@ namespace osmium { return protozero::decode_zigzag64(protozero::decode_varint(data, end)); } - bool ensure_bytes_available(size_t need_bytes) { + bool ensure_bytes_available(std::size_t need_bytes) { if ((m_end - m_data) >= long(need_bytes)) { return true; } @@ -174,7 +174,7 @@ namespace osmium { m_input.erase(0, m_data - m_input.data()); while (m_input.size() < need_bytes) { - std::string data = get_input(); + const std::string data{get_input()}; if (input_done()) { return false; } @@ -589,11 +589,8 @@ namespace osmium { public: - O5mParser(future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) : - Parser(input_queue, output_queue, header_promise, options), + explicit O5mParser(parser_arguments& args) : + Parser(args), m_header(), m_buffer(buffer_size), m_input(), @@ -616,11 +613,8 @@ namespace osmium { // 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::io::detail::reader_options options) { - return std::unique_ptr(new O5mParser(input_queue, output_queue, header_promise, options)); + [](parser_arguments& args) { + return std::unique_ptr(new O5mParser{args}); }); // dummy function to silence the unused variable warning from above diff --git a/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp index 62598f706..f5fcd00ca 100644 --- a/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp @@ -33,19 +33,16 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include +#include #include #include #include #include #include -#include #include #include #include -#include #include namespace osmium { @@ -54,10 +51,56 @@ namespace osmium { namespace detail { + // Feed data coming in blocks line by line to the OPL parser + // function. This has been broken out of the OPLParser class + // where it belongs into a standalone template function to be + // better testable. + template + void line_by_line(T& worker) { + std::string rest; + + while (!worker.input_done()) { + std::string input{worker.get_input()}; + std::string::size_type ppos = 0; + + if (!rest.empty()) { + ppos = input.find_first_of("\n\r"); + if (ppos == std::string::npos) { + rest.append(input); + continue; + } + rest.append(input, 0, ppos); + if (!rest.empty()) { + worker.parse_line(rest.data()); + rest.clear(); + } + ++ppos; + } + + for (auto pos = input.find_first_of("\n\r", ppos); + pos != std::string::npos; + pos = input.find_first_of("\n\r", ppos)) { + const char* data = &input[ppos]; + input[pos] = '\0'; + if (data[0] != '\0') { + worker.parse_line(data); + } + ppos = pos + 1; + if (ppos >= input.size()) { + break; + } + } + rest.assign(input, ppos, std::string::npos); + } + + if (!rest.empty()) { + worker.parse_line(rest.data()); + } + } + class OPLParser : public Parser { osmium::memory::Buffer m_buffer{1024*1024}; - const char* m_data = nullptr; uint64_t m_line_count = 0; void maybe_flush() { @@ -70,63 +113,26 @@ namespace osmium { } } - void parse_line() { - if (opl_parse_line(m_line_count, m_data, m_buffer, read_types())) { - maybe_flush(); - } - ++m_line_count; - } - public: - OPLParser(future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) : - Parser(input_queue, output_queue, header_promise, options) { + explicit OPLParser(parser_arguments& args) : + Parser(args) { set_header_value(osmium::io::Header{}); } ~OPLParser() noexcept final = default; + void parse_line(const char* data) { + if (opl_parse_line(m_line_count, data, m_buffer, read_types())) { + maybe_flush(); + } + ++m_line_count; + } + void run() final { osmium::thread::set_thread_name("_osmium_opl_in"); - std::string rest; - while (!input_done()) { - std::string input = get_input(); - std::string::size_type ppos = 0; - - if (!rest.empty()) { - ppos = input.find('\n'); - if (ppos == std::string::npos) { - rest.append(input); - continue; - } - rest.append(input.substr(0, ppos)); - m_data = rest.data(); - parse_line(); - rest.clear(); - } - - std::string::size_type pos = input.find('\n', ppos); - while (pos != std::string::npos) { - m_data = &input[ppos]; - input[pos] = '\0'; - parse_line(); - ppos = pos + 1; - if (ppos >= input.size()) { - break; - } - pos = input.find('\n', ppos); - } - rest = input.substr(ppos); - } - - if (!rest.empty()) { - m_data = rest.data(); - parse_line(); - } + line_by_line(*this); if (m_buffer.committed() > 0) { send_to_output_queue(std::move(m_buffer)); @@ -139,11 +145,8 @@ namespace osmium { // the variable is only a side-effect, it will never be used const bool registered_opl_parser = ParserFactory::instance().register_parser( file_format::opl, - [](future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) { - return std::unique_ptr(new OPLParser(input_queue, output_queue, header_promise, options)); + [](parser_arguments& args) { + return std::unique_ptr(new OPLParser{args}); }); // dummy function to silence the unused variable warning from above 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 bfcbcb171..d1fb2e501 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 @@ -141,14 +141,15 @@ namespace osmium { } void write_location(const osmium::Location& location, const char x, const char y) { + const bool not_undefined = !location.is_undefined(); *m_out += ' '; *m_out += x; - if (location) { + if (not_undefined) { osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.x()); } *m_out += ' '; *m_out += y; - if (location) { + if (not_undefined) { osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.y()); } } @@ -283,8 +284,8 @@ namespace osmium { public: - OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : - OutputFormat(output_queue), + OPLOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(pool, output_queue), m_options() { m_options.add_metadata = file.is_not_false("add_metadata"); m_options.locations_on_ways = file.is_true("locations_on_ways"); @@ -297,7 +298,7 @@ namespace osmium { ~OPLOutputFormat() noexcept final = default; void write_buffer(osmium::memory::Buffer&& buffer) final { - m_output_queue.push(osmium::thread::Pool::instance().submit(OPLOutputBlock{std::move(buffer), m_options})); + m_output_queue.push(m_pool.submit(OPLOutputBlock{std::move(buffer), m_options})); } }; // class OPLOutputFormat @@ -305,8 +306,8 @@ namespace osmium { // 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); + [](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::OPLOutputFormat(pool, file, output_queue); }); // dummy function to silence the unused variable warning from above diff --git a/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp b/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp index 622a7caaa..f21145b67 100644 --- a/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp +++ b/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp @@ -81,7 +81,7 @@ namespace osmium { } explicit opl_error(const char* what, const char* d = nullptr) : - io_error(std::string("OPL error: ") + what), + io_error(std::string{"OPL error: "} + what), data(d), msg("OPL error: ") { msg.append(what); @@ -291,7 +291,7 @@ namespace osmium { ++*data; return; } - std::string msg = "expected '"; + std::string msg{"expected '"}; msg += c; msg += "'"; throw opl_error{msg, *data}; @@ -599,8 +599,7 @@ namespace osmium { const char* tags_begin = nullptr; - osmium::Location location1; - osmium::Location location2; + osmium::Box box; std::string user; while (**data) { opl_parse_space(data); @@ -630,22 +629,22 @@ namespace osmium { break; case 'x': if (opl_non_empty(*data)) { - location1.set_lon_partial(data); + box.bottom_left().set_lon_partial(data); } break; case 'y': if (opl_non_empty(*data)) { - location1.set_lat_partial(data); + box.bottom_left().set_lat_partial(data); } break; case 'X': if (opl_non_empty(*data)) { - location2.set_lon_partial(data); + box.top_right().set_lon_partial(data); } break; case 'Y': if (opl_non_empty(*data)) { - location2.set_lat_partial(data); + box.top_right().set_lat_partial(data); } break; case 'T': @@ -661,13 +660,7 @@ namespace osmium { } - if (location1.valid() && location2.valid()) { - osmium::Box box; - box.extend(location1); - box.extend(location2); - builder.set_bounds(box); - } - + builder.set_bounds(box); builder.set_user(user); if (tags_begin) { 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 ccb0efd76..21d5e86eb 100644 --- a/third_party/libosmium/include/osmium/io/detail/output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/output_format.hpp @@ -46,6 +46,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -107,6 +108,7 @@ namespace osmium { protected: + osmium::thread::Pool& m_pool; future_string_queue_type& m_output_queue; /** @@ -119,7 +121,8 @@ namespace osmium { public: - explicit OutputFormat(future_string_queue_type& output_queue) : + OutputFormat(osmium::thread::Pool& pool, future_string_queue_type& output_queue) : + m_pool(pool), m_output_queue(output_queue) { } @@ -152,7 +155,7 @@ namespace osmium { public: - using create_output_type = std::function; + using create_output_type = std::function; private: @@ -178,22 +181,52 @@ namespace osmium { return true; } - std::unique_ptr create_output(const osmium::io::File& file, future_string_queue_type& output_queue) { - auto it = m_callbacks.find(file.format()); + std::unique_ptr create_output(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) { + const auto it = m_callbacks.find(file.format()); if (it != m_callbacks.end()) { - return std::unique_ptr((it->second)(file, output_queue)); + return std::unique_ptr((it->second)(pool, file, output_queue)); } - throw unsupported_file_format_error( - std::string("Can not open file '") + + 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."); + "'. No support for writing this format in this program."}; } }; // class OutputFormatFactory + class BlackholeOutputFormat : public osmium::io::detail::OutputFormat { + + public: + + BlackholeOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& /*file*/, future_string_queue_type& output_queue) : + OutputFormat(pool, output_queue) { + } + + BlackholeOutputFormat(const BlackholeOutputFormat&) = delete; + BlackholeOutputFormat& operator=(const BlackholeOutputFormat&) = delete; + + ~BlackholeOutputFormat() noexcept final = default; + + void write_buffer(osmium::memory::Buffer&& /*buffer*/) final { + } + + }; // class BlackholeOutputFormat + + // 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_blackhole_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::blackhole, + [](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::BlackholeOutputFormat(pool, file, output_queue); + }); + + // dummy function to silence the unused variable warning from above + inline bool get_registered_blackhole_output() noexcept { + return registered_blackhole_output; + } + } // namespace detail } // namespace io 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 e47cff6ad..6b0530124 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp @@ -99,21 +99,21 @@ namespace osmium { void decode_stringtable(const data_view& data) { if (!m_stringtable.empty()) { - throw osmium::pbf_error("more than one stringtable in pbf file"); + throw osmium::pbf_error{"more than one stringtable in pbf file"}; } - protozero::pbf_message pbf_string_table(data); + protozero::pbf_message pbf_string_table{data}; while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) { const auto str_view = pbf_string_table.get_view(); if (str_view.size() > osmium::max_osm_string_length) { - throw osmium::pbf_error("overlong string in string table"); + throw osmium::pbf_error{"overlong string in string table"}; } m_stringtable.emplace_back(str_view.data(), osmium::string_size_type(str_view.size())); } } void decode_primitive_block_metadata() { - protozero::pbf_message pbf_primitive_block(m_data); + protozero::pbf_message pbf_primitive_block{m_data}; while (pbf_primitive_block.next()) { switch (pbf_primitive_block.tag()) { case OSMFormat::PrimitiveBlock::required_StringTable_stringtable: @@ -138,7 +138,7 @@ namespace osmium { } void decode_primitive_block_data() { - protozero::pbf_message pbf_primitive_block(m_data); + protozero::pbf_message pbf_primitive_block{m_data}; while (pbf_primitive_block.next(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup)) { protozero::pbf_message pbf_primitive_group = pbf_primitive_block.get_message(); while (pbf_primitive_group.next()) { @@ -187,16 +187,16 @@ namespace osmium { } osm_string_len_type decode_info(const data_view& data, osmium::OSMObject& object) { - osm_string_len_type user = std::make_pair("", 0); + osm_string_len_type user{"", 0}; - protozero::pbf_message pbf_info(data); + protozero::pbf_message pbf_info{data}; while (pbf_info.next()) { switch (pbf_info.tag()) { case OSMFormat::Info::optional_int32_version: { const auto version = pbf_info.get_int32(); if (version < 0) { - throw osmium::pbf_error("object version must not be negative"); + throw osmium::pbf_error{"object version must not be negative"}; } object.set_version(static_cast_with_assert(version)); } @@ -208,7 +208,7 @@ namespace osmium { { const auto changeset_id = pbf_info.get_int64(); if (changeset_id < 0) { - throw osmium::pbf_error("object changeset_id must not be negative"); + throw osmium::pbf_error{"object changeset_id must not be negative"}; } object.set_changeset(static_cast_with_assert(changeset_id)); } @@ -240,7 +240,7 @@ namespace osmium { while (kit != keys.end()) { if (vit == vals.end()) { // this is against the spec, must have same number of elements - throw osmium::pbf_error("PBF format error"); + throw osmium::pbf_error{"PBF format error"}; } const auto& k = m_stringtable.at(*kit++); const auto& v = m_stringtable.at(*vit++); @@ -262,9 +262,9 @@ namespace osmium { int64_t lon = std::numeric_limits::max(); int64_t lat = std::numeric_limits::max(); - osm_string_len_type user = { "", 0 }; + osm_string_len_type user{"", 0}; - protozero::pbf_message pbf_node(data); + protozero::pbf_message pbf_node{data}; while (pbf_node.next()) { switch (pbf_node.tag()) { case OSMFormat::Node::required_sint64_id: @@ -297,12 +297,12 @@ namespace osmium { if (node.visible()) { if (lon == std::numeric_limits::max() || lat == std::numeric_limits::max()) { - throw osmium::pbf_error("illegal coordinate format"); + throw osmium::pbf_error{"illegal coordinate format"}; } - node.set_location(osmium::Location( + node.set_location(osmium::Location{ convert_pbf_coordinate(lon), convert_pbf_coordinate(lat) - )); + }); } builder.set_user(user.first, user.second); @@ -319,9 +319,9 @@ namespace osmium { protozero::iterator_range lats; protozero::iterator_range lons; - osm_string_len_type user = { "", 0 }; + osm_string_len_type user{"", 0}; - protozero::pbf_message pbf_way(data); + protozero::pbf_message pbf_way{data}; while (pbf_way.next()) { switch (pbf_way.tag()) { case OSMFormat::Way::required_int64_id: @@ -391,9 +391,9 @@ namespace osmium { protozero::iterator_range refs; protozero::iterator_range types; - osm_string_len_type user = { "", 0 }; + osm_string_len_type user{"", 0}; - protozero::pbf_message pbf_relation(data); + protozero::pbf_message pbf_relation{data}; while (pbf_relation.next()) { switch (pbf_relation.tag()) { case OSMFormat::Relation::required_int64_id: @@ -435,7 +435,7 @@ namespace osmium { const auto& r = m_stringtable.at(roles.front()); const int type = types.front(); if (type < 0 || type > 2) { - throw osmium::pbf_error("unknown relation member type"); + throw osmium::pbf_error{"unknown relation member type"}; } rml_builder.add_member( osmium::item_type(type + 1), @@ -457,7 +457,7 @@ namespace osmium { while (it != last && *it != 0) { const auto& k = m_stringtable.at(*it++); if (it == last) { - throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs + throw osmium::pbf_error{"PBF format error"}; // this is against the spec, keys/vals must come in pairs } const auto& v = m_stringtable.at(*it++); tl_builder.add_tag(k.first, k.second, v.first, v.second); @@ -475,7 +475,7 @@ namespace osmium { protozero::iterator_range tags; - protozero::pbf_message pbf_dense_nodes(data); + protozero::pbf_message pbf_dense_nodes{data}; while (pbf_dense_nodes.next()) { switch (pbf_dense_nodes.tag()) { case OSMFormat::DenseNodes::packed_sint64_id: @@ -505,7 +505,7 @@ namespace osmium { if (lons.empty() || lats.empty()) { // this is against the spec, must have same number of elements - throw osmium::pbf_error("PBF format error"); + throw osmium::pbf_error{"PBF format error"}; } osmium::builder::NodeBuilder builder{m_buffer}; @@ -547,7 +547,7 @@ namespace osmium { protozero::iterator_range user_sids; protozero::iterator_range visibles; - protozero::pbf_message pbf_dense_nodes(data); + protozero::pbf_message pbf_dense_nodes{data}; while (pbf_dense_nodes.next()) { switch (pbf_dense_nodes.tag()) { case OSMFormat::DenseNodes::packed_sint64_id: @@ -556,7 +556,7 @@ namespace osmium { case OSMFormat::DenseNodes::optional_DenseInfo_denseinfo: { has_info = true; - protozero::pbf_message pbf_dense_info = pbf_dense_nodes.get_message(); + protozero::pbf_message pbf_dense_info{pbf_dense_nodes.get_message()}; while (pbf_dense_info.next()) { switch (pbf_dense_info.tag()) { case OSMFormat::DenseInfo::packed_int32_version: @@ -612,7 +612,7 @@ namespace osmium { if (lons.empty() || lats.empty()) { // this is against the spec, must have same number of elements - throw osmium::pbf_error("PBF format error"); + throw osmium::pbf_error{"PBF format error"}; } bool visible = true; @@ -630,20 +630,20 @@ namespace osmium { uids.empty() || user_sids.empty()) { // this is against the spec, must have same number of elements - throw osmium::pbf_error("PBF format error"); + throw osmium::pbf_error{"PBF format error"}; } const auto version = versions.front(); versions.drop_front(); if (version < 0) { - throw osmium::pbf_error("object version must not be negative"); + throw osmium::pbf_error{"object version must not be negative"}; } node.set_version(static_cast(version)); const auto changeset_id = dense_changeset.update(changesets.front()); changesets.drop_front(); if (changeset_id < 0) { - throw osmium::pbf_error("object changeset_id must not be negative"); + throw osmium::pbf_error{"object changeset_id must not be negative"}; } node.set_changeset(static_cast(changeset_id)); @@ -655,7 +655,7 @@ namespace osmium { if (has_visibles) { if (visibles.empty()) { // this is against the spec, must have same number of elements - throw osmium::pbf_error("PBF format error"); + throw osmium::pbf_error{"PBF format error"}; } visible = (visibles.front() != 0); visibles.drop_front(); @@ -674,10 +674,10 @@ namespace osmium { const auto lat = dense_latitude.update(lats.front()); lats.drop_front(); if (visible) { - builder.object().set_location(osmium::Location( + builder.object().set_location(osmium::Location{ convert_pbf_coordinate(lon), convert_pbf_coordinate(lat) - )); + }); } if (tag_it != tags.end()) { @@ -708,7 +708,7 @@ namespace osmium { decode_primitive_block_metadata(); decode_primitive_block_data(); } catch (const std::out_of_range&) { - throw osmium::pbf_error("string id out of range"); + throw osmium::pbf_error{"string id out of range"}; } return std::move(m_buffer); @@ -720,30 +720,30 @@ namespace osmium { int32_t raw_size = 0; protozero::data_view zlib_data; - protozero::pbf_message pbf_blob(blob_data); + protozero::pbf_message pbf_blob{blob_data}; while (pbf_blob.next()) { switch (pbf_blob.tag()) { case FileFormat::Blob::optional_bytes_raw: { - auto data_len = pbf_blob.get_view(); + const auto data_len = pbf_blob.get_view(); if (data_len.size() > max_uncompressed_blob_size) { - throw osmium::pbf_error("illegal blob size"); + throw osmium::pbf_error{"illegal blob size"}; } return data_len; } case FileFormat::Blob::optional_int32_raw_size: raw_size = pbf_blob.get_int32(); if (raw_size <= 0 || uint32_t(raw_size) > max_uncompressed_blob_size) { - throw osmium::pbf_error("illegal blob size"); + throw osmium::pbf_error{"illegal blob size"}; } break; case FileFormat::Blob::optional_bytes_zlib_data: zlib_data = pbf_blob.get_view(); break; case FileFormat::Blob::optional_bytes_lzma_data: - throw osmium::pbf_error("lzma blobs not implemented"); + throw osmium::pbf_error{"lzma blobs not implemented"}; default: - throw osmium::pbf_error("unknown compression"); + throw osmium::pbf_error{"unknown compression"}; } } @@ -756,7 +756,7 @@ namespace osmium { ); } - throw osmium::pbf_error("blob contains no data"); + throw osmium::pbf_error{"blob contains no data"}; } inline osmium::Box decode_header_bbox(const data_view& data) { @@ -765,7 +765,7 @@ namespace osmium { int64_t top = std::numeric_limits::max(); int64_t bottom = std::numeric_limits::max(); - protozero::pbf_message pbf_header_bbox(data); + protozero::pbf_message pbf_header_bbox{data}; while (pbf_header_bbox.next()) { switch (pbf_header_bbox.tag()) { case OSMFormat::HeaderBBox::required_sint64_left: @@ -789,7 +789,7 @@ namespace osmium { right == std::numeric_limits::max() || top == std::numeric_limits::max() || bottom == std::numeric_limits::max()) { - throw osmium::pbf_error("invalid bbox"); + throw osmium::pbf_error{"invalid bbox"}; } osmium::Box box; @@ -803,7 +803,7 @@ namespace osmium { osmium::io::Header header; int i = 0; - protozero::pbf_message pbf_header_block(data); + protozero::pbf_message pbf_header_block{data}; while (pbf_header_block.next()) { switch (pbf_header_block.tag()) { case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox: @@ -819,9 +819,9 @@ namespace osmium { } else if (!std::strncmp("HistoricalInformation", feature.data(), feature.size())) { header.set_has_multiple_object_versions(true); } else { - std::string msg("required feature not supported: "); + std::string msg{"required feature not supported: "}; msg.append(feature.data(), feature.size()); - throw osmium::pbf_error(msg); + throw osmium::pbf_error{msg}; } } break; @@ -833,7 +833,7 @@ namespace osmium { break; case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp: { - const auto timestamp = osmium::Timestamp(pbf_header_block.get_int64()).to_iso(); + const auto timestamp = osmium::Timestamp{pbf_header_block.get_int64()}.to_iso(); header.set("osmosis_replication_timestamp", timestamp); header.set("timestamp", timestamp); } @@ -889,7 +889,7 @@ namespace osmium { osmium::memory::Buffer operator()() { std::string output; - PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types, m_read_metadata); + PBFPrimitiveBlockDecoder decoder{decode_blob(*m_input_buffer, output), m_read_types, m_read_metadata}; return decoder(); } 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 be5f86097..1ab459573 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 @@ -33,15 +33,14 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include -#include #include #include #include +#include #include #include @@ -50,7 +49,6 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: export #include #include -#include #include #include #include @@ -77,14 +75,14 @@ namespace osmium { */ std::string read_from_input_queue(size_t size) { while (m_input_buffer.size() < size) { - const std::string new_data = get_input(); + const std::string new_data{get_input()}; if (input_done()) { - throw osmium::pbf_error("truncated data (EOF encountered)"); + throw osmium::pbf_error{"truncated data (EOF encountered)"}; } m_input_buffer += new_data; } - std::string output { m_input_buffer.substr(size) }; + std::string output{m_input_buffer.substr(size)}; m_input_buffer.resize(size); using std::swap; @@ -101,7 +99,7 @@ namespace osmium { uint32_t size_in_network_byte_order; try { - const 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 (const osmium::pbf_error&) { return 0; // EOF @@ -109,7 +107,7 @@ namespace osmium { const uint32_t size = ntohl(size_in_network_byte_order); if (size > static_cast(max_blob_header_size)) { - throw osmium::pbf_error("invalid BlobHeader size (> max_blob_header_size)"); + throw osmium::pbf_error{"invalid BlobHeader size (> max_blob_header_size)"}; } return size; @@ -137,11 +135,11 @@ namespace osmium { } if (blob_header_datasize == 0) { - throw osmium::pbf_error("PBF format error: BlobHeader.datasize missing or zero."); + throw osmium::pbf_error{"PBF format error: BlobHeader.datasize missing or zero."}; } if (std::strncmp(expected_type, blob_header_type.data(), blob_header_type.size())) { - throw osmium::pbf_error("blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)"); + throw osmium::pbf_error{"blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)"}; } return blob_header_datasize; @@ -155,35 +153,34 @@ namespace osmium { return 0; } - const 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); } 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))); + throw osmium::pbf_error{std::string{"invalid blob size: "} + + std::to_string(size)}; } return read_from_input_queue(size); } // 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)); + osmium::io::Header 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); + std::string input_buffer{read_from_input_queue_with_check(size)}; PBFDataBlobDecoder data_blob_parser{std::move(input_buffer), read_types(), read_metadata()}; if (osmium::config::use_pool_threads_for_pbf_parsing()) { - send_to_output_queue(osmium::thread::Pool::instance().submit(std::move(data_blob_parser))); + send_to_output_queue(get_pool().submit(std::move(data_blob_parser))); } else { send_to_output_queue(data_blob_parser()); } @@ -192,11 +189,8 @@ namespace osmium { public: - PBFParser(future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) : - Parser(input_queue, output_queue, header_promise, options), + explicit PBFParser(parser_arguments& args) : + Parser(args), m_input_buffer() { } @@ -218,11 +212,8 @@ namespace osmium { // 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::io::detail::reader_options options) { - return std::unique_ptr(new PBFParser(input_queue, output_queue, header_promise, options)); + [](parser_arguments& args) { + return std::unique_ptr(new PBFParser{args}); }); // dummy function to silence the unused variable warning from above 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 d431a6796..55cd0eaf6 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 @@ -33,15 +33,14 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include -#include #include #include #include +#include #include #include @@ -171,7 +170,7 @@ namespace osmium { assert(m_msg.size() <= max_uncompressed_blob_size); std::string blob_data; - protozero::pbf_builder pbf_blob(blob_data); + protozero::pbf_builder pbf_blob{blob_data}; if (m_use_compression) { pbf_blob.add_int32(FileFormat::Blob::optional_int32_raw_size, int32_t(m_msg.size())); @@ -181,12 +180,12 @@ namespace osmium { } std::string blob_header_data; - protozero::pbf_builder pbf_blob_header(blob_header_data); + protozero::pbf_builder pbf_blob_header{blob_header_data}; 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())); + const 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; @@ -270,7 +269,7 @@ namespace osmium { m_delta_lon.clear(); } - size_t size() const { + std::size_t size() const { return m_ids.size() * 3 * sizeof(int64_t); } @@ -300,12 +299,12 @@ namespace osmium { std::string serialize() const { std::string data; - protozero::pbf_builder pbf_dense_nodes(data); + protozero::pbf_builder pbf_dense_nodes{data}; pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_id, m_ids.cbegin(), m_ids.cend()); if (m_options.add_metadata) { - protozero::pbf_builder pbf_dense_info(pbf_dense_nodes, OSMFormat::DenseNodes::optional_DenseInfo_denseinfo); + 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()); pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_changeset, m_changesets.cbegin(), m_changesets.cend()); @@ -390,7 +389,7 @@ namespace osmium { return m_type; } - size_t size() const { + std::size_t size() const { return m_pbf_primitive_group_data.size() + m_stringtable.size() + m_dense_nodes.size(); } @@ -400,7 +399,7 @@ namespace osmium { * enough space for the string table (which typically * needs about 0.1 to 0.3% of the block size). */ - constexpr static size_t max_used_blob_size = max_uncompressed_blob_size * 95 / 100; + constexpr static std::size_t max_used_blob_size = max_uncompressed_blob_size * 95 / 100; bool can_add(OSMFormat::PrimitiveGroup type) const { if (type != m_type) { @@ -426,16 +425,16 @@ namespace osmium { } std::string primitive_block_data; - protozero::pbf_builder primitive_block(primitive_block_data); + protozero::pbf_builder primitive_block{primitive_block_data}; { - protozero::pbf_builder pbf_string_table(primitive_block, OSMFormat::PrimitiveBlock::required_StringTable_stringtable); + protozero::pbf_builder pbf_string_table{primitive_block, OSMFormat::PrimitiveBlock::required_StringTable_stringtable}; m_primitive_block.write_stringtable(pbf_string_table); } primitive_block.add_message(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup, m_primitive_block.group_data()); - m_output_queue.push(osmium::thread::Pool::instance().submit( + m_output_queue.push(m_pool.submit( SerializeBlob{std::move(primitive_block_data), pbf_blob_type::data, m_options.use_compression} @@ -459,7 +458,7 @@ namespace osmium { } if (m_options.add_metadata) { - protozero::pbf_builder pbf_info(pbf_object, T::enum_type::optional_Info_info); + protozero::pbf_builder pbf_info{pbf_object, T::enum_type::optional_Info_info}; 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())); @@ -481,8 +480,8 @@ namespace osmium { public: - PBFOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : - OutputFormat(output_queue), + PBFOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(pool, output_queue), m_options(), m_primitive_block(m_options) { m_options.use_dense_nodes = file.is_not_false("pbf_dense_nodes"); @@ -500,10 +499,10 @@ namespace osmium { void write_header(const osmium::io::Header& header) final { std::string data; - protozero::pbf_builder pbf_header_block(data); + protozero::pbf_builder pbf_header_block{data}; if (!header.boxes().empty()) { - protozero::pbf_builder pbf_header_bbox(pbf_header_block, OSMFormat::HeaderBlock::optional_HeaderBBox_bbox); + 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, int64_t(box.bottom_left().lon() * lonlat_resolution)); @@ -528,23 +527,23 @@ namespace osmium { pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator")); - const std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp"); + const std::string osmosis_replication_timestamp{header.get("osmosis_replication_timestamp")}; if (!osmosis_replication_timestamp.empty()) { - osmium::Timestamp ts(osmosis_replication_timestamp.c_str()); + osmium::Timestamp ts{osmosis_replication_timestamp.c_str()}; pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts)); } - const std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number"); + const std::string osmosis_replication_sequence_number{header.get("osmosis_replication_sequence_number")}; if (!osmosis_replication_sequence_number.empty()) { pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number, std::atoll(osmosis_replication_sequence_number.c_str())); } - const std::string osmosis_replication_base_url = header.get("osmosis_replication_base_url"); + const std::string osmosis_replication_base_url{header.get("osmosis_replication_base_url")}; if (!osmosis_replication_base_url.empty()) { pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url, osmosis_replication_base_url); } - m_output_queue.push(osmium::thread::Pool::instance().submit( + m_output_queue.push(m_pool.submit( SerializeBlob{std::move(data), pbf_blob_type::header, m_options.use_compression} @@ -567,7 +566,7 @@ namespace osmium { } switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Node_nodes); - protozero::pbf_builder pbf_node{ m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Node_nodes }; + protozero::pbf_builder pbf_node{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Node_nodes}; pbf_node.add_sint64(OSMFormat::Node::required_sint64_id, node.id()); add_meta(node, pbf_node); @@ -578,7 +577,7 @@ namespace osmium { void way(const osmium::Way& way) { switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Way_ways); - protozero::pbf_builder pbf_way{ m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Way_ways }; + protozero::pbf_builder pbf_way{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Way_ways}; pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id()); add_meta(way, pbf_way); @@ -611,7 +610,7 @@ namespace osmium { void relation(const osmium::Relation& relation) { switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Relation_relations); - protozero::pbf_builder pbf_relation { m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Relation_relations }; + protozero::pbf_builder pbf_relation{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Relation_relations}; pbf_relation.add_int64(OSMFormat::Relation::required_int64_id, relation.id()); add_meta(relation, pbf_relation); @@ -644,8 +643,8 @@ namespace osmium { // 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); + [](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) { + return new osmium::io::detail::PBFOutputFormat{pool, file, output_queue}; }); // dummy function to silence the unused variable warning from above 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 8bb2e56c6..58fff5694 100644 --- a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp +++ b/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp @@ -169,4 +169,4 @@ namespace osmium { } // namespace osmium -#endif // OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP +#endif // OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP 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 d6aa8bc62..388d9713a 100644 --- a/third_party/libosmium/include/osmium/io/detail/read_write.hpp +++ b/third_party/libosmium/include/osmium/io/detail/read_write.hpp @@ -85,7 +85,7 @@ namespace osmium { #endif const 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 + "'"); + throw std::system_error{errno, std::system_category(), std::string("Open failed for '") + filename + "'"}; } return fd; } @@ -109,7 +109,7 @@ namespace osmium { #endif const int fd = ::open(filename.c_str(), flags); if (fd < 0) { - throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); + throw std::system_error{errno, std::system_category(), std::string("Open failed for '") + filename + "'"}; } return fd; } @@ -134,7 +134,7 @@ namespace osmium { } const auto length = ::write(fd, output_buffer + offset, static_cast(write_count)); if (length < 0) { - throw std::system_error(errno, std::system_category(), "Write failed"); + throw std::system_error{errno, std::system_category(), "Write failed"}; } offset += static_cast(length); } while (offset < size); @@ -160,13 +160,13 @@ namespace osmium { #else if (::fsync(fd) != 0) { #endif - throw std::system_error(errno, std::system_category(), "Fsync failed"); + 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"); + throw std::system_error{errno, std::system_category(), "Close failed"}; } } 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 ba725e5e9..16172c54e 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_table.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_table.hpp @@ -136,8 +136,12 @@ namespace osmium { const_iterator& operator++() { assert(m_it != m_last); const auto last_pos = m_it->c_str() + m_it->size(); - while (m_pos != last_pos && *m_pos) ++m_pos; - if (m_pos != last_pos) ++m_pos; + while (m_pos != last_pos && *m_pos) { + ++m_pos; + } + if (m_pos != last_pos) { + ++m_pos; + } if (m_pos == last_pos) { ++m_it; if (m_it != m_last) { @@ -150,7 +154,7 @@ namespace osmium { } const_iterator operator++(int) { - const_iterator tmp(*this); + const_iterator tmp{*this}; operator++(); return tmp; } @@ -175,11 +179,11 @@ namespace osmium { if (m_chunks.front().empty()) { return end(); } - return const_iterator(m_chunks.begin(), m_chunks.end()); + return {m_chunks.begin(), m_chunks.end()}; } const_iterator end() const { - return const_iterator(m_chunks.end(), m_chunks.end()); + return {m_chunks.end(), m_chunks.end()}; } // These functions get you some idea how much memory was @@ -273,7 +277,7 @@ namespace osmium { m_index[cs] = ++m_size; if (m_size > max_entries) { - throw osmium::pbf_error("string table has too many 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 index df6c3e29a..b450b7b86 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_util.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_util.hpp @@ -134,10 +134,10 @@ namespace osmium { // Write out the value with four or more hex digits. inline void append_min_4_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { auto - v = value & 0xf0000000; if (v) out += hex_digits[v >> 28]; - v = value & 0x0f000000; if (v) out += hex_digits[v >> 24]; - v = value & 0x00f00000; if (v) out += hex_digits[v >> 20]; - v = value & 0x000f0000; if (v) out += hex_digits[v >> 16]; + v = value & 0xf0000000; if (v) { out += hex_digits[v >> 28]; } + v = value & 0x0f000000; if (v) { out += hex_digits[v >> 24]; } + v = value & 0x00f00000; if (v) { out += hex_digits[v >> 20]; } + v = value & 0x000f0000; if (v) { out += hex_digits[v >> 16]; } out += hex_digits[(value >> 12) & 0xf]; out += hex_digits[(value >> 8) & 0xf]; @@ -181,7 +181,7 @@ namespace osmium { inline void append_xml_encoded_string(std::string& out, const char* data) { for (; *data != '\0'; ++data) { - switch(*data) { + switch (*data) { case '&': out += "&"; break; case '\"': out += """; break; case '\'': out += "'"; break; 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 1e22155f4..575bc6b5c 100644 --- a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp +++ b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp @@ -33,11 +33,11 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include +#include #include #include @@ -83,7 +83,7 @@ namespace osmium { try { while (true) { - std::string data = m_queue.pop(); + const std::string data{m_queue.pop()}; if (at_end_of_data(data)) { break; } 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 96a348b7f..82c74ebfc 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 @@ -444,12 +444,14 @@ namespace osmium { item_type type = item_type::undefined; object_id_type ref = 0; + bool ref_is_set = false; const char* role = ""; - check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) { + check_attributes(attrs, [&](const XML_Char* name, const XML_Char* value) { if (!std::strcmp(name, "type")) { type = char_to_item_type(value[0]); } else if (!std::strcmp(name, "ref")) { ref = osmium::string_to_object_id(value); + ref_is_set = true; } else if (!std::strcmp(name, "role")) { role = static_cast(value); } @@ -457,7 +459,7 @@ namespace osmium { if (type != item_type::node && type != item_type::way && type != item_type::relation) { throw osmium::xml_error{"Unknown type on relation member"}; } - if (ref == 0) { + if (!ref_is_set) { throw osmium::xml_error{"Missing ref on relation member"}; } m_rml_builder->add_member(type, ref, role); @@ -625,11 +627,8 @@ namespace osmium { public: - XMLParser(future_string_queue_type& input_queue, - future_buffer_queue_type& output_queue, - std::promise& header_promise, - osmium::io::detail::reader_options options) : - Parser(input_queue, output_queue, header_promise, options), + explicit XMLParser(parser_arguments& args) : + Parser(args), m_context(context::root), m_last_context(context::root), m_in_delete_section(false), @@ -673,11 +672,8 @@ namespace osmium { // 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::io::detail::reader_options options) { - return std::unique_ptr(new XMLParser{input_queue, output_queue, header_promise, options}); + [](parser_arguments& args) { + return std::unique_ptr(new XMLParser{args}); }); // dummy function to silence the unused variable warning from above 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 44d68313c..a88987405 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 @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include @@ -397,7 +396,8 @@ namespace osmium { write_attribute("uid", changeset.uid()); } - if (changeset.bounds()) { + if (!changeset.bounds().bottom_left().is_undefined() || + !changeset.bounds().top_right().is_undefined()) { detail::append_lat_lon_attributes(*m_out, "min_lat", "min_lon", changeset.bounds().bottom_left()); detail::append_lat_lon_attributes(*m_out, "max_lat", "max_lon", changeset.bounds().top_right()); } @@ -431,8 +431,8 @@ namespace osmium { public: - XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : - OutputFormat(output_queue), + XMLOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) : + OutputFormat(pool, 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"); @@ -446,14 +446,14 @@ namespace osmium { ~XMLOutputFormat() noexcept final = default; void write_header(const osmium::io::Header& header) final { - std::string out = "\n"; + std::string out{"\n"}; if (m_options.use_change_ops) { out += " suffixes = detail::split(name, '.'); - if (suffixes.empty()) return; + if (suffixes.empty()) { + return; + } // if the last suffix is one of a known set of compressions, // set that compression @@ -204,7 +206,9 @@ namespace osmium { suffixes.pop_back(); } - if (suffixes.empty()) return; + if (suffixes.empty()) { + return; + } // if the last suffix is one of a known set of formats, // set that format @@ -231,19 +235,30 @@ namespace osmium { } else if (suffixes.back() == "debug") { m_file_format = file_format::debug; suffixes.pop_back(); + } else if (suffixes.back() == "blackhole") { + m_file_format = file_format::blackhole; + suffixes.pop_back(); } - if (suffixes.empty()) return; + if (suffixes.empty()) { + return; + } if (suffixes.back() == "osm") { - if (m_file_format == file_format::unknown) m_file_format = file_format::xml; + if (m_file_format == file_format::unknown) { + m_file_format = file_format::xml; + } suffixes.pop_back(); } else if (suffixes.back() == "osh") { - if (m_file_format == file_format::unknown) m_file_format = file_format::xml; + if (m_file_format == file_format::unknown) { + m_file_format = file_format::xml; + } m_has_multiple_object_versions = true; suffixes.pop_back(); } else if (suffixes.back() == "osc") { - if (m_file_format == file_format::unknown) m_file_format = file_format::xml; + if (m_file_format == file_format::unknown) { + m_file_format = file_format::xml; + } m_has_multiple_object_versions = true; set("xml_change_format", true); suffixes.pop_back(); @@ -258,7 +273,7 @@ namespace osmium { */ const File& check() const { if (m_file_format == file_format::unknown) { - std::string msg = "Could not detect file format"; + std::string msg{"Could not detect file format"}; if (!m_format_string.empty()) { msg += " from format string '"; msg += m_format_string; @@ -272,7 +287,7 @@ namespace osmium { msg += "'"; } msg += "."; - throw io_error(msg); + throw io_error{msg}; } return *this; } diff --git a/third_party/libosmium/include/osmium/io/file_compression.hpp b/third_party/libosmium/include/osmium/io/file_compression.hpp index 9b3679516..407558dc6 100644 --- a/third_party/libosmium/include/osmium/io/file_compression.hpp +++ b/third_party/libosmium/include/osmium/io/file_compression.hpp @@ -45,20 +45,17 @@ namespace osmium { bzip2 = 2 }; -// avoid g++ false positive -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-type" inline const char* as_string(file_compression compression) { switch (compression) { - case file_compression::none: - return "none"; case file_compression::gzip: return "gzip"; case file_compression::bzip2: return "bzip2"; + default: // file_compression::none: + break; } + return "none"; } -#pragma GCC diagnostic pop template inline std::basic_ostream& operator<<(std::basic_ostream& out, const file_compression compression) { diff --git a/third_party/libosmium/include/osmium/io/file_format.hpp b/third_party/libosmium/include/osmium/io/file_format.hpp index 01b560b45..cde12996c 100644 --- a/third_party/libosmium/include/osmium/io/file_format.hpp +++ b/third_party/libosmium/include/osmium/io/file_format.hpp @@ -40,13 +40,14 @@ namespace osmium { namespace io { enum class file_format { - unknown = 0, - xml = 1, - pbf = 2, - opl = 3, - json = 4, - o5m = 5, - debug = 6 + unknown = 0, + xml = 1, + pbf = 2, + opl = 3, + json = 4, + o5m = 5, + debug = 6, + blackhole = 7 }; enum class read_meta { @@ -54,13 +55,8 @@ namespace osmium { yes = 1 }; -// avoid g++ false positive -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-type" inline const char* as_string(file_format format) { switch (format) { - case file_format::unknown: - return "unknown"; case file_format::xml: return "XML"; case file_format::pbf: @@ -73,9 +69,13 @@ namespace osmium { return "O5M"; case file_format::debug: return "DEBUG"; + case file_format::blackhole: + return "BLACKHOLE"; + default: // file_format::unknown + break; } + return "unknown"; } -#pragma GCC diagnostic pop template inline std::basic_ostream& operator<<(std::basic_ostream& out, const file_format format) { diff --git a/third_party/libosmium/include/osmium/io/gzip_compression.hpp b/third_party/libosmium/include/osmium/io/gzip_compression.hpp index 27e18eefd..49300d365 100644 --- a/third_party/libosmium/include/osmium/io/gzip_compression.hpp +++ b/third_party/libosmium/include/osmium/io/gzip_compression.hpp @@ -84,7 +84,7 @@ namespace osmium { namespace detail { OSMIUM_NORETURN inline void throw_gzip_error(gzFile gzfile, const char* msg, int zlib_error = 0) { - std::string error("gzip error: "); + std::string error{"gzip error: "}; error += msg; error += ": "; int errnum = zlib_error; @@ -93,7 +93,7 @@ namespace osmium { } else { error += ::gzerror(gzfile, &errnum); } - throw osmium::gzip_error(error, errnum); + throw osmium::gzip_error{error, errnum}; } } // namespace detail @@ -124,7 +124,7 @@ namespace osmium { void write(const std::string& data) final { if (!data.empty()) { - int nwrite = ::gzwrite(m_gzfile, data.data(), static_cast_with_assert(data.size())); + const int nwrite = ::gzwrite(m_gzfile, data.data(), static_cast_with_assert(data.size())); if (nwrite == 0) { detail::throw_gzip_error(m_gzfile, "write failed"); } @@ -133,7 +133,7 @@ namespace osmium { void close() final { if (m_gzfile) { - int result = ::gzclose(m_gzfile); + const int result = ::gzclose(m_gzfile); m_gzfile = nullptr; if (result != Z_OK) { detail::throw_gzip_error(m_gzfile, "write close failed", result); @@ -184,7 +184,7 @@ namespace osmium { void close() final { if (m_gzfile) { - int result = ::gzclose(m_gzfile); + const int result = ::gzclose(m_gzfile); m_gzfile = nullptr; if (result != Z_OK) { detail::throw_gzip_error(m_gzfile, "read close failed", result); @@ -208,13 +208,13 @@ namespace osmium { m_zstream() { m_zstream.next_in = reinterpret_cast(const_cast(buffer)); m_zstream.avail_in = static_cast_with_assert(size); - int result = inflateInit2(&m_zstream, MAX_WBITS | 32); + const int result = inflateInit2(&m_zstream, MAX_WBITS | 32); if (result != Z_OK) { - std::string message("gzip error: decompression init failed: "); + std::string message{"gzip error: decompression init failed: "}; if (m_zstream.msg) { message.append(m_zstream.msg); } - throw osmium::gzip_error(message, result); + throw osmium::gzip_error{message, result}; } } @@ -234,7 +234,7 @@ namespace osmium { output.append(buffer_size, '\0'); m_zstream.next_out = reinterpret_cast(const_cast(output.data())); m_zstream.avail_out = buffer_size; - int result = inflate(&m_zstream, Z_SYNC_FLUSH); + const int result = inflate(&m_zstream, Z_SYNC_FLUSH); if (result != Z_OK) { m_buffer = nullptr; @@ -246,7 +246,7 @@ namespace osmium { if (m_zstream.msg) { message.append(m_zstream.msg); } - throw osmium::gzip_error(message, result); + throw osmium::gzip_error{message, result}; } output.resize(static_cast(m_zstream.next_out - reinterpret_cast(output.data()))); @@ -266,9 +266,9 @@ namespace osmium { // 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, 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); } + [](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}; } ); // dummy function to silence the unused variable warning from above diff --git a/third_party/libosmium/include/osmium/io/header.hpp b/third_party/libosmium/include/osmium/io/header.hpp index c0a204380..f52929415 100644 --- a/third_party/libosmium/include/osmium/io/header.hpp +++ b/third_party/libosmium/include/osmium/io/header.hpp @@ -75,14 +75,20 @@ namespace osmium { * this stream of objects? This should be true for history files * and for change files, but not for normal OSM data files. */ - bool m_has_multiple_object_versions = false; + bool m_has_multiple_object_versions; public: - Header() = default; + Header() : + Options(), + m_boxes(), + m_has_multiple_object_versions(false) { + } explicit Header(const std::initializer_list& values) : - Options(values) { + Options(values), + m_boxes(), + m_has_multiple_object_versions(false) { } /** diff --git a/third_party/libosmium/include/osmium/io/reader.hpp b/third_party/libosmium/include/osmium/io/reader.hpp index 825cb41ce..728693375 100644 --- a/third_party/libosmium/include/osmium/io/reader.hpp +++ b/third_party/libosmium/include/osmium/io/reader.hpp @@ -61,6 +61,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include @@ -70,13 +71,13 @@ namespace osmium { namespace detail { - inline size_t get_input_queue_size() noexcept { - const size_t n = osmium::config::get_max_queue_size("INPUT", 20); + inline std::size_t get_input_queue_size() noexcept { + const std::size_t n = osmium::config::get_max_queue_size("INPUT", 20); return n > 2 ? n : 2; } - inline size_t get_osmdata_queue_size() noexcept { - const size_t n = osmium::config::get_max_queue_size("OSMDATA", 20); + inline std::size_t get_osmdata_queue_size() noexcept { + const std::size_t n = osmium::config::get_max_queue_size("OSMDATA", 20); return n > 2 ? n : 2; } @@ -92,12 +93,14 @@ namespace osmium { osmium::io::File m_file; + osmium::thread::Pool* m_pool = nullptr; + detail::ParserFactory::create_parser_type m_creator; enum class status { okay = 0, // normal reading error = 1, // some error occurred while reading - closed = 2, // close() called successfully after eof + closed = 2, // close() called eof = 3 // eof of file was reached without error } m_status; @@ -117,27 +120,41 @@ namespace osmium { osmium::thread::thread_handler m_thread; - size_t m_file_size; + std::size_t m_file_size; - osmium::io::detail::reader_options m_options; + osmium::osm_entity_bits::type m_read_which_entities = osmium::osm_entity_bits::all; + osmium::io::read_meta m_read_metadata = osmium::io::read_meta::yes; + + void set_option(osmium::thread::Pool& pool) noexcept { + m_pool = &pool; + } void set_option(osmium::osm_entity_bits::type value) noexcept { - m_options.read_which_entities = value; + m_read_which_entities = value; } void set_option(osmium::io::read_meta value) noexcept { - m_options.read_metadata = value; + m_read_metadata = value; } // This function will run in a separate thread. - static void parser_thread(const detail::ParserFactory::create_parser_type& creator, + static void parser_thread(osmium::thread::Pool& pool, + const detail::ParserFactory::create_parser_type& creator, detail::future_string_queue_type& input_queue, detail::future_buffer_queue_type& osmdata_queue, std::promise&& header_promise, - osmium::io::detail::reader_options options) { - std::promise promise = std::move(header_promise); - const auto parser = creator(input_queue, osmdata_queue, promise, options); - parser->parse(); + osmium::osm_entity_bits::type read_which_entities, + osmium::io::read_meta read_metadata) { + std::promise promise{std::move(header_promise)}; + osmium::io::detail::parser_arguments args = { + pool, + input_queue, + osmdata_queue, + promise, + read_which_entities, + read_metadata + }; + creator(args)->parse(); } #ifndef _WIN32 @@ -155,11 +172,11 @@ namespace osmium { static int execute(const std::string& command, const std::string& filename, int* childpid) { int pipefd[2]; if (pipe(pipefd) < 0) { - throw std::system_error(errno, std::system_category(), "opening pipe failed"); + throw std::system_error{errno, std::system_category(), "opening pipe failed"}; } const pid_t pid = fork(); if (pid < 0) { - throw std::system_error(errno, std::system_category(), "fork failed"); + throw std::system_error{errno, std::system_category(), "fork failed"}; } if (pid == 0) { // child // close all file descriptors except one end of the pipe @@ -198,16 +215,15 @@ namespace osmium { * @throws std::system_error if a system call fails. */ static int open_input_file_or_url(const std::string& filename, int* childpid) { - std::string protocol = filename.substr(0, filename.find_first_of(':')); + const std::string protocol{filename.substr(0, filename.find_first_of(':'))}; if (protocol == "http" || protocol == "https" || protocol == "ftp" || protocol == "file") { #ifndef _WIN32 return execute("curl", filename, childpid); #else - throw io_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); } + return osmium::io::detail::open_for_reading(filename); } public: @@ -256,9 +272,13 @@ namespace osmium { (set_option(args), 0)... }; + if (!m_pool) { + m_pool = &thread::Pool::default_instance(); + } + std::promise header_promise; m_header_future = header_promise.get_future(); - m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_creator), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), m_options}; + m_thread = osmium::thread::thread_handler{parser_thread, std::ref(*m_pool), std::ref(m_creator), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), m_read_which_entities, m_read_metadata}; } template @@ -313,7 +333,7 @@ namespace osmium { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { - throw std::system_error(errno, std::system_category(), "subprocess returned error"); + throw std::system_error{errno, std::system_category(), "subprocess returned error"}; } #pragma GCC diagnostic pop m_childpid = 0; @@ -329,30 +349,26 @@ namespace osmium { */ osmium::io::Header header() { if (m_status == status::error) { - throw io_error("Can not get header from reader when in 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_options.read_which_entities == osmium::osm_entity_bits::nothing) { - m_status = status::eof; - } } } catch (...) { close(); m_status = status::error; throw; } + return m_header; } /** * Reads the next buffer from the input. An invalid buffer signals - * end-of-file. After end-of-file all read() calls will return an - * invalid buffer. An invalid buffer is also always returned if - * osmium::osm_entity_bits::nothing was set when the Reader was - * constructed. + * end-of-file. After end-of-file all read() calls will throw an + * osmium::io_error. * * @returns Buffer. * @throws Some form of osmium::io_error if there is an error. @@ -360,9 +376,13 @@ namespace osmium { osmium::memory::Buffer read() { osmium::memory::Buffer buffer; - if (m_status != status::okay || - m_options.read_which_entities == osmium::osm_entity_bits::nothing) { - throw io_error("Can not read from reader when in status 'closed', 'eof', or 'error'"); + if (m_status != status::okay) { + throw io_error{"Can not read from reader when in status 'closed', 'eof', or 'error'"}; + } + + if (m_read_which_entities == osmium::osm_entity_bits::nothing) { + m_status = status::eof; + return buffer; } try { @@ -400,7 +420,7 @@ namespace osmium { * Get the size of the input file. Returns 0 if the file size * is not available (for instance when reading from stdin). */ - size_t file_size() const noexcept { + std::size_t file_size() const noexcept { return m_file_size; } @@ -418,7 +438,7 @@ namespace osmium { * object you are reading. Depending on the file type it might * do an expensive system call. */ - size_t offset() const noexcept { + std::size_t offset() const noexcept { return m_decompressor->offset(); } @@ -434,10 +454,10 @@ namespace osmium { */ template osmium::memory::Buffer read_file(TArgs&&... args) { - osmium::memory::Buffer buffer(1024*1024, osmium::memory::Buffer::auto_grow::yes); + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; - Reader reader(std::forward(args)...); - while (osmium::memory::Buffer read_buffer = reader.read()) { + Reader reader{std::forward(args)...}; + while (auto read_buffer = reader.read()) { buffer.add_buffer(read_buffer); buffer.commit(); } diff --git a/third_party/libosmium/include/osmium/io/writer.hpp b/third_party/libosmium/include/osmium/io/writer.hpp index 043f68f8d..dd90a2e5e 100644 --- a/third_party/libosmium/include/osmium/io/writer.hpp +++ b/third_party/libosmium/include/osmium/io/writer.hpp @@ -53,8 +53,10 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include +#include namespace osmium { @@ -168,8 +170,13 @@ namespace osmium { osmium::io::Header header; overwrite allow_overwrite = overwrite::no; fsync sync = fsync::no; + osmium::thread::Pool* pool = nullptr; }; + static void set_option(options_type& options, osmium::thread::Pool& pool) { + options.pool = &pool; + } + static void set_option(options_type& options, const osmium::io::Header& header) { options.header = header; } @@ -182,6 +189,17 @@ namespace osmium { options.sync = value; } + void do_close() { + 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); + }); + } + } + public: /** @@ -211,7 +229,7 @@ namespace osmium { explicit Writer(const osmium::io::File& file, TArgs&&... args) : m_file(file.check()), m_output_queue(detail::get_output_queue_size(), "raw_output"), - m_output(osmium::io::detail::OutputFormatFactory::instance().create_output(m_file, m_output_queue)), + m_output(nullptr), m_buffer(), m_buffer_size(default_buffer_size), m_write_future(), @@ -224,6 +242,16 @@ namespace osmium { (set_option(options, args), 0)... }; + if (!options.pool) { + options.pool = &thread::Pool::default_instance(); + } + + m_output = osmium::io::detail::OutputFormatFactory::instance().create_output(*options.pool, m_file, m_output_queue); + + if (options.header.get("generator") == "") { + options.header.set("generator", "libosmium/" LIBOSMIUM_VERSION_STRING); + } + std::unique_ptr compressor = CompressionFactory::instance().create_compressor(file.compression(), osmium::io::detail::open_for_writing(m_file.filename(), options.allow_overwrite), @@ -240,12 +268,12 @@ namespace osmium { template explicit Writer(const std::string& filename, TArgs&&... args) : - Writer(osmium::io::File(filename), std::forward(args)...) { + Writer(osmium::io::File{filename}, std::forward(args)...) { } template explicit Writer(const char* filename, TArgs&&... args) : - Writer(osmium::io::File(filename), std::forward(args)...) { + Writer(osmium::io::File{filename}, std::forward(args)...) { } Writer(const Writer&) = delete; @@ -256,7 +284,7 @@ namespace osmium { ~Writer() noexcept { try { - close(); + do_close(); } catch (...) { // Ignore any exceptions because destructor must not throw. } @@ -337,14 +365,7 @@ namespace osmium { * @throws Some form of osmium::io_error when there is a problem. */ void close() { - 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); - }); - } + do_close(); if (m_write_future.valid()) { m_write_future.get(); diff --git a/third_party/libosmium/include/osmium/memory/buffer.hpp b/third_party/libosmium/include/osmium/memory/buffer.hpp index 1b75b4fa5..deaee42f3 100644 --- a/third_party/libosmium/include/osmium/memory/buffer.hpp +++ b/third_party/libosmium/include/osmium/memory/buffer.hpp @@ -1,6 +1,7 @@ #ifndef OSMIUM_MEMORY_BUFFER_HPP #define OSMIUM_MEMORY_BUFFER_HPP +#include /* This file is part of Osmium (http://osmcode.org/libosmium). @@ -58,7 +59,7 @@ namespace osmium { struct buffer_is_full : public std::runtime_error { buffer_is_full() : - std::runtime_error("Osmium buffer is full") { + std::runtime_error{"Osmium buffer is full"} { } }; // struct buffer_is_full @@ -110,22 +111,22 @@ namespace osmium { std::unique_ptr m_memory; unsigned char* m_data; - size_t m_capacity; - size_t m_written; - size_t m_committed; + std::size_t m_capacity; + std::size_t m_written; + std::size_t m_committed; #ifndef NDEBUG - uint8_t m_builder_count{0}; + uint8_t m_builder_count = 0; #endif auto_grow m_auto_grow{auto_grow::no}; std::function m_full; - static size_t calculate_capacity(size_t capacity) noexcept { + static std::size_t calculate_capacity(std::size_t capacity) noexcept { // The majority of all Nodes will fit into this size. - constexpr static const size_t min_capacity = 64; + constexpr static const std::size_t min_capacity = 64; if (capacity < min_capacity) { return min_capacity; } - return capacity; + return padded_length(capacity); } public: @@ -156,14 +157,14 @@ namespace osmium { * @throws std::invalid_argument if the size isn't a multiple of * the alignment. */ - explicit Buffer(unsigned char* data, size_t size) : + explicit Buffer(unsigned char* data, std::size_t size) : m_memory(), m_data(data), m_capacity(size), m_written(size), m_committed(size) { if (size % align_bytes != 0) { - throw std::invalid_argument("buffer size needs to be multiple of alignment"); + throw std::invalid_argument{"buffer size needs to be multiple of alignment"}; } } @@ -176,19 +177,23 @@ namespace osmium { * @param committed The size of the initialized data. If this is 0, the buffer startes out empty. * * @throws std::invalid_argument if the capacity or committed isn't - * a multiple of the alignment. + * a multiple of the alignment or if committed is larger + * than capacity. */ - explicit Buffer(unsigned char* data, size_t capacity, size_t committed) : + explicit Buffer(unsigned char* data, std::size_t capacity, std::size_t committed) : m_memory(), m_data(data), m_capacity(capacity), m_written(committed), m_committed(committed) { if (capacity % align_bytes != 0) { - throw std::invalid_argument("buffer capacity needs to be multiple of alignment"); + throw std::invalid_argument{"buffer capacity needs to be multiple of alignment"}; } if (committed % align_bytes != 0) { - throw std::invalid_argument("buffer parameter 'committed' needs to be multiple of alignment"); + throw std::invalid_argument{"buffer parameter 'committed' needs to be multiple of alignment"}; + } + if (committed > capacity) { + throw std::invalid_argument{"buffer parameter 'committed' can not be larger than capacity"}; } } @@ -200,22 +205,17 @@ namespace osmium { * is destroyed. * * @param capacity The (initial) size of the memory for this buffer. + * Actual capacity might be larger tue to alignment. * @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) : + explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) : m_memory(new unsigned char[calculate_capacity(capacity)]), m_data(m_memory.get()), m_capacity(calculate_capacity(capacity)), m_written(0), m_committed(0), m_auto_grow(auto_grow) { - if (m_capacity % align_bytes != 0) { - throw std::invalid_argument("buffer capacity needs to be multiple of alignment"); - } } // buffers can not be copied @@ -257,7 +257,7 @@ namespace osmium { * Returns the capacity of the buffer, ie how many bytes it can * contain. Always returns 0 on invalid buffers. */ - size_t capacity() const noexcept { + std::size_t capacity() const noexcept { return m_capacity; } @@ -265,7 +265,7 @@ namespace osmium { * Returns the number of bytes already filled in this buffer. * Always returns 0 on invalid buffers. */ - size_t committed() const noexcept { + std::size_t committed() const noexcept { return m_committed; } @@ -274,7 +274,7 @@ namespace osmium { * are not yet committed. * Always returns 0 on invalid buffers. */ - size_t written() const noexcept { + std::size_t written() const noexcept { return m_written; } @@ -302,20 +302,16 @@ namespace osmium { * 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(). + * the CallbackBuffer class. */ - OSMIUM_DEPRECATED void set_full_callback(std::function full) { + OSMIUM_DEPRECATED void set_full_callback(const std::function& full) { assert(m_data && "This must be a valid buffer"); m_full = full; } /** - * Grow capacity of this buffer to the given size. + * Grow capacity of this buffer to the given size (which will be + * rounded up to the alignment needed). * This works only with internally memory-managed buffers. * If the given size is not larger than the current capacity, * nothing is done. @@ -326,19 +322,15 @@ namespace osmium { * * @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) { + void grow(std::size_t size) { assert(m_data && "This must be a valid buffer"); if (!m_memory) { - throw std::logic_error("Can't grow Buffer if it doesn't use internal memory management."); + throw std::logic_error{"Can't grow Buffer if it doesn't use internal memory management."}; } + size = calculate_capacity(size); if (m_capacity < size) { - if (size % align_bytes != 0) { - throw std::invalid_argument("buffer capacity needs to be multiple of alignment"); - } std::unique_ptr memory(new unsigned char[size]); std::copy_n(m_memory.get(), m_capacity, memory.get()); using std::swap; @@ -360,12 +352,12 @@ namespace osmium { * used as an offset into the buffer to get to the * object being committed by this call. */ - size_t commit() { + std::size_t commit() { assert(m_data && "This must be a valid buffer"); assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); assert(is_aligned()); - const size_t offset = m_committed; + const std::size_t offset = m_committed; m_committed = m_written; return offset; } @@ -391,9 +383,9 @@ namespace osmium { * * @returns Number of bytes in the buffer before it was cleared. */ - size_t clear() { + std::size_t clear() { assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); - const size_t committed = m_committed; + const std::size_t committed = m_committed; m_written = 0; m_committed = 0; return committed; @@ -410,7 +402,7 @@ namespace osmium { * buffer. */ template - T& get(const size_t offset) const { + T& get(const std::size_t offset) const { assert(m_data && "This must be a valid buffer"); return *reinterpret_cast(&m_data[offset]); } @@ -448,7 +440,7 @@ namespace osmium { * @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) { + unsigned char* reserve_space(const std::size_t size) { assert(m_data && "This must be a valid buffer"); // try to flush the buffer empty first. if (m_written + size > m_capacity && m_full) { @@ -458,13 +450,13 @@ namespace osmium { if (m_written + size > m_capacity) { if (m_memory && (m_auto_grow == auto_grow::yes)) { // double buffer size until there is enough space - size_t new_capacity = m_capacity * 2; + std::size_t new_capacity = m_capacity * 2; while (m_written + size > new_capacity) { new_capacity *= 2; } grow(new_capacity); } else { - throw osmium::buffer_is_full(); + throw osmium::buffer_is_full{}; } } unsigned char* data = &m_data[m_written]; @@ -603,7 +595,7 @@ namespace osmium { * in the buffer. */ template - t_iterator get_iterator(size_t offset) { + t_iterator get_iterator(std::size_t offset) { assert(m_data && "This must be a valid buffer"); return t_iterator(m_data + offset, m_data + m_committed); } @@ -617,7 +609,7 @@ namespace osmium { * @returns Iterator to first OSMEntity after given offset in the * buffer. */ - iterator get_iterator(size_t offset) { + iterator get_iterator(std::size_t offset) { assert(m_data && "This must be a valid buffer"); return iterator(m_data + offset, m_data + m_committed); } @@ -661,12 +653,12 @@ namespace osmium { } template - t_const_iterator get_iterator(size_t offset) const { + t_const_iterator get_iterator(std::size_t offset) const { assert(m_data && "This must be a valid buffer"); return t_const_iterator(m_data + offset, m_data + m_committed); } - const_iterator get_iterator(size_t offset) const { + const_iterator get_iterator(std::size_t offset) const { assert(m_data && "This must be a valid buffer"); return const_iterator(m_data + offset, m_data + m_committed); } @@ -751,8 +743,8 @@ namespace osmium { if (it_read != it_write) { assert(it_read.data() >= data()); assert(it_write.data() >= data()); - size_t old_offset = static_cast(it_read.data() - data()); - size_t new_offset = static_cast(it_write.data() - data()); + const auto old_offset = static_cast(it_read.data() - data()); + const auto new_offset = static_cast(it_write.data() - data()); callback->moving_in_buffer(old_offset, new_offset); std::memmove(it_write.data(), it_read.data(), it_read->padded_size()); } @@ -761,7 +753,7 @@ namespace osmium { } assert(it_write.data() >= data()); - m_written = static_cast(it_write.data() - data()); + m_written = static_cast(it_write.data() - data()); m_committed = m_written; } diff --git a/third_party/libosmium/include/osmium/memory/callback_buffer.hpp b/third_party/libosmium/include/osmium/memory/callback_buffer.hpp new file mode 100644 index 000000000..7e9be7397 --- /dev/null +++ b/third_party/libosmium/include/osmium/memory/callback_buffer.hpp @@ -0,0 +1,189 @@ +#ifndef OSMIUM_MEMORY_CALLBACK_BUFFER_HPP +#define OSMIUM_MEMORY_CALLBACK_BUFFER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace memory { + + /** + * This is basically a wrapper around osmium::memory::Buffer with an + * additional callback function that is called whenever the buffer is + * full. + * + * The internal buffer is created with the `initial_buffer_size` set + * in the constructor. When it grows beyond the `max_buffer_size` set + * in the constructor, the callback function is called with the buffer + * and a new, empty buffer is created internally. + * + * Note that the buffer can grow beyond the initial buffer size if + * needed. This can happen if a new object doesn't fit into the rest + * of the buffer available or if no callback function is set (yet). + * + * Example: + * @code + * CallbackBuffer cb; + * cb.set_callback([&](osmium::memory::Buffer&& buffer) { + * ...handle buffer... + * } + * osmium::builder::add_node(cb.buffer(), _id(9), ...); + * osmium::builder::add_way(cb.buffer(), _id(27), ...); + * @endcode + */ + class CallbackBuffer { + + public: + + /// The type for the callback function + using callback_func_type = std::function; + + private: + + static constexpr const std::size_t default_initial_buffer_size = 1024 * 1024; + static constexpr const std::size_t default_max_buffer_size = 800 * 1024; + + osmium::memory::Buffer m_buffer; + std::size_t m_initial_buffer_size; + std::size_t m_max_buffer_size; + callback_func_type m_callback; + + public: + + /** + * Construct a CallbackBuffer without a callback function. You + * can later call set the callback with set_callback(). + * + * @param initial_buffer_size The initial size of newly created + * internal buffers. + * @param max_buffer_size If the buffer grows beyond this size the + * callback will be called. + */ + explicit CallbackBuffer(std::size_t initial_buffer_size = default_initial_buffer_size, std::size_t max_buffer_size = default_max_buffer_size) : + m_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes), + m_initial_buffer_size(initial_buffer_size), + m_max_buffer_size(max_buffer_size), + m_callback(nullptr) { + } + + /** + * Construct a CallbackBuffer with a callback function. + * + * @param callback The callback function. Must be of type + * @code void(osmium::memory::Buffer&&) @endcode + * @param initial_buffer_size The initial size of newly created + * internal buffers. + * @param max_buffer_size If the buffer grows beyond this size the + * callback will be called. + */ + explicit CallbackBuffer(const callback_func_type& callback, std::size_t initial_buffer_size = default_initial_buffer_size, std::size_t max_buffer_size = default_max_buffer_size) : + m_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes), + m_initial_buffer_size(initial_buffer_size), + m_max_buffer_size(max_buffer_size), + m_callback(callback) { + } + + /** + * Access the internal buffer. This is used to fill the buffer, + * the CallbackBuffer still owns the buffer. + * + * Use read() or the callback if you need to own the buffer. + */ + osmium::memory::Buffer& buffer() noexcept { + return m_buffer; + } + + /** + * Set the callback. The function must take a rvalue reference to + * a buffer and return void. + * + * @param callback The callback function. Must be of type + * @code void(osmium::memory::Buffer&&) @endcode + */ + void set_callback(const callback_func_type& callback = nullptr) noexcept { + m_callback = callback; + } + + /** + * Flush the internal buffer regardless of how full it is. Calls + * the callback with the buffer and creates an new empty internal + * one. + * + * This will do nothing if no callback is set or if the buffer + * is empty. + */ + void flush() { + if (m_callback && m_buffer.committed() > 0) { + m_callback(read()); + } + } + + /** + * Flush the internal buffer if and only if it contains more than + * the max_buffer_size set in the constructor. Calls the callback + * with the buffer and creates an new empty internal one. + * + * This will do nothing if no callback is set or if the buffer + * is empty. + */ + void possibly_flush() { + if (m_buffer.committed() > m_max_buffer_size) { + flush(); + } + } + + /** + * Return the internal buffer and create a new empty internal one. + * You can use this as an alternative access instead of using the + * callback. + */ + osmium::memory::Buffer read() { + osmium::memory::Buffer buffer{m_initial_buffer_size, osmium::memory::Buffer::auto_grow::yes}; + using std::swap; + swap(buffer, m_buffer); + return buffer; + } + + }; // class CallbackBuffer + + } // namespace memory + +} // namespace osmium + +#endif // OSMIUM_MEMORY_CALLBACK_BUFFER_HPP diff --git a/third_party/libosmium/include/osmium/memory/item.hpp b/third_party/libosmium/include/osmium/memory/item.hpp index 6714ce20b..0f02df9e7 100644 --- a/third_party/libosmium/include/osmium/memory/item.hpp +++ b/third_party/libosmium/include/osmium/memory/item.hpp @@ -59,7 +59,7 @@ namespace osmium { using item_size_type = uint32_t; // align datastructures to this many bytes - constexpr const item_size_type align_bytes = 8; + constexpr const std::size_t align_bytes = 8; inline constexpr std::size_t padded_length(std::size_t length) noexcept { return (length + align_bytes - 1) & ~(align_bytes - 1); diff --git a/third_party/libosmium/include/osmium/object_pointer_collection.hpp b/third_party/libosmium/include/osmium/object_pointer_collection.hpp index 5accf4210..685211d94 100644 --- a/third_party/libosmium/include/osmium/object_pointer_collection.hpp +++ b/third_party/libosmium/include/osmium/object_pointer_collection.hpp @@ -84,20 +84,39 @@ namespace osmium { } /** - * Sort objects according to the given order functor. + * Sort objects according to the specified order functor. */ template void sort(TCompare&& compare) { std::sort(m_objects.begin(), m_objects.end(), std::forward(compare)); } - /// Is the collection empty? + /** + * Make objects unique according to the specified equality functor. + * + * Complexity: Linear in the number of items. + */ + template + void unique(TEqual&& equal) { + const auto last = std::unique(m_objects.begin(), m_objects.end(), std::forward(equal)); + m_objects.erase(last, m_objects.end()); + } + + /** + * Is the collection empty? + * + * Complexity: Constant. + */ bool empty() const noexcept { return m_objects.empty(); } - /// Return size of the collection. - size_t size() const noexcept { + /** + * Return size of the collection. + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { return m_objects.size(); } @@ -107,19 +126,19 @@ namespace osmium { } iterator begin() { - return iterator{m_objects.begin()}; + return {m_objects.begin()}; } iterator end() { - return iterator{m_objects.end()}; + return {m_objects.end()}; } const_iterator cbegin() const { - return const_iterator{m_objects.cbegin()}; + return {m_objects.cbegin()}; } const_iterator cend() const { - return const_iterator{m_objects.cend()}; + return {m_objects.cend()}; } }; // class ObjectPointerCollection diff --git a/third_party/libosmium/include/osmium/opl.hpp b/third_party/libosmium/include/osmium/opl.hpp index c77a6c0e7..99699149a 100644 --- a/third_party/libosmium/include/osmium/opl.hpp +++ b/third_party/libosmium/include/osmium/opl.hpp @@ -33,8 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include +#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/osm/area.hpp b/third_party/libosmium/include/osmium/osm/area.hpp index a98055222..12a73bece 100644 --- a/third_party/libosmium/include/osmium/osm/area.hpp +++ b/third_party/libosmium/include/osmium/osm/area.hpp @@ -171,8 +171,8 @@ namespace osmium { std::pair num_rings() const { std::pair counter{0, 0}; - for (auto it = cbegin(); it != cend(); ++it) { - switch (it->type()) { + for (const auto& item : *this) { + switch (item.type()) { case osmium::item_type::outer_ring: ++counter.first; break; diff --git a/third_party/libosmium/include/osmium/osm/changeset.hpp b/third_party/libosmium/include/osmium/osm/changeset.hpp index 7fbd4667a..d79ec3103 100644 --- a/third_party/libosmium/include/osmium/osm/changeset.hpp +++ b/third_party/libosmium/include/osmium/osm/changeset.hpp @@ -61,9 +61,9 @@ namespace osmium { friend class osmium::builder::ChangesetDiscussionBuilder; osmium::Timestamp m_date; - osmium::user_id_type m_uid {0}; + osmium::user_id_type m_uid = 0; + changeset_comment_size_type m_text_size; string_size_type m_user_size; - string_size_type m_text_size; ChangesetComment(const ChangesetComment&) = delete; ChangesetComment(ChangesetComment&&) = delete; @@ -94,7 +94,7 @@ namespace osmium { m_user_size = size; } - void set_text_size(string_size_type size) noexcept { + void set_text_size(changeset_comment_size_type size) noexcept { m_text_size = size; } @@ -105,8 +105,8 @@ namespace osmium { ChangesetComment(osmium::Timestamp date, osmium::user_id_type uid) noexcept : m_date(date), m_uid(uid), - m_user_size(0), - m_text_size(0) { + m_text_size(0), + m_user_size(0) { } osmium::Timestamp date() const noexcept { @@ -153,13 +153,13 @@ namespace osmium { osmium::Box m_bounds; osmium::Timestamp m_created_at; osmium::Timestamp m_closed_at; - 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_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 = 0; + int16_t m_padding1 = 0; + int32_t m_padding2 = 0; Changeset() : OSMEntity(sizeof(Changeset), osmium::item_type::changeset) { diff --git a/third_party/libosmium/include/osmium/osm/crc.hpp b/third_party/libosmium/include/osmium/osm/crc.hpp index 5de52ed18..c191db93a 100644 --- a/third_party/libosmium/include/osmium/osm/crc.hpp +++ b/third_party/libosmium/include/osmium/osm/crc.hpp @@ -246,4 +246,4 @@ namespace osmium { } // namespace osmium -#endif // OSMIUM_OSM_CRC +#endif // OSMIUM_OSM_CRC_HPP diff --git a/third_party/libosmium/include/osmium/osm/item_type.hpp b/third_party/libosmium/include/osmium/osm/item_type.hpp index 2b6dec22f..74d448221 100644 --- a/third_party/libosmium/include/osmium/osm/item_type.hpp +++ b/third_party/libosmium/include/osmium/osm/item_type.hpp @@ -87,8 +87,6 @@ namespace osmium { inline item_type char_to_item_type(const char c) noexcept { switch (c) { - case 'X': - return item_type::undefined; case 'n': return item_type::node; case 'w': @@ -113,18 +111,14 @@ namespace osmium { return item_type::inner_ring; case 'D': return item_type::changeset_discussion; - default: - return item_type::undefined; + default: // 'X' + break; } + return item_type::undefined; } -// avoid g++ false positive -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-type" inline char item_type_to_char(const item_type type) noexcept { switch (type) { - case item_type::undefined: - return 'X'; case item_type::node: return 'n'; case item_type::way: @@ -149,13 +143,14 @@ namespace osmium { return 'I'; case item_type::changeset_discussion: return 'D'; + default: // item_type::undefined + break; } + return 'X'; } inline const char* item_type_to_name(const item_type type) noexcept { switch (type) { - case item_type::undefined: - return "undefined"; case item_type::node: return "node"; case item_type::way: @@ -180,9 +175,11 @@ namespace osmium { return "inner_ring"; case item_type::changeset_discussion: return "changeset_discussion"; + default: // item_type::undefined + break; } + return "undefined"; } -#pragma GCC diagnostic pop template inline std::basic_ostream& operator<<(std::basic_ostream& out, const item_type item_type) { diff --git a/third_party/libosmium/include/osmium/osm/location.hpp b/third_party/libosmium/include/osmium/osm/location.hpp index b2fdc1b75..f5ae9e5e3 100644 --- a/third_party/libosmium/include/osmium/osm/location.hpp +++ b/third_party/libosmium/include/osmium/osm/location.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -198,6 +199,12 @@ namespace osmium { // Convert integer as used by location for coordinates into a string. template inline T append_location_coordinate_to_string(T iterator, int32_t value) { + // need to special-case this, because later `value = -value` would overflow. + if (value == std::numeric_limits::min()) { + static const char minresult[] = "-214.7483648"; + return std::copy_n(minresult, sizeof(minresult) - 1, iterator); + } + // handle negative values if (value < 0) { *iterator++ = '-'; @@ -329,6 +336,9 @@ namespace osmium { /** * Check whether the coordinates of this location * are defined. + * + * @deprecated Use is_defined() or is_undefined() or is_valid() which + * have all slightly different meanings. */ explicit constexpr operator bool() const noexcept { return m_x != undefined_coordinate && m_y != undefined_coordinate; @@ -337,6 +347,8 @@ namespace osmium { /** * Check whether the coordinates are inside the * usual bounds (-180<=lon<=180, -90<=lat<=90). + * + * See also is_defined() and is_undefined(). */ constexpr bool valid() const noexcept { return m_x >= -180 * detail::coordinate_precision @@ -345,6 +357,24 @@ namespace osmium { && m_y <= 90 * detail::coordinate_precision; } + /** + * Returns true if at least one of the coordinates is defined. + * + * See also is_undefined() and is_valid(). + */ + constexpr bool is_defined() const noexcept { + return m_x != undefined_coordinate || m_y != undefined_coordinate; + } + + /** + * Returns true if both coordinates are undefined. + * + * See also is_defined() and is_valid(). + */ + constexpr bool is_undefined() const noexcept { + return m_x == undefined_coordinate && m_y == undefined_coordinate; + } + constexpr int32_t x() const noexcept { return m_x; } @@ -370,7 +400,7 @@ namespace osmium { */ double lon() const { if (!valid()) { - throw osmium::invalid_location("invalid location"); + throw osmium::invalid_location{"invalid location"}; } return fix_to_double(m_x); } @@ -389,7 +419,7 @@ namespace osmium { */ double lat() const { if (!valid()) { - throw osmium::invalid_location("invalid location"); + throw osmium::invalid_location{"invalid location"}; } return fix_to_double(m_y); } @@ -449,7 +479,7 @@ namespace osmium { template T as_string(T iterator, const char separator = ',') const { if (!valid()) { - throw osmium::invalid_location("invalid location"); + throw osmium::invalid_location{"invalid location"}; } return as_string_without_check(iterator, separator); } diff --git a/third_party/libosmium/include/osmium/osm/object.hpp b/third_party/libosmium/include/osmium/osm/object.hpp index 66f891a7c..1e477db13 100644 --- a/third_party/libosmium/include/osmium/osm/object.hpp +++ b/third_party/libosmium/include/osmium/osm/object.hpp @@ -193,7 +193,7 @@ namespace osmium { } else if (!std::strcmp("false", visible)) { set_visible(false); } else { - throw std::invalid_argument("Unknown value for visible attribute (allowed is 'true' or 'false')"); + throw std::invalid_argument{"Unknown value for visible attribute (allowed is 'true' or 'false')"}; } return *this; } @@ -468,18 +468,19 @@ namespace osmium { * ordering by timestamp is not necessary as there shouldn't be two * objects with the same type, id, and version. But this can happen when * creating diff files from extracts, so we take the timestamp into - * account here. + * account here. * * Note that we use the absolute value of the id for a better ordering - * of objects with negative id. If the IDs have the same absolute value, - * the positive ID comes first. + * of objects with negative id. All the negative IDs come first, then the + * positive IDs. IDs are ordered by their absolute values. (This is the + * same ordering JOSM uses.) * * See object_order_type_id_reverse_version if you need a different * ordering. */ inline bool operator<(const OSMObject& lhs, const OSMObject& rhs) noexcept { - return const_tie(lhs.type(), lhs.positive_id(), lhs.id() < 0, lhs.version(), lhs.timestamp()) < - const_tie(rhs.type(), rhs.positive_id(), rhs.id() < 0, rhs.version(), rhs.timestamp()); + return const_tie(lhs.type(), lhs.id() > 0, lhs.positive_id(), lhs.version(), lhs.timestamp()) < + const_tie(rhs.type(), rhs.id() > 0, rhs.positive_id(), rhs.version(), rhs.timestamp()); } inline bool operator>(const OSMObject& lhs, const OSMObject& rhs) noexcept { diff --git a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp index 69dbf7981..a762f190f 100644 --- a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp +++ b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include @@ -44,7 +45,7 @@ namespace osmium { /** * Function object class for comparing OSM objects for equality by type, - * id, and version. + * ID, and version. */ struct object_equal_type_id_version { @@ -62,7 +63,7 @@ namespace osmium { /** * Function object class for comparing OSM objects for equality by type - * and id, ignoring the version. + * and ID, ignoring the version. */ struct object_equal_type_id { @@ -79,6 +80,19 @@ namespace osmium { }; // struct object_equal_type_id + /** + * Compare two objects IDs. Order is as follows: 0 first, then negative + * IDs, then positive IDs, both ordered by their absolute values. + */ + struct id_order { + + bool operator()(const object_id_type lhs, const object_id_type rhs) const noexcept { + return const_tie(lhs > 0, std::abs(lhs)) < + const_tie(rhs > 0, std::abs(rhs)); + } + + }; // struct id_order + /** * Function object class for ordering OSM objects by type, id, version, * and timestamp. @@ -98,17 +112,18 @@ namespace osmium { }; // struct object_order_type_id_version /** - * Function object class for ordering OSM objects by type, id, and - * reverse version, timestamp. So objects are ordered by type and id, but - * later versions of an object are ordered before earlier versions of the - * same object. This is useful when the last version of an object needs - * to be used. + * Function object class for ordering OSM objects by type, ID, and + * reverse version, timestamp. So objects are ordered by type and ID + * (negative IDs first, then positive IDs, both in the order of their + * absolute values), but later versions of an object are ordered before + * earlier versions of the same object. This is useful when the last + * version of an object needs to be used. */ struct object_order_type_id_reverse_version { bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept { - return const_tie(lhs.type(), lhs.id() < 0, lhs.positive_id(), rhs.version(), rhs.timestamp()) < - const_tie(rhs.type(), rhs.id() < 0, rhs.positive_id(), lhs.version(), lhs.timestamp()); + return const_tie(lhs.type(), lhs.id() > 0, lhs.positive_id(), rhs.version(), rhs.timestamp()) < + const_tie(rhs.type(), rhs.id() > 0, rhs.positive_id(), lhs.version(), lhs.timestamp()); } /// @pre lhs and rhs must not be nullptr diff --git a/third_party/libosmium/include/osmium/osm/relation.hpp b/third_party/libosmium/include/osmium/osm/relation.hpp index deac43afa..8122a1fc5 100644 --- a/third_party/libosmium/include/osmium/osm/relation.hpp +++ b/third_party/libosmium/include/osmium/osm/relation.hpp @@ -61,7 +61,7 @@ namespace osmium { object_id_type m_ref; item_type m_type; uint16_t m_flags; - string_size_type m_role_size {0}; + string_size_type m_role_size = 0; RelationMember(const RelationMember&) = delete; RelationMember(RelationMember&&) = delete; @@ -183,14 +183,21 @@ namespace osmium { return t == itemtype; } + /// Get a reference to the member list. RelationMemberList& members() { return osmium::detail::subitem_of_type(begin(), end()); } + /// Get a const reference to the member list. const RelationMemberList& members() const { return osmium::detail::subitem_of_type(cbegin(), cend()); } + /// Get a const reference to the member list. + const RelationMemberList& cmembers() const { + return osmium::detail::subitem_of_type(cbegin(), cend()); + } + }; // class Relation static_assert(sizeof(Relation) % osmium::memory::align_bytes == 0, "Class osmium::Relation has wrong size to be aligned properly!"); diff --git a/third_party/libosmium/include/osmium/osm/types.hpp b/third_party/libosmium/include/osmium/osm/types.hpp index 6a801c180..8c7e48a12 100644 --- a/third_party/libosmium/include/osmium/osm/types.hpp +++ b/third_party/libosmium/include/osmium/osm/types.hpp @@ -58,6 +58,13 @@ namespace osmium { */ using string_size_type = uint16_t; + /** + * This is the size type for the text in a changeset discussion comment. + * There is no official limit for this. 16 bit is not enough for existing + * OSM changesets, hopefully 32 bit is. + */ + using changeset_comment_size_type = uint32_t; + // maximum of 256 characters of max 4 bytes each (in UTF-8 encoding) constexpr const int max_osm_string_length = 256 * 4; 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 d2a1421bc..43f6203a5 100644 --- a/third_party/libosmium/include/osmium/osm/types_from_string.hpp +++ b/third_party/libosmium/include/osmium/osm/types_from_string.hpp @@ -61,12 +61,14 @@ namespace osmium { assert(input); if (*input != '\0' && !std::isspace(*input)) { char* end; - auto id = std::strtoll(input, &end, 10); - if (id != std::numeric_limits::min() && id != std::numeric_limits::max() && *end == '\0') { + const auto id = std::strtoll(input, &end, 10); + if (id != std::numeric_limits::min() && + id != std::numeric_limits::max() && + *end == '\0') { return id; } } - throw std::range_error(std::string("illegal id: '") + input + "'"); + throw std::range_error{std::string{"illegal id: '"} + input + "'"}; } /** @@ -95,12 +97,12 @@ namespace osmium { if (std::isdigit(*input)) { return std::make_pair(default_type, string_to_object_id(input)); } - osmium::item_type t = osmium::char_to_item_type(*input); + const osmium::item_type t = osmium::char_to_item_type(*input); if (osmium::osm_entity_bits::from_item_type(t) & types) { return std::make_pair(t, string_to_object_id(input + 1)); } } - throw std::range_error(std::string("not a valid id: '") + input + "'"); + throw std::range_error{std::string{"not a valid id: '"} + input + "'"}; } namespace detail { @@ -108,12 +110,12 @@ namespace osmium { 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); + const auto value = std::strtoul(input, &end, 10); if (value != std::numeric_limits::max() && *end == '\0') { return value; } } - throw std::range_error(std::string("illegal ") + name + ": '" + input + "'"); + throw std::range_error{std::string{"illegal "} + name + ": '" + input + "'"}; } } // namespace detail diff --git a/third_party/libosmium/include/osmium/relations/collector.hpp b/third_party/libosmium/include/osmium/relations/collector.hpp index bede76405..ccfed99b1 100644 --- a/third_party/libosmium/include/osmium/relations/collector.hpp +++ b/third_party/libosmium/include/osmium/relations/collector.hpp @@ -48,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -124,6 +125,7 @@ namespace osmium { */ class HandlerPass2 : public osmium::handler::Handler { + osmium::handler::CheckOrder m_check_order; TCollector& m_collector; public: @@ -134,6 +136,7 @@ namespace osmium { void node(const osmium::Node& node) { if (TNodes) { + m_check_order.node(node); if (! m_collector.find_and_add_object(node)) { m_collector.node_not_in_any_relation(node); } @@ -142,6 +145,7 @@ namespace osmium { void way(const osmium::Way& way) { if (TWays) { + m_check_order.way(way); if (! m_collector.find_and_add_object(way)) { m_collector.way_not_in_any_relation(way); } @@ -150,6 +154,7 @@ namespace osmium { void relation(const osmium::Relation& relation) { if (TRelations) { + m_check_order.relation(relation); if (! m_collector.find_and_add_object(relation)) { m_collector.relation_not_in_any_relation(relation); } @@ -245,7 +250,7 @@ namespace osmium { * this is for instance used to only keep members of type way and * ignore all others. */ - bool keep_member(const osmium::relations::RelationMeta& /*relation_meta*/, const osmium::RelationMember& /*member*/) const { + bool keep_member(const RelationMeta& /*relation_meta*/, const osmium::RelationMember& /*member*/) const { return true; } @@ -293,17 +298,6 @@ namespace osmium { void flush() { } - /** - * This removes all relations that have already been assembled - * from the m_relations vector. - */ - void clean_assembled_relations() { - m_relations.erase( - std::remove_if(m_relations.begin(), m_relations.end(), has_all_members()), - m_relations.end() - ); - } - const osmium::Relation& get_relation(size_t offset) const { assert(m_relations_buffer.committed() > offset); return m_relations_buffer.get(offset); @@ -425,7 +419,7 @@ namespace osmium { return true; } - void clear_member_metas(const osmium::relations::RelationMeta& relation_meta) { + void clear_member_metas(const RelationMeta& relation_meta) { const osmium::Relation& relation = get_relation(relation_meta); for (const auto& member : relation.members()) { if (member.ref() != 0) { @@ -545,9 +539,8 @@ namespace osmium { assert(!range.empty()); if (range.begin()->is_available()) { return std::make_pair(true, range.begin()->buffer_offset()); - } else { - return std::make_pair(false, 0); } + return std::make_pair(false, 0); } template diff --git a/third_party/libosmium/include/osmium/relations/manager_util.hpp b/third_party/libosmium/include/osmium/relations/manager_util.hpp new file mode 100644 index 000000000..1ee8dd482 --- /dev/null +++ b/third_party/libosmium/include/osmium/relations/manager_util.hpp @@ -0,0 +1,197 @@ +#ifndef OSMIUM_RELATIONS_MANAGER_UTIL_HPP +#define OSMIUM_RELATIONS_MANAGER_UTIL_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace relations { + + /** + * This is a handler class used for the second pass of relation + * managers. An object of this class is instantiated as a member + * of the Manager and used to re-direct all calls to the handler + * to the "parent" manager. + * + * @tparam TManager The manager we want to call functions on. + */ + template + class SecondPassHandler : public osmium::handler::Handler { + + TManager& m_manager; + + public: + + explicit SecondPassHandler(TManager& manager) noexcept : + m_manager(manager) { + } + + /** + * Overwrites the function in the handler parent class. + */ + void node(const osmium::Node& node) { + m_manager.handle_node(node); + } + + /** + * Overwrites the function in the handler parent class. + */ + void way(const osmium::Way& way) { + m_manager.handle_way(way); + } + + /** + * Overwrites the function in the handler parent class. + */ + void relation(const osmium::Relation& relation) { + m_manager.handle_relation(relation); + } + + /** + * Overwrites the function in the handler parent class. + * + * Calls the flush_output() function on the manager. + */ + void flush() { + m_manager.flush_output(); + } + + }; // class SecondPassHandler + + /** + * Read relations from file and feed them into all the managers + * specified as parameters. Opens an osmium::io::Reader internally + * with the file parameter. + * + * After the file is read, the prepare_for_lookup() function is called + * on all the managers making them ready for querying the data they + * have stored. + * + * @tparam TManager Any number of relation manager types. + * @param file The file that should be opened with an osmium::io::Reader. + * @param managers Relation managers we want the relations to be sent + * to. + */ + template + void read_relations(const osmium::io::File& file, TManager&& ...managers) { + static_assert(sizeof...(TManager) > 0, "Need at least one manager as parameter."); + osmium::io::Reader reader{file, osmium::osm_entity_bits::relation}; + osmium::apply(reader, std::forward(managers)...); + reader.close(); + (void)std::initializer_list{ + (std::forward(managers).prepare_for_lookup(), 0)... + }; + } + + /** + * Read relations from file and feed them into all the managers + * specified as parameters. Opens an osmium::io::Reader internally + * with the file parameter. + * + * After the file is read, the prepare_for_lookup() function is called + * on all the managers making them ready for querying the data they + * have stored. + * + * @tparam TManager Any number of relation manager types. + * @param progress_bar Reference to osmium::ProgressBar object that + * will be updated while reading the data. + * @param file The file that should be opened with an osmium::io::Reader. + * @param managers Relation managers we want the relations to be sent + * to. + */ + template + void read_relations(osmium::ProgressBar& progress_bar, const osmium::io::File& file, TManager&& ...managers) { + static_assert(sizeof...(TManager) > 0, "Need at least one manager as parameter."); + osmium::io::Reader reader{file, osmium::osm_entity_bits::relation}; + while (auto buffer = reader.read()) { + progress_bar.update(reader.offset()); + osmium::apply(buffer, std::forward(managers)...); + } + reader.close(); + (void)std::initializer_list{ + (std::forward(managers).prepare_for_lookup(), 0)... + }; + progress_bar.file_done(file.size()); + } + + /** + * Struct for memory usage numbers returned by various relations + * managers from the used_memory() function. + */ + struct relations_manager_memory_usage { + std::size_t relations_db; + std::size_t members_db; + std::size_t stash; + }; + + /** + * Prints relations managers memory usage numbers to the specified + * stream. + * + * @tparam TStream Output stream type (like std::cout, std::cerr, or + * osmium::util::VerboseOutput). + * @param stream Reference to stream where the output should go. + * @param mu Memory usage data as returned by the used_memory() + * functions of various relations managers. + */ + template + void print_used_memory(TStream& stream, const relations_manager_memory_usage& mu) { + const auto total = mu.relations_db + mu.members_db + mu.stash; + + stream << " relations: " << std::setw(8) << (mu.relations_db / 1024) << " kB\n" + << " members: " << std::setw(8) << (mu.members_db / 1024) << " kB\n" + << " stash: " << std::setw(8) << (mu.stash / 1024) << " kB\n" + << " total: " << std::setw(8) << (total / 1024) << " kB\n" + << " ======================\n"; + } + + } // namespace relations + +} // namespace osmium + +#endif // OSMIUM_RELATIONS_MANAGER_UTIL_HPP diff --git a/third_party/libosmium/include/osmium/relations/members_database.hpp b/third_party/libosmium/include/osmium/relations/members_database.hpp new file mode 100644 index 000000000..975f00388 --- /dev/null +++ b/third_party/libosmium/include/osmium/relations/members_database.hpp @@ -0,0 +1,406 @@ +#ifndef OSMIUM_RELATIONS_MEMBERS_DATABASE_HPP +#define OSMIUM_RELATIONS_MEMBERS_DATABASE_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace relations { + + /** + * This is the parent class for the MembersDatabase class. All the + * functionality which doesn't depend on the template parameter used + * in derived databases is contained in this class. + * + * Usually you want to use the MembersDatabase class only. + */ + class MembersDatabaseCommon { + + struct element { + + /** + * Special value used for member_num to mark the element as + * removed. + */ + static const size_t removed_value = std::numeric_limits::max(); + + /** + * Object ID of this relation member. Can be a node, way, + * or relation ID. It depends on the database in which this + * object is stored which kind of object is referenced here. + */ + osmium::object_id_type member_id; + + /** + * Position of this member in the parent relation. + */ + std::size_t member_num; + + /** + * Position of the parent relation in the relations database. + */ + std::size_t relation_pos; + + /** + * Handle to the stash where the object is stored. + * + * The default value is the invalid one signifying that the + * object hasn't been found yet. + */ + osmium::ItemStash::handle_type object_handle; + + explicit element(std::size_t rel_pos, osmium::object_id_type memb_id, std::size_t memb_num) noexcept : + member_id(memb_id), + member_num(memb_num), + relation_pos(rel_pos) { + } + + /** + * This constructor is used to create dummy elements that + * can be compared to the elements in a vector using the + * equal_range algorithm. + */ + explicit element(osmium::object_id_type m_id) noexcept : + member_id(m_id), + member_num(0), + relation_pos(0) { + } + + bool is_removed() const noexcept { + return member_num == removed_value; + } + + void remove() noexcept { + member_num = removed_value; + } + + bool operator<(const element& other) const noexcept { + return std::tie(member_id, member_num, relation_pos) < + std::tie(other.member_id, other.member_num, other.relation_pos); + } + + }; // struct element + + // comparison function only comparing member_id. + struct compare_member_id { + bool operator()(const element& a, const element& b) const noexcept { + return a.member_id < b.member_id; + } + }; + + std::vector m_elements; + + protected: + + osmium::ItemStash& m_stash; + osmium::relations::RelationsDatabase& m_relations_db; + +#ifndef NDEBUG + // This is used only in debug builds to make sure the + // prepare_for_lookup() function is called at the right place. + bool m_init_phase = true; +#endif + + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + iterator_range find(osmium::object_id_type id) { + return make_range(std::equal_range(m_elements.begin(), m_elements.end(), element{id}, compare_member_id{})); + } + + iterator_range find(osmium::object_id_type id) const { + return make_range(std::equal_range(m_elements.cbegin(), m_elements.cend(), element{id}, compare_member_id{})); + } + + static typename iterator_range::iterator::difference_type count_not_removed(const iterator_range& range) noexcept { + return std::count_if(range.begin(), range.end(), [](const element& elem) { + return !elem.is_removed(); + }); + } + + void add_object(const osmium::OSMObject& object, iterator_range& range) { + const auto handle = m_stash.add_item(object); + for (auto& elem : range) { + elem.object_handle = handle; + } + } + + MembersDatabaseCommon(osmium::ItemStash& stash, osmium::relations::RelationsDatabase& relations_db) : + m_elements(), + m_stash(stash), + m_relations_db(relations_db) { + } + + public: + + /** + * Return an estimate of the number of bytes currently needed + * for the MembersDatabase. This does NOT include the memory used + * in the stash. Used for debugging. + */ + std::size_t used_memory() const noexcept { + return sizeof(element) * m_elements.capacity() + + sizeof(MembersDatabaseCommon); + } + + /** + * The number of members tracked in the database. Includes + * members tracked, but not found yet, members found and members + * marked as removed. + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { + return m_elements.size(); + } + + /** + * Result from the count() function. + */ + struct counts { + /// The number of members tracked and not found yet. + std::size_t tracked = 0; + /// The number of members tracked and found already. + std::size_t available = 0; + /// The number of members that were tracked, found and then removed because of a completed relation. + std::size_t removed = 0; + }; + + /** + * Counts the number of members in different states. Usually only + * used for testing and debugging. + * + * Complexity: Linear in the number of members tracked. + */ + counts count() const noexcept { + counts c; + + for (const auto& elem : m_elements) { + if (elem.is_removed()) { + ++c.removed; + } else if (elem.object_handle.valid()) { + ++c.available; + } else { + ++c.tracked; + } + } + + return c; + } + + /** + * Tell the database that you are interested in an object with + * the specified id and that it is a member of the given relation + * (as specified through the relation handle). + * + * @param rel_handle Relation this object is a member of. + * @param member_id Id of an object of type TObject. + * @param member_num This is the nth member in the relation. + */ + void track(RelationHandle& rel_handle, osmium::object_id_type member_id, std::size_t member_num) { + assert(m_init_phase && "Can not call MembersDatabase::track() after MembersDatabase::prepare_for_lookup()."); + assert(rel_handle.relation_database() == &m_relations_db); + m_elements.emplace_back(rel_handle.pos(), member_id, member_num); + rel_handle.increment_members(); + } + + /** + * Prepare the database for lookup. Call this function after + * calling track() for all objects needed and before adding + * the first object with add() or querying the first object + * with get(). You can only call this function once. + */ + void prepare_for_lookup() { + assert(m_init_phase && "Can not call MembersDatabase::prepare_for_lookup() twice."); + std::sort(m_elements.begin(), m_elements.end()); +#ifndef NDEBUG + m_init_phase = false; +#endif + } + + /** + * Remove the entry with the specified member_id and relation_id + * from the database. If the entry doesn't exist, nothing happens. + */ + void remove(osmium::object_id_type member_id, osmium::object_id_type relation_id) { + const auto range = find(member_id); + + if (range.empty()) { + return; + } + + // If this is the last time this object was needed, remove it + // from the stash. + if (count_not_removed(range) == 1) { + m_stash.remove_item(range.begin()->object_handle); + } + + for (auto& elem : range) { + if (!elem.is_removed() && relation_id == m_relations_db[elem.relation_pos]->id()) { + elem.remove(); + break; + } + } + } + + /** + * Find the object with the specified id in the database and + * return a pointer to it. Returns nullptr if there is no object + * with that id in the database. + * + * Complexity: Logarithmic in the number of members tracked (as + * returned by size()). + */ + const osmium::OSMObject* get_object(osmium::object_id_type id) const { + assert(!m_init_phase && "Call MembersDatabase::prepare_for_lookup() before calling get_object()."); + const auto range = find(id); + if (range.empty()) { + return nullptr; + } + const auto handle = range.begin()->object_handle; + if (handle.valid()) { + return &m_stash.get(handle); + } + return nullptr; + } + + }; // class MembersDatabaseCommon + + /** + * A MembersDatabase is used together with a RelationsDatabase to + * bring a relation and their members together. It tracks all members + * of a specific type needed to complete a relation. + * + * More documentation is in the MembersDatabaseCommon parent class + * which contains all the pieces that aren't dependent on the + * template parameter. + * + * @tparam TObject The object type stores in the members database. + * Can be osmium::Node, Way, or Relation. + */ + template + class MembersDatabase : public MembersDatabaseCommon { + + static_assert(std::is_base_of::value, "TObject must be osmium::Node, Way, or Relation."); + + public: + + /** + * Construct a MembersDatabase. + * + * @param stash Reference to an ItemStash object. All member objects + * will be stored in this stash. It must be available + * until the MembersDatabase is destroyed. + * @param relation_db The RelationsDatabase where relations are + * stored. Usually it will use the same ItemStash + * as the MembersDatabase. + */ + MembersDatabase(osmium::ItemStash& stash, osmium::relations::RelationsDatabase& relation_db) : + MembersDatabaseCommon(stash, relation_db) { + } + + /** + * Add the specified object to the database. + * + * @param object Object to add. + * @param func If the object is the last member to complete a + * relation, this function is called with the relation + * as a parameter. + * @returns true if the object was actually added, false if no + * relation needed this object. + */ + template + bool add(const TObject& object, TFunc&& func) { + assert(!m_init_phase && "Call MembersDatabase::prepare_for_lookup() before calling add()."); + auto range = find(object.id()); + + if (range.empty()) { + // No relation needs this object. + return false; + } + + // At least one relation needs this object. Store it and + // "tell" all relations. + add_object(object, range); + + for (auto& elem : range) { + assert(!elem.is_removed()); + assert(elem.member_id == object.id()); + + auto rel_handle = m_relations_db[elem.relation_pos]; + assert(elem.member_num < rel_handle->members().size()); + rel_handle.decrement_members(); + + if (rel_handle.has_all_members()) { + func(rel_handle); + } + } + + return true; + } + + /** + * Find the object with the specified id in the database and + * return a pointer to it. Returns nullptr if there is no object + * with that id in the database. + * + * Complexity: Logarithmic in the number of members tracked (as + * returned by size()). + */ + const TObject* get(osmium::object_id_type id) const { + assert(!m_init_phase && "Call MembersDatabase::prepare_for_lookup() before calling get()."); + return static_cast(get_object(id)); + } + + }; // class MembersDatabase + + } // namespace relations + +} // namespace osmium + +#endif // OSMIUM_RELATIONS_MEMBERS_DATABASE_HPP diff --git a/third_party/libosmium/include/osmium/relations/relations_database.hpp b/third_party/libosmium/include/osmium/relations/relations_database.hpp new file mode 100644 index 000000000..80dd10242 --- /dev/null +++ b/third_party/libosmium/include/osmium/relations/relations_database.hpp @@ -0,0 +1,335 @@ +#ifndef OSMIUM_RELATIONS_RELATIONS_DATABASE_HPP +#define OSMIUM_RELATIONS_RELATIONS_DATABASE_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace relations { + + class RelationHandle; + + /** + * The RelationsDatabase is used for bringing relations and their + * members together. It stores the relations in memory and keeps + * track of how many members are needed to "complete" the relation. + * It is intended to work together with the MembersDatabase template + * class and usually used by relations manager classes. + * + * To access relations stored in the database a RelationHandle is + * used. It is returned from the add() function. The handle is used + * for all operations on the database contents, such as accessing + * the stored relation, incrementing the member count or removing a + * relation from the database. + * + * From the handle a "position" can be accessed, which, together with + * the database object, can be turned into a handle again. The position + * alone is smaller than the handle, so it can be stored elsewhere more + * efficiently. Specifically this is used in the MembersDatabase. + * + * @code + * osmium::ItemStash stash; + * osmium::relations::RelationsDatabase db{stash}; + * auto handle = db.add(relation); + * auto pos = handle.pos(); + * auto second_handle = db[pos]; + * @endcode + * + * Now the `handle` and `second_handle` refer to the same relation. + * + * See the RelationHandle for information about what you can do with + * it. + */ + class RelationsDatabase { + + friend class RelationHandle; + + struct element { + + /// A handle to the relation in the ItemStash. + osmium::ItemStash::handle_type handle; + + /** + * The number of members still needed before the relation is + * complete. This will be set to the number of members we are + * interested in (which can be all members of a relation or + * a subset of them) and then count down for every member we + * find. When it is 0, the relation is complete. + */ + std::size_t members; + + }; // struct element + + osmium::ItemStash& m_stash; + std::vector m_elements; + + osmium::Relation& get_relation(std::size_t pos) { + assert(pos < m_elements.size()); + return m_stash.get(m_elements[pos].handle); + } + + /** + * Access the number of members of the entry at the specified + * position. This returns a reference so it can be changed. + */ + std::size_t& members(std::size_t pos) noexcept { + return m_elements[pos].members; + } + + void remove(std::size_t pos) { + auto& elem = m_elements[pos]; + m_stash.remove_item(elem.handle); + elem = element{osmium::ItemStash::handle_type{}, 0}; + } + + public: + + /** + * Construct a RelationsDatabase. + * + * @param stash Reference to an ItemStash object. All relations + * will be stored in this stash. It must be available + * until the RelationsDatabase is destroyed. + */ + explicit RelationsDatabase(osmium::ItemStash& stash) : + m_stash(stash) { + } + + /** + * Return an estimate of the number of bytes currently needed for + * the RelationsDatabase. This does NOT include the memory used + * in the stash. Used for debugging. + * + * Complexity: Constant. + */ + std::size_t used_memory() const noexcept { + return sizeof(element) * m_elements.capacity() + + sizeof(RelationsDatabase); + } + + /** + * The number of relations stored in the database. Includes + * relations marked as removed. + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { + return m_elements.size(); + } + + /** + * Insert a relation into the database. The relation is copied + * into the stash. + * + * Complexity: Amortized constant. + * + * @param relation The relation to be copied into the database. + * @returns A handle to the relation. + */ + RelationHandle add(const osmium::Relation& relation); + + /** + * Return a handle to the relation at the specified position in + * the database. + * + * Complexity: Constant. + */ + RelationHandle operator[](std::size_t pos) noexcept; + + /** + * Return the number of non-removed relations in the database. + * + * Complexity: Linear in the number of relations (as returned + * by size()). + */ + std::size_t count_relations() const noexcept { + return std::count_if(m_elements.cbegin(), m_elements.cend(), [&](const element& elem) { + return elem.handle.valid(); + }); + } + + /** + * Iterate over all (not-removed) relations in the database. + * + * @tparam TFunc Function with type void(const RelationHandle&). + * @param func Callback function which will be called for every + * not-removed relation with a RelationHandle. + */ + template + void for_each_relation(TFunc&& func); + + }; // RelationsDatabase + + /** + * A RelationHandle is used to access elements in a RelationsDatabase. + * + * RelationHandles can not be created by user code, they are only + * given out by a RelationsDatabase object. + */ + class RelationHandle { + + friend class RelationsDatabase; + + RelationsDatabase* m_relation_database; + std::size_t m_pos; + + RelationHandle(RelationsDatabase* relation_database, std::size_t pos) : + m_relation_database(relation_database), + m_pos(pos) { + } + + public: + + /** + * The RelationsDatabase this handle refers to. + */ + RelationsDatabase* relation_database() const noexcept { + return m_relation_database; + } + + /** + * The position of the element in the RelationsDatabase. Use the + * RelationsDatabase::operator[] to get the handle back from this + * position: + * @code + * auto pos = handle.pos(); + * auto second_handle = relation_db[pos]; + * @endcode + */ + std::size_t pos() const noexcept { + return m_pos; + } + + /** + * Access the relation stored in the database. + */ + Relation& operator*() { + return m_relation_database->get_relation(m_pos); + } + + /** + * Access the relation stored in the database. + */ + const Relation& operator*() const { + return m_relation_database->get_relation(m_pos); + } + + /** + * Call a function on the relation stored in the database. + */ + Relation* operator->() { + return &m_relation_database->get_relation(m_pos); + } + + /** + * Call a function on the relation stored in the database. + */ + const Relation* operator->() const { + return &m_relation_database->get_relation(m_pos); + } + + /** + * Remove the relation referred to by this handle from the database. + * All handles referring to this database element become invalid. + */ + void remove() { + m_relation_database->remove(pos()); + } + + /** + * Set the number of relation members that we want to track. + */ + void set_members(std::size_t value) noexcept { + m_relation_database->members(m_pos) = value; + } + + /** + * Increment the number of relation members that we want to track. + */ + void increment_members() noexcept { + ++(m_relation_database->members(m_pos)); + } + + /** + * Decrement the number of relation members that we want to track. + * + * @pre @code has_all_members() == false @endcode + */ + void decrement_members() noexcept { + assert(m_relation_database->members(m_pos) > 0); + --(m_relation_database->members(m_pos)); + } + + /** + * Do we have all members? This is true if the number of tracked + * members is zero. + */ + bool has_all_members() const noexcept { + return m_relation_database->members(m_pos) == 0; + } + + }; // class RelationHandle + + inline RelationHandle RelationsDatabase::operator[](std::size_t pos) noexcept { + assert(pos < m_elements.size()); + return {this, pos}; + } + + inline RelationHandle RelationsDatabase::add(const osmium::Relation& relation) { + m_elements.push_back(element{m_stash.add_item(relation), 0}); + return {this, m_elements.size() - 1}; + } + + template + void RelationsDatabase::for_each_relation(TFunc&& func) { + for (std::size_t pos = 0; pos < m_elements.size(); ++pos) { + if (m_elements[pos].handle.valid()) { + std::forward(func)(RelationHandle{this, pos}); + } + } + } + + } // namespace relations + +} // namespace osmium + +#endif // OSMIUM_RELATIONS_RELATIONS_DATABASE_HPP diff --git a/third_party/libosmium/include/osmium/relations/relations_manager.hpp b/third_party/libosmium/include/osmium/relations/relations_manager.hpp new file mode 100644 index 000000000..1831cb6c9 --- /dev/null +++ b/third_party/libosmium/include/osmium/relations/relations_manager.hpp @@ -0,0 +1,567 @@ +#ifndef OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP +#define OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + namespace relations { + + /** + * This is a base class of the RelationsManager class template. It + * contains databases for the relations and the members that we need + * to keep track of and handles the ouput buffer. Unlike the + * RelationsManager class template this is a plain class. + * + * Usually it is better to use the RelationsManager class template + * as a basis for your code, but you can also use this class if you + * have special needs. + */ + class RelationsManagerBase : public osmium::handler::Handler { + + // All relations and members we are interested in will be kept + // in here. + osmium::ItemStash m_stash; + + /// Database of all relations we are interested in. + relations::RelationsDatabase m_relations_db; + + /// Databases of all members we are interested in. + relations::MembersDatabase m_member_nodes_db; + relations::MembersDatabase m_member_ways_db; + relations::MembersDatabase m_member_relations_db; + + /// Output buffer. + osmium::memory::CallbackBuffer m_output; + + public: + + RelationsManagerBase() : + m_stash(), + m_relations_db(m_stash), + m_member_nodes_db(m_stash, m_relations_db), + m_member_ways_db(m_stash, m_relations_db), + m_member_relations_db(m_stash, m_relations_db), + m_output() { + } + + /// Access the internal RelationsDatabase. + osmium::relations::RelationsDatabase& relations_database() noexcept { + return m_relations_db; + } + + /// Access the internal database containing member nodes. + osmium::relations::MembersDatabase& member_nodes_database() noexcept { + return m_member_nodes_db; + } + + /// Access the internal database containing member nodes. + const osmium::relations::MembersDatabase& member_nodes_database() const noexcept { + return m_member_nodes_db; + } + + /// Access the internal database containing member ways. + osmium::relations::MembersDatabase& member_ways_database() noexcept { + return m_member_ways_db; + } + + /// Access the internal database containing member ways. + const osmium::relations::MembersDatabase& member_ways_database() const noexcept { + return m_member_ways_db; + } + + /// Access the internal database containing member relations. + osmium::relations::MembersDatabase& member_relations_database() noexcept { + return m_member_relations_db; + } + + /// Access the internal database containing member relations. + const osmium::relations::MembersDatabase& member_relations_database() const noexcept { + return m_member_relations_db; + } + + /** + * Access the internal database containing members of the + * specified type (non-const version of this function). + * + * @param type osmium::item_type::node, way, or relation. + */ + relations::MembersDatabaseCommon& member_database(osmium::item_type type) { + switch (type) { + case osmium::item_type::node: + return m_member_nodes_db; + case osmium::item_type::way: + return m_member_ways_db; + case osmium::item_type::relation: + return m_member_relations_db; + default: + break; + } + throw std::logic_error{"Should not be here."}; + } + + /** + * Access the internal database containing members of the + * specified type (const version of this function). + * + * @param type osmium::item_type::node, way, or relation. + */ + const relations::MembersDatabaseCommon& member_database(osmium::item_type type) const { + switch (type) { + case osmium::item_type::node: + return m_member_nodes_db; + case osmium::item_type::way: + return m_member_ways_db; + case osmium::item_type::relation: + return m_member_relations_db; + default: + break; + } + throw std::logic_error{"Should not be here."}; + } + + /** + * Get member object from relation member. + * + * @returns A pointer to the member object if it is available. + * Returns nullptr otherwise. + */ + const osmium::OSMObject* get_member_object(const osmium::RelationMember& member) const noexcept { + if (member.ref() == 0) { + return nullptr; + } + return member_database(member.type()).get_object(member.ref()); + } + + /** + * Get node with specified ID from members database. + * + * @param id The node ID we are looking for. + * @returns A pointer to the member node if it is available. + * Returns nullptr otherwise. + */ + const osmium::Node* get_member_node(osmium::object_id_type id) const noexcept { + if (id == 0) { + return nullptr; + } + return member_nodes_database().get(id); + } + + /** + * Get way with specified ID from members database. + * + * @param id The way ID we are looking for. + * @returns A pointer to the member way if it is available. + * Returns nullptr otherwise. + */ + const osmium::Way* get_member_way(osmium::object_id_type id) const noexcept { + if (id == 0) { + return nullptr; + } + return member_ways_database().get(id); + } + + /** + * Get relation with specified ID from members database. + * + * @param id The relation ID we are looking for. + * @returns A pointer to the member relation if it is available. + * Returns nullptr otherwise. + */ + const osmium::Relation* get_member_relation(osmium::object_id_type id) const noexcept { + if (id == 0) { + return nullptr; + } + return member_relations_database().get(id); + } + + /** + * Sort the members databases to prepare them for reading. Usually + * this is called between the first and second pass reading through + * an OSM data file. + */ + void prepare_for_lookup() { + m_member_nodes_db.prepare_for_lookup(); + m_member_ways_db.prepare_for_lookup(); + m_member_relations_db.prepare_for_lookup(); + } + + /** + * Return the memory used by different components of the manager. + */ + relations_manager_memory_usage used_memory() const noexcept { + return { + m_relations_db.used_memory(), + m_member_nodes_db.used_memory() + + m_member_ways_db.used_memory() + + m_member_relations_db.used_memory(), + m_stash.used_memory() + }; + } + + /// Access the output buffer. + osmium::memory::Buffer& buffer() noexcept { + return m_output.buffer(); + } + + /// Set the callback called when the output buffer is full. + void set_callback(const std::function& callback) { + m_output.set_callback(callback); + } + + /// Flush the output buffer. + void flush_output() { + m_output.flush(); + } + + /// Flush the output buffer if it is full. + void possibly_flush() { + m_output.possibly_flush(); + } + + /// Return the contents of the output buffer. + osmium::memory::Buffer read() { + return m_output.read(); + } + + }; // class RelationsManagerBase + + /** + * This is a base class for RelationManager classes. It keeps track of + * all interesting relations and all interesting members of those + * relations. When all members are available it calls code to handle + * the completed relation. + * + * This class is intended as a base class for classes that handle + * specific types of relations. Derive from this class, implement + * the complete_relation() function and overwrite certain other + * functions as needed. + * + * @tparam TManager The derived class (Uses CRTP). + * @tparam TNodes Are we interested in member nodes? + * @tparam TWays Are we interested in member ways? + * @tparam TRelations Are we interested in member relations? + * @tparam TCheckOrder Should the order of the input data be checked? + * + * @pre The Ids of all objects must be unique in the input data. + */ + template + class RelationsManager : public RelationsManagerBase { + + using check_order_handler = typename std::conditional::type; + + check_order_handler m_check_order_handler; + + SecondPassHandler m_handler_pass2; + + static bool wanted_type(osmium::item_type type) noexcept { + return (TNodes && type == osmium::item_type::node) || + (TWays && type == osmium::item_type::way) || + (TRelations && type == osmium::item_type::relation); + } + + /** + * This method is called from the first pass handler for every + * relation in the input, to check whether it should be kept. + * + * Overwrite this method in a derived class to only add relations + * you are interested in, for instance depending on the type tag. + * Storing relations takes a lot of memory, so it makes sense to + * filter this as much as possible. + */ + bool new_relation(const osmium::Relation& /*relation*/) const noexcept { + return true; + } + + /** + * This method is called for every member of every relation that + * will be kept. It should decide if the member is interesting or + * not and return true or false to signal that. Only interesting + * members are later added to the relation. + * + * Overwrite this method in a derived class. In the + * MultipolygonManager class this is used for instance to only + * keep members of type way and ignore all others. + */ + bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& /*member*/, std::size_t /*n*/) const noexcept { + return true; + } + + /** + * This method is called for each complete relation, ie when + * all members you have expressed interest in are available. + * + * You have to overwrite this in a derived class. + */ + void complete_relation(const osmium::Relation& /*relation*/) const noexcept { + } + + /** + * This method is called for all nodes during the second pass + * before the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void before_node(const osmium::Node& /*node*/) const noexcept { + } + + /** + * This method is called for all nodes that are not a member of + * any relation. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void node_not_in_any_relation(const osmium::Node& /*node*/) const noexcept { + } + + /** + * This method is called for all nodes during the second pass + * after the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void after_node(const osmium::Node& /*node*/) const noexcept { + } + + /** + * This method is called for all ways during the second pass + * before the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void before_way(const osmium::Way& /*way*/) const noexcept { + } + + /** + * This method is called for all ways that are not a member of + * any relation. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void way_not_in_any_relation(const osmium::Way& /*way*/) const noexcept { + } + + /** + * This method is called for all ways during the second pass + * after the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void after_way(const osmium::Way& /*way*/) const noexcept { + } + + /** + * This method is called for all relations during the second pass + * before the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void before_relation(const osmium::Relation& /*relation*/) const noexcept { + } + + /** + * This method is called for all relations that are not a member of + * any relation. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void relation_not_in_any_relation(const osmium::Relation& /*relation*/) const noexcept { + } + + /** + * This method is called for all relations during the second pass + * after the relation member handling. + * + * Overwrite this method in a derived class if you are interested + * in this. + */ + void after_relation(const osmium::Relation& /*relation*/) const noexcept { + } + + TManager& derived() noexcept { + return *static_cast(this); + } + + void handle_complete_relation(RelationHandle& rel_handle) { + derived().complete_relation(*rel_handle); + possibly_flush(); + + for (const auto& member : rel_handle->members()) { + if (member.ref() != 0) { + member_database(member.type()).remove(member.ref(), rel_handle->id()); + } + } + + rel_handle.remove(); + } + + public: + + RelationsManager() : + RelationsManagerBase(), + m_check_order_handler(), + m_handler_pass2(*this) { + } + + /** + * Return reference to second pass handler. + */ + SecondPassHandler& handler(const std::function& callback = nullptr) { + set_callback(callback); + return m_handler_pass2; + } + + /** + * Add the specified relation to the list of relations we want to + * build. This calls the new_relation() and new_member() + * functions to actually decide what to keep. + * + * This member function is named relation() so the manager can + * be used as a handler for the first pass through a data file. + * + * @param relation Relation we might want to build. + */ + void relation(const osmium::Relation& relation) { + if (derived().new_relation(relation)) { + auto rel_handle = relations_database().add(relation); + + std::size_t n = 0; + for (auto& member : rel_handle->members()) { + if (wanted_type(member.type()) && + derived().new_member(relation, member, n)) { + member_database(member.type()).track(rel_handle, member.ref(), n); + } else { + member.set_ref(0); // set member id to zero to indicate we are not interested + } + ++n; + } + } + } + + void handle_node(const osmium::Node& node) { + if (TNodes) { + m_check_order_handler.node(node); + derived().before_node(node); + const bool added = member_nodes_database().add(node, [this](RelationHandle& rel_handle) { + handle_complete_relation(rel_handle); + }); + if (! added) { + derived().node_not_in_any_relation(node); + } + derived().after_node(node); + possibly_flush(); + } + } + + void handle_way(const osmium::Way& way) { + if (TWays) { + m_check_order_handler.way(way); + derived().before_way(way); + const bool added = member_ways_database().add(way, [this](RelationHandle& rel_handle) { + handle_complete_relation(rel_handle); + }); + if (! added) { + derived().way_not_in_any_relation(way); + } + derived().after_way(way); + possibly_flush(); + } + } + + void handle_relation(const osmium::Relation& relation) { + if (TRelations) { + m_check_order_handler.relation(relation); + derived().before_relation(relation); + const bool added = member_relations_database().add(relation, [this](RelationHandle& rel_handle) { + handle_complete_relation(rel_handle); + }); + if (! added) { + derived().relation_not_in_any_relation(relation); + } + derived().after_relation(relation); + possibly_flush(); + } + } + + /** + * Call this function it will call your function back for every + * incomplete relation, that is all relations that have missing + * members in the input data. Usually you call this only after + * your second pass through the data if you are interested in + * any relations that have all or some of their members missing + * in the input data. + */ + template + void for_each_incomplete_relation(TFunc&& func) { + relations_database().for_each_relation(std::forward(func)); + } + + }; // class RelationsManager + + } // namespace relations + +} // namespace osmium + +#endif // OSMIUM_RELATIONS_RELATIONS_MANAGER_HPP diff --git a/third_party/libosmium/include/osmium/storage/item_stash.hpp b/third_party/libosmium/include/osmium/storage/item_stash.hpp new file mode 100644 index 000000000..b49d88bcb --- /dev/null +++ b/third_party/libosmium/include/osmium/storage/item_stash.hpp @@ -0,0 +1,342 @@ +#ifndef OSMIUM_ITEM_STASH_HPP +#define OSMIUM_ITEM_STASH_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +#ifdef OSMIUM_ITEM_STORAGE_GC_DEBUG +# include +# include +#endif + +#include +#include + +namespace osmium { + + /** + * Class for storing OSM data in memory. Any osmium::memory::Item can be + * added to the stash and it will be copied into its internal Buffer. To + * access the item again, an opaque handle is used. + */ + class ItemStash { + + public: + + /** + * This is the type of the handle returned by the add_item() call. + * It is used to access the item again with get_item() or get<>() + * or erase it with remove_item(). + * + * There is one special handle, the invalid handle. It can be created + * by calling the default constructor. You can use it for instance to + * mark removed objects in your data structures. Valid handles can + * only be constructed by the ItemStash class. + */ + class handle_type { + + friend class ItemStash; + + std::size_t value; + + explicit handle_type(std::size_t new_value) noexcept : + value(new_value) { + assert(new_value > 0); + } + + public: + + /// The defalt constructor creates an invalid handle. + handle_type() noexcept : + value(0) { + } + + /// Is this a valid handle? + bool valid() const noexcept { + return value != 0; + } + + /** + * Print the handle for debugging purposes. An invalid handle + * will be printed as the single letter '-'. A valid handle will + * be printed as a unique (for an ItemStash object) number. + */ + template + friend inline std::basic_ostream& operator<<(std::basic_ostream& out, const ItemStash::handle_type& handle) { + if (handle.valid()) { + out << handle.value; + } else { + out << '-'; + } + return out; + } + + }; // class handle_type + + private: + + static constexpr const std::size_t initial_buffer_size = 1024 * 1024; + static constexpr const std::size_t removed_item_offset = std::numeric_limits::max(); + + osmium::memory::Buffer m_buffer; + std::vector m_index; + std::size_t m_count_items = 0; + std::size_t m_count_removed = 0; +#ifdef OSMIUM_ITEM_STORAGE_GC_DEBUG + int64_t m_gc_time = 0; +#endif + + class cleanup_helper { + + std::vector& m_index; + std::size_t m_pos = 0; + + public: + + explicit cleanup_helper(std::vector& index) : + m_index(index) { + } + + void moving_in_buffer(std::size_t old_offset, std::size_t new_offset) { + while (m_index[m_pos] != old_offset) { + ++m_pos; + assert(m_pos < m_index.size()); + } + m_index[m_pos] = new_offset; + ++m_pos; + } + + }; // cleanup_helper + + std::size_t& get_item_offset_ref(handle_type handle) noexcept { + assert(handle.valid() && "handle must be valid"); + assert(handle.value <= m_index.size()); + auto& offset = m_index[handle.value - 1]; + assert(offset != removed_item_offset); + assert(offset < m_buffer.committed()); + return offset; + } + + std::size_t get_item_offset(handle_type handle) const noexcept { + assert(handle.valid() && "handle must be valid"); + assert(handle.value <= m_index.size()); + const auto& offset = m_index[handle.value - 1]; + assert(offset != removed_item_offset); + assert(offset < m_buffer.committed()); + return offset; + } + + // This function decides whether it makes sense to garbage collect the + // database. The values here are the result of some experimentation + // with real data. We need to balance the memory use with the time + // spent on garbage collecting. We don't need to garbage collect if + // there is enough space in the buffer anyway (*4). On the other hand, + // if there aren't enough removed objects we would just call the + // garbage collection again and again, then it is better to let the + // buffer grow (*3). The checks (*1) and (*2) make sure there is + // minimum and maximum for the number of removed objects. + bool should_gc() const noexcept { + if (m_count_removed < 10 * 1000) { // *1 + return false; + } + if (m_count_removed > 5 * 1000 * 1000) { // *2 + return true; + } + if (m_count_removed * 5 < m_count_items) { // *3 + return false; + } + return m_buffer.capacity() - m_buffer.committed() < 10 * 1024; // *4 + } + + public: + + ItemStash() : + m_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes) { + } + + /** + * Return an estimate of the number of bytes currently used by this + * ItemStash instance. + * + * Complexity: Constant. + */ + std::size_t used_memory() const noexcept { + return sizeof(ItemStash) + + m_buffer.capacity() + + m_index.capacity() * sizeof(std::size_t); + } + + /** + * The number of items currently in the stash. This is the number + * added minus the number removed. + * + * Complexity: Constant. + */ + std::size_t size() const noexcept { + return m_count_items; + } + + /** + * The number of removed items currently still taking up memory in + * the stash. You can call garbage_collect() to remove them. + * + * Complexity: Constant. + */ + std::size_t count_removed() const noexcept { + return m_count_removed; + } + + /** + * Clear all items from the stash. This will not necessarily release + * any memory. All handles are invalidated. + */ + void clear() { + m_buffer.clear(); + m_index.clear(); + m_count_items = 0; + m_count_removed = 0; + } + + /** + * Add an item to the stash. This will invalidate any pointers and + * references into the stash, but handles are still valid. + * + * Complexity: Amortized constant. + */ + handle_type add_item(const osmium::memory::Item& item) { + if (should_gc()) { + garbage_collect(); + } + ++m_count_items; + const auto offset = m_buffer.committed(); + m_buffer.add_item(item); + m_buffer.commit(); + m_index.push_back(offset); + return handle_type{m_index.size()}; + } + + /** + * Get a reference to an item in the stash. Note that this reference + * will be invalidated by any add_item() or clear() calls. + * + * Complexity: Constant. + * + * @param handle A handle returned by add_item(). + * + * @pre Handle must be a valid handle and referring to a non-removed + * item. + */ + osmium::memory::Item& get_item(handle_type handle) const { + return m_buffer.get(get_item_offset(handle)); + } + + /** + * Get a reference to an item in the stash. Note that this reference + * will be invalidated by any add_item() or clear() calls. + * + * Complexity: Constant. + * + * @param handle A handle returned by add_item(). + * @tparam T Type you want to the data to be interpreted as. You must + * be sure that the item has the specified type, this will + * not be checked! + * @returns Reference of given type pointing to the data in the + * stash. + * @pre Handle must be a valid handle and referring to a non-removed + * item. + */ + template + T& get(handle_type handle) const { + return static_cast(get_item(handle)); + } + + /** + * Garbage collect the memory used by the ItemStash. This will free up + * memory for adding new items. No memory is actually returned to the + * OS. Usually you do not need to call this, because add_item() will + * call it for you as necessary. + * + * Complexity: Linear in size() + count_removed(). + */ + void garbage_collect() { +#ifdef OSMIUM_ITEM_STORAGE_GC_DEBUG + std::cerr << "GC items=" << m_count_items << " removed=" << m_count_removed << " buffer.committed=" << m_buffer.committed() << " buffer.capacity=" << m_buffer.capacity() << "\n"; + using clock = std::chrono::high_resolution_clock; + std::chrono::time_point start = clock::now(); +#endif + + m_count_removed = 0; + cleanup_helper helper{m_index}; + m_buffer.purge_removed(&helper); + +#ifdef OSMIUM_ITEM_STORAGE_GC_DEBUG + std::chrono::time_point stop = clock::now(); + const int64_t time = std::chrono::duration_cast(stop - start).count(); + m_gc_time += time; + std::cerr << " time=" << time + << "us total=" << m_gc_time << "us\n"; +#endif + } + + /** + * Remove an item from the stash. The item will be marked as removed + * and the handle will be invalidated. No memory will actually be + * freed. + * + * Complexity: Constant. + * + * @param handle A handle returned by add_item(). + * + * @pre Handle must be a valid handle and referring to a non-removed + * item. + */ + void remove_item(handle_type handle) { + auto& offset = get_item_offset_ref(handle); + auto& item = m_buffer.get(offset); + assert(!item.removed() && "can not call remove_item() on already removed item"); + item.set_removed(true); + offset = removed_item_offset; + --m_count_items; + ++m_count_removed; + } + + }; // class ItemStash + +} // namespace osmium + +#endif // OSMIUM_ITEM_STASH_HPP diff --git a/third_party/libosmium/include/osmium/tags/filter.hpp b/third_party/libosmium/include/osmium/tags/filter.hpp index 797565db8..89c6f1102 100644 --- a/third_party/libosmium/include/osmium/tags/filter.hpp +++ b/third_party/libosmium/include/osmium/tags/filter.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include @@ -49,31 +50,46 @@ namespace osmium { template struct match_key { - bool operator()(const TKey& rule_key, const char* tag_key) { + bool operator()(const TKey& rule_key, const char* tag_key) const { return rule_key == tag_key; } }; // struct match_key + template <> + struct match_key { + bool operator()(const std::string& rule_key, const char* tag_key) const { + return !std::strcmp(rule_key.c_str(), tag_key); + } + }; // struct match_key + struct match_key_prefix { - bool operator()(const std::string& rule_key, const char* tag_key) { + bool operator()(const std::string& rule_key, const char* tag_key) const { return rule_key.compare(0, std::string::npos, tag_key, 0, rule_key.size()) == 0; } }; // struct match_key_prefix template struct match_value { - bool operator()(const TValue& rule_value, const char* tag_value) { + bool operator()(const TValue& rule_value, const char* tag_value) const { return rule_value == tag_value; } }; // struct match_value + template <> + struct match_value { + bool operator()(const std::string& rule_value, const char* tag_value) const { + return !std::strcmp(rule_value.c_str(), tag_value); + } + }; // struct match_value + template <> struct match_value { - bool operator()(const bool, const char*) { + bool operator()(const bool, const char*) const noexcept { return true; } }; // struct match_value + /// @deprecated Use osmium::TagsFilter instead. template , typename TValueComp=match_value> class Filter { @@ -138,22 +154,31 @@ namespace osmium { /** * Return the number of rules in this filter. + * + * Complexity: Constant. */ - size_t count() const { + size_t count() const noexcept { return m_rules.size(); } /** * Is this filter empty, ie are there no rules defined? + * + * Complexity: Constant. */ - bool empty() const { + bool empty() const noexcept { return m_rules.empty(); } }; // class Filter + /// @deprecated Use osmium::TagsFilter instead. using KeyValueFilter = Filter; + + /// @deprecated Use osmium::TagsFilter instead. using KeyFilter = Filter; + + /// @deprecated Use osmium::TagsFilter instead. using KeyPrefixFilter = Filter; } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/matcher.hpp b/third_party/libosmium/include/osmium/tags/matcher.hpp new file mode 100644 index 000000000..4b1978b2d --- /dev/null +++ b/third_party/libosmium/include/osmium/tags/matcher.hpp @@ -0,0 +1,132 @@ +#ifndef OSMIUM_TAGS_MATCHER_HPP +#define OSMIUM_TAGS_MATCHER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + /** + * Compares a tags key (and value) against the specified StringMatchers. + */ + class TagMatcher { + + osmium::StringMatcher m_key_matcher; + osmium::StringMatcher m_value_matcher; + bool m_result; + + public: + + /** + * Create a default TagMatcher that matches no tags. + */ + TagMatcher() : + m_key_matcher(osmium::StringMatcher::always_false{}), + m_value_matcher(osmium::StringMatcher::always_false{}), + m_result(true) { + } + + /** + * Create a TagMatcher matching the key against the specified + * StringMatcher. + * + * @param key_matcher StringMatcher for matching the key. + */ + template ::value, int>::type = 0> + TagMatcher(TKey&& key_matcher) : + m_key_matcher(std::forward(key_matcher)), + m_value_matcher(osmium::StringMatcher::always_true{}), + m_result(true) { + } + + /** + * Create a TagMatcher matching the key and value against the specified + * StringMatchers. + * + * @param key_matcher StringMatcher for matching the key. + * @param value_matcher StringMatcher for matching the value. + * @param invert If set to true, invert the result of the value_matcher. + */ + template ::value, int>::type = 0, + typename std::enable_if::value, int>::type = 0> + TagMatcher(TKey&& key_matcher, TValue&& value_matcher, bool invert = false) : + m_key_matcher(std::forward(key_matcher)), + m_value_matcher(std::forward(value_matcher)), + m_result(!invert) { + } + + /** + * Match against the specified key and value. + * + * @returns true if the tag matches. + */ + bool operator()(const char* key, const char* value) const noexcept { + return m_key_matcher(key) && + (m_value_matcher(value) == m_result); + } + + /** + * Match against the specified tag. + * + * @returns true if the tag matches. + */ + bool operator()(const osmium::Tag& tag) const noexcept { + return operator()(tag.key(), tag.value()); + } + + /** + * Match against the specified tags. + * + * @returns true if any of the tags in the TagList matches. + */ + bool operator()(const osmium::TagList& tags) const noexcept { + for (const auto& tag : tags) { + if (operator()(tag)) { + return true; + } + } + return false; + } + + }; // class TagMatcher + +} // namespace osmium + +#endif // OSMIUM_TAGS_MATCHER_HPP diff --git a/third_party/libosmium/include/osmium/tags/regex_filter.hpp b/third_party/libosmium/include/osmium/tags/regex_filter.hpp index 72c3217e5..98ed94d80 100644 --- a/third_party/libosmium/include/osmium/tags/regex_filter.hpp +++ b/third_party/libosmium/include/osmium/tags/regex_filter.hpp @@ -44,18 +44,19 @@ namespace osmium { template <> struct match_key { - bool operator()(const std::regex& rule_key, const char* tag_key) { + bool operator()(const std::regex& rule_key, const char* tag_key) const { return std::regex_match(tag_key, rule_key); } }; // struct match_key template <> struct match_value { - bool operator()(const std::regex& rule_value, const char* tag_value) { + bool operator()(const std::regex& rule_value, const char* tag_value) const { return std::regex_match(tag_value, rule_value); } }; // struct match_value + /// @deprecated Use osmium::TagsFilter instead. using RegexFilter = Filter; } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/taglist.hpp b/third_party/libosmium/include/osmium/tags/taglist.hpp index 30db9e99c..3f640d6b2 100644 --- a/third_party/libosmium/include/osmium/tags/taglist.hpp +++ b/third_party/libosmium/include/osmium/tags/taglist.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include // IWYU pragma: keep #include @@ -46,18 +47,18 @@ namespace osmium { namespace tags { 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)); + inline bool match_any_of(const osmium::TagList& tag_list, const TFilter& filter) { + return std::any_of(tag_list.cbegin(), tag_list.cend(), std::cref(filter)); } 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)); + inline bool match_all_of(const osmium::TagList& tag_list, const TFilter& filter) { + return std::all_of(tag_list.cbegin(), tag_list.cend(), std::cref(filter)); } 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)); + inline bool match_none_of(const osmium::TagList& tag_list, const TFilter& filter) { + return std::none_of(tag_list.cbegin(), tag_list.cend(), std::cref(filter)); } } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/tags_filter.hpp b/third_party/libosmium/include/osmium/tags/tags_filter.hpp new file mode 100644 index 000000000..85d763e43 --- /dev/null +++ b/third_party/libosmium/include/osmium/tags/tags_filter.hpp @@ -0,0 +1,154 @@ +#ifndef OSMIUM_TAGS_TAGS_FILTER_HPP +#define OSMIUM_TAGS_TAGS_FILTER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +namespace osmium { + + /** + * A TagsFilter is a list of rules (defined using TagMatchers) to check + * tags against. The first rule that matches sets the result. + * + * Here is an example matching any "highway" tag except "highway=motorway": + * @code + * osmium::TagsFilter filter{false}; + * filter.add_rule(false, osmium::TagMatcher{"highway", "motorway"}); + * filter.add_rule(true, osmium::TagMatcher{"highway"}); + * + * osmium::Tag& tag = ...; + * bool result = filter(tag); + * @endcode + * + * Use this instead of the old osmium::tags::Filter. + */ + class TagsFilter { + + std::vector> m_rules; + bool m_default_result; + + public: + + using iterator = boost::filter_iterator; + + /** + * Constructor. + * + * @param default_result The result the matching function will return + * if none of the rules matched. + */ + explicit TagsFilter(bool default_result = false) : + m_default_result(default_result) { + } + + /** + * Set the default result, the result the matching function will + * return if none of the rules matched. + */ + void set_default_result(bool default_result) noexcept { + m_default_result = default_result; + } + + /** + * Add a rule to the filter. + * + * @param result The result returned when this rule matches. + * @param matcher The TagMatcher for checking tags. + * @returns A reference to this filter for chaining. + */ + TagsFilter& add_rule(bool result, const TagMatcher& matcher) { + m_rules.emplace_back(result, matcher); + return *this; + } + + /** + * Add a rule to the filter. + * + * @param result The result returned when this rule matches. + * @param args Arguments to construct a TagMatcher from that is used + * for checking tags. + * @returns A reference to this filter for chaining. + */ + template + TagsFilter& add_rule(bool result, TArgs&&... args) { + m_rules.emplace_back(result, osmium::TagMatcher{std::forward(args)...}); + return *this; + } + + /** + * Matching function. Check the specified tag against the rules. + * + * @param tag A tag. + * @returns The result of the matching rule, or, if none of the rules + * matched, the default result. + */ + bool operator()(const osmium::Tag& tag) const noexcept { + for (const auto& rule : m_rules) { + if (rule.second(tag)) { + return rule.first; + } + } + return m_default_result; + } + + /** + * Return the number of rules in this filter. + * + * Complexity: Constant. + */ + size_t count() const noexcept { + return m_rules.size(); + } + + /** + * Is this filter empty, ie are there no rules defined? + * + * Complexity: Constant. + */ + bool empty() const noexcept { + return m_rules.empty(); + } + + }; // class TagsFilter + +} // namespace osmium + + +#endif // OSMIUM_TAGS_TAGS_FILTER_HPP diff --git a/third_party/libosmium/include/osmium/thread/function_wrapper.hpp b/third_party/libosmium/include/osmium/thread/function_wrapper.hpp index 2ada084d3..ed68d8f8a 100644 --- a/third_party/libosmium/include/osmium/thread/function_wrapper.hpp +++ b/third_party/libosmium/include/osmium/thread/function_wrapper.hpp @@ -33,8 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include +#include namespace osmium { @@ -79,6 +79,7 @@ namespace osmium { // Constructor must not be "explicit" for wrapper // to work seemlessly. template + // cppcheck-suppress noExplicitConstructor function_wrapper(TFunction&& f) : impl(new impl_type(std::forward(f))) { } @@ -86,7 +87,7 @@ namespace osmium { // 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) : + explicit function_wrapper(int) : impl(new impl_base()) { } diff --git a/third_party/libosmium/include/osmium/thread/pool.hpp b/third_party/libosmium/include/osmium/thread/pool.hpp index 06d96f52a..ef64954cb 100644 --- a/third_party/libosmium/include/osmium/thread/pool.hpp +++ b/third_party/libosmium/include/osmium/thread/pool.hpp @@ -33,11 +33,11 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include +#include #include #include @@ -56,7 +56,7 @@ namespace osmium { // Maximum number of allowed pool threads (just to keep the user // from setting something silly). - constexpr const int max_pool_threads = 256; + constexpr const int max_pool_threads = 32; inline int get_pool_size(int num_threads, int user_setting, unsigned hardware_concurrency) { if (num_threads == 0) { @@ -76,8 +76,8 @@ namespace osmium { return num_threads; } - inline size_t get_work_queue_size() noexcept { - const size_t n = osmium::config::get_max_queue_size("WORK", 10); + inline std::size_t get_work_queue_size() noexcept { + const std::size_t n = osmium::config::get_max_queue_size("WORK", 10); return n > 2 ? n : 2; } @@ -130,6 +130,11 @@ namespace osmium { } } + public: + + static constexpr int default_num_threads = 0; + static constexpr int default_queue_size = 0; + /** * Create thread pool with the given number of threads. If * num_threads is 0, the number of threads is read from @@ -141,9 +146,12 @@ namespace osmium { * given number, ie it will leave a number of cores unused. * * In all cases the minimum number of threads in the pool is 1. + * + * If max_queue_size is 0, the queue size is read from + * the environment variable OSMIUM_MAX_WORK_QUEUE_SIZE. */ - explicit Pool(int num_threads, size_t max_queue_size) : - m_work_queue(max_queue_size, "work"), + explicit Pool(int num_threads = default_num_threads, std::size_t max_queue_size = default_queue_size) : + m_work_queue(max_queue_size > 0 ? max_queue_size : detail::get_work_queue_size(), "work"), m_threads(), m_joiner(m_threads), m_num_threads(detail::get_pool_size(num_threads, osmium::config::get_pool_threads(), std::thread::hardware_concurrency())) { @@ -158,12 +166,8 @@ namespace osmium { } } - public: - - static constexpr int default_num_threads = 0; - - static Pool& instance() { - static Pool pool(default_num_threads, detail::get_work_queue_size()); + static Pool& default_instance() { + static Pool pool{}; return pool; } @@ -178,7 +182,11 @@ namespace osmium { shutdown_all_workers(); } - size_t queue_size() const { + int num_threads() const noexcept { + return m_num_threads; + } + + std::size_t queue_size() const { return m_work_queue.size(); } @@ -188,11 +196,10 @@ namespace osmium { template std::future::type> submit(TFunction&& func) { - using result_type = typename std::result_of::type; - std::packaged_task task(std::forward(func)); - std::future future_result(task.get_future()); + std::packaged_task task{std::forward(func)}; + std::future future_result{task.get_future()}; m_work_queue.push(std::move(task)); return future_result; diff --git a/third_party/libosmium/include/osmium/thread/queue.hpp b/third_party/libosmium/include/osmium/thread/queue.hpp index 3896fef56..39df6857c 100644 --- a/third_party/libosmium/include/osmium/thread/queue.hpp +++ b/third_party/libosmium/include/osmium/thread/queue.hpp @@ -39,7 +39,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include // IWYU pragma: keep #ifdef OSMIUM_DEBUG_QUEUE_SIZE @@ -61,7 +60,7 @@ namespace osmium { /// Maximum size of this queue. If the queue is full pushing to /// the queue will block. - const size_t m_max_size; + const std::size_t m_max_size; /// Name of this queue (for debugging only). const std::string m_name; @@ -78,7 +77,7 @@ namespace osmium { #ifdef OSMIUM_DEBUG_QUEUE_SIZE /// The largest size the queue has been so far. - size_t m_largest_size; + std::size_t m_largest_size; /// The number of times push() was called on the queue. std::atomic m_push_counter; @@ -107,7 +106,7 @@ namespace osmium { * 0 for an unlimited size. * @param name Optional name for this queue. (Used for debugging.) */ - explicit Queue(size_t max_size = 0, const std::string& name = "") : + explicit Queue(std::size_t max_size = 0, const std::string& name = "") : m_max_size(max_size), m_name(name), m_mutex(), @@ -216,7 +215,7 @@ namespace osmium { return m_queue.empty(); } - size_t size() const { + std::size_t size() const { std::lock_guard lock{m_mutex}; return m_queue.size(); } diff --git a/third_party/libosmium/include/osmium/thread/util.hpp b/third_party/libosmium/include/osmium/thread/util.hpp index 4eb59c9d1..53d16a19e 100644 --- a/third_party/libosmium/include/osmium/thread/util.hpp +++ b/third_party/libosmium/include/osmium/thread/util.hpp @@ -115,4 +115,4 @@ namespace osmium { } // namespace osmium -#endif // OSMIUM_THREAD_UTIL_HPP +#endif // OSMIUM_THREAD_UTIL_HPP diff --git a/third_party/libosmium/include/osmium/util/config.hpp b/third_party/libosmium/include/osmium/util/config.hpp index 8dba9119f..f9cc96cfc 100644 --- a/third_party/libosmium/include/osmium/util/config.hpp +++ b/third_party/libosmium/include/osmium/util/config.hpp @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include #include @@ -66,7 +68,8 @@ namespace osmium { return true; } - inline size_t get_max_queue_size(const char* queue_name, size_t default_value) noexcept { + inline std::size_t get_max_queue_size(const char* queue_name, std::size_t default_value) noexcept { + assert(queue_name); std::string name{"OSMIUM_MAX_"}; name += queue_name; name += "_QUEUE_SIZE"; diff --git a/third_party/libosmium/include/osmium/util/file.hpp b/third_party/libosmium/include/osmium/util/file.hpp index a26d88fc1..4af6d77eb 100644 --- a/third_party/libosmium/include/osmium/util/file.hpp +++ b/third_party/libosmium/include/osmium/util/file.hpp @@ -42,8 +42,12 @@ DEALINGS IN THE SOFTWARE. #include #ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN // Prevent winsock.h inclusion; avoid winsock2.h conflict +# endif # include # include +# include #endif #ifndef _MSC_VER @@ -56,6 +60,44 @@ namespace osmium { namespace util { +#ifdef _MSC_VER + namespace detail { + + // Disable parameter validation on Windows and reenable it + // automatically when scope closes. + // https://docs.microsoft.com/en-us/cpp/c-runtime-library/parameter-validation + class disable_invalid_parameter_handler { + + static void invalid_parameter_handler( + const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved + ) { + // do nothing + } + + _invalid_parameter_handler old_handler; + int old_report_mode; + + public: + + disable_invalid_parameter_handler() : + old_handler(_set_thread_local_invalid_parameter_handler(invalid_parameter_handler)), + old_report_mode(_CrtSetReportMode(_CRT_ASSERT, 0)) { + } + + ~disable_invalid_parameter_handler() { + _CrtSetReportMode(_CRT_ASSERT, old_report_mode); + _set_thread_local_invalid_parameter_handler(old_handler); + } + + }; // class disable_invalid_parameter_handler + + } +#endif + /** * Get file size. * This is a small wrapper around a system call. @@ -64,22 +106,23 @@ namespace osmium { * @returns file size * @throws std::system_error If system call failed */ - inline size_t file_size(int fd) { + inline std::size_t file_size(int fd) { #ifdef _MSC_VER // Windows implementation + detail::disable_invalid_parameter_handler diph; // https://msdn.microsoft.com/en-us/library/dfbc2kec.aspx const auto size = ::_filelengthi64(fd); - if (size == -1L) { - throw std::system_error(errno, std::system_category(), "_filelengthi64 failed"); + if (size < 0) { + throw std::system_error{errno, std::system_category(), "Could not get file size"}; } - return size_t(size); + return static_cast(size); #else // Unix implementation struct stat s; if (::fstat(fd, &s) != 0) { - throw std::system_error(errno, std::system_category(), "fstat failed"); + throw std::system_error{errno, std::system_category(), "Could not get file size"}; } - return size_t(s.st_size); + return static_cast(s.st_size); #endif } @@ -90,23 +133,24 @@ namespace osmium { * @param name File name * @returns file size * @throws std::system_error If system call failed + * @pre name must not be nullptr */ - inline size_t file_size(const char* name) { + inline std::size_t file_size(const char* name) { #ifdef _MSC_VER // Windows implementation // https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx struct _stat64 s; if (::_stati64(name, &s) != 0) { - throw std::system_error(errno, std::system_category(), "_stati64 failed"); + throw std::system_error{errno, std::system_category(), std::string{"Could not get file size of file '"} + name + "'"}; } #else // Unix implementation struct stat s; if (::stat(name, &s) != 0) { - throw std::system_error(errno, std::system_category(), "stat failed"); + throw std::system_error{errno, std::system_category(), std::string{"Could not get file size of file '"} + name + "'"}; } #endif - return size_t(s.st_size); + return static_cast(s.st_size); } /** @@ -117,7 +161,7 @@ namespace osmium { * @returns file size * @throws std::system_error If system call failed */ - inline size_t file_size(const std::string& name) { + inline std::size_t file_size(const std::string& name) { return file_size(name.c_str()); } @@ -129,21 +173,22 @@ namespace osmium { * @param new_size New size * @throws std::system_error If ftruncate(2) call failed */ - inline void resize_file(int fd, size_t new_size) { + inline void resize_file(int fd, std::size_t new_size) { #ifdef _WIN32 + detail::disable_invalid_parameter_handler diph; // https://msdn.microsoft.com/en-us/library/whx354w1.aspx if (::_chsize_s(fd, static_cast_with_assert<__int64>(new_size)) != 0) { #else if (::ftruncate(fd, static_cast_with_assert(new_size)) != 0) { #endif - throw std::system_error(errno, std::system_category(), "resizing file failed"); + throw std::system_error{errno, std::system_category(), "Could not resize file"}; } } /** * Get the page size for this system. */ - inline size_t get_pagesize() { + inline std::size_t get_pagesize() { #ifdef _WIN32 // Windows implementation SYSTEM_INFO si; @@ -151,7 +196,7 @@ namespace osmium { return si.dwPageSize; #else // Unix implementation - return size_t(::sysconf(_SC_PAGESIZE)); + return static_cast(::sysconf(_SC_PAGESIZE)); #endif } @@ -161,17 +206,18 @@ namespace osmium { * @param fd Open file descriptor. * @returns File offset or 0 if it is not available. */ - inline size_t file_offset(int fd) { + inline std::size_t file_offset(int fd) { #ifdef _MSC_VER + detail::disable_invalid_parameter_handler diph; // https://msdn.microsoft.com/en-us/library/1yee101t.aspx - auto offset = _lseeki64(fd, 0, SEEK_CUR); + const auto offset = _lseeki64(fd, 0, SEEK_CUR); #else - auto offset = ::lseek(fd, 0, SEEK_CUR); + const auto offset = ::lseek(fd, 0, SEEK_CUR); #endif if (offset == -1) { return 0; } - return size_t(offset); + return static_cast(offset); } /** @@ -179,6 +225,7 @@ namespace osmium { */ inline bool isatty(int fd) { #ifdef _MSC_VER + detail::disable_invalid_parameter_handler diph; // https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx return _isatty(fd) != 0; #else diff --git a/third_party/libosmium/include/osmium/util/iterator.hpp b/third_party/libosmium/include/osmium/util/iterator.hpp index 1cc6e0e36..bd8a6927b 100644 --- a/third_party/libosmium/include/osmium/util/iterator.hpp +++ b/third_party/libosmium/include/osmium/util/iterator.hpp @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include @@ -44,27 +43,19 @@ namespace osmium { using iterator = It; - explicit iterator_range(P&& p) : + explicit iterator_range(P&& p) noexcept : P(std::forward

(p)) { } -/* - It begin() { + + It begin() const noexcept { return this->first; } - It end() { - return this->second; - } -*/ - It begin() const { - return this->first; - } - - It end() const { + It end() const noexcept { return this->second; } - size_t empty() const { + bool empty() const noexcept { return begin() == end(); } @@ -74,9 +65,9 @@ namespace osmium { * Helper function to create iterator_range from std::pair. */ template - inline iterator_range make_range(P&& p) { + inline iterator_range make_range(P&& p) noexcept { static_assert(std::is_same>::value, "make_range needs pair of iterators as argument"); - return iterator_range(std::forward

(p)); + return iterator_range{std::forward

(p)}; } } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/memory_mapping.hpp b/third_party/libosmium/include/osmium/util/memory_mapping.hpp index 9996c8ab7..c4059798e 100644 --- a/third_party/libosmium/include/osmium/util/memory_mapping.hpp +++ b/third_party/libosmium/include/osmium/util/memory_mapping.hpp @@ -105,7 +105,7 @@ namespace osmium { private: /// The size of the mapping - size_t m_size; + std::size_t m_size; /// Offset into the file off_t m_offset; @@ -137,9 +137,9 @@ namespace osmium { flag_type get_flags() const noexcept; - static size_t check_size(size_t size) { + static std::size_t check_size(std::size_t size) { if (size == 0) { - throw std::runtime_error("Zero-sized mapping is not allowed."); + return osmium::util::get_pagesize(); } return size; } @@ -181,14 +181,14 @@ namespace osmium { * @param offset Offset into the file where the mapping should start * @throws std::system_error if the mapping fails */ - MemoryMapping(size_t size, mapping_mode mode, int fd=-1, off_t offset=0); + MemoryMapping(std::size_t size, mapping_mode mode, 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) : + OSMIUM_DEPRECATED MemoryMapping(std::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) { } @@ -240,7 +240,7 @@ namespace osmium { * * @throws std::system_error if the remapping fails. */ - void resize(size_t new_size); + void resize(std::size_t new_size); /** * In a boolean context a MemoryMapping is true when it is a valid @@ -255,7 +255,7 @@ namespace osmium { * the mapping with. The actual mapping will probably be larger * because the system will round it to the page size. */ - size_t size() const noexcept { + std::size_t size() const noexcept { return m_size; } @@ -285,7 +285,7 @@ namespace osmium { if (is_valid()) { return reinterpret_cast(m_addr); } - throw std::runtime_error("invalid memory mapping"); + throw std::runtime_error{"invalid memory mapping"}; } }; // class MemoryMapping @@ -304,7 +304,7 @@ namespace osmium { public: - explicit AnonymousMemoryMapping(size_t size) : + explicit AnonymousMemoryMapping(std::size_t size) : MemoryMapping(size, mapping_mode::write_private) { } @@ -313,7 +313,7 @@ namespace osmium { * On systems other than Linux anonymous mappings can not be * resized! */ - void resize(size_t) = delete; + void resize(std::size_t) = delete; #endif }; // class AnonymousMemoryMapping @@ -340,7 +340,7 @@ namespace osmium { * @param size Number of objects of type T to be mapped * @throws std::system_error if the mapping fails */ - explicit TypedMemoryMapping(size_t size) : + explicit TypedMemoryMapping(std::size_t size) : m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) { } @@ -354,7 +354,7 @@ namespace osmium { * @param offset Offset into the file where the mapping should start * @throws std::system_error if the mapping fails */ - TypedMemoryMapping(size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset = 0) : + TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset = 0) : m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) { } @@ -363,8 +363,11 @@ namespace osmium { * 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) { + OSMIUM_DEPRECATED TypedMemoryMapping(std::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) { } /// You can not copy construct a TypedMemoryMapping. @@ -410,7 +413,7 @@ namespace osmium { * @param new_size Number of objects of type T to resize to * @throws std::system_error if the remapping fails */ - void resize(size_t new_size) { + void resize(std::size_t new_size) { m_mapping.resize(sizeof(T) * new_size); } @@ -427,7 +430,7 @@ namespace osmium { * you created the mapping with. The actual mapping will probably * be larger because the system will round it to the page size. */ - size_t size() const noexcept { + std::size_t size() const noexcept { assert(m_mapping.size() % sizeof(T) == 0); return m_mapping.size() / sizeof(T); } @@ -489,7 +492,7 @@ namespace osmium { public: - explicit AnonymousTypedMemoryMapping(size_t size) : + explicit AnonymousTypedMemoryMapping(std::size_t size) : TypedMemoryMapping(size) { } @@ -498,7 +501,7 @@ namespace osmium { * On systems other than Linux anonymous mappings can not be * resized! */ - void resize(size_t) = delete; + void resize(std::size_t) = delete; #endif }; // class AnonymousTypedMemoryMapping @@ -547,7 +550,7 @@ inline int osmium::util::MemoryMapping::get_flags() const noexcept { return MAP_PRIVATE; } -inline osmium::util::MemoryMapping::MemoryMapping(size_t size, mapping_mode mode, int fd, off_t offset) : +inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, mapping_mode mode, int fd, off_t offset) : m_size(check_size(size)), m_offset(offset), m_fd(resize_fd(fd)), @@ -555,7 +558,7 @@ inline osmium::util::MemoryMapping::MemoryMapping(size_t size, mapping_mode mode m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) { assert(!(fd == -1 && mode == mapping_mode::readonly)); if (!is_valid()) { - throw std::system_error(errno, std::system_category(), "mmap failed"); + throw std::system_error{errno, std::system_category(), "mmap failed"}; } } @@ -582,19 +585,19 @@ inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmiu inline void osmium::util::MemoryMapping::unmap() { if (is_valid()) { if (::munmap(m_addr, m_size) != 0) { - throw std::system_error(errno, std::system_category(), "munmap failed"); + throw std::system_error{errno, std::system_category(), "munmap failed"}; } make_invalid(); } } -inline void osmium::util::MemoryMapping::resize(size_t new_size) { +inline void osmium::util::MemoryMapping::resize(std::size_t new_size) { assert(new_size > 0 && "can not resize to zero size"); if (m_fd == -1) { // anonymous mapping #ifdef __linux__ m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE); if (!is_valid()) { - throw std::system_error(errno, std::system_category(), "mremap failed"); + throw std::system_error{errno, std::system_category(), "mremap failed"}; } m_size = new_size; #else @@ -606,7 +609,7 @@ inline void osmium::util::MemoryMapping::resize(size_t new_size) { resize_fd(m_fd); m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset); if (!is_valid()) { - throw std::system_error(errno, std::system_category(), "mmap (remap) failed"); + throw std::system_error{errno, std::system_category(), "mmap (remap) failed"}; } } } @@ -644,9 +647,10 @@ inline DWORD osmium::util::MemoryMapping::get_protection() const noexcept { return PAGE_READONLY; case mapping_mode::write_private: return PAGE_WRITECOPY; - case mapping_mode::write_shared: - return PAGE_READWRITE; + default: // mapping_mode::write_shared + break; } + return PAGE_READWRITE; } inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept { @@ -655,9 +659,10 @@ inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept { return FILE_MAP_READ; case mapping_mode::write_private: return FILE_MAP_COPY; - case mapping_mode::write_shared: - return FILE_MAP_WRITE; + default: // mapping_mode::write_shared + break; } + return FILE_MAP_WRITE; } inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept { @@ -671,11 +676,20 @@ 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); + 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); } inline void* osmium::util::MemoryMapping::map_view_of_file() const noexcept { - return MapViewOfFile(m_handle, get_flags(), osmium::util::dword_hi(m_offset), osmium::util::dword_lo(m_offset), m_size); + return MapViewOfFile(m_handle, + get_flags(), + osmium::util::dword_hi(m_offset), + osmium::util::dword_lo(m_offset), + m_size); } inline bool osmium::util::MemoryMapping::is_valid() const noexcept { @@ -686,7 +700,14 @@ inline void osmium::util::MemoryMapping::make_invalid() noexcept { m_addr = nullptr; } -inline osmium::util::MemoryMapping::MemoryMapping(size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) : +// GetLastError() returns a DWORD (A 32-bit unsigned integer), but the error +// code for std::system_error is an int. So we convert this here and hope +// it all works. +inline int last_error() noexcept { + return static_cast(GetLastError()); +} + +inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) : m_size(check_size(size)), m_offset(offset), m_fd(resize_fd(fd)), @@ -695,12 +716,12 @@ inline osmium::util::MemoryMapping::MemoryMapping(size_t size, MemoryMapping::ma m_addr(nullptr) { if (!m_handle) { - throw std::system_error(GetLastError(), std::system_category(), "CreateFileMapping failed"); + throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"}; } m_addr = map_view_of_file(); if (!is_valid()) { - throw std::system_error(GetLastError(), std::system_category(), "MapViewOfFile failed"); + throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"}; } } @@ -730,21 +751,21 @@ inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmiu inline void osmium::util::MemoryMapping::unmap() { if (is_valid()) { - if (! UnmapViewOfFile(m_addr)) { - throw std::system_error(GetLastError(), std::system_category(), "UnmapViewOfFile failed"); + if (!UnmapViewOfFile(m_addr)) { + throw std::system_error{last_error(), std::system_category(), "UnmapViewOfFile failed"}; } make_invalid(); } if (m_handle) { - if (! CloseHandle(m_handle)) { - throw std::system_error(GetLastError(), std::system_category(), "CloseHandle failed"); + if (!CloseHandle(m_handle)) { + throw std::system_error{last_error(), std::system_category(), "CloseHandle failed"}; } m_handle = nullptr; } } -inline void osmium::util::MemoryMapping::resize(size_t new_size) { +inline void osmium::util::MemoryMapping::resize(std::size_t new_size) { unmap(); m_size = new_size; @@ -752,12 +773,12 @@ inline void osmium::util::MemoryMapping::resize(size_t new_size) { m_handle = create_file_mapping(); if (!m_handle) { - throw std::system_error(GetLastError(), std::system_category(), "CreateFileMapping failed"); + throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"}; } m_addr = map_view_of_file(); if (!is_valid()) { - throw std::system_error(GetLastError(), std::system_category(), "MapViewOfFile failed"); + throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"}; } } diff --git a/third_party/libosmium/include/osmium/util/options.hpp b/third_party/libosmium/include/osmium/util/options.hpp index ddc113833..c39bdbf6f 100644 --- a/third_party/libosmium/include/osmium/util/options.hpp +++ b/third_party/libosmium/include/osmium/util/options.hpp @@ -107,14 +107,13 @@ namespace osmium { * contains no equal sign, the whole string is the key and it will * be set to "true". */ - void set(std::string data) { - const size_t pos = data.find_first_of('='); + void set(const std::string& data) { + const std::size_t pos = data.find_first_of('='); if (pos == std::string::npos) { m_options[data] = "true"; } else { - std::string value = data.substr(pos+1); - data.erase(pos); - set(data, value); + const std::string value{data.substr(pos+1)}; + set(data.substr(0, pos), value); } } @@ -122,7 +121,7 @@ namespace osmium { * 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 { + std::string get(const std::string& key, const std::string& default_value = "") const noexcept { const auto it = m_options.find(key); if (it == m_options.end()) { return default_value; @@ -135,7 +134,7 @@ namespace osmium { * Will return false if the value is unset. */ bool is_true(const std::string& key) const noexcept { - const std::string value = get(key); + const std::string value{get(key)}; return (value == "true" || value == "yes"); } @@ -144,14 +143,14 @@ namespace osmium { * Will return true if the value is unset. */ bool is_not_false(const std::string& key) const noexcept { - const std::string value = get(key); + const std::string value{get(key)}; return !(value == "false" || value == "no"); } /** * The number of options set. */ - size_t size() const noexcept { + std::size_t size() const noexcept { return m_options.size(); } diff --git a/third_party/libosmium/include/osmium/util/progress_bar.hpp b/third_party/libosmium/include/osmium/util/progress_bar.hpp index 6e6d01224..fed37eb4c 100644 --- a/third_party/libosmium/include/osmium/util/progress_bar.hpp +++ b/third_party/libosmium/include/osmium/util/progress_bar.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include namespace osmium { @@ -51,23 +52,23 @@ namespace osmium { return " "; } - static constexpr const size_t length = 70; + static constexpr const std::size_t length = 70; // The max size is the file size if there is a single file and the // sum of all file sizes if there are multiple files. It corresponds // to 100%. - size_t m_max_size; + std::size_t m_max_size; // The sum of the file sizes already done. - size_t m_done_size = 0; + std::size_t m_done_size = 0; // The currently read size in the current file. - size_t m_current_size = 0; + std::size_t m_current_size = 0; // The percentage calculated when it was last displayed. Used to decide // whether we need to update the display. Start setting is one that // will always be different from any legal setting. - size_t m_prev_percent = 100 + 1; + std::size_t m_prev_percent = 100 + 1; // Is the progress bar enabled at all? bool m_enable; @@ -77,13 +78,13 @@ namespace osmium { bool m_do_cleanup = true; void display() { - const size_t percent = 100 * (m_done_size + m_current_size) / m_max_size; + const std::size_t percent = 100 * (m_done_size + m_current_size) / m_max_size; if (m_prev_percent == percent) { return; } m_prev_percent = percent; - const size_t num = size_t(percent * (length / 100.0)); + const auto num = static_cast(percent * (length / 100.0)); std::cerr << '['; if (num >= length) { std::cerr << bar(); @@ -109,7 +110,7 @@ namespace osmium { * @param enable Set to false to disable (for instance if stderr is * not a TTY). */ - ProgressBar(size_t max_size, bool enable) noexcept : + ProgressBar(std::size_t max_size, bool enable) noexcept : m_max_size(max_size), m_enable(max_size > 0 && enable) { } @@ -133,7 +134,7 @@ namespace osmium { * @param current_size Current size. Used together with the max_size * from constructor to calculate the percentage. */ - void update(size_t current_size) { + void update(std::size_t current_size) { if (!m_enable) { return; } @@ -149,7 +150,7 @@ namespace osmium { * * @param file_size The size of the file just finished. */ - void file_done(size_t file_size) { + void file_done(std::size_t file_size) { if (m_enable) { m_done_size += file_size; m_current_size = 0; diff --git a/third_party/libosmium/include/osmium/util/string.hpp b/third_party/libosmium/include/osmium/util/string.hpp index 595307ed8..6eef05f21 100644 --- a/third_party/libosmium/include/osmium/util/string.hpp +++ b/third_party/libosmium/include/osmium/util/string.hpp @@ -44,15 +44,15 @@ namespace osmium { * * @param str The string to be split. * @param sep The separator character. - * @param compact Set this to true to remove empty strings from result + * @param compact Set this to true to remove empty strings from result. * @returns Vector with the parts of the string split up. */ inline std::vector split_string(const std::string& str, const char sep, bool compact = false) { std::vector tokens; if (!str.empty()) { - size_t pos = 0; - size_t nextpos = str.find_first_of(sep); + std::size_t pos = 0; + std::size_t nextpos = str.find_first_of(sep); while (nextpos != std::string::npos) { if (!compact || (nextpos - pos != 0)) { tokens.push_back(str.substr(pos, nextpos-pos)); @@ -69,19 +69,19 @@ namespace osmium { } /** - * Split string on the separator character(s). + * Split string on any of the separator characters. * * @param str The string to be split. * @param sep The separator character(s). - * @param compact Set this to true to remove empty strings from result + * @param compact Set this to true to remove empty strings from result. * @returns Vector with the parts of the string split up. */ inline std::vector split_string(const std::string& str, const char* sep, bool compact = false) { std::vector tokens; if (!str.empty()) { - size_t pos = 0; - size_t nextpos = str.find_first_of(sep); + std::size_t pos = 0; + std::size_t nextpos = str.find_first_of(sep); while (nextpos != std::string::npos) { if (!compact || (nextpos - pos != 0)) { tokens.push_back(str.substr(pos, nextpos-pos)); diff --git a/third_party/libosmium/include/osmium/util/string_matcher.hpp b/third_party/libosmium/include/osmium/util/string_matcher.hpp new file mode 100644 index 000000000..1aff423a3 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/string_matcher.hpp @@ -0,0 +1,429 @@ +#ifndef OSMIUM_UTIL_STRING_MATCHER_HPP +#define OSMIUM_UTIL_STRING_MATCHER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2017 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 + +// std::regex isn't implemented properly in glibc++ (before the version +// delivered with GCC 4.9) and libc++ before the version 3.6, so the use is +// disabled by these checks. Checks for GLIBC were based on +// http://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions +// Checks for libc++ are simply based on compiler defines. This is probably +// not optimal but seems to work for now. +#if defined(__GLIBCXX__) +# if ((__cplusplus >= 201402L) || \ + defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \ + defined(_GLIBCXX_REGEX_STATE_LIMIT)) +# define OSMIUM_WITH_REGEX +# else +# pragma message("Disabling regex functionality. See source code for info.") +# endif +#elif defined(__clang__) +# if ((__clang_major__ > 3) || \ + (__clang_minor__ == 3 && __clang_minor__ > 5)) +# define OSMIUM_WITH_REGEX +# else +# pragma message("Disabling regex functionality") +# endif +#endif + +#include + +namespace osmium { + + /** + * Implements various string matching functions. + */ + class StringMatcher { + + public: + + // Parent class for all matcher classes. Used for enable_if check. + class matcher { + }; + + /** + * Never matches. + */ + class always_false : public matcher { + + public: + + bool match(const char* /*test_string*/) const noexcept { + return false; + } + + template + void print(std::basic_ostream& out) const { + out << "always_false"; + } + + }; // class always_false + + /** + * Always matches. + */ + class always_true : public matcher { + + public: + + bool match(const char* /*test_string*/) const noexcept { + return true; + } + + template + void print(std::basic_ostream& out) const { + out << "always_true"; + } + + }; // class always_true + + /** + * Matches if the test string is equal to the stored string. + */ + class equal : public matcher { + + std::string m_str; + + public: + + explicit equal(const std::string& str) : + m_str(str) { + } + + explicit equal(const char* str) : + m_str(str) { + } + + bool match(const char* test_string) const noexcept { + return !std::strcmp(m_str.c_str(), test_string); + } + + template + void print(std::basic_ostream& out) const { + out << "equal[" << m_str << ']'; + } + + }; // class equal + + /** + * Matches if the test string starts with the stored string. + */ + class prefix : public matcher { + + std::string m_str; + + public: + + explicit prefix(const std::string& str) : + m_str(str) { + } + + explicit prefix(const char* str) : + m_str(str) { + } + + bool match(const char* test_string) const noexcept { + return m_str.compare(0, std::string::npos, test_string, 0, m_str.size()) == 0; + } + + template + void print(std::basic_ostream& out) const { + out << "prefix[" << m_str << ']'; + } + + }; // class prefix + + /** + * Matches if the test string is a substring of the stored string. + */ + class substring : public matcher { + + std::string m_str; + + public: + + explicit substring(const std::string& str) : + m_str(str) { + } + + explicit substring(const char* str) : + m_str(str) { + } + + bool match(const char* test_string) const noexcept { + return std::strstr(test_string, m_str.c_str()) != nullptr; + } + + template + void print(std::basic_ostream& out) const { + out << "substring[" << m_str << ']'; + } + + }; // class substring + +#ifdef OSMIUM_WITH_REGEX + /** + * Matches if the test string matches the regular expression. + */ + class regex : public matcher { + + std::regex m_regex; + + public: + + explicit regex(const std::regex& regex) : + m_regex(regex) { + } + + bool match(const char* test_string) const noexcept { + return std::regex_search(test_string, m_regex); + } + + template + void print(std::basic_ostream& out) const { + out << "regex"; + } + + }; // class regex +#endif + + /** + * Matches if the test string is equal to any of the stored strings. + */ + class list : public matcher { + + std::vector m_strings; + + public: + + explicit list() : + m_strings() { + } + + explicit list(const std::vector& strings) : + m_strings(strings) { + } + + list& add_string(const char* str) { + m_strings.push_back(str); + return *this; + } + + list& add_string(const std::string& str) { + m_strings.push_back(str); + return *this; + } + + bool match(const char* test_string) const noexcept { + for (const auto& s : m_strings) { + if (!std::strcmp(s.c_str(), test_string)) { + return true; + } + } + return false; + + } + + template + void print(std::basic_ostream& out) const { + out << "list["; + for (const auto& s : m_strings) { + out << '[' << s << ']'; + } + out << ']'; + } + + }; // class list + + private: + + using matcher_type = boost::variant; + + matcher_type m_matcher; + + class match_visitor : public boost::static_visitor { + + const char* m_str; + + public: + + match_visitor(const char* str) noexcept : + m_str(str) { + } + + template + bool operator()(const TMatcher& t) const noexcept { + return t.match(m_str); + } + + }; // class match_visitor + + template + class print_visitor : public boost::static_visitor { + + std::basic_ostream* m_out; + + public: + + print_visitor(std::basic_ostream& out) : + m_out(&out) { + } + + template + void operator()(const TMatcher& t) const noexcept { + t.print(*m_out); + } + + }; // class print_visitor + + public: + + /** + * Create a string matcher that will never match. + */ + StringMatcher() : + m_matcher(always_false{}) { + } + + /** + * Create a string matcher that will always or never match based on + * the argument. + * Shortcut for + * @code StringMatcher{StringMatcher::always_true}; @endcode + * or + * @code StringMatcher{StringMatcher::always_false}; @endcode + */ + StringMatcher(bool result) : + m_matcher(always_false{}) { + if (result) { + m_matcher = always_true{}; + } + } + + /** + * Create a string matcher that will match the specified string. + * Shortcut for + * @code StringMatcher{StringMatcher::equal{str}}; @endcode + */ + StringMatcher(const char* str) : + m_matcher(equal{str}) { + } + + /** + * Create a string matcher that will match the specified string. + * Shortcut for + * @code StringMatcher{StringMatcher::equal{str}}; @endcode + */ + StringMatcher(const std::string& str) : + m_matcher(equal{str}) { + } + +#ifdef OSMIUM_WITH_REGEX + /** + * Create a string matcher that will match the specified regex. + * Shortcut for + * @code StringMatcher{StringMatcher::regex{aregex}}; @endcode + */ + StringMatcher(const std::regex& aregex) : + m_matcher(regex{aregex}) { + } +#endif + + /** + * Create a string matcher that will match if any of the strings + * match. + * Shortcut for + * @code StringMatcher{StringMatcher::list{strings}}; @endcode + */ + StringMatcher(const std::vector& strings) : + m_matcher(list{strings}) { + } + + /** + * Create a string matcher. + * + * @tparam TMatcher Must be one of the matcher classes + * osmium::StringMatcher::always_false, always_true, + * equal, prefix, substring, regex or list. + */ + template ::value, int>::type = 0> + StringMatcher(TMatcher&& matcher) : + m_matcher(std::forward(matcher)) { + } + + /** + * Match the specified string. + */ + bool operator()(const char* str) const noexcept { + return boost::apply_visitor(match_visitor{str}, m_matcher); + } + + /** + * Match the specified string. + */ + bool operator()(const std::string& str) const noexcept { + return operator()(str.c_str()); + } + + template + void print(std::basic_ostream& out) const { + boost::apply_visitor(print_visitor{out}, m_matcher); + } + + }; // class StringMatcher + + template + inline std::basic_ostream& operator<<(std::basic_ostream& out, const StringMatcher& matcher) { + matcher.print(out); + return out; + } + +} // namespace osmium + +#endif // OSMIUM_UTIL_STRING_MATCHER_HPP diff --git a/third_party/libosmium/include/osmium/util/verbose_output.hpp b/third_party/libosmium/include/osmium/util/verbose_output.hpp index a47d29a8c..e6f6654a0 100644 --- a/third_party/libosmium/include/osmium/util/verbose_output.hpp +++ b/third_party/libosmium/include/osmium/util/verbose_output.hpp @@ -88,7 +88,7 @@ namespace osmium { public: explicit VerboseOutput(bool verbose = false) noexcept : - m_start(time(NULL)), + m_start(time(nullptr)), m_verbose(verbose), m_newline(true) { } @@ -101,7 +101,7 @@ namespace osmium { VerboseOutput& operator=(VerboseOutput&&) = default; time_t runtime() const noexcept { - return time(NULL) - m_start; + return time(nullptr) - m_start; } /// Get "verbose" setting. diff --git a/third_party/libosmium/include/osmium/version.hpp b/third_party/libosmium/include/osmium/version.hpp index a36a4d900..004ad0ed4 100644 --- a/third_party/libosmium/include/osmium/version.hpp +++ b/third_party/libosmium/include/osmium/version.hpp @@ -34,9 +34,9 @@ DEALINGS IN THE SOFTWARE. */ #define LIBOSMIUM_VERSION_MAJOR 2 -#define LIBOSMIUM_VERSION_MINOR 11 -#define LIBOSMIUM_VERSION_PATCH 3 +#define LIBOSMIUM_VERSION_MINOR 13 +#define LIBOSMIUM_VERSION_PATCH 1 -#define LIBOSMIUM_VERSION_STRING "2.11.3" +#define LIBOSMIUM_VERSION_STRING "2.13.1" #endif // OSMIUM_VERSION_HPP diff --git a/third_party/libosmium/include/protozero/pbf_builder.hpp b/third_party/libosmium/include/protozero/pbf_builder.hpp index c40727c17..819739585 100644 --- a/third_party/libosmium/include/protozero/pbf_builder.hpp +++ b/third_party/libosmium/include/protozero/pbf_builder.hpp @@ -46,7 +46,7 @@ public: using enum_type = T; - pbf_builder(std::string& data) noexcept : + explicit pbf_builder(std::string& data) noexcept : pbf_writer(data) { } @@ -83,10 +83,18 @@ public: pbf_writer::add_bytes(pbf_tag_type(tag), value, size); } + void add_bytes(T tag, const data_view& value) { + pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + void add_bytes(T tag, const std::string& value) { pbf_writer::add_bytes(pbf_tag_type(tag), value); } + void add_bytes(T tag, const char* value) { + pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + template void add_bytes_vectored(T tag, Ts&&... values) { pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward(values)...); @@ -96,6 +104,10 @@ public: pbf_writer::add_string(pbf_tag_type(tag), value, size); } + void add_string(T tag, const data_view& value) { + pbf_writer::add_string(pbf_tag_type(tag), value); + } + void add_string(T tag, const std::string& value) { pbf_writer::add_string(pbf_tag_type(tag), value); } @@ -108,6 +120,10 @@ public: pbf_writer::add_message(pbf_tag_type(tag), value, size); } + void add_message(T tag, const data_view& value) { + pbf_writer::add_message(pbf_tag_type(tag), value); + } + void add_message(T tag, const std::string& value) { pbf_writer::add_message(pbf_tag_type(tag), value); } diff --git a/third_party/libosmium/include/protozero/pbf_message.hpp b/third_party/libosmium/include/protozero/pbf_message.hpp index f66604a94..c599cf1ef 100644 --- a/third_party/libosmium/include/protozero/pbf_message.hpp +++ b/third_party/libosmium/include/protozero/pbf_message.hpp @@ -83,6 +83,10 @@ public: return pbf_reader::next(pbf_tag_type(next_tag)); } + bool next(T next_tag, pbf_wire_type type) { + return pbf_reader::next(pbf_tag_type(next_tag), type); + } + T tag() const noexcept { return T(pbf_reader::tag()); } diff --git a/third_party/libosmium/include/protozero/pbf_reader.hpp b/third_party/libosmium/include/protozero/pbf_reader.hpp index 98920fac5..905ca0aad 100644 --- a/third_party/libosmium/include/protozero/pbf_reader.hpp +++ b/third_party/libosmium/include/protozero/pbf_reader.hpp @@ -179,7 +179,7 @@ public: * * @post There is no current field. */ - pbf_reader(const std::pair& data) noexcept + explicit pbf_reader(const std::pair& data) noexcept : m_data(data.first), m_end(data.first + data.second), m_wire_type(pbf_wire_type::unknown), @@ -196,7 +196,7 @@ public: * * @post There is no current field. */ - pbf_reader(const std::string& data) noexcept + explicit pbf_reader(const std::string& data) noexcept : m_data(data.data()), m_end(data.data() + data.size()), m_wire_type(pbf_wire_type::unknown), @@ -367,9 +367,9 @@ public: * @pre There must be no current field. * @post If it returns `true` there is a current field now with the given tag. */ - bool next(pbf_tag_type next_tag, pbf_wire_type wire_type) { + bool next(pbf_tag_type next_tag, pbf_wire_type type) { while (next()) { - if (m_tag == next_tag && m_wire_type == wire_type) { + if (m_tag == next_tag && m_wire_type == type) { return true; } else { skip(); diff --git a/third_party/libosmium/include/protozero/pbf_writer.hpp b/third_party/libosmium/include/protozero/pbf_writer.hpp index 39dd79591..af626bd99 100644 --- a/third_party/libosmium/include/protozero/pbf_writer.hpp +++ b/third_party/libosmium/include/protozero/pbf_writer.hpp @@ -501,6 +501,17 @@ public: add_bytes(tag, value.data(), value.size()); } + /** + * Add "bytes" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag (field number) of the field + * @param value Pointer to zero-delimited value to be written + */ + void add_bytes(pbf_tag_type tag, const char* value) { + add_bytes(tag, value, std::strlen(value)); + } + /** * Add "bytes" field to data using vectored input. All the data in the * 2nd and further arguments is "concatenated" with only a single copy diff --git a/third_party/libosmium/include/protozero/types.hpp b/third_party/libosmium/include/protozero/types.hpp index 5e1497262..3dbdaf17b 100644 --- a/third_party/libosmium/include/protozero/types.hpp +++ b/third_party/libosmium/include/protozero/types.hpp @@ -16,6 +16,7 @@ documentation. * @brief Contains the declaration of low-level types used in the pbf format. */ +#include #include #include #include @@ -138,6 +139,11 @@ public: return m_size; } + /// Returns true if size is 0. + constexpr bool empty() const noexcept { + return m_size == 0; + } + /** * Convert data view to string. * @@ -178,7 +184,7 @@ inline void swap(data_view& lhs, data_view& rhs) noexcept { * @param rhs Second object. */ inline bool operator==(const data_view& lhs, const data_view& rhs) noexcept { - return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data()); + return lhs.size() == rhs.size() && std::equal(lhs.data(), lhs.data() + lhs.size(), rhs.data()); } /** diff --git a/third_party/libosmium/include/protozero/version.hpp b/third_party/libosmium/include/protozero/version.hpp index 9103bdc57..6d828236f 100644 --- a/third_party/libosmium/include/protozero/version.hpp +++ b/third_party/libosmium/include/protozero/version.hpp @@ -23,12 +23,12 @@ documentation. #define PROTOZERO_VERSION_MINOR 5 /// The patch number -#define PROTOZERO_VERSION_PATCH 1 +#define PROTOZERO_VERSION_PATCH 2 /// The complete version number #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) /// Version number as string -#define PROTOZERO_VERSION_STRING "1.5.1" +#define PROTOZERO_VERSION_STRING "1.5.2" #endif // PROTOZERO_VERSION_HPP diff --git a/third_party/libosmium/test/CMakeLists.txt b/third_party/libosmium/test/CMakeLists.txt index 68f8887c4..b23c39a66 100644 --- a/third_party/libosmium/test/CMakeLists.txt +++ b/third_party/libosmium/test/CMakeLists.txt @@ -58,6 +58,9 @@ function(add_unit_test _tgroup _tname) message(" Adding libs: ${_param_LIBS}") endif() target_link_libraries(${_testid} ${_param_LIBS}) + if(_param_LIBS MATCHES "lpthread") + set_pthread_on_target(${_testid}) + endif() endif() add_test(NAME ${_testid} @@ -119,6 +122,7 @@ endif() # #----------------------------------------------------------------------------- add_unit_test(area test_area_id) +add_unit_test(area test_assembler) add_unit_test(area test_node_ref_segment) add_unit_test(osm test_area) @@ -138,6 +142,8 @@ add_unit_test(osm test_way) add_unit_test(memory test_buffer_basics) add_unit_test(memory test_buffer_node) add_unit_test(memory test_buffer_purge) +add_unit_test(memory test_callback_buffer) +add_unit_test(memory test_item) add_unit_test(memory test_type_is_compatible) add_unit_test(builder test_attr) @@ -157,6 +163,9 @@ add_unit_test(geom test_tile) add_unit_test(geom test_wkb) add_unit_test(geom test_wkt) +add_unit_test(handler test_check_order_handler) +add_unit_test(handler test_dynamic_handler) + add_unit_test(index test_id_set) add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND}) add_unit_test(index test_file_based_index) @@ -178,13 +187,24 @@ add_unit_test(io test_writer ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRAR 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(relations test_members_database) +add_unit_test(relations test_read_relations ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) +add_unit_test(relations test_relations_database) +add_unit_test(relations test_relations_manager ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) + +add_unit_test(storage test_item_stash) + add_unit_test(tags test_filter) add_unit_test(tags test_operators) add_unit_test(tags test_tag_list) +add_unit_test(tags test_tag_matcher) +add_unit_test(tags test_tags_filter) add_unit_test(thread test_pool ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) +add_unit_test(thread test_util ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(util test_cast_with_assert) +add_unit_test(util test_config) add_unit_test(util test_delta) add_unit_test(util test_double) add_unit_test(util test_file) @@ -193,6 +213,9 @@ add_unit_test(util test_memory_mapping) add_unit_test(util test_minmax) add_unit_test(util test_options) add_unit_test(util test_string) +add_unit_test(util test_string_matcher) +add_unit_test(util test_timer_disabled) +add_unit_test(util test_timer_enabled) #----------------------------------------------------------------------------- diff --git a/third_party/libosmium/test/data-tests/CMakeLists.txt b/third_party/libosmium/test/data-tests/CMakeLists.txt index a36c31cfd..eed19f32f 100644 --- a/third_party/libosmium/test/data-tests/CMakeLists.txt +++ b/third_party/libosmium/test/data-tests/CMakeLists.txt @@ -37,6 +37,7 @@ include_directories("../include") #----------------------------------------------------------------------------- file(GLOB TESTCASE_CPPS testcases/*.cpp) add_executable(testdata-testcases testdata-testcases.cpp ${TESTCASE_CPPS}) +set_pthread_on_target(testdata-testcases) target_link_libraries(testdata-testcases ${OSMIUM_XML_LIBRARIES} ) @@ -54,6 +55,7 @@ set_tests_properties(testdata-testcases PROPERTIES # #----------------------------------------------------------------------------- add_executable(testdata-xml testdata-xml.cpp) +set_pthread_on_target(testdata-xml) target_link_libraries(testdata-xml ${OSMIUM_XML_LIBRARIES} ) @@ -71,6 +73,7 @@ set_tests_properties(testdata-xml PROPERTIES # #----------------------------------------------------------------------------- add_executable(testdata-overview testdata-overview.cpp) +set_pthread_on_target(testdata-overview) target_link_libraries(testdata-overview ${OSMIUM_XML_LIBRARIES} ${GDAL_LIBRARIES} @@ -89,11 +92,15 @@ set_tests_properties(testdata-overview PROPERTIES #----------------------------------------------------------------------------- find_program(RUBY ruby) -find_package(Gem COMPONENTS json) +if(RUBY) + find_package(Gem COMPONENTS json) +endif() + find_program(SPATIALITE spatialite) if(RUBY AND GEM_json_FOUND AND SPATIALITE) add_executable(testdata-multipolygon testdata-multipolygon.cpp) + set_pthread_on_target(testdata-multipolygon) target_link_libraries(testdata-multipolygon ${OSMIUM_XML_LIBRARIES} ${GDAL_LIBRARIES} @@ -106,6 +113,7 @@ if(RUBY AND GEM_json_FOUND AND SPATIALITE) -P ${CMAKE_CURRENT_SOURCE_DIR}/run-testdata-multipolygon.cmake) set_tests_properties(testdata-multipolygon PROPERTIES LABELS "data;slow") + configure_file(multipolygon.qgs ${CMAKE_CURRENT_BINARY_DIR}/multipolygon.qgs @ONLY) else() message(WARNING "Disabled testdata-multipolygon test because 'ruby' and/or 'json' ruby gem and/or 'spatialite' was not found") endif() diff --git a/third_party/libosmium/test/data-tests/multipolygon.qgs b/third_party/libosmium/test/data-tests/multipolygon.qgs index 5553670bb..cf121c396 100644 --- a/third_party/libosmium/test/data-tests/multipolygon.qgs +++ b/third_party/libosmium/test/data-tests/multipolygon.qgs @@ -1,880 +1,1213 @@ - - - - - degrees - - 0.77500024999999972 - -0.84791712574962541 - 10.22498975000000065 - 3.94791712574962572 - - 0 - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - - - - - - - - - - - - - - - - - - lerrors20140228172357933 - dbname='./multipolygon.db' table="lerrors" (GEOMETRY) sql= - - - - - - Error Lines - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - spatialite - COALESCE( "OGC_FID", '<NULL>' ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 0 - OGC_FID - - - - - - - - - - - - . - - 0 - . - generatedlayout - - - - - - multipolygons20140221151811742 - dbname='./multipolygon.db' table="multipolygons" (GEOMETRY) sql= - - - - - - multipolygons - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - spatialite - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 0 - OGC_FID - - - - - - - - - . - - 0 - . - generatedlayout - - - - - - perrors20140228163658956 - dbname='./multipolygon.db' table="perrors" (GEOMETRY) sql= - - - - - - Error Points - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - spatialite - COALESCE( "OGC_FID", '<NULL>' ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 0 - OGC_FID - - - - - - - - - - - - . - - 0 - . - generatedlayout - - - - - - - - - - - 0.82500024999999999 - -0.35415386986094277 - 8.17498974999999994 - 3.45415386986094308 - - - - - - - false - - mp test - - - - 2 - true - D - - - - true - - false - - NONE - - - - - - - - 0 - 255 - 255 - 255 - 255 - 255 - 255 - - - - - - - - - - - EPSG:4326 - - - - - - true - 255 - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + degrees + + 0.77500024999999972 + 0.97250000000000003 + 10.22498975000000065 + 2.12750000000000039 + + 0 + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + + + perrors20140228163658956 + lerrors20140228172357933 + multipolygons20140221151811742 + labels20140221110837046 + nodes20140221105814325 + ways20140221105814338 + multipolygons20140224153504671 + titles20140220153751089 + grid20140213154358889 + + + + + + + + + + + + + + + + + + lerrors20140228172357933 + dbname='./multipolygon.db' table="lerrors" (GEOMETRY) sql= + + + + Error Lines + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + spatialite + COALESCE( "OGC_FID", '<NULL>' ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + OGC_FID + + + + . + + + + + + + + + + + + + + + + . + + 0 + + + 0 + generatedlayout + + + + + + + + multipolygons20140221151811742 + dbname='./multipolygon.db' table="multipolygons" (GEOMETRY) sql= + + + + multipolygons + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + OGC_FID + + + + . + + + + + + + + + + + + . + + 0 + + + 0 + generatedlayout + + + + + + + + perrors20140228163658956 + dbname='./multipolygon.db' table="perrors" (GEOMETRY) sql= + + + + Error Points + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + spatialite + COALESCE( "OGC_FID", '<NULL>' ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + OGC_FID + + + + . + + + + + + + + + + + + + + + + . + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + 0.82500024999999999 + -0.35415386986094277 + 8.17498974999999994 + 3.45415386986094308 + + + + + + + false + + mp test + + + + 2 + true + D + + + + true + + false + + NONE + + + + + + + + 0 + 255 + 255 + 255 + 255 + 255 + 255 + + + + + + + + + + + false + + + EPSG:4326 + + + + + + true + 255 + + + + + meters + m2 + + + + diff --git a/third_party/libosmium/test/data-tests/testcases/test-100.cpp b/third_party/libosmium/test/data-tests/testcases/test-100.cpp index feafe771a..df616429b 100644 --- a/third_party/libosmium/test/data-tests/testcases/test-100.cpp +++ b/third_party/libosmium/test/data-tests/testcases/test-100.cpp @@ -1,4 +1,8 @@ +#include +#include +#include + #include "common.hpp" class TestHandler100 : public osmium::handler::Handler { @@ -9,17 +13,18 @@ public: osmium::handler::Handler() { } - void node(osmium::Node& node) { + void node(const osmium::Node& node) { + constexpr const double epsilon = 0.00000001; if (node.id() == 100000) { REQUIRE(node.version() == 1); - REQUIRE(node.timestamp() == osmium::Timestamp("2014-01-01T00:00:00Z")); + REQUIRE(node.timestamp() == osmium::Timestamp{"2014-01-01T00:00:00Z"}); REQUIRE(node.uid() == 1); - REQUIRE(!strcmp(node.user(), "test")); + REQUIRE(!std::strcmp(node.user(), "test")); REQUIRE(node.changeset() == 1); - REQUIRE(node.location().lon() == 1.02); - REQUIRE(node.location().lat() == 1.02); + REQUIRE(std::abs(node.location().lon() - 1.02) < epsilon); + REQUIRE(std::abs(node.location().lat() - 1.02) < epsilon); } else { - throw std::runtime_error("Unknown ID"); + throw std::runtime_error{"Unknown ID"}; } } @@ -28,10 +33,10 @@ public: TEST_CASE("100") { SECTION("test 100") { - osmium::io::Reader reader(dirname + "/1/100/data.osm"); + osmium::io::Reader reader{dirname + "/1/100/data.osm"}; - CheckBasicsHandler check_basics_handler(100, 1, 0, 0); - CheckWKTHandler check_wkt_handler(dirname, 100); + CheckBasicsHandler check_basics_handler{100, 1, 0, 0}; + CheckWKTHandler check_wkt_handler{dirname, 100}; TestHandler100 test_handler; osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler); diff --git a/third_party/libosmium/test/data-tests/testcases/test-101.cpp b/third_party/libosmium/test/data-tests/testcases/test-101.cpp index de2a5fdb7..1693a71ff 100644 --- a/third_party/libosmium/test/data-tests/testcases/test-101.cpp +++ b/third_party/libosmium/test/data-tests/testcases/test-101.cpp @@ -1,4 +1,7 @@ +#include +#include + #include "common.hpp" class TestHandler101 : public osmium::handler::Handler { @@ -9,19 +12,20 @@ public: osmium::handler::Handler() { } - void node(osmium::Node& node) { + void node(const osmium::Node& node) { + constexpr const double epsilon = 0.00000001; if (node.id() == 101000) { REQUIRE(node.version() == 1); - REQUIRE(node.location().lon() == 1.12); - REQUIRE(node.location().lat() == 1.02); + REQUIRE(std::abs(node.location().lon() - 1.12) < epsilon); + REQUIRE(std::abs(node.location().lat() - 1.02) < epsilon); } else if (node.id() == 101001) { REQUIRE(node.version() == 1); - REQUIRE(node.location().lon() == 1.12); - REQUIRE(node.location().lat() == 1.03); + REQUIRE(std::abs(node.location().lon() - 1.12) < epsilon); + REQUIRE(std::abs(node.location().lat() - 1.03) < epsilon); } else if (node.id() == 101002) { } else if (node.id() == 101003) { } else { - throw std::runtime_error("Unknown ID"); + throw std::runtime_error{"Unknown ID"}; } } @@ -30,10 +34,10 @@ public: TEST_CASE("101") { SECTION("test 101") { - osmium::io::Reader reader(dirname + "/1/101/data.osm"); + osmium::io::Reader reader{dirname + "/1/101/data.osm"}; - CheckBasicsHandler check_basics_handler(101, 4, 0, 0); - CheckWKTHandler check_wkt_handler(dirname, 101); + CheckBasicsHandler check_basics_handler{101, 4, 0, 0}; + CheckWKTHandler check_wkt_handler{dirname, 101}; TestHandler101 test_handler; osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler); diff --git a/third_party/libosmium/test/data-tests/testcases/test-110.cpp b/third_party/libosmium/test/data-tests/testcases/test-110.cpp index 16b039b1b..72fbb4dfb 100644 --- a/third_party/libosmium/test/data-tests/testcases/test-110.cpp +++ b/third_party/libosmium/test/data-tests/testcases/test-110.cpp @@ -1,4 +1,8 @@ +#include +#include +#include + #include "common.hpp" class TestHandler110 : public osmium::handler::Handler { @@ -10,14 +14,15 @@ public: } void node(const osmium::Node& node) { + constexpr const double epsilon = 0.00000001; if (node.id() == 110000) { - REQUIRE(node.location().lon() == 1.02); - REQUIRE(node.location().lat() == 1.12); + REQUIRE(std::abs(node.location().lon() - 1.02) < epsilon); + REQUIRE(std::abs(node.location().lat() - 1.12) < epsilon); } else if (node.id() == 110001) { - REQUIRE(node.location().lon() == 1.07); - REQUIRE(node.location().lat() == 1.13); + REQUIRE(std::abs(node.location().lon() - 1.07) < epsilon); + REQUIRE(std::abs(node.location().lat() - 1.13) < epsilon); } else { - throw std::runtime_error("Unknown ID"); + throw std::runtime_error{"Unknown ID"}; } } @@ -29,9 +34,9 @@ public: const char *test_id = way.tags().get_value_by_key("test:id"); REQUIRE(test_id); - REQUIRE(!strcmp(test_id, "110")); + REQUIRE(!std::strcmp(test_id, "110")); } else { - throw std::runtime_error("Unknown ID"); + throw std::runtime_error{"Unknown ID"}; } } diff --git a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp index 6d0328c8d..06385fb2b 100644 --- a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp +++ b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp @@ -9,7 +9,7 @@ #include -#include +#include #include #include #include @@ -156,12 +156,12 @@ int main(int argc, char* argv[]) { gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}}; osmium::area::ProblemReporterOGR problem_reporter{dataset}; - osmium::area::Assembler::config_type assembler_config; + osmium::area::AssemblerLegacy::config_type assembler_config; assembler_config.problem_reporter = &problem_reporter; assembler_config.check_roles = true; assembler_config.create_empty_areas = true; assembler_config.debug_level = 2; - osmium::area::MultipolygonCollector collector{assembler_config}; + osmium::area::MultipolygonCollector collector{assembler_config}; std::cerr << "Pass 1...\n"; osmium::io::Reader reader1{input_filename}; diff --git a/third_party/libosmium/test/data-tests/testdata-xml.cpp b/third_party/libosmium/test/data-tests/testdata-xml.cpp index eb984f851..0808d3fb5 100644 --- a/third_party/libosmium/test/data-tests/testdata-xml.cpp +++ b/third_party/libosmium/test/data-tests/testdata-xml.cpp @@ -15,11 +15,11 @@ #include #include -std::string S_(const char* s) { +static std::string S_(const char* s) { return std::string{s}; } -std::string filename(const char* test_id, const char* suffix = "osm") { +static std::string filename(const char* test_id, const char* suffix = "osm") { const char* testdir = getenv("TESTDIR"); if (!testdir) { std::cerr << "You have to set TESTDIR environment variable before running testdata-xml\n"; @@ -48,7 +48,7 @@ struct header_buffer_type { // operations, because they make certain assumptions, for instance that // file contents fit into small buffers. -std::string read_file(const char* test_id) { +static std::string read_file(const char* test_id) { const int fd = osmium::io::detail::open_for_reading(filename(test_id)); assert(fd >= 0); @@ -62,19 +62,20 @@ std::string read_file(const char* test_id) { return input; } -std::string read_gz_file(const char* test_id, const char* suffix) { +static std::string read_gz_file(const char* test_id, const char* suffix) { const int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix)); assert(fd >= 0); osmium::io::GzipDecompressor gzip_decompressor{fd}; - std::string input = gzip_decompressor.read(); + const std::string input = gzip_decompressor.read(); gzip_decompressor.close(); return input; } - -header_buffer_type parse_xml(std::string input) { +// cppcheck-suppress passedByValue +static header_buffer_type parse_xml(std::string input) { + osmium::thread::Pool pool; osmium::io::detail::future_string_queue_type input_queue; osmium::io::detail::future_buffer_queue_type output_queue; std::promise header_promise; @@ -83,7 +84,15 @@ header_buffer_type parse_xml(std::string input) { 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::io::detail::reader_options{}}; + osmium::io::detail::parser_arguments args = { + pool, + input_queue, + output_queue, + header_promise, + osmium::osm_entity_bits::all, + osmium::io::read_meta::yes + }; + osmium::io::detail::XMLParser parser{args}; parser.parse(); header_buffer_type result; @@ -101,463 +110,405 @@ header_buffer_type parse_xml(std::string input) { return result; } -header_buffer_type read_xml(const char* test_id) { - std::string input = read_file(test_id); +static header_buffer_type read_xml(const char* test_id) { + const std::string input = read_file(test_id); return parse_xml(input); } // ============================================= -TEST_CASE("Reading OSM XML 100") { +TEST_CASE("Reading OSM XML 100: Direct") { + header_buffer_type r = read_xml("100-correct_but_no_data"); - SECTION("Direct") { - header_buffer_type r = read_xml("100-correct_but_no_data"); + REQUIRE(r.header.get("generator") == "testdata"); + REQUIRE(0 == r.buffer.committed()); + REQUIRE(! r.buffer); +} - REQUIRE(r.header.get("generator") == "testdata"); - REQUIRE(0 == r.buffer.committed()); - REQUIRE(! r.buffer); +TEST_CASE("Reading OSM XML 100: Using Reader") { + osmium::io::Reader reader{filename("100-correct_but_no_data")}; + + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(0 == buffer.committed()); + REQUIRE(! buffer); + reader.close(); +} + +TEST_CASE("Reading OSM XML 100: Using Reader asking for header only") { + osmium::io::Reader reader{filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing}; + + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + reader.close(); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 101: Direct") { + REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error); + try { + read_xml("101-missing_version"); + } catch (const osmium::format_version_error& e) { + REQUIRE(e.version.empty()); } +} - SECTION("Using Reader") { - osmium::io::Reader reader{filename("100-correct_but_no_data")}; - +TEST_CASE("Reading OSM XML 101: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("101-missing_version")}; const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(0 == buffer.committed()); - REQUIRE(! buffer); - reader.close(); - } - - SECTION("Using Reader asking for header only") { - osmium::io::Reader reader{filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - reader.close(); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 101") { - - SECTION("Direct") { - REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error); - try { - read_xml("101-missing_version"); - } catch (const osmium::format_version_error& e) { - REQUIRE(e.version.empty()); - } - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("101-missing_version")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::format_version_error); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 102") { - - SECTION("Direct") { - REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error); - try { - read_xml("102-wrong_version"); - } catch (const osmium::format_version_error& e) { - REQUIRE(e.version == "0.1"); - } - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("102-wrong_version")}; - - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::format_version_error); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 103") { - - SECTION("Direct") { - REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error); - try { - read_xml("103-old_version"); - } catch (const osmium::format_version_error& e) { - REQUIRE(e.version == "0.5"); - } - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("103-old_version")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::format_version_error); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 104") { - - SECTION("Direct") { - REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error); - try { - read_xml("104-empty_file"); - } catch (const osmium::xml_error& e) { - REQUIRE(e.line == 1); - REQUIRE(e.column == 0); - } - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("104-empty_file")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::xml_error); - } -} - -// ============================================= - -TEST_CASE("Reading OSM XML 105") { - - SECTION("Direct") { - REQUIRE_THROWS_AS(read_xml("105-incomplete_xml_file"), osmium::xml_error); - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("105-incomplete_xml_file")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::xml_error); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 120") { - - SECTION("Direct") { - std::string data = read_gz_file("120-correct_gzip_file_without_data", "osm.gz"); - - REQUIRE(data.size() == 102); - - header_buffer_type r = parse_xml(data); - REQUIRE(r.header.get("generator") == "testdata"); - REQUIRE(0 == r.buffer.committed()); - REQUIRE(! r.buffer); - } - - SECTION("Using Reader") { - osmium::io::Reader reader{filename("120-correct_gzip_file_without_data", "osm.gz")}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(0 == buffer.committed()); - REQUIRE(! buffer); - reader.close(); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 121") { - - SECTION("Direct") { - REQUIRE_THROWS_AS( { - read_gz_file("121-truncated_gzip_file", "osm.gz"); - }, osmium::gzip_error); - } - - SECTION("Using Reader") { - // can throw osmium::gzip_error or osmium::xml_error - REQUIRE_THROWS({ - osmium::io::Reader reader{filename("121-truncated_gzip_file", "osm.gz")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 122") { - - SECTION("Direct") { - REQUIRE_THROWS_AS( { - read_xml("122-no_osm_element"); - }, osmium::xml_error); - } - - SECTION("Using Reader") { - REQUIRE_THROWS_AS({ - osmium::io::Reader reader{filename("122-no_osm_element")}; - const osmium::io::Header header{reader.header()}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - }, osmium::xml_error); - } - -} - -// ============================================= - -TEST_CASE("Reading OSM XML 140") { - - SECTION("Using Reader") { - osmium::io::Reader reader{filename("140-unicode")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); + }(), osmium::format_version_error); +} - int count = 0; - for (const auto& node : buffer.select()) { - ++count; - REQUIRE(node.id() == count); - const osmium::TagList& t = node.tags(); +// ============================================= - const char* uc = t["unicode_char"]; +TEST_CASE("Reading OSM XML 102: Direct") { + REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error); + try { + read_xml("102-wrong_version"); + } catch (const osmium::format_version_error& e) { + REQUIRE(e.version == "0.1"); + } +} - const auto len = atoi(t["unicode_utf8_length"]); - REQUIRE(len == strlen(uc)); +TEST_CASE("Reading OSM XML 102: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("102-wrong_version")}; - REQUIRE(S_(uc) == t["unicode_xml"]); + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }(), osmium::format_version_error); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 103: Direct") { + REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error); + try { + read_xml("103-old_version"); + } catch (const osmium::format_version_error& e) { + REQUIRE(e.version == "0.5"); + } +} + +TEST_CASE("Reading OSM XML 103: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("103-old_version")}; + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }(), osmium::format_version_error); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 104: Direct") { + REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error); + try { + read_xml("104-empty_file"); + } catch (const osmium::xml_error& e) { + REQUIRE(e.line == 1); + REQUIRE(e.column == 0); + } +} + +TEST_CASE("Reading OSM XML 104: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("104-empty_file")}; + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }(), osmium::xml_error); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 105: Direct") { + REQUIRE_THROWS_AS(read_xml("105-incomplete_xml_file"), osmium::xml_error); +} + +TEST_CASE("Reading OSM XML 105: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("105-incomplete_xml_file")}; + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }(), osmium::xml_error); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 120: Direct") { + std::string data = read_gz_file("120-correct_gzip_file_without_data", "osm.gz"); + + REQUIRE(data.size() == 102); + + header_buffer_type r = parse_xml(data); + REQUIRE(r.header.get("generator") == "testdata"); + REQUIRE(0 == r.buffer.committed()); + REQUIRE(! r.buffer); +} + +TEST_CASE("Reading OSM XML 120: Using Reader") { + osmium::io::Reader reader{filename("120-correct_gzip_file_without_data", "osm.gz")}; + + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(0 == buffer.committed()); + REQUIRE(! buffer); + reader.close(); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 121: Direct") { + REQUIRE_THROWS_AS(read_gz_file("121-truncated_gzip_file", "osm.gz"), osmium::gzip_error); +} + +TEST_CASE("Reading OSM XML 121: Using Reader") { + // can throw osmium::gzip_error or osmium::xml_error + REQUIRE_THROWS([](){ + osmium::io::Reader reader{filename("121-truncated_gzip_file", "osm.gz")}; + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }()); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 122: Direct") { + REQUIRE_THROWS_AS(read_xml("122-no_osm_element"), osmium::xml_error); +} + +TEST_CASE("Reading OSM XML 122: Using Reader") { + REQUIRE_THROWS_AS([](){ + osmium::io::Reader reader{filename("122-no_osm_element")}; + const osmium::io::Header header{reader.header()}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }(), osmium::xml_error); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 140: Using Reader") { + osmium::io::Reader reader{filename("140-unicode")}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + + int count = 0; + for (const auto& node : buffer.select()) { + ++count; + REQUIRE(node.id() == count); + const osmium::TagList& t = node.tags(); + + const char* uc = t["unicode_char"]; + + const auto len = atoi(t["unicode_utf8_length"]); + REQUIRE(len == strlen(uc)); + + REQUIRE(S_(uc) == t["unicode_xml"]); // workaround for missing support for u8 string literals on Windows #if !defined(_MSC_VER) - switch (count) { - case 1: - REQUIRE(S_(uc) == u8"a"); - break; - case 2: - REQUIRE(S_(uc) == u8"\u00e4"); - break; - case 3: - REQUIRE(S_(uc) == u8"\u30dc"); - break; - case 4: - REQUIRE(S_(uc) == u8"\U0001d11e"); - break; - case 5: - REQUIRE(S_(uc) == u8"\U0001f6eb"); - break; - default: - REQUIRE(false); // should not be here - } + switch (count) { + case 1: + REQUIRE(S_(uc) == u8"a"); + break; + case 2: + REQUIRE(S_(uc) == u8"\u00e4"); + break; + case 3: + REQUIRE(S_(uc) == u8"\u30dc"); + break; + case 4: + REQUIRE(S_(uc) == u8"\U0001d11e"); + break; + case 5: + REQUIRE(S_(uc) == u8"\U0001f6eb"); + break; + default: + REQUIRE(false); // should not be here + } #endif - } - REQUIRE(count == 5); } + REQUIRE(count == 5); +} +// ============================================= + +TEST_CASE("Reading OSM XML 141: Using Reader") { + osmium::io::Reader reader{filename("141-entities")}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.get(0).type() == osmium::item_type::node); + + const osmium::Node& node = buffer.get(0); + const osmium::TagList& tags = node.tags(); + + REQUIRE(S_(tags["less-than"]) == "<"); + REQUIRE(S_(tags["greater-than"]) == ">"); + REQUIRE(S_(tags["apostrophe"]) == "'"); + REQUIRE(S_(tags["ampersand"]) == "&"); + REQUIRE(S_(tags["quote"]) == "\""); +} + +// ============================================= + +TEST_CASE("Reading OSM XML 142: Using Reader to read nodes") { + osmium::io::Reader reader{filename("142-whitespace")}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + + int count = 0; + for (const auto& node : buffer.select()) { + ++count; + REQUIRE(node.id() == count); + REQUIRE(node.tags().size() == 1); + const osmium::Tag& tag = *(node.tags().begin()); + + switch (count) { + case 1: + REQUIRE(S_(node.user()) == "user name"); + REQUIRE(S_(tag.key()) == "key with space"); + REQUIRE(S_(tag.value()) == "value with space"); + break; + case 2: + REQUIRE(S_(node.user()) == "line\nfeed"); + REQUIRE(S_(tag.key()) == "key with\nlinefeed"); + REQUIRE(S_(tag.value()) == "value with\nlinefeed"); + break; + case 3: + REQUIRE(S_(node.user()) == "carriage\rreturn"); + REQUIRE(S_(tag.key()) == "key with\rcarriage\rreturn"); + REQUIRE(S_(tag.value()) == "value with\rcarriage\rreturn"); + break; + case 4: + REQUIRE(S_(node.user()) == "tab\tulator"); + REQUIRE(S_(tag.key()) == "key with\ttab"); + REQUIRE(S_(tag.value()) == "value with\ttab"); + break; + case 5: + REQUIRE(S_(node.user()) == "unencoded linefeed"); + REQUIRE(S_(tag.key()) == "key with unencoded linefeed"); + REQUIRE(S_(tag.value()) == "value with unencoded linefeed"); + break; + default: + REQUIRE(false); // should not be here + } + } + REQUIRE(count == 5); +} + +TEST_CASE("Reading OSM XML 142: Using Reader to read relation") { + osmium::io::Reader reader{filename("142-whitespace")}; + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + + auto it = buffer.select().begin(); + REQUIRE(it != buffer.select().end()); + REQUIRE(it->id() == 21); + const auto& members = it->members(); + REQUIRE(members.size() == 5); + + int count = 0; + for (const auto& member : members) { + ++count; + switch (count) { + case 1: + REQUIRE(S_(member.role()) == "role with whitespace"); + break; + case 2: + REQUIRE(S_(member.role()) == "role with\nlinefeed"); + break; + case 3: + REQUIRE(S_(member.role()) == "role with\rcarriage\rreturn"); + break; + case 4: + REQUIRE(S_(member.role()) == "role with\ttab"); + break; + case 5: + REQUIRE(S_(member.role()) == "role with unencoded linefeed"); + break; + default: + REQUIRE(false); // should not be here + } + } + REQUIRE(count == 5); } // ============================================= -TEST_CASE("Reading OSM XML 141") { - - SECTION("Using Reader") { - osmium::io::Reader reader{filename("141-entities")}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - REQUIRE(buffer.committed() > 0); - REQUIRE(buffer.get(0).type() == osmium::item_type::node); - - const osmium::Node& node = buffer.get(0); - const osmium::TagList& tags = node.tags(); - - REQUIRE(S_(tags["less-than"]) == "<"); - REQUIRE(S_(tags["greater-than"]) == ">"); - REQUIRE(S_(tags["apostrophe"]) == "'"); - REQUIRE(S_(tags["ampersand"]) == "&"); - REQUIRE(S_(tags["quote"]) == "\""); - } +TEST_CASE("Reading OSM XML 200: Direct") { + header_buffer_type r = read_xml("200-nodes"); + REQUIRE(r.header.get("generator") == "testdata"); + REQUIRE(r.buffer.committed() > 0); + REQUIRE(r.buffer.get(0).type() == osmium::item_type::node); + REQUIRE(r.buffer.get(0).id() == 36966060); + REQUIRE(std::distance(r.buffer.begin(), r.buffer.end()) == 3); } +TEST_CASE("Reading OSM XML 200: Using Reader") { + osmium::io::Reader reader{filename("200-nodes")}; -// ============================================= - -TEST_CASE("Reading OSM XML 142") { - - SECTION("Using Reader to read nodes") { - osmium::io::Reader reader{filename("142-whitespace")}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - - int count = 0; - for (const auto& node : buffer.select()) { - ++count; - REQUIRE(node.id() == count); - REQUIRE(node.tags().size() == 1); - const osmium::Tag& tag = *(node.tags().begin()); - - switch (count) { - case 1: - REQUIRE(S_(node.user()) == "user name"); - REQUIRE(S_(tag.key()) == "key with space"); - REQUIRE(S_(tag.value()) == "value with space"); - break; - case 2: - REQUIRE(S_(node.user()) == "line\nfeed"); - REQUIRE(S_(tag.key()) == "key with\nlinefeed"); - REQUIRE(S_(tag.value()) == "value with\nlinefeed"); - break; - case 3: - REQUIRE(S_(node.user()) == "carriage\rreturn"); - REQUIRE(S_(tag.key()) == "key with\rcarriage\rreturn"); - REQUIRE(S_(tag.value()) == "value with\rcarriage\rreturn"); - break; - case 4: - REQUIRE(S_(node.user()) == "tab\tulator"); - REQUIRE(S_(tag.key()) == "key with\ttab"); - REQUIRE(S_(tag.value()) == "value with\ttab"); - break; - case 5: - REQUIRE(S_(node.user()) == "unencoded linefeed"); - REQUIRE(S_(tag.key()) == "key with unencoded linefeed"); - REQUIRE(S_(tag.value()) == "value with unencoded linefeed"); - break; - default: - REQUIRE(false); // should not be here - } - } - REQUIRE(count == 5); - } - - SECTION("Using Reader to read relation") { - osmium::io::Reader reader{filename("142-whitespace")}; - osmium::memory::Buffer buffer = reader.read(); - reader.close(); - - auto it = buffer.select().begin(); - REQUIRE(it != buffer.select().end()); - REQUIRE(it->id() == 21); - const auto& members = it->members(); - REQUIRE(members.size() == 5); - - int count = 0; - for (const auto& member : members) { - ++count; - switch (count) { - case 1: - REQUIRE(S_(member.role()) == "role with whitespace"); - break; - case 2: - REQUIRE(S_(member.role()) == "role with\nlinefeed"); - break; - case 3: - REQUIRE(S_(member.role()) == "role with\rcarriage\rreturn"); - break; - case 4: - REQUIRE(S_(member.role()) == "role with\ttab"); - break; - case 5: - REQUIRE(S_(member.role()) == "role with unencoded linefeed"); - break; - default: - REQUIRE(false); // should not be here - } - } - REQUIRE(count == 5); - } + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.get(0).type() == osmium::item_type::node); + REQUIRE(buffer.get(0).id() == 36966060); + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3); + reader.close(); } +TEST_CASE("Reading OSM XML 200: Using Reader asking for nodes") { + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::node}; -// ============================================= - -TEST_CASE("Reading OSM XML 200") { - - SECTION("Direct") { - header_buffer_type r = read_xml("200-nodes"); - - REQUIRE(r.header.get("generator") == "testdata"); - REQUIRE(r.buffer.committed() > 0); - REQUIRE(r.buffer.get(0).type() == osmium::item_type::node); - REQUIRE(r.buffer.get(0).id() == 36966060); - REQUIRE(std::distance(r.buffer.begin(), r.buffer.end()) == 3); - } - - SECTION("Using Reader") { - osmium::io::Reader reader{filename("200-nodes")}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(buffer.committed() > 0); - REQUIRE(buffer.get(0).type() == osmium::item_type::node); - REQUIRE(buffer.get(0).id() == 36966060); - REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3); - reader.close(); - } - - SECTION("Using Reader asking for nodes") { - osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::node}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(buffer.committed() > 0); - REQUIRE(buffer.get(0).type() == osmium::item_type::node); - REQUIRE(buffer.get(0).id() == 36966060); - REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3); - reader.close(); - } - - SECTION("Using Reader asking for header only") { - osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::nothing}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - REQUIRE_THROWS({ - reader.read(); - }); - - reader.close(); - } - - SECTION("Using Reader asking for ways") { - osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::way}; - - const osmium::io::Header header{reader.header()}; - REQUIRE(header.get("generator") == "testdata"); - - osmium::memory::Buffer buffer = reader.read(); - REQUIRE(0 == buffer.committed()); - REQUIRE(! buffer); - reader.close(); - } + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.get(0).type() == osmium::item_type::node); + REQUIRE(buffer.get(0).id() == 36966060); + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3); + reader.close(); +} + +TEST_CASE("Reading OSM XML 200: Using Reader asking for header only") { + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::nothing}; + + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + + REQUIRE_FALSE(reader.read()); + REQUIRE_THROWS(reader.read()); + + reader.close(); +} + +TEST_CASE("Reading OSM XML 200: Using Reader asking for ways") { + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::way}; + + const osmium::io::Header header{reader.header()}; + REQUIRE(header.get("generator") == "testdata"); + + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(0 == buffer.committed()); + REQUIRE(! buffer); + reader.close(); } diff --git a/third_party/libosmium/test/examples/CMakeLists.txt b/third_party/libosmium/test/examples/CMakeLists.txt index 90bf76ea9..f6d427d39 100644 --- a/third_party/libosmium/test/examples/CMakeLists.txt +++ b/third_party/libosmium/test/examples/CMakeLists.txt @@ -8,13 +8,40 @@ message(STATUS "Configuring example tests") -file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/t/*) +set(EXAMPLE_TESTS + amenity_list + area_test + change_tags + convert + count + create_pois + debug + dump_internal + filter_discussions + index_lookup + location_cache + pub_names + read + read_with_progress + road_length + tiles +) -foreach(_dir ${_dirs}) +# This is the list of all tests that are in the repository. It should be the +# same as the configured list above, if not we'll find out about that below +# and create a fatal error. +file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/t ${CMAKE_CURRENT_SOURCE_DIR}/t/*) + +foreach(_dir ${EXAMPLE_TESTS}) message(STATUS " adding test: ${_dir}") - add_subdirectory("${_dir}") + add_subdirectory("t/${_dir}") + list(REMOVE_ITEM _dirs ${_dir}) endforeach() +if(NOT ${_dirs} STREQUAL "") + message(FATAL_ERROR "Found example tests that are not configured: ${_dirs}") +endif() + message(STATUS "Configuring example tests - done") diff --git a/third_party/libosmium/test/examples/t/amenity_list/CMakeLists.txt b/third_party/libosmium/test/examples/t/amenity_list/CMakeLists.txt index c12fc7df2..3a99a8a6d 100644 --- a/third_party/libosmium/test/examples/t/amenity_list/CMakeLists.txt +++ b/third_party/libosmium/test/examples/t/amenity_list/CMakeLists.txt @@ -1,7 +1,13 @@ -add_test(NAME examples_amenity_list +add_test(NAME examples_amenity_list_node COMMAND osmium_amenity_list ${CMAKE_CURRENT_SOURCE_DIR}/node.osm) -set_tests_properties(examples_amenity_list PROPERTIES +set_tests_properties(examples_amenity_list_node PROPERTIES PASS_REGULAR_EXPRESSION " 8\\.8721, 53\\.0966 post_office") +add_test(NAME examples_amenity_list_area + COMMAND osmium_amenity_list ${CMAKE_CURRENT_SOURCE_DIR}/area.osm) + +set_tests_properties(examples_amenity_list_area PROPERTIES + PASS_REGULAR_EXPRESSION " 8\\.5839, 53\\.5602 restaurant") + diff --git a/third_party/libosmium/test/examples/t/amenity_list/area.osm b/third_party/libosmium/test/examples/t/amenity_list/area.osm new file mode 100644 index 000000000..bbf60fb7a --- /dev/null +++ b/third_party/libosmium/test/examples/t/amenity_list/area.osm @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/area_test/CMakeLists.txt b/third_party/libosmium/test/examples/t/area_test/CMakeLists.txt new file mode 100644 index 000000000..81899a161 --- /dev/null +++ b/third_party/libosmium/test/examples/t/area_test/CMakeLists.txt @@ -0,0 +1,25 @@ + +add_test(NAME examples_area_test_help + COMMAND osmium_area_test -h) + +set_tests_properties(examples_area_test_help PROPERTIES + PASS_REGULAR_EXPRESSION "^osmium_area_test .* OSMFILE") + +add_test(NAME examples_area_test_data + COMMAND osmium_area_test ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_area_test_data PROPERTIES + PASS_REGULAR_EXPRESSION "\nWarning! Some member ways missing for these multipolygon relations: 701901\n$") + +add_test(NAME examples_area_test_dump + COMMAND osmium_area_test -o ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_area_test_dump PROPERTIES + PASS_REGULAR_EXPRESSION " id=1403801") + +add_test(NAME examples_area_test_wkt + COMMAND osmium_area_test -w ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_area_test_wkt PROPERTIES + PASS_REGULAR_EXPRESSION "MULTIPOLYGON\\(\\(\\(7.11 1.01,7.14 1.01,7.14 1.04,7.11 1.04,7.11 1.01\\)\\)\\)\n") + diff --git a/third_party/libosmium/test/examples/t/area_test/data.osm b/third_party/libosmium/test/examples/t/area_test/data.osm new file mode 100644 index 000000000..cbdeeaa28 --- /dev/null +++ b/third_party/libosmium/test/examples/t/area_test/data.osm @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/change_tags/.gitattributes b/third_party/libosmium/test/examples/t/change_tags/.gitattributes new file mode 100644 index 000000000..f4111dd15 --- /dev/null +++ b/third_party/libosmium/test/examples/t/change_tags/.gitattributes @@ -0,0 +1 @@ +result.osm text eol=lf diff --git a/third_party/libosmium/test/examples/t/change_tags/CMakeLists.txt b/third_party/libosmium/test/examples/t/change_tags/CMakeLists.txt new file mode 100644 index 000000000..b054aa9be --- /dev/null +++ b/third_party/libosmium/test/examples/t/change_tags/CMakeLists.txt @@ -0,0 +1,9 @@ + +add_test(NAME examples_change_tags + COMMAND osmium_change_tags ${CMAKE_CURRENT_SOURCE_DIR}/data.osm ${CMAKE_CURRENT_BINARY_DIR}/result.osm) + +add_test(NAME examples_change_tags_compare + COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_CURRENT_SOURCE_DIR}/result.osm ${CMAKE_CURRENT_BINARY_DIR}/result.osm) + +set_tests_properties(examples_change_tags_compare PROPERTIES DEPENDS example_change_tags) + diff --git a/third_party/libosmium/test/examples/t/change_tags/data.osm b/third_party/libosmium/test/examples/t/change_tags/data.osm new file mode 100644 index 000000000..d65c716c5 --- /dev/null +++ b/third_party/libosmium/test/examples/t/change_tags/data.osm @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/change_tags/result.osm b/third_party/libosmium/test/examples/t/change_tags/result.osm new file mode 100644 index 000000000..1898b8952 --- /dev/null +++ b/third_party/libosmium/test/examples/t/change_tags/result.osm @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/convert/CMakeLists.txt b/third_party/libosmium/test/examples/t/convert/CMakeLists.txt new file mode 100644 index 000000000..a5f06fa2e --- /dev/null +++ b/third_party/libosmium/test/examples/t/convert/CMakeLists.txt @@ -0,0 +1,46 @@ + +add_test(NAME examples_convert_help + COMMAND osmium_convert -h) + +set_tests_properties(examples_convert_help PROPERTIES + PASS_REGULAR_EXPRESSION "^osmium_convert .*OUTFILE") + + +add_test(NAME examples_convert_xml_debug + COMMAND osmium_convert -t debug ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_convert_xml_debug PROPERTIES + PASS_REGULAR_EXPRESSION "node 701000\n version: 1 visible") + + +add_test(NAME examples_convert_xml_opl + COMMAND osmium_convert -f osm -t opl ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_convert_xml_opl PROPERTIES + PASS_REGULAR_EXPRESSION "n701001 v1 dV c1 t2014-01-01T00:00:00Z i1 utest T x7.11 y1.04") + + +add_test(NAME examples_convert_xml_pbf + COMMAND osmium_convert -t pbf ${CMAKE_CURRENT_SOURCE_DIR}/data.osm -) + + +add_test(NAME examples_convert_xml_xml + COMMAND osmium_convert -t xml ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_convert_xml_xml PROPERTIES + PASS_REGULAR_EXPRESSION " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/count/CMakeLists.txt b/third_party/libosmium/test/examples/t/count/CMakeLists.txt new file mode 100644 index 000000000..07ac6f474 --- /dev/null +++ b/third_party/libosmium/test/examples/t/count/CMakeLists.txt @@ -0,0 +1,8 @@ + +add_test(NAME examples_count + COMMAND osmium_count ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_count PROPERTIES + PASS_REGULAR_EXPRESSION "^Nodes: 2\nWays: 1\nRelations: 1\n\nMemory used:" +) + diff --git a/third_party/libosmium/test/examples/t/count/data.osm b/third_party/libosmium/test/examples/t/count/data.osm new file mode 100644 index 000000000..2a62ece24 --- /dev/null +++ b/third_party/libosmium/test/examples/t/count/data.osm @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/create_pois/CMakeLists.txt b/third_party/libosmium/test/examples/t/create_pois/CMakeLists.txt new file mode 100644 index 000000000..a2aa7cb3e --- /dev/null +++ b/third_party/libosmium/test/examples/t/create_pois/CMakeLists.txt @@ -0,0 +1,13 @@ + +add_test(NAME examples_create_pois_okay + COMMAND osmium_create_pois -) + +set_tests_properties(examples_create_pois_okay PROPERTIES + PASS_REGULAR_EXPRESSION "^n-1 v1 dV c0 t....-..-..T..:..:..Z i0 u Tamenity=post_box x1\\.23 y3\\.45\nn-2 v1 dV c0 t....-..-..T..:..:..Z i0 u Tamenity=restaurant,name=Chez%20%OSM x1\\.24 y3\\.46\n$" +) + +add_test(NAME examples_create_pois_unknown_file_type + COMMAND osmium_create_pois foo) + +set_tests_properties(examples_create_pois_unknown_file_type PROPERTIES WILL_FAIL true) + diff --git a/third_party/libosmium/test/examples/t/debug/CMakeLists.txt b/third_party/libosmium/test/examples/t/debug/CMakeLists.txt new file mode 100644 index 000000000..6e2462c00 --- /dev/null +++ b/third_party/libosmium/test/examples/t/debug/CMakeLists.txt @@ -0,0 +1,55 @@ + +add_test(NAME examples_debug_all + COMMAND osmium_debug ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + +set_tests_properties(examples_debug_all PROPERTIES + PASS_REGULAR_EXPRESSION " id=1\n.* id=2\n.* id=10\n.* id=20\n" +) + + +add_test(NAME examples_debug_nodes + COMMAND osmium_debug ${CMAKE_CURRENT_SOURCE_DIR}/data.osm n) + +set_tests_properties(examples_debug_nodes PROPERTIES + PASS_REGULAR_EXPRESSION "id=1\n.* id=2\n" +) + +set_tests_properties(examples_debug_nodes PROPERTIES + FAIL_REGULAR_EXPRESSION "id=10\n" +) + + +add_test(NAME examples_debug_ways + COMMAND osmium_debug ${CMAKE_CURRENT_SOURCE_DIR}/data.osm w) + +set_tests_properties(examples_debug_ways PROPERTIES + PASS_REGULAR_EXPRESSION " id=10\n" +) + +set_tests_properties(examples_debug_ways PROPERTIES + FAIL_REGULAR_EXPRESSION "id=20\n" +) + + +add_test(NAME examples_debug_relations + COMMAND osmium_debug ${CMAKE_CURRENT_SOURCE_DIR}/data.osm r) + +set_tests_properties(examples_debug_relations PROPERTIES + PASS_REGULAR_EXPRESSION " id=20\n" +) + +set_tests_properties(examples_debug_relations PROPERTIES + FAIL_REGULAR_EXPRESSION "id=10\n" +) + +add_test(NAME examples_debug_changesets + COMMAND osmium_debug ${CMAKE_CURRENT_SOURCE_DIR}/changesets.osm c) + +set_tests_properties(examples_debug_changesets PROPERTIES + PASS_REGULAR_EXPRESSION " id=15449962\n" +) + +set_tests_properties(examples_debug_changesets PROPERTIES + FAIL_REGULAR_EXPRESSION "id=10\n" +) + diff --git a/third_party/libosmium/test/examples/t/debug/changesets.osm b/third_party/libosmium/test/examples/t/debug/changesets.osm new file mode 100644 index 000000000..61ee023c4 --- /dev/null +++ b/third_party/libosmium/test/examples/t/debug/changesets.osm @@ -0,0 +1,7 @@ + + + + + + + diff --git a/third_party/libosmium/test/examples/t/debug/data.osm b/third_party/libosmium/test/examples/t/debug/data.osm new file mode 100644 index 000000000..15fbda3ec --- /dev/null +++ b/third_party/libosmium/test/examples/t/debug/data.osm @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/dump_internal/CMakeLists.txt b/third_party/libosmium/test/examples/t/dump_internal/CMakeLists.txt new file mode 100644 index 000000000..fa942ef1a --- /dev/null +++ b/third_party/libosmium/test/examples/t/dump_internal/CMakeLists.txt @@ -0,0 +1,36 @@ + +add_test(NAME examples_dump_internal + COMMAND osmium_dump_internal ${CMAKE_CURRENT_SOURCE_DIR}/data.osm ${CMAKE_CURRENT_BINARY_DIR}/out) + + +add_test(NAME examples_dump_internal_index_nodes + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/nodes.idx --type=offset --dump) + +set_tests_properties(examples_dump_internal_index_nodes PROPERTIES + DEPENDS examples_dump_internal + PASS_REGULAR_EXPRESSION "^701000 .*\n701001 .*\n") + + +add_test(NAME examples_dump_internal_index_ways + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/ways.idx --type=offset --dump) + +set_tests_properties(examples_dump_internal_index_ways PROPERTIES + DEPENDS examples_dump_internal + PASS_REGULAR_EXPRESSION "^701800 .*\n701801 .*\n") + + +add_test(NAME examples_dump_internal_map_node2way_dump + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --dump) + +set_tests_properties(examples_dump_internal_map_node2way_dump PROPERTIES + DEPENDS examples_dump_internal + PASS_REGULAR_EXPRESSION "^701000 701800\n701000 701801\n701001 701800\n") + + +add_test(NAME examples_dump_internal_map_node2way_search + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --search=701002) + +set_tests_properties(examples_dump_internal_map_node2way_search PROPERTIES + DEPENDS examples_dump_internal + PASS_REGULAR_EXPRESSION "^701002 701800\n701002 701801\n$") + diff --git a/third_party/libosmium/test/examples/t/dump_internal/data.osm b/third_party/libosmium/test/examples/t/dump_internal/data.osm new file mode 100644 index 000000000..cbdeeaa28 --- /dev/null +++ b/third_party/libosmium/test/examples/t/dump_internal/data.osm @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/filter_discussions/CMakeLists.txt b/third_party/libosmium/test/examples/t/filter_discussions/CMakeLists.txt new file mode 100644 index 000000000..21e8f7208 --- /dev/null +++ b/third_party/libosmium/test/examples/t/filter_discussions/CMakeLists.txt @@ -0,0 +1,10 @@ + +add_test(NAME examples_filter_discussions + COMMAND osmium_filter_discussions ${CMAKE_CURRENT_SOURCE_DIR}/changesets.osm -) + +set_tests_properties(examples_filter_discussions PROPERTIES + FAIL_REGULAR_EXPRESSION " + + + + + + fake comment + + + + diff --git a/third_party/libosmium/test/examples/t/index_lookup/CMakeLists.txt b/third_party/libosmium/test/examples/t/index_lookup/CMakeLists.txt new file mode 100644 index 000000000..ac4c43463 --- /dev/null +++ b/third_party/libosmium/test/examples/t/index_lookup/CMakeLists.txt @@ -0,0 +1,46 @@ + +add_test(NAME examples_index_lookup_help + COMMAND osmium_index_lookup -h) + +set_tests_properties(examples_index_lookup_help PROPERTIES + PASS_REGULAR_EXPRESSION "^Usage: osmium_index_lookup") + +# Fails with message if index file doesn't exist +add_test(NAME examples_index_lookup_no_file + COMMAND osmium_index_lookup --list=file_does_not_exist --type=location --dump) + +set_tests_properties(examples_index_lookup_no_file PROPERTIES + PASS_REGULAR_EXPRESSION "^Can not open file") + + +# Fails with message if --type option is not used +add_test(NAME examples_index_lookup_no_type_option + COMMAND osmium_index_lookup --list=file_does_not_exist --dump) + +set_tests_properties(examples_index_lookup_no_type_option PROPERTIES + PASS_REGULAR_EXPRESSION "^Need --type argument.") + + +# Fails with message if --type option is used with unknown type +add_test(NAME examples_index_lookup_unknown_type + COMMAND osmium_index_lookup --list=file_does_not_exist --type=UNKNOWN --dump) + +set_tests_properties(examples_index_lookup_unknown_type PROPERTIES + PASS_REGULAR_EXPRESSION "^Unknown type 'UNKNOWN'") + + +# Fails with message when combining options --array and --list +add_test(NAME examples_index_lookup_array_list + COMMAND osmium_index_lookup --list=a --array=b) + +set_tests_properties(examples_index_lookup_array_list PROPERTIES + PASS_REGULAR_EXPRESSION "^Need option --array or --list, but not both\n$") + + +# Fails with message when combining options --dump and --search +add_test(NAME examples_index_lookup_dump_search + COMMAND osmium_index_lookup --list=x --dump --search=123) + +set_tests_properties(examples_index_lookup_dump_search PROPERTIES + PASS_REGULAR_EXPRESSION "Need option --dump or --search, but not both") + diff --git a/third_party/libosmium/test/examples/t/location_cache/CMakeLists.txt b/third_party/libosmium/test/examples/t/location_cache/CMakeLists.txt new file mode 100644 index 000000000..8ca8b79e5 --- /dev/null +++ b/third_party/libosmium/test/examples/t/location_cache/CMakeLists.txt @@ -0,0 +1,36 @@ + +add_test(NAME examples_location_cache_create + COMMAND osmium_location_cache_create ${CMAKE_CURRENT_SOURCE_DIR}/data.osm ${CMAKE_CURRENT_BINARY_DIR}/locations.idx) + + +# Fails with message if index file doesn't exist +add_test(NAME examples_location_cache_no_file + COMMAND osmium_location_cache_use ${CMAKE_CURRENT_SOURCE_DIR}/way.osm ${CMAKE_CURRENT_BINARY_DIR}/file_does_not_exist) + +set_tests_properties(examples_location_cache_no_file PROPERTIES + PASS_REGULAR_EXPRESSION "Can not open location cache file") + + +add_test(NAME examples_location_cache_use + COMMAND osmium_location_cache_use ${CMAKE_CURRENT_SOURCE_DIR}/way.osm ${CMAKE_CURRENT_BINARY_DIR}/locations.idx) + +set_tests_properties(examples_location_cache_use PROPERTIES + DEPENDS examples_location_cache_create + PASS_REGULAR_EXPRESSION "^way 20\n node 10 \\(7.11,1.01\\)\n node 11 \\(7.11,1.04\\)\n node 12 \\(7.14,1.04\\)\n$") + + +add_test(NAME examples_location_cache_dump + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/locations.idx --type=location --dump) + +set_tests_properties(examples_location_cache_dump PROPERTIES + DEPENDS examples_location_cache_create + PASS_REGULAR_EXPRESSION "^10 \\(7.11,1.01\\)\n11 \\(7.11,1.04\\)\n12 \\(7.14,1.04\\)\n13 \\(7.14,1.01\\)\n$") + + +add_test(NAME examples_location_cache_search + COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/locations.idx --type=location --search=12) + +set_tests_properties(examples_location_cache_search PROPERTIES + DEPENDS examples_location_cache_create + PASS_REGULAR_EXPRESSION "^12 \\(7.14,1.04\\)\n$") + diff --git a/third_party/libosmium/test/examples/t/location_cache/data.osm b/third_party/libosmium/test/examples/t/location_cache/data.osm new file mode 100644 index 000000000..04cb6fda4 --- /dev/null +++ b/third_party/libosmium/test/examples/t/location_cache/data.osm @@ -0,0 +1,7 @@ + + + + + + + diff --git a/third_party/libosmium/test/examples/t/location_cache/way.osm b/third_party/libosmium/test/examples/t/location_cache/way.osm new file mode 100644 index 000000000..f64a170d9 --- /dev/null +++ b/third_party/libosmium/test/examples/t/location_cache/way.osm @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/pub_names/CMakeLists.txt b/third_party/libosmium/test/examples/t/pub_names/CMakeLists.txt index 9a68ae869..3abefa39f 100644 --- a/third_party/libosmium/test/examples/t/pub_names/CMakeLists.txt +++ b/third_party/libosmium/test/examples/t/pub_names/CMakeLists.txt @@ -1,7 +1,21 @@ -add_test(NAME examples_pub_names - COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pubs.osm) - -set_tests_properties(examples_pub_names PROPERTIES +add_test(NAME examples_pub_names_node + COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pub-node.osm) +set_tests_properties(examples_pub_names_node PROPERTIES PASS_REGULAR_EXPRESSION "^Im Holze\n$") +add_test(NAME examples_pub_names_way + COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pub-way.osm) +set_tests_properties(examples_pub_names_way PROPERTIES + PASS_REGULAR_EXPRESSION "^Vereinsheim\n$") + +add_test(NAME examples_pub_names_noname + COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pub-noname.osm) +set_tests_properties(examples_pub_names_noname PROPERTIES + PASS_REGULAR_EXPRESSION "^pub with unknown name\n$") + +add_test(NAME examples_pub_names_addr + COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pub-addr.osm) +set_tests_properties(examples_pub_names_addr PROPERTIES + PASS_REGULAR_EXPRESSION "^Im Holze\n addr:city: Bremen\n") + diff --git a/third_party/libosmium/test/examples/t/pub_names/pub-addr.osm b/third_party/libosmium/test/examples/t/pub_names/pub-addr.osm new file mode 100644 index 000000000..619996126 --- /dev/null +++ b/third_party/libosmium/test/examples/t/pub_names/pub-addr.osm @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/pub_names/pubs.osm b/third_party/libosmium/test/examples/t/pub_names/pub-node.osm similarity index 100% rename from third_party/libosmium/test/examples/t/pub_names/pubs.osm rename to third_party/libosmium/test/examples/t/pub_names/pub-node.osm diff --git a/third_party/libosmium/test/examples/t/pub_names/pub-noname.osm b/third_party/libosmium/test/examples/t/pub_names/pub-noname.osm new file mode 100644 index 000000000..321788978 --- /dev/null +++ b/third_party/libosmium/test/examples/t/pub_names/pub-noname.osm @@ -0,0 +1,6 @@ + + + + + + diff --git a/third_party/libosmium/test/examples/t/pub_names/pub-way.osm b/third_party/libosmium/test/examples/t/pub_names/pub-way.osm new file mode 100644 index 000000000..aec7a8636 --- /dev/null +++ b/third_party/libosmium/test/examples/t/pub_names/pub-way.osm @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/read/CMakeLists.txt b/third_party/libosmium/test/examples/t/read/CMakeLists.txt new file mode 100644 index 000000000..ff2b7aa58 --- /dev/null +++ b/third_party/libosmium/test/examples/t/read/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_test(NAME examples_read + COMMAND osmium_read ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + diff --git a/third_party/libosmium/test/examples/t/read/data.osm b/third_party/libosmium/test/examples/t/read/data.osm new file mode 100644 index 000000000..98462b619 --- /dev/null +++ b/third_party/libosmium/test/examples/t/read/data.osm @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/read_with_progress/CMakeLists.txt b/third_party/libosmium/test/examples/t/read_with_progress/CMakeLists.txt new file mode 100644 index 000000000..2bc088188 --- /dev/null +++ b/third_party/libosmium/test/examples/t/read_with_progress/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_test(NAME examples_read_with_progress + COMMAND osmium_read_with_progress ${CMAKE_CURRENT_SOURCE_DIR}/data.osm) + diff --git a/third_party/libosmium/test/examples/t/read_with_progress/data.osm b/third_party/libosmium/test/examples/t/read_with_progress/data.osm new file mode 100644 index 000000000..98462b619 --- /dev/null +++ b/third_party/libosmium/test/examples/t/read_with_progress/data.osm @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/examples/t/tiles/CMakeLists.txt b/third_party/libosmium/test/examples/t/tiles/CMakeLists.txt new file mode 100644 index 000000000..5a4fbaa9e --- /dev/null +++ b/third_party/libosmium/test/examples/t/tiles/CMakeLists.txt @@ -0,0 +1,18 @@ + +add_test(NAME examples_tiles_zoom_too_large + COMMAND osmium_tiles 50 1 1) + +set_tests_properties(examples_tiles_zoom_too_large PROPERTIES WILL_FAIL true) + +add_test(NAME examples_tiles_location_invalid + COMMAND osmium_tiles 1 200 200) + +set_tests_properties(examples_tiles_location_invalid PROPERTIES WILL_FAIL true) + +add_test(NAME examples_tiles_okay + COMMAND osmium_tiles 8 55.3 11.7) + +set_tests_properties(examples_tiles_okay PROPERTIES + PASS_REGULAR_EXPRESSION "^WGS84: lon=55.3 lat=11.7\nMercator: x=.*\nTile: zoom=8 x=167 y=119\n$" +) + diff --git a/third_party/libosmium/test/include/catch.hpp b/third_party/libosmium/test/include/catch.hpp index 3d18eadb1..7c351e931 100644 --- a/third_party/libosmium/test/include/catch.hpp +++ b/third_party/libosmium/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.9 - * Generated: 2016-11-29 12:14:38.049276 + * Catch v1.9.7 + * Generated: 2017-08-10 23:49:15.233907 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -40,6 +40,8 @@ #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" + # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif @@ -60,21 +62,6 @@ // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include -#include - // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -89,11 +76,15 @@ // 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_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -129,11 +120,46 @@ # endif # if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) # endif #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# endif + +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -160,10 +186,6 @@ # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif - // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -173,6 +195,8 @@ // Visual C++ #ifdef _MSC_VER +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR @@ -181,6 +205,8 @@ #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -199,7 +225,7 @@ // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ ( defined __clang__ && __clang_major__ >= 3 ) #define CATCH_INTERNAL_CONFIG_COUNTER @@ -246,6 +272,12 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) +# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L @@ -268,21 +300,42 @@ #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) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_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) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_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) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for +// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) # define CATCH_CONFIG_COUNTER #endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_SHUFFLE +#endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: @@ -315,6 +368,20 @@ # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include + namespace Catch { struct IConfig; @@ -352,14 +419,14 @@ namespace Catch { }; template - inline void deleteAll( ContainerT& container ) { + 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 ) { + void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) @@ -367,7 +434,9 @@ namespace Catch { } bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); @@ -387,8 +456,8 @@ namespace Catch { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; @@ -397,7 +466,7 @@ namespace Catch { bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; - std::string file; + char const* file; std::size_t line; }; @@ -431,15 +500,12 @@ namespace Catch { #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 {} @@ -565,10 +631,6 @@ namespace Catch { #pragma clang diagnostic pop #endif -#include -#include -#include - namespace Catch { class TestCase; @@ -712,59 +774,76 @@ void registerTestCaseFunction /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + #endif // #included from: internal/catch_capture.hpp @@ -832,27 +911,83 @@ namespace Catch { namespace Catch { + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + + private: + DecomposedExpression& operator = (DecomposedExpression const&); + }; + struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, + AssertionInfo(); + AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg = ""); - std::string macroName; + char const * macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + char const * capturedExpression; ResultDisposition::Flags resultDisposition; + char const * secondArg; }; struct AssertionResultData { - AssertionResultData() : resultType( ResultWas::Unknown ) {} + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} - std::string reconstructedExpression; + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } + + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; + bool negated; + bool parenthesized; }; class AssertionResult { @@ -879,6 +1014,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; protected: AssertionInfo m_info; @@ -894,313 +1031,161 @@ namespace Catch { namespace Matchers { namespace Impl { - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; - 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> { + class MatcherUntypedBase { 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 ); + std::string toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; } - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; private: - Ptr< Matcher > m_matcher; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; - template - class AllOf : public MatcherImpl, ExpressionT> { - public: + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - 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 ) ) + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (!m_matchers[i]->match(arg)) return false; + } return true; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); + description += " and "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - 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() ); + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); 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 ) ) + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (m_matchers[i]->match(arg)) return true; + } return false; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); + description += " or "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - private: - std::vector > > m_matchers; + std::vector const*> m_matchers; }; - } // namespace Generic + template + struct MatchNotOf : MatcherBase { - 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; + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + return !m_underlyingMatcher.match( arg ); } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; + + virtual std::string describe() const CATCH_OVERRIDE { + return "not " + m_underlyingMatcher.toString(); } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; + MatcherBase const& m_underlyingMatcher; }; - 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 ){} + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } - 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 ); + // - deprecated: prefer ||, && and ! + template + Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { + return Impl::MatchNotOf( underlyingMatcher ); } - - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAllOf() && m1 && 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 + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAllOf() && m1 && m2 && m3; } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAnyOf() || m1 || 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 ) ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAnyOf() || m1 || m2 || m3; } } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBase; } // namespace Catch @@ -1210,28 +1195,27 @@ namespace Catch { 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.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; - class ResultBuilder { + class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); + ~ResultBuilder(); template ExpressionLhs operator <= ( T const& operand ); @@ -1239,46 +1223,60 @@ namespace Catch { template ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; + 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(); + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - std::string reconstructExpression() const; AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) 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 captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + + void setExceptionGuard(); + void unsetExceptionGuard(); + private: AssertionInfo m_assertionInfo; AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; + + CopyableStream &stream() + { + if(!m_usedStream) + { + m_usedStream = true; + m_stream().oss.str(""); + } + return m_stream(); + } + + static CopyableStream &m_stream() + { + static CopyableStream s; + return s; + } bool m_shouldDebugBreak; bool m_shouldThrow; + bool m_guardException; + bool m_usedStream; }; } // namespace Catch @@ -1293,6 +1291,7 @@ namespace Catch { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include @@ -1318,7 +1317,7 @@ namespace Internal { template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template - inline T& opCast(T const& t) { return const_cast(t); } + 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 @@ -1328,7 +1327,7 @@ namespace Internal { // 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{}; + struct Evaluator{}; template struct Evaluator { @@ -1594,7 +1593,7 @@ std::string toString( std::nullptr_t ); #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); std::string toString( NSObject* const& nsObject ); #endif @@ -1602,6 +1601,7 @@ namespace Detail { extern const std::string unprintableString; + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; @@ -1620,6 +1620,20 @@ namespace Detail { static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; +#else + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; +#endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template - inline std::string rawMemoryToString( const T& object ) { + std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } @@ -1796,90 +1810,159 @@ std::string toString( T const& value ) { 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 +template +class BinaryExpression; +template +class MatchExpression; + +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { 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 + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + ExpressionLhs& operator = ( const ExpressionLhs& ); template - ResultBuilder& operator == ( RhsT const& rhs ) { + BinaryExpression + operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator != ( RhsT const& rhs ) { + BinaryExpression + operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator < ( RhsT const& rhs ) { + BinaryExpression + operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator > ( RhsT const& rhs ) { + BinaryExpression + operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator <= ( RhsT const& rhs ) { + BinaryExpression + operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator >= ( RhsT const& rhs ) { + BinaryExpression + operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator == ( bool rhs ) { + BinaryExpression operator == ( bool rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator != ( bool rhs ) { + BinaryExpression operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { - bool value = m_lhs ? true : false; + m_truthy = m_lhs ? true : false; m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // 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& ); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_lhs ); + } 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() ); + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + BinaryExpression& operator = ( BinaryExpression& ); + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch @@ -1888,7 +1971,7 @@ private: namespace Catch { template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } @@ -1896,6 +1979,14 @@ namespace Catch { return ExpressionLhs( *this, value ); } + template + void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + } // namespace Catch // #included from: catch_message.h @@ -1985,7 +2076,13 @@ namespace Catch { virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + virtual void assertionRun() = 0; }; IResultCapture& getResultCapture(); @@ -1998,11 +2095,19 @@ namespace Catch { #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC +# define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif #endif #include @@ -2017,27 +2122,36 @@ namespace Catch{ // 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 + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) /* NOLINT */ + #else + #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) #endif +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif #elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } + #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } + #define CATCH_TRAP() DebugBreak() #endif -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h @@ -2052,6 +2166,41 @@ namespace Catch { }; } +#if defined(CATCH_CONFIG_FAST_COMPILE) +/////////////////////////////////////////////////////////////////////////////// +// We can speedup compilation significantly by breaking into debugger lower in +// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER +// macro in each assertion +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +// This can potentially cause false negative, if the test code catches +// the exception before it propagates back up to the runner. +#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look +// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +#else /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. @@ -2060,37 +2209,40 @@ namespace Catch { #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); +#endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } \ catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + __catchResult.useActiveException( resultDisposition ); \ } \ 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 + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ @@ -2100,12 +2252,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ @@ -2117,12 +2269,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ @@ -2138,7 +2290,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ @@ -2146,7 +2298,7 @@ namespace Catch { INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ @@ -2156,21 +2308,15 @@ namespace Catch { #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ +#define INTERNAL_CATCH_INFO( macroName, log ) \ 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 ) \ +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ 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(); \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ @@ -2253,6 +2399,8 @@ namespace Catch { }; } +#include + namespace Catch { struct SectionInfo { @@ -2281,14 +2429,19 @@ namespace Catch { // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; +#ifdef _MSC_VER + +namespace Catch { + typedef unsigned long long UInt64; +} #else #include +namespace Catch { + typedef uint64_t UInt64; +} #endif namespace Catch { - class Timer { public: Timer() : m_ticks( 0 ) {} @@ -2298,7 +2451,7 @@ namespace Catch { double getElapsedSeconds() const; private: - uint64_t m_ticks; + UInt64 m_ticks; }; } // namespace Catch @@ -2337,7 +2490,6 @@ namespace Catch { // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#include #include #include #include @@ -2451,7 +2603,7 @@ public: private: void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } @@ -2533,12 +2685,15 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; @@ -2548,6 +2703,7 @@ namespace Catch { virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; }; IRegistryHub& getRegistryHub(); @@ -2623,6 +2779,10 @@ namespace Catch { #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { @@ -2630,30 +2790,111 @@ namespace Detail { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), 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 ); } +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + + template ::value>::type> + Approx operator()( T value ) { + Approx approx( static_cast(value) ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } + + template ::value>::type> + explicit Approx( T value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); + if (relativeOK) { + return true; + } + return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin; + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) { + return double(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) { + return lhs.m_value > double(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T newEpsilon ) { + m_epsilon = double(newEpsilon); + return *this; + } + + template ::value>::type> + Approx& margin( T newMargin ) { + m_margin = double(newMargin); + return *this; + } + + template ::value>::type> + Approx& scale( T newScale ) { + m_scale = double(newScale); + return *this; + } + +#else + Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); + approx.margin( m_margin ); 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) ) ); + bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + if (relativeOK) { + return true; + } + return std::fabs(lhs - rhs.m_value) < rhs.m_margin; } friend bool operator == ( Approx const& lhs, double rhs ) { @@ -2668,15 +2909,37 @@ namespace Detail { return !operator==( rhs, lhs ); } + friend bool operator <= ( double lhs, Approx const& rhs ) { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) { + return lhs.m_value > rhs || lhs == rhs; + } + Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; } + Approx& margin( double newMargin ) { + m_margin = newMargin; + return *this; + } + Approx& scale( double newScale ) { m_scale = newScale; return *this; } +#endif std::string toString() const { std::ostringstream oss; @@ -2686,6 +2949,7 @@ namespace Detail { private: double m_epsilon; + double m_margin; double m_scale; double m_value; }; @@ -2698,6 +2962,153 @@ inline std::string toString( Detail::Approx const& value ) { } // end namespace Catch +// #included from: internal/catch_matchers_string.h +#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + virtual std::string describe() const CATCH_OVERRIDE; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// #included from: internal/catch_matchers_vector.h +#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace Vector { + + template + struct ContainsElementMatcher : MatcherBase, T> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + return std::find(v.begin(), v.end(), m_comparator) != v.end(); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase, std::vector > { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (size_t i = 0; i < m_comparator.size(); ++i) + if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase, std::vector > { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Equals: " + Catch::toString( m_comparator ); + } + std::vector const& m_comparator; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + +} // namespace Matchers +} // namespace Catch + // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -2709,7 +3120,7 @@ inline std::string toString( Detail::Approx const& value ) { namespace Catch { struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; @@ -2781,8 +3192,18 @@ namespace Catch { } private: - T* nullableValue; - char storage[sizeof(T)]; + T *nullableValue; + union { + char storage[sizeof(T)]; + + // These are here to force alignment for the storage + long double dummy1; + void (*dummy2)(); + long double dummy3; +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + long long dummy4; +#endif + }; }; } // end namespace Catch @@ -2822,7 +3243,8 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, @@ -2980,64 +3402,67 @@ namespace Catch { namespace Impl { namespace NSStringMatchers { - template - struct StringHolder : MatcherImpl{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } + virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + return false; + } + NSString* m_substr; }; - struct Equals : StringHolder { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const CATCH_OVERRIDE { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "equals string: " + Catch::toString( m_substr ); } }; - struct Contains : StringHolder { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "contains string: " + Catch::toString( m_substr ); } }; - struct StartsWith : StringHolder { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "starts with: " + Catch::toString( m_substr ); } }; - struct EndsWith : StringHolder { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "ends with: " + Catch::toString( m_substr ); } }; @@ -3078,6 +3503,29 @@ return @ desc; \ #endif #ifdef CATCH_IMPL + +// !TBD: Move the leak detector code into a separate header +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include +class LeakDetector { +public: + LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +}; +#else +class LeakDetector {}; +#endif + +LeakDetector leakDetector; + // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3117,6 +3565,8 @@ return @ desc; \ // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#include + namespace Catch { class WildcardPattern { @@ -3134,11 +3584,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } @@ -3257,23 +3707,25 @@ namespace Catch { namespace Catch { class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag }; + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; + std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), 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 ); + m_escapeChars.clear(); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) @@ -3292,6 +3744,7 @@ namespace Catch { case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); default: startNewMode( Name, m_pos ); break; } } @@ -3307,7 +3760,11 @@ namespace Catch { addPattern(); startNewMode( Tag, ++m_pos ); } + else if( c == '\\' ) + escape(); } + else if( m_mode == EscapedName ) + m_mode = Name; else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) @@ -3317,10 +3774,19 @@ namespace Catch { m_mode = mode; m_start = start; } + void escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); + for( size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); @@ -3354,7 +3820,7 @@ namespace Catch { // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED -#include +#include #include #include @@ -3406,6 +3872,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } @@ -3434,6 +3902,7 @@ namespace Catch { std::ostream& cout(); std::ostream& cerr(); + std::ostream& clog(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; @@ -3474,8 +3943,7 @@ namespace Catch { #include #include #include -#include -#include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3490,6 +3958,7 @@ namespace Catch { listTags( false ), listReporters( false ), listTestNamesOnly( false ), + listExtraInfo( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), @@ -3509,6 +3978,7 @@ namespace Catch { bool listTags; bool listReporters; bool listTestNamesOnly; + bool listExtraInfo; bool showSuccessfulTests; bool shouldDebugBreak; @@ -3532,6 +4002,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; class Config : public SharedImpl { @@ -3556,8 +4027,7 @@ namespace Catch { } } - virtual ~Config() { - } + virtual ~Config() {} std::string const& getFilename() const { return m_data.outputFilename ; @@ -3567,30 +4037,30 @@ namespace Catch { bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } + bool listExtraInfo() const { return m_data.listExtraInfo; } std::string getProcessName() const { return m_data.processName; } - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - std::vector getReporterNames() const { return m_data.reporterNames; } - - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } private: @@ -3655,6 +4125,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3764,7 +4235,7 @@ namespace Tbc { return oss.str(); } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + 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() ) @@ -3997,7 +4468,7 @@ namespace Clara { _dest = _source; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; @@ -4151,12 +4622,13 @@ namespace Clara { } void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { + for( std::size_t i = 0; i < arg.size(); ++i ) { char c = arg[i]; if( c == '"' ) inQuotes = !inQuotes; mode = handleMode( i, c, arg, tokens ); } + mode = handleMode( arg.size(), '\0', arg, tokens ); } Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { switch( mode ) { @@ -4189,6 +4661,7 @@ namespace Clara { default: from = i; return ShortOpt; } } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) return mode; @@ -4520,7 +4993,7 @@ namespace Clara { } std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[0]; + std::string processName = args.empty() ? std::string() : args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); @@ -4652,6 +5125,7 @@ STITCH_CLARA_CLOSE_NAMESPACE #endif #include +#include namespace Catch { @@ -4662,13 +5136,14 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } 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 + "'" ); + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -4678,7 +5153,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -4689,7 +5164,7 @@ namespace Catch { ss << seed; ss >> config.rngSeed; if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -4724,10 +5199,10 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) - line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); } } } @@ -4815,11 +5290,19 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // 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-extra-info"] + .describe( "list all/matching test cases with more info" ) + .bind( &ConfigData::listExtraInfo ); + cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); @@ -4887,19 +5370,16 @@ namespace Tbc { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + width( consoleWidth-1 ) {} 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 { @@ -4907,62 +5387,76 @@ namespace Tbc { 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; + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { - 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 ); + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; } else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; } - } - } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); - 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 ); + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; + } } typedef std::vector::const_iterator const_iterator; @@ -5071,7 +5565,6 @@ namespace Catch { #include #include #include -#include namespace Catch { @@ -5338,8 +5831,9 @@ namespace Catch { } std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; + TextAttributes nameAttr, descAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + descAttr.setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -5354,14 +5848,21 @@ namespace Catch { Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( config.listExtraInfo() ) { + Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Text( description, descAttr ) << 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; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -5376,10 +5877,13 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) - Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; else - Catch::cout() << testCaseInfo.name << std::endl; + Catch::cout() << testCaseInfo.name; + if ( config.listExtraInfo() ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; } return matchedTests; } @@ -5440,9 +5944,9 @@ namespace Catch { .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; + Catch::cout() << oss.str() << wrapper << '\n'; } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } @@ -5461,9 +5965,9 @@ namespace Catch { .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first - << ":" + << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; + << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); @@ -5471,7 +5975,7 @@ namespace Catch { inline Option list( Config const& config ) { Option listedCount; - if( config.listTests() ) + if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); @@ -5490,19 +5994,32 @@ namespace Catch { // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#include +#include #include #include #include +#include + +CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5518,7 +6035,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5526,7 +6043,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5588,30 +6105,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; 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 ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -5630,8 +6149,8 @@ namespace TestCaseTracking { 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 ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -5709,41 +6228,65 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { - - section->open(); - } + if( !ctx.completedCycle() ) + section->tryOpen(); return *section; } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } }; 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 ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -5751,17 +6294,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -5789,7 +6332,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; @@ -5804,40 +6347,144 @@ using TestCaseTracking::IndexTracker; } // namespace Catch +CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + // #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 ) { + // Report the error condition + inline void reportFatal( std::string const& message ) { 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 ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + struct FatalConditionHandler { - void reset() {} - }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = CATCH_NULL; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + static void reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = CATCH_NULL; + isSet = false; + } + } + + ~FatalConditionHandler() { + reset(); + } + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { - struct SignalDefs { int id; const char* name; }; + struct SignalDefs { + int id; + const char* name; + }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, @@ -5846,37 +6493,70 @@ namespace Catch { { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + }; struct FatalConditionHandler { + static bool isSet; + static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[SIGSTKSZ]; + 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 ); + std::string name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( 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; + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } - bool m_isSet; + ~FatalConditionHandler() { + reset(); + } + static void reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + isSet = false; + } + } }; + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows #include @@ -5907,6 +6587,29 @@ namespace Catch { std::string& m_targetString; }; + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes and cannot use StreamRedirect on its own + class StdErrRedirect { + public: + StdErrRedirect(std::string& targetString) + :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()), + m_targetString(targetString){ + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + ~StdErrRedirect() { + m_targetString += m_oss.str(); + cerr().rdbuf(m_cerrBuf); + clog().rdbuf(m_clogBuf); + } + private: + std::streambuf* m_cerrBuf; + std::streambuf* m_clogBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { @@ -5921,7 +6624,8 @@ namespace Catch { m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), - m_reporter( reporter ) + m_reporter( reporter ), + m_shouldReportUnexpected ( true ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -5953,10 +6657,12 @@ namespace Catch { m_activeTestCase = &testCase; do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -5997,23 +6703,38 @@ namespace Catch { m_totals.assertions.failed++; } - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } + virtual bool lastAssertionPassed() + { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + virtual void assertionPassed() + { + m_totals.assertions.passed++; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + virtual void assertionRun() + { + m_prevPassed = m_totals.assertions.passed; + } + virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); @@ -6072,18 +6793,26 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } + virtual void exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult; + tempResult.resultType = ResultWas::FatalErrorCondition; + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); handleUnfinishedSections(); @@ -6100,13 +6829,14 @@ namespace Catch { Totals deltaTotals; deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -6124,6 +6854,7 @@ namespace Catch { m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; + m_shouldReportUnexpected = true; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); @@ -6133,7 +6864,7 @@ namespace Catch { timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + StdErrRedirect errRedir( redirectedCerr ); invokeActiveTestCase(); } else { @@ -6145,7 +6876,11 @@ namespace Catch { // This just means the test was aborted due to failure } catch(...) { - makeUnexpectedResultBuilder().useActiveException(); + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + makeUnexpectedResultBuilder().useActiveException(); + } } m_testCaseTracker->close(); handleUnfinishedSections(); @@ -6173,9 +6908,9 @@ namespace Catch { private: ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + return ResultBuilder( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ); } @@ -6205,6 +6940,8 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + size_t m_prevPassed; + bool m_shouldReportUnexpected; }; IResultCapture& getResultCapture() { @@ -6226,7 +6963,7 @@ namespace Catch { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; @@ -6234,7 +6971,7 @@ namespace Catch { unsigned int const patchNumber; // buildNumber is only used if branchName is not null - std::string const branchName; + char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); @@ -6243,7 +6980,7 @@ namespace Catch { void operator=( Version const& ); }; - extern Version libraryVersion; + inline Version libraryVersion(); } #include @@ -6262,10 +6999,14 @@ namespace Catch { return reporter; } +#if !defined(CATCH_CONFIG_DEFAULT_REPORTER) +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) - reporters.push_back( "console" ); + reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); @@ -6325,11 +7066,11 @@ namespace Catch { if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); - std::string::size_type lastDot = filename.find_last_of( "." ); + std::string::size_type lastDot = filename.find_last_of( '.' ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); - tags.insert( "#" + filename ); + tags.insert( '#' + filename ); setTags( test, tags ); } } @@ -6355,7 +7096,7 @@ namespace Catch { } void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + 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; @@ -6396,6 +7137,32 @@ namespace Catch { return returnCode; } + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t const* const* const argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + if( returnCode == 0 ) + returnCode = run(); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } + #endif + int run() { if( m_configData.showHelp ) return 0; @@ -6455,13 +7222,8 @@ namespace Catch { #include #include #include -#include #include -#ifdef CATCH_CPP14_OR_GREATER -#include -#endif - namespace Catch { struct RandomNumberGenerator { @@ -6469,7 +7231,7 @@ namespace Catch { result_type operator()( result_type n ) const { return std::rand() % n; } -#ifdef CATCH_CPP14_OR_GREATER +#ifdef CATCH_CONFIG_CPP11_SHUFFLE static constexpr result_type min() { return 0; } static constexpr result_type max() { return 1000000; } result_type operator()() const { return std::rand() % max(); } @@ -6477,7 +7239,7 @@ namespace Catch { template static void shuffle( V& vector ) { RandomNumberGenerator rng; -#ifdef CATCH_CPP14_OR_GREATER +#ifdef CATCH_CONFIG_CPP11_SHUFFLE std::shuffle( vector.begin(), vector.end(), rng ); #else std::random_shuffle( vector.begin(), vector.end(), rng ); @@ -6520,7 +7282,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -6552,7 +7314,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -6601,7 +7363,7 @@ namespace Catch { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) + if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); @@ -6748,6 +7510,26 @@ namespace Catch { }; } +// #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( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + namespace Catch { namespace { @@ -6769,6 +7551,9 @@ namespace Catch { virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } + virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + return m_tagAliasRegistry; + } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { @@ -6783,11 +7568,15 @@ namespace Catch { virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; }; // Single, global, instance @@ -6819,7 +7608,7 @@ namespace Catch { // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#include +#include namespace Catch { @@ -6891,7 +7680,7 @@ namespace Catch { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; + oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } @@ -6933,6 +7722,9 @@ namespace Catch { std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { + return std::clog; + } #endif } @@ -6944,6 +7736,11 @@ namespace Catch { Context( Context const& ); void operator=( Context const& ); + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); + } + public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; @@ -7027,6 +7824,23 @@ namespace Catch { // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// #included from: catch_errno_guard.hpp +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED + +#include + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard():m_oldErrno(errno){} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + +} + namespace Catch { namespace { @@ -7057,16 +7871,6 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - namespace Catch { namespace { @@ -7147,7 +7951,7 @@ namespace { 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::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" ); @@ -7172,6 +7976,7 @@ namespace { }; IColourImpl* platformColourInstance() { + ErrnoGuard guard; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() @@ -7290,14 +8095,18 @@ namespace Catch { namespace Catch { - AssertionInfo::AssertionInfo( std::string const& _macroName, + AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){} + + AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) + resultDisposition( _resultDisposition ), + secondArg( _secondArg ) {} AssertionResult::AssertionResult() {} @@ -7324,24 +8133,30 @@ namespace Catch { } bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); + return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } + std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { + return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') + ? capturedExpression + : std::string(capturedExpression) + ", " + secondArg; + } + std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; + return '!' + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else - return m_info.capturedExpression; + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); } std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; + if( m_info.macroName[0] == 0 ) + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; + return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; } bool AssertionResult::hasExpandedExpression() const { @@ -7349,7 +8164,7 @@ namespace Catch { } std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; + return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { @@ -7363,15 +8178,25 @@ namespace Catch { return m_info.macroName; } + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include + namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; @@ -7381,25 +8206,23 @@ namespace Catch { return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::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); + std::ostringstream ss; + ss << Colour(Colour::Red) + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << Colour(Colour::FileName) + << _lineInfo << '\n'; + throw std::runtime_error(ss.str()); } } @@ -7455,7 +8278,7 @@ namespace Catch { std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; + oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); @@ -7561,7 +8384,7 @@ namespace Catch { ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), @@ -7571,18 +8394,21 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." + os << version.majorVersion << '.' + << version.minorVersion << '.' << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } return os; } - Version libraryVersion( 1, 5, 9, "", 0 ); + inline Version libraryVersion() { + static Version version( 1, 9, 7, "", 0 ); + return version; + } } @@ -7616,7 +8442,9 @@ namespace Catch { {} ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } } } // end namespace Catch @@ -7753,30 +8581,32 @@ namespace Catch #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; + UInt64 getCurrentTicks() { + static UInt64 hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } - uint64_t t; + UInt64 t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else - uint64_t getCurrentTicks() { + UInt64 getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } @@ -7802,19 +8632,28 @@ namespace Catch { // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == 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; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); @@ -7829,7 +8668,7 @@ namespace Catch { 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 ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7852,29 +8691,25 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo() : file(""), 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(); + return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { @@ -7887,16 +8722,16 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + 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 << "'"; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } @@ -7923,6 +8758,10 @@ namespace Catch { m_timer.start(); } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); @@ -7932,6 +8771,9 @@ namespace Catch { getResultCapture().sectionEnded( endInfo ); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif // This indicates whether the section should be executed or not Section::operator bool() const { @@ -7943,8 +8785,6 @@ namespace Catch { // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#include - #ifdef CATCH_PLATFORM_MAC #include @@ -7993,6 +8833,36 @@ namespace Catch { } } // namespace Catch +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { @@ -8014,7 +8884,7 @@ namespace Catch { #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() ); @@ -8090,7 +8960,7 @@ std::string toString( std::string const& value ) { } } } - return "\"" + s + "\""; + return '"' + s + '"'; } std::string toString( std::wstring const& value ) { @@ -8111,19 +8981,19 @@ std::string toString( char* const value ) { std::string toString( const wchar_t* const value ) { - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { - return Catch::toString( static_cast( 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 << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8131,7 +9001,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8159,7 +9029,7 @@ std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; + return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { @@ -8167,9 +9037,19 @@ std::string toString( bool value ) { } std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } std::string toString( signed char value ) { @@ -8185,14 +9065,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + 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 << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif @@ -8209,7 +9089,7 @@ std::string toString( std::nullptr_t ) { return "nil"; return "@" + toString([nsstring UTF8String]); } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); @@ -8226,21 +9106,28 @@ 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, char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), m_shouldDebugBreak( false ), - m_shouldThrow( false ) + m_shouldThrow( false ), + m_guardException( false ), + m_usedStream( false ) {} + ResultBuilder::~ResultBuilder() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if ( m_guardException ) { + stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + captureResult( ResultWas::ThrewException ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } +#endif + } + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; @@ -8249,27 +9136,27 @@ namespace Catch { 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::endExpression( DecomposedExpression const& expr ) { + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + m_data.negate( expr.isBinaryExpression() ); + } + + getResultCapture().assertionRun(); + + if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) + { + AssertionResult result = build( expr ); + handleResult( result ); + } + else + getResultCapture().assertionPassed(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); + stream().oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } @@ -8277,19 +9164,20 @@ namespace Catch { setResultType( resultType ); captureExpression(); } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); + captureExpectedException( Matchers::Impl::MatchAllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - assert( m_exprComponents.testFalse == false ); + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; + data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { @@ -8304,6 +9192,7 @@ namespace Catch { AssertionResult result = build(); handleResult( result ); } + void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); @@ -8315,7 +9204,17 @@ namespace Catch { m_shouldThrow = true; } } + void ResultBuilder::react() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if (m_shouldDebugBreak) { + /////////////////////////////////////////////////////////////////// + // To inspect the state during test, you need to go one level up the callstack + // To go back to the test and change execution, jump over the throw statement + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } +#endif if( m_shouldThrow ) throw Catch::TestFailureException(); } @@ -8325,43 +9224,35 @@ namespace Catch { AssertionResult ResultBuilder::build() const { - assert( m_data.resultType != ResultWas::Unknown ); + return build( *this ); + } + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) 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 + ")"; - } + if(m_usedStream) + data.message = m_stream().oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction 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}"; + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); + } + + void ResultBuilder::setExceptionGuard() { + m_guardException = true; + } + void ResultBuilder::unsetExceptionGuard() { + m_guardException = false; } } // end namespace Catch @@ -8369,30 +9260,6 @@ 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() {} @@ -8420,44 +9287,120 @@ namespace Catch { return expandedTestSpec; } - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" + << Colour( Colour::FileName ) + << lineInfo << '\n'; 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; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " + << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' + << Colour( Colour::Red ) << "\tRedefined at " + << Colour( Colour::FileName) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } } - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); } - 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); - } + getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } } // end namespace Catch +// #included from: catch_matchers_string.hpp + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + +} // namespace Matchers +} // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED @@ -8601,9 +9544,34 @@ Ptr addReporter( Ptr const& existingRepo #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include +#include +#include +#include namespace Catch { + namespace { + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + } + struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) @@ -8698,12 +9666,13 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } private: - void operator=( BySectionInfo const& ); + void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; @@ -8759,6 +9728,12 @@ namespace Catch { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { @@ -8793,6 +9768,13 @@ namespace Catch { virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + Ptr m_config; std::ostream& stream; std::vector m_assertions; @@ -8813,7 +9795,7 @@ namespace Catch { char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; @@ -8898,7 +9880,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; @@ -8916,9 +9898,13 @@ namespace Catch { #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +// Deprecated - use the form without INTERNAL_ #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED @@ -8967,8 +9953,11 @@ namespace Catch { default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 - if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } else os << c; } @@ -9022,20 +10011,17 @@ namespace Catch { XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &Catch::cout() ) + m_os( Catch::cout() ) { - // We encode control characters, which requires - // XML 1.1 - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - *m_os << "\n"; + writeDeclaration(); } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &os ) + m_os( os ) { - *m_os << "\n"; + writeDeclaration(); } ~XmlWriter() { @@ -9046,7 +10032,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << "<" << name; + m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -9063,24 +10049,25 @@ namespace Catch { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { - stream() << "/>\n"; + m_os << "/>"; m_tagIsOpen = false; } else { - stream() << m_indent << "\n"; + m_os << m_indent << ""; } + m_os << std::endl; 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 ) << "\""; + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -9096,8 +10083,8 @@ namespace Catch { bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); + m_os << m_indent; + m_os << XmlEncode( text ); m_needsNewline = true; } return *this; @@ -9105,39 +10092,39 @@ namespace Catch { XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); - stream() << m_indent << ""; + m_os << m_indent << ""; m_needsNewline = true; return *this; } + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + m_os << '\n'; return *this; } - void setStream( std::ostream& os ) { - m_os = &os; + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } } 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 writeDeclaration() { + m_os << "\n"; } void newlineIfNecessary() { if( m_needsNewline ) { - stream() << "\n"; + m_os << std::endl; m_needsNewline = false; } } @@ -9146,24 +10133,10 @@ namespace Catch { bool m_needsNewline; std::vector m_tags; std::string m_indent; - std::ostream* m_os; + 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 { @@ -9182,6 +10155,16 @@ namespace Catch { return "Reports test results as an XML document"; } + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { @@ -9190,6 +10173,9 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); @@ -9203,10 +10189,16 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); + m_xml.ensureTagClosed(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { @@ -9215,77 +10207,84 @@ namespace Catch { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } } 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 ) { + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults ) { + // Print any info messages in tags. for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { + it != itEnd; + ++it ) { if( it->type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) - .writeText( it->message ); + .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. - if( assertionResult.hasExpression() ) { + if( result.hasExpression() ) { m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); + .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); + .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { + switch( result.getResultType() ) { case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "FatalErrorCondition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); + .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; default: break; } - if( assertionResult.hasExpression() ) + if( result.hasExpression() ) m_xml.endElement(); return true; @@ -9314,6 +10313,11 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + m_xml.endElement(); } @@ -9353,11 +10357,42 @@ namespace Catch { namespace Catch { + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + } + class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), - xml( _config.stream() ) + xml( _config.stream() ), + unexpectedExceptions( 0 ), + m_okToFail( false ) { m_reporterPrefs.shouldRedirectStdOut = true; } @@ -9383,8 +10418,11 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } + virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { + m_okToFail = testCaseInfo.okToFail(); + } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } @@ -9417,7 +10455,7 @@ namespace Catch { xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( TestGroupNode::ChildNodes::const_iterator @@ -9452,7 +10490,7 @@ namespace Catch { SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) - name = rootName + "/" + name; + name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || @@ -9530,14 +10568,14 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; + 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 << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -9549,6 +10587,7 @@ namespace Catch { std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; + bool m_okToFail; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) @@ -9558,6 +10597,9 @@ namespace Catch { // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include +#include + namespace Catch { struct ConsoleReporter : StreamingReporterBase { @@ -9572,7 +10614,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { @@ -9581,18 +10623,15 @@ namespace Catch { virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; - bool printInfoMessages = true; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - // 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; - } + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return false; lazyPrint(); - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; @@ -9612,14 +10651,11 @@ namespace Catch { 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; + if( m_config->showDurations() == ShowDurations::Always ) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + if( m_headerPrinted ) { + m_headerPrinted = false; } StreamingReporterBase::sectionEnded( _sectionStats ); } @@ -9633,7 +10669,7 @@ namespace Catch { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } @@ -9685,7 +10721,11 @@ namespace Catch { case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; @@ -9725,13 +10765,13 @@ namespace Catch { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) - stream << "\n"; + stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { - stream << "\n"; + stream << '\n'; } printMessage(); } @@ -9748,25 +10788,25 @@ namespace Catch { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); - stream << "\n"; + stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; + 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"; + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { @@ -9798,10 +10838,10 @@ namespace Catch { } } void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; + stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" + << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) @@ -9829,22 +10869,22 @@ namespace Catch { printHeaderString( it->name, 2 ); } - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; + stream << lineInfo << '\n'; } - stream << getLineOfChars<'.'>() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; + stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -9861,7 +10901,7 @@ namespace Catch { i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { @@ -9876,9 +10916,9 @@ namespace Catch { std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) - *it = " " + *it; + *it = ' ' + *it; while( it->size() > row.size() ) - row = " " + row; + row = ' ' + row; } rows.push_back( row ); return *this; @@ -9898,8 +10938,8 @@ namespace Catch { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; } else { @@ -9934,10 +10974,10 @@ namespace Catch { else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) - << value << " " << it->label; + << value << ' ' << it->label; } } - stream << "\n"; + stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { @@ -9973,10 +11013,10 @@ namespace Catch { else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } - stream << "\n"; + stream << '\n'; } void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; } private: @@ -10011,11 +11051,10 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) { - } + virtual void assertionStarting( AssertionInfo const& ) {} virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; @@ -10036,9 +11075,15 @@ namespace Catch { return true; } + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -10138,26 +11183,26 @@ namespace Catch { void printSourceInfo() const { Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; + stream << result.getSourceInfo() << ':'; } - void printResultType( Colour::Code colour, std::string passOrFail ) const { + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); - stream << " " << passOrFail; + stream << ' ' << passOrFail; } - stream << ":"; + stream << ':'; } } - void printIssue( std::string issue ) const { - stream << " " << issue; + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { - stream << ";"; + stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; @@ -10168,7 +11213,7 @@ namespace Catch { void printOriginalExpression() const { if( result.hasExpression() ) { - stream << " " << result.getExpression(); + stream << ' ' << result.getExpression(); } } @@ -10184,7 +11229,7 @@ namespace Catch { void printMessage() { if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; ++itMessage; } } @@ -10199,13 +11244,13 @@ namespace Catch { { Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; + 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 << "'"; + stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; @@ -10231,7 +11276,7 @@ namespace Catch { // - 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 " ; + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { @@ -10242,12 +11287,12 @@ namespace Catch { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; + bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; + pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << @@ -10259,14 +11304,14 @@ namespace Catch { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + "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" ) << "."; + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; @@ -10322,11 +11367,7 @@ namespace Catch { 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() {} + Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} @@ -10350,9 +11391,16 @@ namespace Catch { #ifndef __OBJC__ +#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { - return Catch::Session().run( argc, argv ); +#endif + + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ @@ -10370,7 +11418,7 @@ int main (int argc, char * const argv[]) { [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ @@ -10386,33 +11434,43 @@ int main (int argc, char * const argv[]) { // 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" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#else +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#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_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#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( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#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 CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#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_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#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" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) @@ -10420,16 +11478,18 @@ int main (int argc, char * const argv[]) { #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__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __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 ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10455,50 +11515,63 @@ int main (int argc, char * const argv[]) { // 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" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#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" ) +#else +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#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 REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#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( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#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 CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#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" ) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#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( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) +#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 ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10527,5 +11600,19 @@ int main (int argc, char * const argv[]) { 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_assembler.cpp b/third_party/libosmium/test/t/area/test_assembler.cpp new file mode 100644 index 000000000..e25ad0f02 --- /dev/null +++ b/third_party/libosmium/test/t/area/test_assembler.cpp @@ -0,0 +1,143 @@ +#include "catch.hpp" + +#include +#include +#include +#include + +using namespace osmium::builder::attr; + +TEST_CASE("Build area from way") { + osmium::memory::Buffer buffer{10240}; + + const auto wpos = osmium::builder::add_way(buffer, + _id(1), + _nodes({ + {1, {1.0, 1.0}}, + {2, {1.0, 2.0}}, + {3, {2.0, 2.0}}, + {4, {2.0, 1.0}}, + {1, {1.0, 1.0}} + }) + ); + + osmium::area::AssemblerConfig config; + osmium::area::Assembler assembler{config}; + + osmium::memory::Buffer area_buffer{10240}; + REQUIRE(assembler(buffer.get(wpos), area_buffer)); + + const auto& area = area_buffer.get(0); + REQUIRE(area.from_way()); + REQUIRE(area.id() == 2); + + const auto it = area.outer_rings().begin(); + REQUIRE(it != area.outer_rings().end()); + REQUIRE(it->size() == 5); + + const auto& s = assembler.stats(); + REQUIRE(s.area_simple_case == 1); + REQUIRE(s.from_ways == 1); + REQUIRE(s.nodes == 4); + REQUIRE(s.duplicate_nodes == 0); + REQUIRE(s.invalid_locations == 0); +} + +TEST_CASE("Build area from way with duplicate nodes") { + osmium::memory::Buffer buffer{10240}; + + const auto wpos = osmium::builder::add_way(buffer, + _id(1), + _nodes({ + {1, {1.0, 1.0}}, + {2, {1.0, 2.0}}, + {3, {2.0, 2.0}}, + {3, {2.0, 2.0}}, + {4, {2.0, 1.0}}, + {1, {1.0, 1.0}} + }) + ); + + osmium::area::AssemblerConfig config; + osmium::area::Assembler assembler{config}; + + osmium::memory::Buffer area_buffer{10240}; + REQUIRE(assembler(buffer.get(wpos), area_buffer)); + + const auto& area = area_buffer.get(0); + REQUIRE(area.from_way()); + REQUIRE(area.id() == 2); + + const auto it = area.outer_rings().begin(); + REQUIRE(it != area.outer_rings().end()); + REQUIRE(it->size() == 5); + + const auto& s = assembler.stats(); + REQUIRE(s.area_simple_case == 1); + REQUIRE(s.from_ways == 1); + REQUIRE(s.nodes == 4); + REQUIRE(s.duplicate_nodes == 1); + REQUIRE(s.invalid_locations == 0); +} + +TEST_CASE("Build area from way with invalid location") { + osmium::memory::Buffer buffer{10240}; + + const auto wpos = osmium::builder::add_way(buffer, + _id(1), + _nodes({ + {1, {1.0, 1.0}}, + {2, {1.0, 2.0}}, + {3}, + {4, {2.0, 1.0}}, + {1, {1.0, 1.0}} + }) + ); + + osmium::area::AssemblerConfig config; + osmium::area::Assembler assembler{config}; + + osmium::memory::Buffer area_buffer{10240}; + REQUIRE_FALSE(assembler(buffer.get(wpos), area_buffer)); + + const auto& s = assembler.stats(); + REQUIRE(s.duplicate_nodes == 0); + REQUIRE(s.invalid_locations == 1); +} + +TEST_CASE("Build area from way with ignored invalid location") { + osmium::memory::Buffer buffer{10240}; + + const auto wpos = osmium::builder::add_way(buffer, + _id(1), + _nodes({ + {1, {1.0, 1.0}}, + {2, {1.0, 2.0}}, + {3}, + {4, {2.0, 1.0}}, + {1, {1.0, 1.0}} + }) + ); + + osmium::area::AssemblerConfig config; + config.ignore_invalid_locations = true; + osmium::area::Assembler assembler{config}; + + osmium::memory::Buffer area_buffer{10240}; + REQUIRE(assembler(buffer.get(wpos), area_buffer)); + + const auto& area = area_buffer.get(0); + REQUIRE(area.from_way()); + REQUIRE(area.id() == 2); + + const auto it = area.outer_rings().begin(); + REQUIRE(it != area.outer_rings().end()); + REQUIRE(it->size() == 4); + + const auto& s = assembler.stats(); + REQUIRE(s.area_simple_case == 1); + REQUIRE(s.from_ways == 1); + REQUIRE(s.nodes == 3); + REQUIRE(s.invalid_locations == 1); +} + 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 9eba380fd..3b47376ad 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 @@ -3,6 +3,7 @@ #include using osmium::area::detail::NodeRefSegment; +using osmium::area::detail::role_type; TEST_CASE("Default construction of NodeRefSegment") { NodeRefSegment s; @@ -18,27 +19,27 @@ TEST_CASE("Construction of NodeRefSegment with NodeRefs") { osmium::NodeRef nr3{3, {1.2, 3.6}}; osmium::NodeRef nr4{4, {1.2, 3.7}}; - NodeRefSegment s1{nr1, nr2}; + NodeRefSegment s1{nr1, nr2, role_type::unknown, nullptr}; REQUIRE(s1.first().ref() == 1); REQUIRE(s1.second().ref() == 2); - NodeRefSegment s2{nr2, nr3}; + NodeRefSegment s2{nr2, nr3, role_type::unknown, nullptr}; REQUIRE(s2.first().ref() == 3); REQUIRE(s2.second().ref() == 2); - NodeRefSegment s3{nr3, nr4}; + NodeRefSegment s3{nr3, nr4, role_type::unknown, nullptr}; REQUIRE(s3.first().ref() == 3); REQUIRE(s3.second().ref() == 4); } TEST_CASE("Intersection of NodeRefSegments") { - NodeRefSegment s1{{ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}}; - NodeRefSegment s2{{ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}}; - NodeRefSegment s3{{ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}}; - NodeRefSegment s4{{ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}}; - NodeRefSegment s5{{ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}}; - NodeRefSegment s6{{11, {0.0, 0.0}}, {12, {1.0, 1.0}}}; - NodeRefSegment s7{{13, {1.0, 1.0}}, {14, {3.0, 3.0}}}; + NodeRefSegment s1{{ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, role_type::unknown, nullptr}; + NodeRefSegment s2{{ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, role_type::unknown, nullptr}; + NodeRefSegment s3{{ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, role_type::unknown, nullptr}; + NodeRefSegment s4{{ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, role_type::unknown, nullptr}; + NodeRefSegment s5{{ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, role_type::unknown, nullptr}; + NodeRefSegment s6{{11, {0.0, 0.0}}, {12, {1.0, 1.0}}, role_type::unknown, nullptr}; + NodeRefSegment s7{{13, {1.0, 1.0}}, {14, {3.0, 3.0}}, role_type::unknown, nullptr}; REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0)); REQUIRE(calculate_intersection(s2, s1) == osmium::Location(1.0, 1.0)); @@ -66,15 +67,15 @@ TEST_CASE("Intersection of NodeRefSegments") { } TEST_CASE("Intersection of collinear NodeRefSegments") { - NodeRefSegment s1{{ 1, {0.0, 0.0}}, { 2, {2.0, 0.0}}}; // *---* - NodeRefSegment s2{{ 3, {2.0, 0.0}}, { 4, {4.0, 0.0}}}; // *---* - NodeRefSegment s3{{ 5, {0.0, 0.0}}, { 6, {1.0, 0.0}}}; // *-* - NodeRefSegment s4{{ 7, {1.0, 0.0}}, { 8, {2.0, 0.0}}}; // *-* - NodeRefSegment s5{{ 9, {1.0, 0.0}}, {10, {3.0, 0.0}}}; // *---* - NodeRefSegment s6{{11, {0.0, 0.0}}, {12, {4.0, 0.0}}}; // *-------* - NodeRefSegment s7{{13, {0.0, 0.0}}, {14, {5.0, 0.0}}}; // *---------* - NodeRefSegment s8{{13, {1.0, 0.0}}, {14, {5.0, 0.0}}}; // *-------* - NodeRefSegment s9{{13, {3.0, 0.0}}, {14, {4.0, 0.0}}}; // *-* + NodeRefSegment s1{{ 1, {0.0, 0.0}}, { 2, {2.0, 0.0}}, role_type::unknown, nullptr}; // *---* + NodeRefSegment s2{{ 3, {2.0, 0.0}}, { 4, {4.0, 0.0}}, role_type::unknown, nullptr}; // *---* + NodeRefSegment s3{{ 5, {0.0, 0.0}}, { 6, {1.0, 0.0}}, role_type::unknown, nullptr}; // *-* + NodeRefSegment s4{{ 7, {1.0, 0.0}}, { 8, {2.0, 0.0}}, role_type::unknown, nullptr}; // *-* + NodeRefSegment s5{{ 9, {1.0, 0.0}}, {10, {3.0, 0.0}}, role_type::unknown, nullptr}; // *---* + NodeRefSegment s6{{11, {0.0, 0.0}}, {12, {4.0, 0.0}}, role_type::unknown, nullptr}; // *-------* + NodeRefSegment s7{{13, {0.0, 0.0}}, {14, {5.0, 0.0}}, role_type::unknown, nullptr}; // *---------* + NodeRefSegment s8{{13, {1.0, 0.0}}, {14, {5.0, 0.0}}, role_type::unknown, nullptr}; // *-------* + NodeRefSegment s9{{13, {3.0, 0.0}}, {14, {4.0, 0.0}}, role_type::unknown, nullptr}; // *-* REQUIRE(calculate_intersection(s1, s1) == osmium::Location()); @@ -110,16 +111,16 @@ TEST_CASE("Intersection of collinear NodeRefSegments") { } TEST_CASE("Intersection of very long NodeRefSegments") { - NodeRefSegment s1{{1, {90.0, 90.0}}, {2, {-90.0, -90.0}}}; - NodeRefSegment s2{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}}; + NodeRefSegment s1{{1, {90.0, 90.0}}, {2, {-90.0, -90.0}}, role_type::unknown, nullptr}; + NodeRefSegment s2{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}, role_type::unknown, nullptr}; REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0)); - NodeRefSegment s3{{1, {-90.0, -90.0}}, {2, {90.0, 90.0}}}; - NodeRefSegment s4{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}}; + NodeRefSegment s3{{1, {-90.0, -90.0}}, {2, {90.0, 90.0}}, role_type::unknown, nullptr}; + NodeRefSegment s4{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}, role_type::unknown, nullptr}; REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0)); - NodeRefSegment s5{{1, {-90.00000001, -90.0}}, {2, {90.0, 90.0}}}; - NodeRefSegment s6{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}}; + NodeRefSegment s5{{1, {-90.00000001, -90.0}}, {2, {90.0, 90.0}}, role_type::unknown, nullptr}; + NodeRefSegment s6{{1, {-90.0, 90.0}}, {2, {90.0, -90.0}}, role_type::unknown, nullptr}; REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0)); } @@ -134,11 +135,11 @@ TEST_CASE("Ordering of NodeRefSegements") { REQUIRE(node_ref1 < node_ref3); REQUIRE(node_ref1 >= node_ref1); - REQUIRE( osmium::location_less()(node_ref1, node_ref2)); - REQUIRE(!osmium::location_less()(node_ref2, node_ref3)); - REQUIRE( osmium::location_less()(node_ref1, node_ref3)); - REQUIRE( osmium::location_less()(node_ref3, node_ref4)); - REQUIRE(!osmium::location_less()(node_ref1, node_ref1)); + REQUIRE( osmium::location_less()(node_ref1, node_ref2)); + REQUIRE_FALSE(osmium::location_less()(node_ref2, node_ref3)); + REQUIRE( osmium::location_less()(node_ref1, node_ref3)); + REQUIRE( osmium::location_less()(node_ref3, node_ref4)); + REQUIRE_FALSE(osmium::location_less()(node_ref1, node_ref1)); } TEST_CASE("More ordering of NodeRefSegments") { @@ -151,13 +152,13 @@ TEST_CASE("More ordering of NodeRefSegments") { osmium::NodeRef nr6{6, {2.0, 2.0}}; osmium::NodeRef nr7{6, {1.0, 2.0}}; - NodeRefSegment s1{nr0, nr1}; - NodeRefSegment s2{nr0, nr2}; - NodeRefSegment s3{nr0, nr3}; - NodeRefSegment s4{nr0, nr4}; - NodeRefSegment s5{nr0, nr5}; - NodeRefSegment s6{nr0, nr6}; - NodeRefSegment s7{nr0, nr7}; + NodeRefSegment s1{nr0, nr1, role_type::unknown, nullptr}; + NodeRefSegment s2{nr0, nr2, role_type::unknown, nullptr}; + NodeRefSegment s3{nr0, nr3, role_type::unknown, nullptr}; + NodeRefSegment s4{nr0, nr4, role_type::unknown, nullptr}; + NodeRefSegment s5{nr0, nr5, role_type::unknown, nullptr}; + NodeRefSegment s6{nr0, nr6, role_type::unknown, nullptr}; + NodeRefSegment s7{nr0, nr7, role_type::unknown, nullptr}; // s1 REQUIRE_FALSE(s1 < s1); diff --git a/third_party/libosmium/test/t/geom/test_geojson.cpp b/third_party/libosmium/test/t/geom/test_geojson.cpp index 572493655..400bb50f5 100644 --- a/third_party/libosmium/test/t/geom/test_geojson.cpp +++ b/third_party/libosmium/test/t/geom/test_geojson.cpp @@ -14,7 +14,7 @@ TEST_CASE("GeoJSON point geometry") { } SECTION("empty_point") { - REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), const osmium::invalid_location&); } } @@ -50,17 +50,17 @@ TEST_CASE("GeoJSON linestring geometry") { SECTION("empty_linestring") { const 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); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), const osmium::geometry_error&); } SECTION("linestring with two same locations") { const 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); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); { const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; @@ -75,7 +75,7 @@ TEST_CASE("GeoJSON linestring geometry") { SECTION("linestring with undefined location") { const auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::invalid_location&); } } @@ -87,7 +87,7 @@ TEST_CASE("GeoJSON area geometry") { SECTION("area_1outer_0inner") { const osmium::Area& area = create_test_area_1outer_0inner(buffer); - REQUIRE(!area.is_multipolygon()); + REQUIRE_FALSE(area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 2); REQUIRE(area.subitems().size() == area.num_rings().first); @@ -98,7 +98,7 @@ TEST_CASE("GeoJSON area geometry") { SECTION("area_1outer_1inner") { const osmium::Area& area = create_test_area_1outer_1inner(buffer); - REQUIRE(!area.is_multipolygon()); + REQUIRE_FALSE(area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 3); REQUIRE(area.subitems().size() == area.num_rings().first); REQUIRE(area.subitems().size() == area.num_rings().second); diff --git a/third_party/libosmium/test/t/geom/test_geos.cpp b/third_party/libosmium/test/t/geom/test_geos.cpp index 8e7fac483..f13861602 100644 --- a/third_party/libosmium/test/t/geom/test_geos.cpp +++ b/third_party/libosmium/test/t/geom/test_geos.cpp @@ -41,7 +41,7 @@ TEST_CASE("GEOS geometry factory - create point with externally created GEOS fac TEST_CASE("GEOS geometry factory - can not create from invalid location") { osmium::geom::GEOSFactory<> factory; - REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), const osmium::invalid_location&); } TEST_CASE("GEOS geometry factory - create linestring") { diff --git a/third_party/libosmium/test/t/geom/test_mercator.cpp b/third_party/libosmium/test/t/geom/test_mercator.cpp index cc16e55cf..4f8bf9764 100644 --- a/third_party/libosmium/test/t/geom/test_mercator.cpp +++ b/third_party/libosmium/test/t/geom/test_mercator.cpp @@ -2,36 +2,33 @@ #include -TEST_CASE("Mercator") { - - SECTION("mercator_projection") { - osmium::geom::MercatorProjection projection; - REQUIRE(3857 == projection.epsg()); - REQUIRE("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" == projection.proj_string()); - } - - SECTION("low_level_mercator_functions") { - osmium::geom::Coordinates c1(17.839, -3.249); - osmium::geom::Coordinates r1 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c1)); - REQUIRE(r1.x == Approx(c1.x).epsilon(0.000001)); - REQUIRE(r1.y == Approx(c1.y).epsilon(0.000001)); - - osmium::geom::Coordinates c2(-89.2, 15.915); - osmium::geom::Coordinates r2 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c2)); - REQUIRE(r2.x == Approx(c2.x).epsilon(0.000001)); - REQUIRE(r2.y == Approx(c2.y).epsilon(0.000001)); - - osmium::geom::Coordinates c3(180.0, 85.0); - osmium::geom::Coordinates r3 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c3)); - REQUIRE(r3.x == Approx(c3.x).epsilon(0.000001)); - REQUIRE(r3.y == Approx(c3.y).epsilon(0.000001)); - } - - SECTION("mercator_bounds") { - osmium::Location mmax(180.0, osmium::geom::MERCATOR_MAX_LAT); - osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(mmax); - REQUIRE(c.x == Approx(c.y).epsilon(0.001)); - REQUIRE(osmium::geom::detail::y_to_lat(osmium::geom::detail::lon_to_x(180.0)) == Approx(osmium::geom::MERCATOR_MAX_LAT).epsilon(0.0000001)); - } - +TEST_CASE("Mercator projection") { + const osmium::geom::MercatorProjection projection; + REQUIRE(3857 == projection.epsg()); + REQUIRE("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" == projection.proj_string()); } + +TEST_CASE("Low level mercator functions") { + const osmium::geom::Coordinates c1{17.839, -3.249}; + const osmium::geom::Coordinates r1 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c1)); + REQUIRE(r1.x == Approx(c1.x).epsilon(0.000001)); + REQUIRE(r1.y == Approx(c1.y).epsilon(0.000001)); + + const osmium::geom::Coordinates c2{-89.2, 15.915}; + const osmium::geom::Coordinates r2 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c2)); + REQUIRE(r2.x == Approx(c2.x).epsilon(0.000001)); + REQUIRE(r2.y == Approx(c2.y).epsilon(0.000001)); + + const osmium::geom::Coordinates c3{180.0, 85.0}; + const osmium::geom::Coordinates r3 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c3)); + REQUIRE(r3.x == Approx(c3.x).epsilon(0.000001)); + REQUIRE(r3.y == Approx(c3.y).epsilon(0.000001)); +} + +TEST_CASE("Mercator bounds") { + const osmium::Location mmax{180.0, osmium::geom::MERCATOR_MAX_LAT}; + const osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(mmax); + REQUIRE(c.x == Approx(c.y).epsilon(0.001)); + REQUIRE(osmium::geom::detail::y_to_lat(osmium::geom::detail::lon_to_x(180.0)) == Approx(osmium::geom::MERCATOR_MAX_LAT).epsilon(0.0000001)); +} + diff --git a/third_party/libosmium/test/t/geom/test_ogr.cpp b/third_party/libosmium/test/t/geom/test_ogr.cpp index 5e0308247..39f2b5c09 100644 --- a/third_party/libosmium/test/t/geom/test_ogr.cpp +++ b/third_party/libosmium/test/t/geom/test_ogr.cpp @@ -15,7 +15,7 @@ TEST_CASE("OGR point geometry") { } SECTION("empty_point") { - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), const osmium::invalid_location&); } } diff --git a/third_party/libosmium/test/t/geom/test_projection.cpp b/third_party/libosmium/test/t/geom/test_projection.cpp index 14df3bfd4..bff568b6d 100644 --- a/third_party/libosmium/test/t/geom/test_projection.cpp +++ b/third_party/libosmium/test/t/geom/test_projection.cpp @@ -19,7 +19,8 @@ TEST_CASE("Projection 4326") { const osmium::Location loc{1.0, 2.0}; const osmium::geom::Coordinates c{1.0, 2.0}; - REQUIRE(c == projection(loc)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } TEST_CASE("Projection 4326 from init string") { @@ -29,15 +30,16 @@ TEST_CASE("Projection 4326 from init string") { const osmium::Location loc{1.0, 2.0}; const osmium::geom::Coordinates c{1.0, 2.0}; - REQUIRE(c == projection(loc)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } TEST_CASE("Creating projection from unknown init string") { - REQUIRE_THROWS_AS(osmium::geom::Projection projection{"abc"}, osmium::projection_error); + REQUIRE_THROWS_AS(osmium::geom::Projection{"abc"}, const osmium::projection_error&); } TEST_CASE("Creating projection from unknown EPSG code") { - REQUIRE_THROWS_AS(osmium::geom::Projection projection{9999999}, osmium::projection_error); + REQUIRE_THROWS_AS(osmium::geom::Projection{9999999}, const osmium::projection_error&); } TEST_CASE("Projection 3857") { @@ -48,29 +50,29 @@ TEST_CASE("Projection 3857") { SECTION("Zero coordinates") { const osmium::Location loc{0.0, 0.0}; const osmium::geom::Coordinates c{0.0, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Max longitude") { const osmium::Location loc{180.0, 0.0}; const osmium::geom::Coordinates c{20037508.34, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Min longitude") { const osmium::Location loc{-180.0, 0.0}; const osmium::geom::Coordinates c{-20037508.34, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Max latitude") { const osmium::Location loc{0.0, 85.0511288}; const osmium::geom::Coordinates c{0.0, 20037508.34}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } } @@ -80,29 +82,29 @@ TEST_CASE("MercatorProjection") { SECTION("Zero coordinates") { const osmium::Location loc{0.0, 0.0}; const osmium::geom::Coordinates c{0.0, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Max longitude") { const osmium::Location loc{180.0, 0.0}; const osmium::geom::Coordinates c{20037508.34, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Min longitude") { const osmium::Location loc{-180.0, 0.0}; const osmium::geom::Coordinates c{-20037508.34, 0.0}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } SECTION("Max latitude") { const osmium::Location loc{0.0, 85.0511288}; const osmium::geom::Coordinates c{0.0, 20037508.34}; - REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); - REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); + REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.00001)); + REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.00001)); } } @@ -118,8 +120,8 @@ TEST_CASE("Compare mercator implementations") { for (int n = 0; n < 10000; ++n) { const osmium::Location loc{dis_x(gen), dis_y(gen)}; - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); + REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.00001)); + REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.00001)); } } diff --git a/third_party/libosmium/test/t/geom/test_tile.cpp b/third_party/libosmium/test/t/geom/test_tile.cpp index 421879758..ca289f76f 100644 --- a/third_party/libosmium/test/t/geom/test_tile.cpp +++ b/third_party/libosmium/test/t/geom/test_tile.cpp @@ -52,7 +52,7 @@ TEST_CASE("Tile from x0.0 y0.0 at zoom 4") { osmium::geom::Tile t{4, l}; - auto n = 1 << (4-1); + const auto n = 1 << (4-1); REQUIRE(t.x == n); REQUIRE(t.y == n); REQUIRE(t.z == 4); @@ -115,3 +115,29 @@ TEST_CASE("Check a random list of tiles") { } } +TEST_CASE("Invalid tiles") { + osmium::geom::Tile tile{0, 0, 0}; + + REQUIRE(tile.valid()); + + SECTION("Zoom level out of bounds") { + tile.z = 100; + } + SECTION("x out of bounds") { + tile.x = 1; + } + SECTION("y out of bounds") { + tile.y = 1; + } + SECTION("x out of bounds") { + tile.z = 4; + tile.x = 100; + } + SECTION("y out of bounds") { + tile.z = 4; + tile.y = 100; + } + + REQUIRE_FALSE(tile.valid()); +} + diff --git a/third_party/libosmium/test/t/geom/test_wkb.cpp b/third_party/libosmium/test/t/geom/test_wkb.cpp index 29b6d6b6f..a4f2d99ec 100644 --- a/third_party/libosmium/test/t/geom/test_wkb.cpp +++ b/third_party/libosmium/test/t/geom/test_wkb.cpp @@ -87,11 +87,11 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { const auto& wnl = create_test_wnl_same_location(buffer); SECTION("unique forwards (default)") { - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); } SECTION("unique backwards") { - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); } SECTION("all forwards") { @@ -110,7 +110,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { const auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::invalid_location&); } } @@ -122,17 +122,17 @@ TEST_CASE("WKB geometry (byte-order-independent)") { osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; SECTION("empty point") { - REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), const osmium::invalid_location&); } SECTION("empty linestring") { osmium::memory::Buffer buffer{10000}; const 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); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), const 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 f6913c40b..7f338680d 100644 --- a/third_party/libosmium/test/t/geom/test_wkt.cpp +++ b/third_party/libosmium/test/t/geom/test_wkt.cpp @@ -7,29 +7,25 @@ #include "wnl_helper.hpp" TEST_CASE("WKT geometry for point") { + const osmium::geom::WKTFactory<> factory; + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "POINT(3.2 4.2)"); +} - osmium::geom::WKTFactory<> factory; - - SECTION("point") { - const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; - REQUIRE(wkt == "POINT(3.2 4.2)"); - } - - SECTION("empty point") { - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); - } - +TEST_CASE("WKT geometry for empty point") { + const osmium::geom::WKTFactory<> factory; + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), const osmium::invalid_location&); } TEST_CASE("WKT geometry for point in ekwt") { - osmium::geom::WKTFactory<> factory(7, osmium::geom::wkt_type::ewkt); + const osmium::geom::WKTFactory<> factory{7, osmium::geom::wkt_type::ewkt}; const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; REQUIRE(wkt == "SRID=4326;POINT(3.2 4.2)"); } TEST_CASE("WKT geometry for point in ekwt in web mercator") { - osmium::geom::WKTFactory factory(2, osmium::geom::wkt_type::ewkt); + const osmium::geom::WKTFactory factory{2, osmium::geom::wkt_type::ewkt}; const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; REQUIRE(wkt == "SRID=3857;POINT(356222.37 467961.14)"); @@ -67,17 +63,17 @@ TEST_CASE("WKT geometry factory") { SECTION("empty linestring") { const 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); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), const osmium::geometry_error&); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), const osmium::geometry_error&); } SECTION("linestring with two same locations") { const auto& wnl = create_test_wnl_same_location(buffer); SECTION("unique forwards") { - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::geometry_error&); try { factory.create_linestring(wnl); @@ -88,7 +84,7 @@ TEST_CASE("WKT geometry factory") { } SECTION("unique backwards") { - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), const osmium::geometry_error&); } SECTION("all forwards") { @@ -105,7 +101,7 @@ TEST_CASE("WKT geometry factory") { SECTION("linestring with undefined location") { const auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), const osmium::invalid_location&); } SECTION("area with one outer and no inner rings") { diff --git a/third_party/libosmium/test/t/handler/test_check_order_handler.cpp b/third_party/libosmium/test/t/handler/test_check_order_handler.cpp new file mode 100644 index 000000000..e6b1600a6 --- /dev/null +++ b/third_party/libosmium/test/t/handler/test_check_order_handler.cpp @@ -0,0 +1,128 @@ +#include "catch.hpp" + +#include +#include +#include +#include + +TEST_CASE("CheckOrder handler if everything is in order") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("n-126", buffer)); + REQUIRE(osmium::opl_parse("n123", buffer)); + REQUIRE(osmium::opl_parse("n124", buffer)); + REQUIRE(osmium::opl_parse("n128", buffer)); + REQUIRE(osmium::opl_parse("w-100", buffer)); + REQUIRE(osmium::opl_parse("w100", buffer)); + REQUIRE(osmium::opl_parse("w102", buffer)); + REQUIRE(osmium::opl_parse("r-200", buffer)); + REQUIRE(osmium::opl_parse("r100", buffer)); + + osmium::handler::CheckOrder handler; + osmium::apply(buffer, handler); + REQUIRE(handler.max_node_id() == 128); + REQUIRE(handler.max_way_id() == 102); + REQUIRE(handler.max_relation_id() == 100); +} + +TEST_CASE("CheckOrder handler: Nodes must be in order") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("n3", buffer)); + + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("n2", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("n-2", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Ways must be in order") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("w3", buffer)); + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("w2", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("w-2", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Relations must be in order") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("r3", buffer)); + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("r2", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("r-2", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Same id twice is not allowed") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("n3", buffer)); + REQUIRE(osmium::opl_parse("n3", buffer)); + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Nodes after ways") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("w50", buffer)); + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("n30", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("n-30", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Nodes after relations") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("r50", buffer)); + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("n30", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("n-30", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + +TEST_CASE("CheckOrder handler: Ways after relations") { + osmium::memory::Buffer buffer{1024}; + + REQUIRE(osmium::opl_parse("r50", buffer)); + SECTION("Positive ID") { + REQUIRE(osmium::opl_parse("w30", buffer)); + } + SECTION("Negative ID") { + REQUIRE(osmium::opl_parse("w-30", buffer)); + } + + osmium::handler::CheckOrder handler; + REQUIRE_THROWS_AS(osmium::apply(buffer, handler), const osmium::out_of_order_error&); +} + diff --git a/third_party/libosmium/test/t/handler/test_dynamic_handler.cpp b/third_party/libosmium/test/t/handler/test_dynamic_handler.cpp new file mode 100644 index 000000000..7b20f129c --- /dev/null +++ b/third_party/libosmium/test/t/handler/test_dynamic_handler.cpp @@ -0,0 +1,116 @@ +#include "catch.hpp" + +#include +#include +#include + +struct Handler1 : public osmium::handler::Handler { + + int& count; + + explicit Handler1(int& c) : + count(c) { + } + + void node(const osmium::Node&) noexcept { + ++count; + } + + void way(const osmium::Way&) noexcept { + ++count; + } + + void relation(const osmium::Relation&) noexcept { + ++count; + } + + void area(const osmium::Area&) noexcept { + ++count; + } + + void changeset(const osmium::Changeset&) noexcept { + ++count; + } + + void flush() noexcept { + ++count; + } + +}; + +struct Handler2 : public osmium::handler::Handler { + + int& count; + + explicit Handler2(int& c) : + count(c) { + } + + void node(const osmium::Node&) noexcept { + count += 2; + } + + void way(const osmium::Way&) noexcept { + count += 2; + } + + void relation(const osmium::Relation&) noexcept { + count += 2; + } + + void area(const osmium::Area&) noexcept { + count += 2; + } + + void changeset(const osmium::Changeset&) noexcept { + count += 2; + } + +}; + +osmium::memory::Buffer fill_buffer() { + using namespace osmium::builder::attr; + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; + + osmium::builder::add_node(buffer, _id(1)); + osmium::builder::add_way(buffer, _id(2)); + osmium::builder::add_relation(buffer, _id(3)); + osmium::builder::add_area(buffer, _id(4)); + osmium::builder::add_changeset(buffer, _cid(5)); + + return buffer; +} + +TEST_CASE("Base test: static handler") { + const auto buffer = fill_buffer(); + + int count = 0; + Handler1 h1{count}; + osmium::apply(buffer, h1); + REQUIRE(count == 6); + + count = 0; + Handler2 h2{count}; + osmium::apply(buffer, h2); + REQUIRE(count == 10); +} + +TEST_CASE("Dynamic handler") { + const auto buffer = fill_buffer(); + + osmium::handler::DynamicHandler handler; + int count = 0; + + osmium::apply(buffer, handler); + REQUIRE(count == 0); + + handler.set(count); + osmium::apply(buffer, handler); + REQUIRE(count == 6); + + count = 0; + handler.set(count); + osmium::apply(buffer, handler); + REQUIRE(count == 10); +} + diff --git a/third_party/libosmium/test/t/index/test_file_based_index.cpp b/third_party/libosmium/test/t/index/test_file_based_index.cpp index 42cf57456..278f43fa1 100644 --- a/third_party/libosmium/test/t/index/test_file_based_index.cpp +++ b/third_party/libosmium/test/t/index/test_file_based_index.cpp @@ -13,7 +13,7 @@ TEST_CASE("File based index") { - int fd = osmium::detail::create_tmp_file(); + const int fd = osmium::detail::create_tmp_file(); REQUIRE(osmium::util::file_size(fd) == 0); @@ -27,17 +27,17 @@ TEST_CASE("File based index") { constexpr const size_t S = sizeof(index_type::element_type); { - index_type index(fd); + index_type index{fd}; REQUIRE(index.size() == 0); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 3), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 6), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 3), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 6), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); index.set(id1, loc1); REQUIRE(index.size() == 7); @@ -50,11 +50,11 @@ TEST_CASE("File based index") { REQUIRE(loc1 == index.get(id1)); REQUIRE(loc2 == index.get(id2)); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.size() == 7); REQUIRE(std::distance(index.cbegin(), index.cend()) == 7); @@ -63,7 +63,7 @@ TEST_CASE("File based index") { } { - index_type index(fd); + index_type index{fd}; REQUIRE(osmium::util::file_size(fd) >= (6 * S)); REQUIRE(index.size() == 7); @@ -71,11 +71,11 @@ TEST_CASE("File based index") { REQUIRE(loc1 == index.get(id1)); REQUIRE(loc2 == index.get(id2)); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.size() == 7); REQUIRE(std::distance(index.cbegin(), index.cend()) == 7); @@ -97,17 +97,17 @@ TEST_CASE("File based index") { constexpr const size_t S = sizeof(index_type::element_type); { - index_type index(fd); + index_type index{fd}; REQUIRE(index.size() == 0); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 3), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 6), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 3), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 6), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); index.set(id1, loc1); REQUIRE(index.size() == 1); @@ -120,11 +120,11 @@ TEST_CASE("File based index") { REQUIRE(loc1 == index.get(id1)); REQUIRE(loc2 == index.get(id2)); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.size() == 2); REQUIRE(std::distance(index.cbegin(), index.cend()) == 2); @@ -133,7 +133,7 @@ TEST_CASE("File based index") { } { - index_type index(fd); + index_type index{fd}; REQUIRE(osmium::util::file_size(fd) >= (2 * S)); REQUIRE(index.size() == 2); @@ -141,11 +141,11 @@ TEST_CASE("File based index") { REQUIRE(loc1 == index.get(id1)); REQUIRE(loc2 == index.get(id2)); - REQUIRE_THROWS_AS(index.get( 0), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 1), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 5), osmium::not_found); - REQUIRE_THROWS_AS(index.get( 7), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get( 0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get( 7), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.size() == 2); REQUIRE(std::distance(index.cbegin(), index.cend()) == 2); diff --git a/third_party/libosmium/test/t/index/test_id_to_location.cpp b/third_party/libosmium/test/t/index/test_id_to_location.cpp index 9e38d12dc..4b6331417 100644 --- a/third_party/libosmium/test/t/index/test_id_to_location.cpp +++ b/third_party/libosmium/test/t/index/test_id_to_location.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -24,17 +25,17 @@ void test_func_all(TIndex& index) { const osmium::Location loc1{1.2, 4.5}; const osmium::Location loc2{3.5, -7.2}; - REQUIRE_THROWS_AS(index.get(id1), osmium::not_found); + REQUIRE_THROWS_AS(index.get(id1), const osmium::not_found&); index.set(id1, loc1); index.set(id2, loc2); index.sort(); - REQUIRE_THROWS_AS(index.get(0), osmium::not_found); - REQUIRE_THROWS_AS(index.get(1), osmium::not_found); - REQUIRE_THROWS_AS(index.get(5), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get(0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE_THROWS_WITH(index.get(0), "id 0 not found"); REQUIRE_THROWS_WITH(index.get(1), "id 1 not found"); @@ -62,10 +63,10 @@ void test_func_real(TIndex& index) { REQUIRE(loc1 == index.get_noexcept(id1)); REQUIRE(loc2 == index.get_noexcept(id2)); - REQUIRE_THROWS_AS(index.get(0), osmium::not_found); - REQUIRE_THROWS_AS(index.get(1), osmium::not_found); - REQUIRE_THROWS_AS(index.get(5), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get(0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.get_noexcept(0) == osmium::Location{}); REQUIRE(index.get_noexcept(1) == osmium::Location{}); @@ -74,13 +75,13 @@ void test_func_real(TIndex& index) { index.clear(); - REQUIRE_THROWS_AS(index.get(id1), osmium::not_found); - REQUIRE_THROWS_AS(index.get(id2), osmium::not_found); + REQUIRE_THROWS_AS(index.get(id1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(id2), const osmium::not_found&); - REQUIRE_THROWS_AS(index.get(0), osmium::not_found); - REQUIRE_THROWS_AS(index.get(1), osmium::not_found); - REQUIRE_THROWS_AS(index.get(5), osmium::not_found); - REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_AS(index.get(0), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(1), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(5), const osmium::not_found&); + REQUIRE_THROWS_AS(index.get(100), const osmium::not_found&); REQUIRE(index.get_noexcept(id1) == osmium::Location{}); REQUIRE(index.get_noexcept(id2) == osmium::Location{}); @@ -180,15 +181,67 @@ TEST_CASE("Map Id to location: SparseMemArray") { test_func_real(index2); } +TEST_CASE("Map Id to location: FlexMem sparse") { + using index_type = osmium::index::map::FlexMem; + + index_type index1; + test_func_all(index1); + + index_type index2; + test_func_real(index2); +} + +TEST_CASE("Map Id to location: FlexMem dense") { + using index_type = osmium::index::map::FlexMem; + + index_type index1{true}; + test_func_all(index1); + + index_type index2{true}; + test_func_real(index2); +} + +TEST_CASE("Map Id to location: FlexMem switch") { + using index_type = osmium::index::map::FlexMem; + + const osmium::Location loc1{1.1, 1.2}; + const osmium::Location loc2{2.2, -9.4}; + + index_type index; + + REQUIRE(index.size() == 0); + + index.set(17, loc1); + index.set(99, loc2); + + REQUIRE_FALSE(index.is_dense()); + REQUIRE(index.size() == 2); + REQUIRE(index.get_noexcept(0) == osmium::Location{}); + REQUIRE(index.get_noexcept(1) == osmium::Location{}); + REQUIRE(index.get_noexcept(17) == loc1); + REQUIRE(index.get_noexcept(99) == loc2); + REQUIRE(index.get_noexcept(2000000000) == osmium::Location{}); + + index.switch_to_dense(); + + REQUIRE(index.is_dense()); + REQUIRE(index.size() >= 2); + REQUIRE(index.get_noexcept(0) == osmium::Location{}); + REQUIRE(index.get_noexcept(1) == osmium::Location{}); + REQUIRE(index.get_noexcept(17) == loc1); + REQUIRE(index.get_noexcept(99) == loc2); + REQUIRE(index.get_noexcept(2000000000) == osmium::Location{}); +} + TEST_CASE("Map Id to location: Dynamic map choice") { using map_type = osmium::index::map::Map; const auto& map_factory = osmium::index::MapFactory::instance(); const std::vector map_type_names = map_factory.map_types(); - REQUIRE(map_type_names.size() >= 5); + REQUIRE(map_type_names.size() >= 6); - REQUIRE_THROWS_AS(map_factory.create_map(""), osmium::map_factory_error); - REQUIRE_THROWS_AS(map_factory.create_map("does not exist"), osmium::map_factory_error); + REQUIRE_THROWS_AS(map_factory.create_map(""), const osmium::map_factory_error&); + REQUIRE_THROWS_AS(map_factory.create_map("does not exist"), const osmium::map_factory_error&); REQUIRE_THROWS_WITH(map_factory.create_map(""), "Need non-empty map type name"); REQUIRE_THROWS_WITH(map_factory.create_map("does not exist"), "Support for map type 'does not exist' not compiled into this binary"); diff --git a/third_party/libosmium/test/t/index/test_relations_map.cpp b/third_party/libosmium/test/t/index/test_relations_map.cpp index 8ef07558e..1d77af286 100644 --- a/third_party/libosmium/test/t/index/test_relations_map.cpp +++ b/third_party/libosmium/test/t/index/test_relations_map.cpp @@ -21,14 +21,17 @@ TEST_CASE("RelationsMapStash lvalue") { REQUIRE_FALSE(stash.empty()); REQUIRE(stash.size() == 2); - auto index= stash.build_index(); + const auto index = stash.build_member_to_parent_index(); REQUIRE_FALSE(index.empty()); REQUIRE(index.size() == 2); - index.for_each_parent(1, [](osmium::unsigned_object_id_type id) { + int count = 0; + index.for_each(1, [&](osmium::unsigned_object_id_type id) { REQUIRE(id == 2); + ++count; }); + REQUIRE(count == 1); } osmium::index::RelationsMapIndex func() { @@ -37,14 +40,71 @@ osmium::index::RelationsMapIndex func() { stash.add(1, 2); stash.add(2, 3); - return stash.build_index(); + return stash.build_member_to_parent_index(); } TEST_CASE("RelationsMapStash rvalue") { const osmium::index::RelationsMapIndex index{func()}; - index.for_each_parent(1, [](osmium::unsigned_object_id_type id) { - REQUIRE(id == 2); + int count = 0; + index.for_each(2, [&](osmium::unsigned_object_id_type id) { + REQUIRE(id == 3); + ++count; }); + REQUIRE(count == 1); +} + +TEST_CASE("RelationsMapStash reverse index") { + osmium::index::RelationsMapStash stash; + REQUIRE(stash.empty()); + REQUIRE(stash.size() == 0); + + stash.add(1, 2); + stash.add(2, 3); + REQUIRE_FALSE(stash.empty()); + REQUIRE(stash.size() == 2); + + const auto index = stash.build_parent_to_member_index(); + + REQUIRE_FALSE(index.empty()); + REQUIRE(index.size() == 2); + + int count = 0; + index.for_each(2, [&](osmium::unsigned_object_id_type id) { + REQUIRE(id == 1); + ++count; + }); + index.for_each(3, [&](osmium::unsigned_object_id_type id) { + REQUIRE(id == 2); + ++count; + }); + REQUIRE(count == 2); +} + +TEST_CASE("RelationsMapStash both indexes") { + osmium::index::RelationsMapStash stash; + REQUIRE(stash.empty()); + REQUIRE(stash.size() == 0); + + stash.add(1, 2); + stash.add(2, 3); + REQUIRE_FALSE(stash.empty()); + REQUIRE(stash.size() == 2); + + const auto index = stash.build_indexes(); + + REQUIRE_FALSE(index.empty()); + REQUIRE(index.size() == 2); + + int count = 0; + index.member_to_parent().for_each(2, [&](osmium::unsigned_object_id_type id) { + REQUIRE(id == 3); + ++count; + }); + index.parent_to_member().for_each(2, [&](osmium::unsigned_object_id_type id) { + REQUIRE(id == 1); + ++count; + }); + REQUIRE(count == 2); } diff --git a/third_party/libosmium/test/t/io/data-cr.opl b/third_party/libosmium/test/t/io/data-cr.opl new file mode 100644 index 000000000..aef666eef --- /dev/null +++ b/third_party/libosmium/test/t/io/data-cr.opl @@ -0,0 +1 @@ +n1 v1 dV c1 t2014-01-01T00:00:00Z i1 utest T x1.02 y1.02 diff --git a/third_party/libosmium/test/t/io/test_bzip2.cpp b/third_party/libosmium/test/t/io/test_bzip2.cpp index 2d1c0f327..3e3fe8182 100644 --- a/third_party/libosmium/test/t/io/test_bzip2.cpp +++ b/third_party/libosmium/test/t/io/test_bzip2.cpp @@ -8,7 +8,7 @@ #include TEST_CASE("Read bzip2-compressed file") { - std::string input_file = with_data_dir("t/io/data_bzip2.txt.bz2"); + const std::string input_file = with_data_dir("t/io/data_bzip2.txt.bz2"); const int fd = ::open(input_file.c_str(), O_RDONLY); REQUIRE(fd > 0); diff --git a/third_party/libosmium/test/t/io/test_compression_factory.cpp b/third_party/libosmium/test/t/io/test_compression_factory.cpp index 5428d8213..97cb1535d 100644 --- a/third_party/libosmium/test/t/io/test_compression_factory.cpp +++ b/third_party/libosmium/test/t/io/test_compression_factory.cpp @@ -3,25 +3,21 @@ #include -TEST_CASE("Compression factory") { +TEST_CASE("Create compressor using factory") { const auto& factory = osmium::io::CompressionFactory::instance(); - - SECTION("compressor") { - REQUIRE(factory.create_compressor(osmium::io::file_compression::none, -1, osmium::io::fsync::no)); - } - - SECTION("decompressor") { - REQUIRE(factory.create_decompressor(osmium::io::file_compression::none, nullptr, 0)); - } - - SECTION("fail on undefined compression") { - REQUIRE_THROWS_AS({ - factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no); - }, osmium::unsupported_file_format_error); - REQUIRE_THROWS_WITH({ - factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no); - }, "Support for compression 'gzip' not compiled into this binary"); - } - + REQUIRE(factory.create_compressor(osmium::io::file_compression::none, -1, osmium::io::fsync::no)); +} + +TEST_CASE("Create decompressor using factory") { + const auto& factory = osmium::io::CompressionFactory::instance(); + REQUIRE(factory.create_decompressor(osmium::io::file_compression::none, nullptr, 0)); +} + +TEST_CASE("Compression factory fails on undefined compression") { + const auto& factory = osmium::io::CompressionFactory::instance(); + REQUIRE_THROWS_AS(factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no), + const osmium::unsupported_file_format_error&); + REQUIRE_THROWS_WITH(factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no), + "Support for compression 'gzip' not compiled into this binary"); } diff --git a/third_party/libosmium/test/t/io/test_file_formats.cpp b/third_party/libosmium/test/t/io/test_file_formats.cpp index 356e3d148..284d47779 100644 --- a/third_party/libosmium/test/t/io/test_file_formats.cpp +++ b/third_party/libosmium/test/t/io/test_file_formats.cpp @@ -5,31 +5,31 @@ #include TEST_CASE("Default file format") { - osmium::io::File f; + const osmium::io::File f; REQUIRE(osmium::io::file_format::unknown == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("File format when empty (stdin/stdout)") { - osmium::io::File f{""}; + const osmium::io::File f{""}; REQUIRE(osmium::io::file_format::unknown == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("File format from dash (stdin/stdout)") { - osmium::io::File f{"-"}; + const osmium::io::File f{"-"}; REQUIRE(osmium::io::file_format::unknown == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("File format from dash with osm.bz2") { - osmium::io::File f{"-", "osm.bz2"}; + const osmium::io::File f{"-", "osm.bz2"}; REQUIRE("" == f.filename()); REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::bzip2 == f.compression()); @@ -38,7 +38,7 @@ TEST_CASE("File format from dash with osm.bz2") { } TEST_CASE("Detect file format by suffix 'osm'") { - osmium::io::File f{"test.osm"}; + const osmium::io::File f{"test.osm"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -46,7 +46,7 @@ TEST_CASE("Detect file format by suffix 'osm'") { } TEST_CASE("Detect file format by suffix 'pbf'") { - osmium::io::File f{"test.pbf"}; + const osmium::io::File f{"test.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -54,7 +54,7 @@ TEST_CASE("Detect file format by suffix 'pbf'") { } TEST_CASE("Detect file format by suffix 'osm.pbf'") { - osmium::io::File f{"test.osm.pbf"}; + const osmium::io::File f{"test.osm.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -62,7 +62,7 @@ TEST_CASE("Detect file format by suffix 'osm.pbf'") { } TEST_CASE("Detect file format by suffix 'opl'") { - osmium::io::File f{"test.opl"}; + const osmium::io::File f{"test.opl"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -70,7 +70,7 @@ TEST_CASE("Detect file format by suffix 'opl'") { } TEST_CASE("Detect file format by suffix 'osm.opl'") { - osmium::io::File f{"test.osm.opl"}; + const osmium::io::File f{"test.osm.opl"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -78,7 +78,7 @@ TEST_CASE("Detect file format by suffix 'osm.opl'") { } TEST_CASE("Detect file format by suffix 'osm.gz'") { - osmium::io::File f{"test.osm.gz"}; + const osmium::io::File f{"test.osm.gz"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -86,7 +86,7 @@ TEST_CASE("Detect file format by suffix 'osm.gz'") { } TEST_CASE("Detect file format by suffix 'opl.bz2'") { - osmium::io::File f{"test.osm.opl.bz2"}; + const osmium::io::File f{"test.osm.opl.bz2"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::bzip2 == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -94,7 +94,7 @@ TEST_CASE("Detect file format by suffix 'opl.bz2'") { } TEST_CASE("Detect file format by suffix 'osc.gz'") { - osmium::io::File f{"test.osc.gz"}; + const osmium::io::File f{"test.osc.gz"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -102,7 +102,7 @@ TEST_CASE("Detect file format by suffix 'osc.gz'") { } TEST_CASE("Detect file format by suffix 'opl.gz'") { - osmium::io::File f{"test.osh.opl.gz"}; + const osmium::io::File f{"test.osh.opl.gz"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -110,7 +110,7 @@ TEST_CASE("Detect file format by suffix 'opl.gz'") { } TEST_CASE("Detect file format by suffix 'osh.pbf'") { - osmium::io::File f{"test.osh.pbf"}; + const osmium::io::File f{"test.osh.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -118,7 +118,7 @@ TEST_CASE("Detect file format by suffix 'osh.pbf'") { } TEST_CASE("Override file format by suffix 'osm'") { - osmium::io::File f{"test", "osm"}; + const osmium::io::File f{"test", "osm"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -126,7 +126,7 @@ TEST_CASE("Override file format by suffix 'osm'") { } TEST_CASE("Override file format by suffix 'pbf'") { - osmium::io::File f{"test", "pbf"}; + const osmium::io::File f{"test", "pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -134,7 +134,7 @@ TEST_CASE("Override file format by suffix 'pbf'") { } TEST_CASE("Override file format by suffix 'osm.pbf'") { - osmium::io::File f{"test", "osm.pbf"}; + const osmium::io::File f{"test", "osm.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -142,7 +142,7 @@ TEST_CASE("Override file format by suffix 'osm.pbf'") { } TEST_CASE("Override file format by suffix 'opl'") { - osmium::io::File f{"test", "opl"}; + const osmium::io::File f{"test", "opl"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -150,7 +150,7 @@ TEST_CASE("Override file format by suffix 'opl'") { } TEST_CASE("Override file format by suffix 'osm.opl'") { - osmium::io::File f{"test", "osm.opl"}; + const osmium::io::File f{"test", "osm.opl"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -158,7 +158,7 @@ TEST_CASE("Override file format by suffix 'osm.opl'") { } TEST_CASE("Override file format by suffix 'osm.gz'") { - osmium::io::File f{"test", "osm.gz"}; + const osmium::io::File f{"test", "osm.gz"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -166,7 +166,7 @@ TEST_CASE("Override file format by suffix 'osm.gz'") { } TEST_CASE("Override file format by suffix 'osm.opl.bz2'") { - osmium::io::File f{"test", "osm.opl.bz2"}; + const osmium::io::File f{"test", "osm.opl.bz2"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::bzip2 == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -174,7 +174,7 @@ TEST_CASE("Override file format by suffix 'osm.opl.bz2'") { } TEST_CASE("Override file format by suffix 'osc.gz'") { - osmium::io::File f{"test", "osc.gz"}; + const osmium::io::File f{"test", "osc.gz"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -182,15 +182,55 @@ TEST_CASE("Override file format by suffix 'osc.gz'") { } TEST_CASE("Override file format by suffix 'osh.opl.gz'") { - osmium::io::File f{"test", "osh.opl.gz"}; + const osmium::io::File f{"test", "osh.opl.gz"}; REQUIRE(osmium::io::file_format::opl == f.format()); REQUIRE(osmium::io::file_compression::gzip == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); f.check(); } +TEST_CASE("File format by suffix 'blackhole'") { + const osmium::io::File f{"test.blackhole"}; + REQUIRE(osmium::io::file_format::blackhole == f.format()); + REQUIRE(osmium::io::file_compression::none == f.compression()); + REQUIRE(false == f.has_multiple_object_versions()); + f.check(); +} + +TEST_CASE("Override file format by suffix 'blackhole'") { + const osmium::io::File f{"test", "blackhole"}; + REQUIRE(osmium::io::file_format::blackhole == f.format()); + REQUIRE(osmium::io::file_compression::none == f.compression()); + REQUIRE(false == f.has_multiple_object_versions()); + f.check(); +} + +TEST_CASE("Override file format by suffix 'osm.blackhole'") { + const osmium::io::File f{"test", "osm.blackhole"}; + REQUIRE(osmium::io::file_format::blackhole == f.format()); + REQUIRE(osmium::io::file_compression::none == f.compression()); + REQUIRE(false == f.has_multiple_object_versions()); + f.check(); +} + +TEST_CASE("Override file format by suffix 'osm.blackhole.bz2'") { + const osmium::io::File f{"test", "osm.blackhole.bz2"}; + REQUIRE(osmium::io::file_format::blackhole == f.format()); + REQUIRE(osmium::io::file_compression::bzip2 == f.compression()); + REQUIRE(false == f.has_multiple_object_versions()); + f.check(); +} + +TEST_CASE("Override file format by suffix 'osh.blackhole.gz'") { + const osmium::io::File f{"test", "osh.blackhole.gz"}; + REQUIRE(osmium::io::file_format::blackhole == f.format()); + REQUIRE(osmium::io::file_compression::gzip == f.compression()); + REQUIRE(true == f.has_multiple_object_versions()); + f.check(); +} + TEST_CASE("Override file format by suffix 'osh.pbf'") { - osmium::io::File f{"test", "osh.pbf"}; + const osmium::io::File f{"test", "osh.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -198,7 +238,7 @@ TEST_CASE("Override file format by suffix 'osh.pbf'") { } TEST_CASE("Format option pbf history") { - osmium::io::File f{"test", "pbf,history=true"}; + const osmium::io::File f{"test", "pbf,history=true"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); @@ -206,7 +246,7 @@ TEST_CASE("Format option pbf history") { } TEST_CASE("Format option pbf foo") { - osmium::io::File f{"test.osm", "pbf,foo=bar"}; + const osmium::io::File f{"test.osm", "pbf,foo=bar"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE("bar" == f.get("foo")); @@ -214,7 +254,7 @@ TEST_CASE("Format option pbf foo") { } TEST_CASE("Format option xml abc something") { - osmium::io::File f{"test.bla", "xml,abc,some=thing"}; + const osmium::io::File f{"test.bla", "xml,abc,some=thing"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE("true" == f.get("abc")); @@ -224,29 +264,29 @@ TEST_CASE("Format option xml abc something") { } TEST_CASE("Unknown format 'foo.bar'") { - osmium::io::File f{"test.foo.bar"}; + const osmium::io::File f{"test.foo.bar"}; REQUIRE(osmium::io::file_format::unknown == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("Unknown format 'foo'") { - osmium::io::File f{"test", "foo"}; - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + const osmium::io::File f{"test", "foo"}; + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("Unknown format 'osm.foo'") { - osmium::io::File f{"test", "osm.foo"}; - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + const osmium::io::File f{"test", "osm.foo"}; + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("Unknown format 'bla=foo'") { - osmium::io::File f{"test", "bla=foo"}; - REQUIRE_THROWS_AS(f.check(), std::runtime_error); + const osmium::io::File f{"test", "bla=foo"}; + REQUIRE_THROWS_AS(f.check(), const std::runtime_error&); } TEST_CASE("URL without format") { - osmium::io::File f{"http://www.example.com/api"}; + const osmium::io::File f{"http://www.example.com/api"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -254,7 +294,7 @@ TEST_CASE("URL without format") { } TEST_CASE("URL without format and filename") { - osmium::io::File f{"http://planet.osm.org/pbf/planet-latest.osm.pbf"}; + const osmium::io::File f{"http://planet.osm.org/pbf/planet-latest.osm.pbf"}; REQUIRE(osmium::io::file_format::pbf == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(false == f.has_multiple_object_versions()); @@ -262,7 +302,7 @@ TEST_CASE("URL without format and filename") { } TEST_CASE("URL with format") { - osmium::io::File f{"http://www.example.com/api", "osh"}; + const osmium::io::File f{"http://www.example.com/api", "osh"}; REQUIRE(osmium::io::file_format::xml == f.format()); REQUIRE(osmium::io::file_compression::none == f.compression()); REQUIRE(true == f.has_multiple_object_versions()); diff --git a/third_party/libosmium/test/t/io/test_opl_parser.cpp b/third_party/libosmium/test/t/io/test_opl_parser.cpp index dad223865..88cd57026 100644 --- a/third_party/libosmium/test/t/io/test_opl_parser.cpp +++ b/third_party/libosmium/test/t/io/test_opl_parser.cpp @@ -12,7 +12,7 @@ namespace oid = osmium::io::detail; TEST_CASE("Parse OPL: base exception") { - osmium::opl_error e{"foo"}; + const osmium::opl_error e{"foo"}; REQUIRE(e.data == nullptr); REQUIRE(e.line == 0); REQUIRE(e.column == 0); @@ -32,20 +32,16 @@ TEST_CASE("Parse OPL: exception with line and column") { } TEST_CASE("Parse OPL: space") { - std::string d{"a b \t c"}; + const std::string d{"a b \t c"}; const char* s = d.data(); - REQUIRE_THROWS_AS({ - oid::opl_parse_space(&s); - }, osmium::opl_error); + REQUIRE_THROWS_AS(oid::opl_parse_space(&s), const osmium::opl_error&); s = d.data() + 1; oid::opl_parse_space(&s); REQUIRE(*s == 'b'); - REQUIRE_THROWS_AS({ - oid::opl_parse_space(&s); - }, osmium::opl_error); + REQUIRE_THROWS_AS(oid::opl_parse_space(&s), const osmium::opl_error&); ++s; oid::opl_parse_space(&s); @@ -62,7 +58,7 @@ TEST_CASE("Parse OPL: check for space") { } TEST_CASE("Parse OPL: skip section") { - std::string d{"abcd efgh"}; + const std::string d{"abcd efgh"}; const char* skip1 = d.data() + 4; const char* skip2 = d.data() + 9; const char* s = d.data(); @@ -78,30 +74,26 @@ TEST_CASE("Parse OPL: parse escaped") { SECTION("Empty string") { const char* s = ""; - REQUIRE_THROWS_WITH({ - oid::opl_parse_escaped(&s, result); - }, "OPL error: eol"); + REQUIRE_THROWS_WITH(oid::opl_parse_escaped(&s, result), + "OPL error: eol"); } SECTION("Illegal character for hex") { const char* s = "x"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_escaped(&s, result); - }, "OPL error: not a hex char"); + REQUIRE_THROWS_WITH(oid::opl_parse_escaped(&s, result), + "OPL error: not a hex char"); } SECTION("Illegal character for hex after legal hex characters") { const char* s = "0123x"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_escaped(&s, result); - }, "OPL error: not a hex char"); + REQUIRE_THROWS_WITH(oid::opl_parse_escaped(&s, result), + "OPL error: not a hex char"); } SECTION("Too long") { const char* s = "123456780"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_escaped(&s, result); - }, "OPL error: hex escape too long"); + REQUIRE_THROWS_WITH(oid::opl_parse_escaped(&s, result), + "OPL error: hex escape too long"); } SECTION("No data") { @@ -239,23 +231,21 @@ TEST_CASE("Parse OPL: parse string") { SECTION("string with invalid escaping") { const char* s = "foo%"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_string(&s, result); - }, "OPL error: eol"); + REQUIRE_THROWS_WITH(oid::opl_parse_string(&s, result), + "OPL error: eol"); } SECTION("string with invalid escaped characters") { const char* s = "foo%x%"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_string(&s, result); - }, "OPL error: not a hex char"); + REQUIRE_THROWS_WITH(oid::opl_parse_string(&s, result), + "OPL error: not a hex char"); } } template T test_parse_int(const char* s) { - auto r = oid::opl_parse_int(&s); + const auto r = oid::opl_parse_int(&s); REQUIRE(*s == 'x'); return r; } @@ -269,25 +259,20 @@ TEST_CASE("Parse OPL: integer") { REQUIRE(test_parse_int("1234567890123x") == 1234567890123); REQUIRE(test_parse_int("-1234567890123x") == -1234567890123); - REQUIRE_THROWS_WITH({ - test_parse_int(""); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(test_parse_int(""), + "OPL error: expected integer"); - REQUIRE_THROWS_WITH({ - test_parse_int("-x"); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(test_parse_int("-x"), + "OPL error: expected integer"); - REQUIRE_THROWS_WITH({ - test_parse_int(" 1"); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(test_parse_int(" 1"), + "OPL error: expected integer"); - REQUIRE_THROWS_WITH({ - test_parse_int("x"); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(test_parse_int("x"), + "OPL error: expected integer"); - REQUIRE_THROWS_WITH({ - test_parse_int("99999999999999999999999x"); - }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("99999999999999999999999x"), + "OPL error: integer too long"); } TEST_CASE("Parse OPL: int32_t") { @@ -295,29 +280,24 @@ TEST_CASE("Parse OPL: int32_t") { REQUIRE(test_parse_int("123x") == 123); REQUIRE(test_parse_int("-123x") == -123); - REQUIRE_THROWS_WITH({ - test_parse_int("12345678901x"); - }, "OPL error: integer too long"); - REQUIRE_THROWS_WITH({ - test_parse_int("-12345678901x"); - }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("12345678901x"), + "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("-12345678901x"), + "OPL error: integer too long"); } TEST_CASE("Parse OPL: uint32_t") { REQUIRE(test_parse_int("0x") == 0); REQUIRE(test_parse_int("123x") == 123); - REQUIRE_THROWS_WITH({ - test_parse_int("-123x"); - }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("-123x"), + "OPL error: integer too long"); - REQUIRE_THROWS_WITH({ - test_parse_int("12345678901x"); - }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("12345678901x"), + "OPL error: integer too long"); - REQUIRE_THROWS_WITH({ - test_parse_int("-12345678901x"); - }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH(test_parse_int("-12345678901x"), + "OPL error: integer too long"); } TEST_CASE("Parse OPL: visible flag") { @@ -337,9 +317,8 @@ TEST_CASE("Parse OPL: deleted flag") { TEST_CASE("Parse OPL: invalid visible flag") { const char* data = "x"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_visible(&data); - }, "OPL error: invalid visible flag"); + REQUIRE_THROWS_WITH(oid::opl_parse_visible(&data), + "OPL error: invalid visible flag"); } TEST_CASE("Parse OPL: timestamp (empty)") { @@ -365,9 +344,8 @@ TEST_CASE("Parse OPL: timestamp (tab)") { TEST_CASE("Parse OPL: timestamp (invalid)") { const char* data = "abc"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_timestamp(&data); - }, "OPL error: can not parse timestamp"); + REQUIRE_THROWS_WITH(oid::opl_parse_timestamp(&data), + "OPL error: can not parse timestamp"); } TEST_CASE("Parse OPL: timestamp (valid)") { @@ -388,9 +366,8 @@ TEST_CASE("Parse OPL: tags") { SECTION("Empty") { const char* data = ""; - REQUIRE_THROWS_WITH({ - oid::opl_parse_tags(data, buffer); - }, "OPL error: expected '='"); + REQUIRE_THROWS_WITH(oid::opl_parse_tags(data, buffer), + "OPL error: expected '='"); } SECTION("One tag") { @@ -431,16 +408,14 @@ TEST_CASE("Parse OPL: tags") { SECTION("No equal signs") { const char* data = "a"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_tags(data, buffer); - }, "OPL error: expected '='"); + REQUIRE_THROWS_WITH(oid::opl_parse_tags(data, buffer), + "OPL error: expected '='"); } SECTION("Two equal signs") { const char* data = "a=b=c"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_tags(data, buffer); - }, "OPL error: expected ','"); + REQUIRE_THROWS_WITH(oid::opl_parse_tags(data, buffer), + "OPL error: expected ','"); } } @@ -456,16 +431,14 @@ TEST_CASE("Parse OPL: nodes") { SECTION("Invalid format, missing n") { const char* const s = "xyz"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); - }, "OPL error: expected 'n'"); + REQUIRE_THROWS_WITH(oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer), + "OPL error: expected 'n'"); } SECTION("Invalid format, missing ID") { const char* const s = "nx"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer), + "OPL error: expected integer"); } SECTION("Valid format: one node") { @@ -536,23 +509,20 @@ TEST_CASE("Parse OPL: members") { SECTION("Invalid: unknown object type") { const char* const s = "x123@foo"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); - }, "OPL error: unknown object type"); + REQUIRE_THROWS_WITH(oid::opl_parse_relation_members(s, s + std::strlen(s), buffer), + "OPL error: unknown object type"); } SECTION("Invalid: illegal ref") { const char* const s = "wx"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); - }, "OPL error: expected integer"); + REQUIRE_THROWS_WITH(oid::opl_parse_relation_members(s, s + std::strlen(s), buffer), + "OPL error: expected integer"); } SECTION("Invalid: missing @") { const char* const s = "n123foo"; - REQUIRE_THROWS_WITH({ - oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); - }, "OPL error: expected '@'"); + REQUIRE_THROWS_WITH(oid::opl_parse_relation_members(s, s + std::strlen(s), buffer), + "OPL error: expected '@'"); } SECTION("Valid format: one member") { @@ -928,16 +898,14 @@ TEST_CASE("Parse line") { } SECTION("Fail") { - REQUIRE_THROWS_WITH({ - oid::opl_parse_line(0, "X", buffer); - }, "OPL error: unknown type on line 0 column 0"); + REQUIRE_THROWS_WITH(oid::opl_parse_line(0, "X", buffer), + "OPL error: unknown type on line 0 column 0"); REQUIRE(buffer.written() == 0); } SECTION("New line at end not allowed") { - REQUIRE_THROWS_WITH({ - oid::opl_parse_line(0, "n12 v3\n", buffer); - }, "OPL error: expected space or tab character on line 0 column 6"); + REQUIRE_THROWS_WITH(oid::opl_parse_line(0, "n12 v3\n", buffer), + "OPL error: expected space or tab character on line 0 column 6"); } SECTION("Node, but not asking for nodes") { @@ -948,28 +916,28 @@ TEST_CASE("Parse line") { SECTION("Node") { REQUIRE(oid::opl_parse_line(0, "n12 v3", buffer)); REQUIRE(buffer.written() > 0); - auto& item = buffer.get(0); + const auto& item = buffer.get(0); REQUIRE(item.type() == osmium::item_type::node); } SECTION("Way") { REQUIRE(oid::opl_parse_line(0, "w12 v3", buffer)); REQUIRE(buffer.written() > 0); - auto& item = buffer.get(0); + const auto& item = buffer.get(0); REQUIRE(item.type() == osmium::item_type::way); } SECTION("Relation") { REQUIRE(oid::opl_parse_line(0, "r12 v3", buffer)); REQUIRE(buffer.written() > 0); - auto& item = buffer.get(0); + const auto& item = buffer.get(0); REQUIRE(item.type() == osmium::item_type::relation); } SECTION("Changeset") { REQUIRE(oid::opl_parse_line(0, "c12", buffer)); REQUIRE(buffer.written() > 0); - auto& item = buffer.get(0); + const auto& item = buffer.get(0); REQUIRE(item.type() == osmium::item_type::changeset); } @@ -1066,9 +1034,8 @@ TEST_CASE("Parse line with external interface") { } SECTION("Failure") { - REQUIRE_THROWS_WITH({ - osmium::opl_parse("x", buffer); - }, "OPL error: unknown type on line 0 column 0"); + REQUIRE_THROWS_WITH(osmium::opl_parse("x", buffer), + "OPL error: unknown type on line 0 column 0"); REQUIRE(buffer.written() == 0); REQUIRE(buffer.committed() == 0); } @@ -1085,6 +1052,16 @@ TEST_CASE("Parse OPL using Reader") { REQUIRE(node.id() == 1); } +TEST_CASE("Parse OPL with CRLF line ending using Reader") { + osmium::io::File file{with_data_dir("t/io/data-cr.opl")}; + osmium::io::Reader reader{file}; + + const auto buffer = reader.read(); + REQUIRE(buffer); + const auto& node = buffer.get(0); + REQUIRE(node.id() == 1); +} + TEST_CASE("Parse OPL with missing newline using Reader") { osmium::io::File file{with_data_dir("t/io/data-nonl.opl")}; osmium::io::Reader reader{file}; @@ -1095,3 +1072,159 @@ TEST_CASE("Parse OPL with missing newline using Reader") { REQUIRE(node.id() == 1); } +class lbl_tester { + + std::vector m_inputs; + std::vector m_outputs; + +public: + + lbl_tester(const std::initializer_list& inputs, + const std::initializer_list& outputs) : + m_inputs(inputs), + m_outputs(outputs) { + } + + bool input_done() { + return m_inputs.empty(); + } + + std::string get_input() { + REQUIRE_FALSE(m_inputs.empty()); + std::string data = std::move(m_inputs.front()); + m_inputs.erase(m_inputs.begin()); + return data; + } + + void parse_line(const char *data) { + REQUIRE_FALSE(m_outputs.empty()); + REQUIRE(m_outputs.front() == data); + m_outputs.erase(m_outputs.begin()); + } + + void check() { + REQUIRE(m_inputs.empty()); + REQUIRE(m_outputs.empty()); + } + +}; // class lbl_tester + +void check_lbl(const std::initializer_list& in, + const std::initializer_list& out) { + lbl_tester tester{in, out}; + osmium::io::detail::line_by_line(tester); + tester.check(); +} + +TEST_CASE("line_by_line for OPL parser 1") { + check_lbl({""}, {}); +} + +TEST_CASE("line_by_line for OPL parser 2") { + check_lbl({"\n"}, {}); +} + +TEST_CASE("line_by_line for OPL parser 3") { + check_lbl({"foo\n"}, {"foo"}); +} + +TEST_CASE("line_by_line for OPL parser 4") { + check_lbl({"foo"}, {"foo"}); +} + +TEST_CASE("line_by_line for OPL parser 5") { + check_lbl({"foo\nbar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 6") { + check_lbl({"foo\nbar"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 7") { + check_lbl({"foo\nbar\nbaz\n"}, {"foo", "bar", "baz"}); +} + +TEST_CASE("line_by_line for OPL parser 8") { + check_lbl({"foo\n", "bar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 9") { + check_lbl({"foo\nb", "ar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 10") { + check_lbl({"foo\nb", "ar\n", "baz\n"}, {"foo", "bar", "baz"}); +} + +TEST_CASE("line_by_line for OPL parser 11") { + check_lbl({"foo", "\nbar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 12") { + check_lbl({"foo", "\nbar"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 13") { + check_lbl({"foo", "\nbar", "\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 14") { + check_lbl({"foo\n", "b", "ar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 15") { + check_lbl({"foo\n", "ba", "r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 16") { + check_lbl({"foo", "\n", "bar\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 17") { + check_lbl({"foo\r\nbar\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 18") { + check_lbl({"foo\r\nb", "ar\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 19") { + check_lbl({"foo\r\nb", "ar\n", "baz\r\n"}, {"foo", "bar", "baz"}); +} + +TEST_CASE("line_by_line for OPL parser 20") { + check_lbl({"foo", "\r\nbar\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 21") { + check_lbl({"foo\r", "\nbar"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 22") { + check_lbl({"foo", "\r\nbar\r", "\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 23") { + check_lbl({"foo\r\n", "b", "ar\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 24") { + check_lbl({"foo\n", "ba", "r\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 25") { + check_lbl({"foo", "\n", "bar\r\n"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 26") { + check_lbl({"foo", "\n\r", "bar\r"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 27") { + check_lbl({"foo\r", "bar\n\r"}, {"foo", "bar"}); +} + +TEST_CASE("line_by_line for OPL parser 28") { + check_lbl({"foo\nb", "ar"}, {"foo", "bar"}); +} + diff --git a/third_party/libosmium/test/t/io/test_output_iterator.cpp b/third_party/libosmium/test/t/io/test_output_iterator.cpp index f554da260..df032a8fc 100644 --- a/third_party/libosmium/test/t/io/test_output_iterator.cpp +++ b/third_party/libosmium/test/t/io/test_output_iterator.cpp @@ -4,33 +4,30 @@ #include #include -TEST_CASE("output iterator") { - - osmium::io::Header header; - - SECTION("should be copy constructable") { - osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow}; - osmium::io::OutputIterator out1{writer}; - - osmium::io::OutputIterator out2{out1}; - } - - SECTION("should be copy assignable") { - osmium::io::Writer writer1{"test1.osm", header, osmium::io::overwrite::allow}; - osmium::io::Writer writer2{"test2.osm", header, osmium::io::overwrite::allow}; - - osmium::io::OutputIterator out1{writer1}; - osmium::io::OutputIterator out2{writer2}; - - out2 = out1; - } - - SECTION("should be incrementable") { - osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow}; - osmium::io::OutputIterator out{writer}; - - ++out; - } +TEST_CASE("Output iterator should be copy constructable") { + const osmium::io::Header header; + osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow}; + osmium::io::OutputIterator out1{writer}; + osmium::io::OutputIterator out2{out1}; +} + +TEST_CASE("Output iterator should be copy assignable") { + const osmium::io::Header header; + osmium::io::Writer writer1{"test1.osm", header, osmium::io::overwrite::allow}; + osmium::io::Writer writer2{"test2.osm", header, osmium::io::overwrite::allow}; + + osmium::io::OutputIterator out1{writer1}; + osmium::io::OutputIterator out2{writer2}; + + out2 = out1; +} + +TEST_CASE("Output iterator should be incrementable") { + const osmium::io::Header header; + osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow}; + osmium::io::OutputIterator out{writer}; + + ++out; } diff --git a/third_party/libosmium/test/t/io/test_reader.cpp b/third_party/libosmium/test/t/io/test_reader.cpp index c95c76bc6..10cc3b105 100644 --- a/third_party/libosmium/test/t/io/test_reader.cpp +++ b/third_party/libosmium/test/t/io/test_reader.cpp @@ -54,20 +54,36 @@ TEST_CASE("Reader can be initialized with string") { osmium::apply(reader, handler); } +TEST_CASE("Reader can be initialized with user-provided pool") { + osmium::thread::Pool pool{4}; + osmium::io::File file{with_data_dir("t/io/data.osm")}; + osmium::io::Reader reader{file, pool}; + osmium::handler::Handler handler; + + osmium::apply(reader, handler); +} + TEST_CASE("Reader should throw after eof") { osmium::io::File file{with_data_dir("t/io/data.osm")}; osmium::io::Reader reader{file}; - REQUIRE(!reader.eof()); + SECTION("Get header") { + const auto header = reader.header(); + REQUIRE(header.get("generator") == "testdata"); + REQUIRE_FALSE(reader.eof()); + } + + REQUIRE_FALSE(reader.eof()); while (osmium::memory::Buffer buffer = reader.read()) { } REQUIRE(reader.eof()); - REQUIRE_THROWS_AS({ - reader.read(); - }, osmium::io_error); + REQUIRE_THROWS_AS(reader.read(), const osmium::io_error&); + + reader.close(); + REQUIRE(reader.eof()); } TEST_CASE("Reader should not hang when apply() is called twice on reader") { @@ -76,9 +92,7 @@ TEST_CASE("Reader should not hang when apply() is called twice on reader") { osmium::handler::Handler handler; osmium::apply(reader, handler); - REQUIRE_THROWS_AS({ - osmium::apply(reader, handler); - }, osmium::io_error); + REQUIRE_THROWS_AS(osmium::apply(reader, handler), const osmium::io_error&); } TEST_CASE("Reader should work with a buffer with uncompressed data") { @@ -137,7 +151,7 @@ TEST_CASE("Reader should work with a buffer with bzip2-compressed data") { TEST_CASE("Reader 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}; + osmium::osm_entity_bits::node}; ZeroPositionNodeCountHandler handler; REQUIRE(handler.count == 0); @@ -151,7 +165,7 @@ TEST_CASE("Reader should decode zero node positions in history (XML)") { TEST_CASE("Reader 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}; + osmium::osm_entity_bits::node}; ZeroPositionNodeCountHandler handler; REQUIRE(handler.count == 0); @@ -164,27 +178,21 @@ TEST_CASE("Reader should decode zero node positions in history (PBF)") { } TEST_CASE("Reader should fail with nonexistent file") { - REQUIRE_THROWS({ - osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm")}; - }); + REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm")}); } TEST_CASE("Reader should fail with nonexistent file (gz)") { - REQUIRE_THROWS({ - osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm.gz")}; - }); + REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm.gz")}); } TEST_CASE("Reader should fail with nonexistent file (pbf)") { - REQUIRE_THROWS({ - osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm.pbf")}; - }); + REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm.pbf")}); } TEST_CASE("Reader 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()); + REQUIRE_FALSE(reader.eof()); throw std::runtime_error{"foo"}; } catch (...) { } @@ -193,7 +201,7 @@ TEST_CASE("Reader should work when there is an exception in main thread before g TEST_CASE("Reader 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()); + REQUIRE_FALSE(reader.eof()); auto header = reader.header(); throw std::runtime_error{"foo"}; } catch (...) { @@ -206,3 +214,44 @@ TEST_CASE("Applying rvalue handler on reader") { osmium::apply(reader, NullHandler{}); } +TEST_CASE("Can call read() exactly once on Reader with entity_bits nothing") { + osmium::io::File file{with_data_dir("t/io/data.osm")}; + osmium::io::Reader reader{file, osmium::osm_entity_bits::nothing}; + REQUIRE_FALSE(reader.eof()); + + SECTION("Get header") { + const auto header = reader.header(); + REQUIRE(header.get("generator") == "testdata"); + REQUIRE_FALSE(reader.eof()); + } + + osmium::memory::Buffer buffer = reader.read(); + REQUIRE_FALSE(buffer); + REQUIRE(reader.eof()); + REQUIRE_THROWS_AS(reader.read(), const osmium::io_error&); + + reader.close(); + REQUIRE(reader.eof()); +} + +TEST_CASE("Can not read after close") { + osmium::io::File file{with_data_dir("t/io/data.osm")}; + osmium::io::Reader reader{file}; + + SECTION("Get header") { + const auto header = reader.header(); + REQUIRE(header.get("generator") == "testdata"); + REQUIRE_FALSE(reader.eof()); + } + + REQUIRE_FALSE(reader.eof()); + + osmium::memory::Buffer buffer = reader.read(); + REQUIRE(buffer); + REQUIRE_FALSE(reader.eof()); + + reader.close(); + REQUIRE(reader.eof()); + REQUIRE_THROWS_AS(reader.read(), const osmium::io_error&); +} + diff --git a/third_party/libosmium/test/t/io/test_reader_fileformat.cpp b/third_party/libosmium/test/t/io/test_reader_fileformat.cpp index a64932e70..83b4a0d95 100644 --- a/third_party/libosmium/test/t/io/test_reader_fileformat.cpp +++ b/third_party/libosmium/test/t/io/test_reader_fileformat.cpp @@ -5,6 +5,6 @@ #include TEST_CASE("Reader throws on unsupported file format") { - REQUIRE_THROWS_AS(osmium::io::Reader{with_data_dir("t/io/data.osm")}, osmium::unsupported_file_format_error); + REQUIRE_THROWS_AS(osmium::io::Reader{with_data_dir("t/io/data.osm")}, const osmium::unsupported_file_format_error&); } 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 index 9ae1b7fde..f0aef07e1 100644 --- 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 @@ -21,12 +21,9 @@ class MockParser : public osmium::io::detail::Parser { 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::io::detail::reader_options options, + MockParser(osmium::io::detail::parser_arguments& args, const std::string& fail_in) : - Parser(input_queue, output_queue, header_promise, options), + Parser(args), m_fail_in(fail_in) { } @@ -56,11 +53,8 @@ TEST_CASE("Test Reader using MockParser") { 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::io::detail::reader_options options) { - return std::unique_ptr(new MockParser(input_queue, output_queue, header_promise, options, fail_in)); + [&](osmium::io::detail::parser_arguments& args) { + return std::unique_ptr(new MockParser(args, fail_in)); }); SECTION("no failure") { @@ -68,7 +62,7 @@ TEST_CASE("Test Reader using MockParser") { osmium::io::Reader reader{with_data_dir("t/io/data.osm")}; auto header = reader.header(); REQUIRE(reader.read()); - REQUIRE(!reader.read()); + REQUIRE_FALSE(reader.read()); REQUIRE(reader.eof()); reader.close(); } @@ -105,7 +99,7 @@ TEST_CASE("Test Reader using MockParser") { REQUIRE(std::string{e.what()} == "error in user code"); } REQUIRE(reader.read()); - REQUIRE(!reader.read()); + REQUIRE_FALSE(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 38418a03b..6b7ccba9a 100644 --- a/third_party/libosmium/test/t/io/test_string_table.cpp +++ b/third_party/libosmium/test/t/io/test_string_table.cpp @@ -2,119 +2,125 @@ #include -TEST_CASE("String store") { +TEST_CASE("Empty StringStore") { + const osmium::io::detail::StringStore ss{100}; + + REQUIRE(ss.begin() == ss.end()); + REQUIRE(ss.get_chunk_size() == 100); + REQUIRE(ss.get_chunk_count() == 1); +} + +TEST_CASE("Add zero-length string to StringStore") { osmium::io::detail::StringStore ss{100}; - SECTION("empty") { - REQUIRE(ss.begin() == ss.end()); - REQUIRE(ss.get_chunk_size() == 100); - REQUIRE(ss.get_chunk_count() == 1); - } + const char* s1 = ss.add(""); + REQUIRE(std::string(s1) == ""); - SECTION("add zero-length string") { - const char* s1 = ss.add(""); - REQUIRE(std::string(s1) == ""); - - auto it = ss.begin(); - REQUIRE(s1 == *it); - REQUIRE(std::string(*it) == ""); - REQUIRE(++it == ss.end()); - - REQUIRE(ss.get_chunk_count() == 1); - } - - SECTION("add strings") { - const char* s1 = ss.add("foo"); - const char* s2 = ss.add("bar"); - REQUIRE(s1 != s2); - REQUIRE(std::string(s1) == "foo"); - REQUIRE(std::string(s2) == "bar"); - - auto it = ss.begin(); - REQUIRE(s1 == *it++); - REQUIRE(s2 == *it++); - REQUIRE(it == ss.end()); - - ss.clear(); - REQUIRE(ss.begin() == ss.end()); - } - - SECTION("add zero-length string and longer strings") { - ss.add(""); - ss.add("xxx"); - ss.add("yyyyy"); - - auto it = ss.begin(); - REQUIRE(std::string(*it++) == ""); - REQUIRE(std::string(*it++) == "xxx"); - REQUIRE(std::string(*it++) == "yyyyy"); - REQUIRE(it == ss.end()); - } - - SECTION("add many strings") { - for (const char* teststring : {"", "a", "abc", "abcd", "abcde"}) { - int i = 0; - for (; i < 200; ++i) { - ss.add(teststring); - } - - for (const char* s : ss) { - REQUIRE(std::string(s) == teststring); - --i; - } - - REQUIRE(i == 0); - REQUIRE(ss.get_chunk_count() > 1); - ss.clear(); - REQUIRE(ss.get_chunk_count() == 1); - } - } + auto it = ss.begin(); + REQUIRE(s1 == *it); + REQUIRE(std::string(*it) == ""); + REQUIRE(++it == ss.end()); + REQUIRE(ss.get_chunk_count() == 1); } -TEST_CASE("String table") { +TEST_CASE("Add strings to StringStore") { + osmium::io::detail::StringStore ss{100}; + + const char* s1 = ss.add("foo"); + const char* s2 = ss.add("bar"); + REQUIRE(s1 != s2); + REQUIRE(std::string(s1) == "foo"); + REQUIRE(std::string(s2) == "bar"); + + auto it = ss.begin(); + REQUIRE(s1 == *it++); + REQUIRE(s2 == *it++); + REQUIRE(it == ss.end()); + + ss.clear(); + REQUIRE(ss.begin() == ss.end()); +} + +TEST_CASE("Add zero-length string and longer strings to StringStore") { + osmium::io::detail::StringStore ss{100}; + + ss.add(""); + ss.add("xxx"); + ss.add("yyyyy"); + + auto it = ss.begin(); + REQUIRE(std::string(*it++) == ""); + REQUIRE(std::string(*it++) == "xxx"); + REQUIRE(std::string(*it++) == "yyyyy"); + REQUIRE(it == ss.end()); +} + +TEST_CASE("Add many strings to StringStore") { + osmium::io::detail::StringStore ss{100}; + + for (const char* teststring : {"", "a", "abc", "abcd", "abcde"}) { + int i = 0; + for (; i < 200; ++i) { + ss.add(teststring); + } + + for (const char* s : ss) { + REQUIRE(std::string(s) == teststring); + --i; + } + + REQUIRE(i == 0); + REQUIRE(ss.get_chunk_count() > 1); + ss.clear(); + REQUIRE(ss.get_chunk_count() == 1); + } +} + +TEST_CASE("Empty StringTable") { + const osmium::io::detail::StringTable st; + + REQUIRE(st.size() == 1); + REQUIRE(std::next(st.begin()) == st.end()); +} + +TEST_CASE("Add strings to StringTable") { osmium::io::detail::StringTable st; - SECTION("empty") { - REQUIRE(st.size() == 1); - REQUIRE(std::next(st.begin()) == st.end()); - } + REQUIRE(st.add("foo") == 1); + REQUIRE(st.add("bar") == 2); + REQUIRE(st.add("bar") == 2); + REQUIRE(st.add("baz") == 3); + REQUIRE(st.add("foo") == 1); + REQUIRE(st.size() == 4); - SECTION("add strings") { - REQUIRE(st.add("foo") == 1); - REQUIRE(st.add("bar") == 2); - REQUIRE(st.add("bar") == 2); - REQUIRE(st.add("baz") == 3); - REQUIRE(st.add("foo") == 1); - REQUIRE(st.size() == 4); - - auto it = st.begin(); - REQUIRE(std::string("") == *it++); - REQUIRE(std::string("foo") == *it++); - REQUIRE(std::string("bar") == *it++); - REQUIRE(std::string("baz") == *it++); - REQUIRE(it == st.end()); - - st.clear(); - REQUIRE(st.size() == 1); - } - - SECTION("add empty string") { - REQUIRE(st.add("") == 1); - REQUIRE(st.size() == 2); - REQUIRE(st.add("") == 1); - REQUIRE(st.size() == 2); - } + auto it = st.begin(); + REQUIRE(std::string("") == *it++); + REQUIRE(std::string("foo") == *it++); + REQUIRE(std::string("bar") == *it++); + REQUIRE(std::string("baz") == *it++); + REQUIRE(it == st.end()); + st.clear(); + REQUIRE(st.size() == 1); } -TEST_CASE("lots of strings in string table so chunk overflows") { +TEST_CASE("Add empty string to StringTable") { + osmium::io::detail::StringTable st; + + REQUIRE(st.add("") == 1); + REQUIRE(st.size() == 2); + REQUIRE(st.add("") == 1); + REQUIRE(st.size() == 2); +} + +TEST_CASE("Lots of strings in string table so chunk overflows") { osmium::io::detail::StringTable st{100}; REQUIRE(st.size() == 1); const int n = 1000; for (int i = 0; i < n; ++i) { - auto s = std::to_string(i); + const auto s = std::to_string(i); st.add(s.c_str()); } @@ -123,7 +129,7 @@ TEST_CASE("lots of strings in string table so chunk overflows") { auto it = st.begin(); REQUIRE(std::string{} == *it++); for (int i = 0; i < n; ++i) { - REQUIRE(atoi(*it++) == i); + REQUIRE(std::atoi(*it++) == i); } REQUIRE(it == st.end()); } diff --git a/third_party/libosmium/test/t/io/test_writer.cpp b/third_party/libosmium/test/t/io/test_writer.cpp index 3d6175986..9f47b77cb 100644 --- a/third_party/libosmium/test/t/io/test_writer.cpp +++ b/third_party/libosmium/test/t/io/test_writer.cpp @@ -28,7 +28,7 @@ TEST_CASE("Writer") { 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); + osmium::memory::Buffer empty_buffer{1024}; writer(std::move(empty_buffer)); writer.close(); } @@ -43,10 +43,10 @@ TEST_CASE("Writer") { osmium::io::Reader reader_check{filename}; osmium::memory::Buffer buffer_check = reader_check.read(); - REQUIRE(!buffer_check); + REQUIRE_FALSE(buffer_check); } - SECTION("Successfull writes") { + SECTION("Successful writes") { SECTION("Writer buffer") { filename = "test-writer-out-buffer.osm"; @@ -54,9 +54,7 @@ TEST_CASE("Writer") { writer(std::move(buffer)); writer.close(); - REQUIRE_THROWS_AS({ - writer(osmium::memory::Buffer{}); - }, osmium::io_error); + REQUIRE_THROWS_AS(writer(osmium::memory::Buffer{}), const osmium::io_error&); } SECTION("Writer item") { @@ -77,7 +75,7 @@ TEST_CASE("Writer") { } osmium::io::Reader reader_check{filename}; - osmium::memory::Buffer buffer_check = reader_check.read(); + const osmium::memory::Buffer buffer_check = reader_check.read(); REQUIRE(buffer_check); REQUIRE(buffer_check.committed() > 0); REQUIRE(buffer_check.select().size() == num); @@ -113,3 +111,40 @@ TEST_CASE("Writer") { } +TEST_CASE("Writer with user-provided pool") { + 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); + + SECTION("with default number of threads") { + osmium::thread::Pool pool; + osmium::io::Writer writer{"test-writer-pool-with-default-threads.osm", pool, header, osmium::io::overwrite::allow}; + writer(std::move(buffer)); + writer.close(); + } + + SECTION("with negative number of threads") { + osmium::thread::Pool pool{-2}; + osmium::io::Writer writer{"test-writer-pool-with-negative-threads.osm", header, pool, osmium::io::overwrite::allow}; + writer(std::move(buffer)); + writer.close(); + } + + SECTION("with outlier negative number of threads") { + osmium::thread::Pool pool{-1000}; + osmium::io::Writer writer{"test-writer-pool-with-outlier-negative-threads.osm", header, osmium::io::overwrite::allow, pool}; + writer(std::move(buffer)); + writer.close(); + } + + SECTION("with outlier positive number of threads") { + osmium::thread::Pool pool{1000}; + osmium::io::Writer writer{"test-writer-pool-with-outlier-positive-threads.osm", header, osmium::io::overwrite::allow, pool}; + writer(std::move(buffer)); + writer.close(); + } +} 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 index 186ce25a9..c29746251 100644 --- 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 @@ -62,11 +62,11 @@ TEST_CASE("Write with mock compressor") { fail_in = "constructor"; - REQUIRE_THROWS_AS({ + 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); + }(), const std::logic_error&); } @@ -74,11 +74,11 @@ TEST_CASE("Write with mock compressor") { fail_in = "write"; - REQUIRE_THROWS_AS({ + 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); + }(), const std::logic_error&); } @@ -86,11 +86,11 @@ TEST_CASE("Write with mock compressor") { fail_in = "close"; - REQUIRE_THROWS_AS({ + 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); + }(), const 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 index b00b0962c..f57ddbd8c 100644 --- 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 @@ -17,8 +17,8 @@ class MockOutputFormat : public osmium::io::detail::OutputFormat { public: - MockOutputFormat(const osmium::io::File&, osmium::io::detail::future_string_queue_type& output_queue, const std::string& fail_in) : - OutputFormat(output_queue), + MockOutputFormat(osmium::thread::Pool& pool, const osmium::io::File&, osmium::io::detail::future_string_queue_type& output_queue, const std::string& fail_in) : + OutputFormat(pool, output_queue), m_fail_in(fail_in) { } @@ -51,8 +51,8 @@ TEST_CASE("Test Writer with MockOutputFormat") { 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::thread::Pool& pool, const osmium::io::File& file, osmium::io::detail::future_string_queue_type& output_queue) { + return new MockOutputFormat{pool, file, output_queue, fail_in}; }); osmium::io::Header header; @@ -68,11 +68,11 @@ TEST_CASE("Test Writer with MockOutputFormat") { fail_in = "header"; - REQUIRE_THROWS_AS({ + 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); + }(), const std::logic_error&); } @@ -80,11 +80,11 @@ TEST_CASE("Test Writer with MockOutputFormat") { fail_in = "write"; - REQUIRE_THROWS_AS({ + 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); + }(), const std::logic_error&); } @@ -92,11 +92,11 @@ TEST_CASE("Test Writer with MockOutputFormat") { fail_in = "write_end"; - REQUIRE_THROWS_AS({ + 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); + }(), const std::logic_error&); } diff --git a/third_party/libosmium/test/t/memory/test_buffer_basics.cpp b/third_party/libosmium/test/t/memory/test_buffer_basics.cpp index d9d174b0f..87c4d46d5 100644 --- a/third_party/libosmium/test/t/memory/test_buffer_basics.cpp +++ b/third_party/libosmium/test/t/memory/test_buffer_basics.cpp @@ -1,16 +1,18 @@ #include "catch.hpp" +#include +#include + #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_FALSE(invalid_buffer1); + REQUIRE_FALSE(invalid_buffer2); REQUIRE(empty_buffer1); REQUIRE(empty_buffer2); @@ -21,15 +23,19 @@ TEST_CASE("Buffer basics") { REQUIRE(invalid_buffer1.capacity() == 0); REQUIRE(invalid_buffer1.written() == 0); REQUIRE(invalid_buffer1.committed() == 0); + REQUIRE(invalid_buffer1.clear() == 0); REQUIRE(empty_buffer1.capacity() == 1024); REQUIRE(empty_buffer1.written() == 0); REQUIRE(empty_buffer1.committed() == 0); + REQUIRE(empty_buffer1.is_aligned()); + REQUIRE(empty_buffer1.clear() == 0); REQUIRE(empty_buffer2.capacity() == 2048); REQUIRE(empty_buffer2.written() == 0); REQUIRE(empty_buffer2.committed() == 0); - + REQUIRE(empty_buffer2.is_aligned()); + REQUIRE(empty_buffer2.clear() == 0); } TEST_CASE("Buffer with zero size") { @@ -47,3 +53,79 @@ TEST_CASE("Buffer with minimum size") { REQUIRE(buffer.capacity() == 64); } +TEST_CASE("Buffer with non-aligned size") { + osmium::memory::Buffer buffer{65}; + REQUIRE(buffer.capacity() > 65); +} + +TEST_CASE("Grow a buffer") { + osmium::memory::Buffer buffer{128}; + REQUIRE(buffer.capacity() == 128); + buffer.grow(256); + REQUIRE(buffer.capacity() == 256); + buffer.grow(257); + REQUIRE(buffer.capacity() > 256); + REQUIRE(buffer.committed() == 0); + REQUIRE(buffer.written() == 0); + REQUIRE(buffer.is_aligned()); +} + +TEST_CASE("Reserve space in a non-growing buffer") { + osmium::memory::Buffer buffer{128, osmium::memory::Buffer::auto_grow::no}; + + REQUIRE(buffer.reserve_space(20) != nullptr); + REQUIRE(buffer.written() == 20); + REQUIRE_THROWS_AS(buffer.reserve_space(1000), const osmium::buffer_is_full&); +} + +TEST_CASE("Reserve space in a growing buffer") { + osmium::memory::Buffer buffer{128, osmium::memory::Buffer::auto_grow::yes}; + + REQUIRE(buffer.reserve_space(20) != nullptr); + REQUIRE(buffer.written() == 20); + REQUIRE(buffer.reserve_space(1000) != nullptr); + REQUIRE(buffer.written() == 1020); +} + +TEST_CASE("Create buffer from existing data with good alignment works") { + std::array data; + + osmium::memory::Buffer buffer{data.data(), data.size()}; + REQUIRE(buffer.capacity() == 128); + REQUIRE(buffer.committed() == 128); +} + +TEST_CASE("Create buffer from existing data with good alignment and committed value works") { + std::array data; + + osmium::memory::Buffer buffer{data.data(), data.size(), 32}; + REQUIRE(buffer.capacity() == 128); + REQUIRE(buffer.committed() == 32); + REQUIRE(buffer.written() == 32); +} + +TEST_CASE("Create buffer from existing data with bad alignment fails") { + std::array data; + + const auto l1 = [&](){ + osmium::memory::Buffer buffer{data.data(), 127}; + }; + + const auto l2 = [&](){ + osmium::memory::Buffer buffer{data.data(), 127, 120}; + }; + + const auto l3 = [&](){ + osmium::memory::Buffer buffer{data.data(), 128, 127}; + }; + + const auto l4 = [&](){ + osmium::memory::Buffer buffer{data.data(), 32, 128}; + }; + + REQUIRE_THROWS_AS(l1(), const std::invalid_argument&); + REQUIRE_THROWS_AS(l2(), const std::invalid_argument&); + REQUIRE_THROWS_AS(l3(), const std::invalid_argument&); + REQUIRE_THROWS_AS(l4(), const std::invalid_argument&); +} + diff --git a/third_party/libosmium/test/t/memory/test_buffer_node.cpp b/third_party/libosmium/test/t/memory/test_buffer_node.cpp index 040cbb80f..19718e7ab 100644 --- a/third_party/libosmium/test/t/memory/test_buffer_node.cpp +++ b/third_party/libosmium/test/t/memory/test_buffer_node.cpp @@ -36,7 +36,7 @@ void check_node_2(const osmium::Node& node) { REQUIRE(osmium::item_type::tag_list == item.type()); } - REQUIRE(!node.tags().empty()); + REQUIRE_FALSE(node.tags().empty()); REQUIRE(2 == std::distance(node.tags().begin(), node.tags().end())); int n = 0; diff --git a/third_party/libosmium/test/t/memory/test_callback_buffer.cpp b/third_party/libosmium/test/t/memory/test_callback_buffer.cpp new file mode 100644 index 000000000..3e94a4026 --- /dev/null +++ b/third_party/libosmium/test/t/memory/test_callback_buffer.cpp @@ -0,0 +1,101 @@ +#include "catch.hpp" + +#include + +#include +#include + +using namespace osmium::builder::attr; + +TEST_CASE("Callback buffer") { + osmium::memory::CallbackBuffer cb; + + REQUIRE(cb.buffer().committed() == 0); + + osmium::builder::add_node(cb.buffer(), _id(1)); + osmium::builder::add_node(cb.buffer(), _id(2)); + osmium::builder::add_node(cb.buffer(), _id(3)); + + auto c = cb.buffer().committed(); + REQUIRE(c > 0); + + REQUIRE(std::distance(cb.buffer().begin(), cb.buffer().end()) == 3); + auto buffer = cb.read(); + + REQUIRE(cb.buffer().committed() == 0); + REQUIRE(buffer.committed() == c); + REQUIRE(std::distance(cb.buffer().begin(), cb.buffer().end()) == 0); + + // no callback defined, so nothing will happen + cb.flush(); +} + +TEST_CASE("Callback buffer with callback triggering every time") { + int run = 0; + + osmium::memory::CallbackBuffer cb{[&](osmium::memory::Buffer&& buffer){ + REQUIRE(buffer.committed() > 0); + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1); + ++run; + }, 1000, 10}; + + osmium::builder::add_node(cb.buffer(), _id(1)); + REQUIRE(cb.buffer().committed() > 10); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(2)); + REQUIRE(cb.buffer().committed() > 10); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(3)); + REQUIRE(cb.buffer().committed() > 10); + cb.possibly_flush(); + + REQUIRE(run == 3); + REQUIRE(std::distance(cb.buffer().begin(), cb.buffer().end()) == 0); +} + +TEST_CASE("Callback buffer with callback triggering sometimes") { + int run = 0; + + osmium::memory::CallbackBuffer cb{[&](osmium::memory::Buffer&& buffer){ + REQUIRE(buffer.committed() > 0); + ++run; + }, 1000, 100}; + + osmium::builder::add_node(cb.buffer(), _id(1)); + REQUIRE(cb.buffer().committed() < 100); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(2)); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(3)); + cb.possibly_flush(); + + REQUIRE(run < 3); +} + +TEST_CASE("Callback buffer with callback set later") { + int run = 0; + + osmium::memory::CallbackBuffer cb{1000, 10}; + + osmium::builder::add_node(cb.buffer(), _id(1)); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(2)); + cb.possibly_flush(); + osmium::builder::add_node(cb.buffer(), _id(3)); + + cb.set_callback([&](osmium::memory::Buffer&& buffer){ + REQUIRE(buffer.committed() > 0); + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3); + ++run; + }); + + REQUIRE(std::distance(cb.buffer().begin(), cb.buffer().end()) == 3); + + cb.possibly_flush(); + + REQUIRE(std::distance(cb.buffer().begin(), cb.buffer().end()) == 0); + + REQUIRE(run == 1); +} + + diff --git a/third_party/libosmium/test/t/memory/test_item.cpp b/third_party/libosmium/test/t/memory/test_item.cpp new file mode 100644 index 000000000..81e18a9cd --- /dev/null +++ b/third_party/libosmium/test/t/memory/test_item.cpp @@ -0,0 +1,29 @@ +#include "catch.hpp" + +#include + +TEST_CASE("padded length") { + REQUIRE(osmium::memory::padded_length(0) == 0); + REQUIRE(osmium::memory::padded_length(1) == 8); + REQUIRE(osmium::memory::padded_length(2) == 8); + REQUIRE(osmium::memory::padded_length(7) == 8); + REQUIRE(osmium::memory::padded_length(8) == 8); + REQUIRE(osmium::memory::padded_length(9) == 16); + + REQUIRE(osmium::memory::padded_length(2147483647) == 2147483648); + REQUIRE(osmium::memory::padded_length(2147483648) == 2147483648); + REQUIRE(osmium::memory::padded_length(2147483650) == 2147483656); + + // The following checks only make sense on a 64 bit system (with + // sizeof(size_t) == 8), because the numbers are too large for 32 bit. + // The casts to size_t do nothing on a 64 bit system, on a 32 bit system + // they bring the numbers into the right range and everything still works. + REQUIRE(osmium::memory::padded_length(static_cast(4294967295)) == static_cast(4294967296)); + REQUIRE(osmium::memory::padded_length(static_cast(4294967296)) == static_cast(4294967296)); + REQUIRE(osmium::memory::padded_length(static_cast(4294967297)) == static_cast(4294967304)); + + REQUIRE(osmium::memory::padded_length(static_cast(7999999999)) == static_cast(8000000000)); + REQUIRE(osmium::memory::padded_length(static_cast(8000000000)) == static_cast(8000000000)); + REQUIRE(osmium::memory::padded_length(static_cast(8000000001)) == static_cast(8000000008)); +} + diff --git a/third_party/libosmium/test/t/osm/test_area.cpp b/third_party/libosmium/test/t/osm/test_area.cpp index c964dbba0..44514774b 100644 --- a/third_party/libosmium/test/t/osm/test_area.cpp +++ b/third_party/libosmium/test/t/osm/test_area.cpp @@ -75,7 +75,7 @@ TEST_CASE("Build area") { crc32.update(area); REQUIRE(crc32().checksum() == 0x2b2b7fa0); - osmium::Box envelope = area.envelope(); + const osmium::Box envelope = area.envelope(); REQUIRE(envelope.bottom_left().lon() == Approx(3.2)); REQUIRE(envelope.bottom_left().lat() == Approx(4.2)); REQUIRE(envelope.top_right().lon() == Approx(3.6)); diff --git a/third_party/libosmium/test/t/osm/test_box.cpp b/third_party/libosmium/test/t/osm/test_box.cpp index 2b7fb3d01..179ae090a 100644 --- a/third_party/libosmium/test/t/osm/test_box.cpp +++ b/third_party/libosmium/test/t/osm/test_box.cpp @@ -8,127 +8,165 @@ #include #include -TEST_CASE("Starting with default constructed box") { +TEST_CASE("Default constructor creates invalid box") { + const osmium::Box b; + REQUIRE_FALSE(b); + REQUIRE_FALSE(b.bottom_left()); + REQUIRE_FALSE(b.top_right()); + REQUIRE_THROWS_AS(b.size(), const osmium::invalid_location&); +} + +TEST_CASE("Extend box with undefined") { 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); - } + REQUIRE_FALSE(b); + b.extend(osmium::Location{}); + REQUIRE_FALSE(b); + REQUIRE_FALSE(b.bottom_left()); + REQUIRE_FALSE(b.top_right()); +} - SECTION("extend with undefined") { - REQUIRE(!b); - b.extend(osmium::Location{}); - REQUIRE(!b); - REQUIRE(!b.bottom_left()); - REQUIRE(!b.top_right()); - } +TEST_CASE("Extend box with invalid") { + 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()); - } + REQUIRE_FALSE(b); + b.extend(osmium::Location{200.0, 100.0}); + REQUIRE_FALSE(b); + REQUIRE_FALSE(b.bottom_left()); + REQUIRE_FALSE(b.top_right()); +} - SECTION("extend with valid") { - osmium::Location loc1 { 1.2, 3.4 }; - b.extend(loc1); - REQUIRE(!!b); - REQUIRE(!!b.bottom_left()); - REQUIRE(!!b.top_right()); - REQUIRE(b.contains(loc1)); +TEST_CASE("Extend box with valid") { + osmium::Box b; - osmium::Location loc2 { 3.4, 4.5 }; - osmium::Location loc3 { 5.6, 7.8 }; + const osmium::Location loc1{1.2, 3.4}; + b.extend(loc1); + REQUIRE(!!b); + REQUIRE(!!b.bottom_left()); + REQUIRE(!!b.top_right()); + REQUIRE(b.contains(loc1)); - b.extend(loc2); - b.extend(loc3); - REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); - REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); + const osmium::Location loc2{3.4, 4.5}; + const osmium::Location loc3{5.6, 7.8}; - // extend with undefined doesn't change anything - b.extend(osmium::Location()); - REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); - REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); + b.extend(loc2); + b.extend(loc3); + REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); + REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); - REQUIRE(b.contains(loc1)); - REQUIRE(b.contains(loc2)); - REQUIRE(b.contains(loc3)); + // extend with undefined doesn't change anything + b.extend(osmium::Location{}); + REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); + REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); - osmium::CRC crc32; - crc32.update(b); - REQUIRE(crc32().checksum() == 0xd381a838); - } + REQUIRE(b.contains(loc1)); + REQUIRE(b.contains(loc2)); + REQUIRE(b.contains(loc3)); - SECTION("output defined") { - b.extend(osmium::Location(1.2, 3.4)); - b.extend(osmium::Location(5.6, 7.8)); - std::stringstream out; - out << b; - REQUIRE(out.str() == "(1.2,3.4,5.6,7.8)"); - REQUIRE(b.size() == Approx(19.36).epsilon(0.000001)); - } + osmium::CRC crc32; + crc32.update(b); + REQUIRE(crc32().checksum() == 0xd381a838); +} - SECTION("output undefined") { - std::stringstream out; - out << b; - REQUIRE(out.str() == "(undefined)"); - } +TEST_CASE("Output of defined Box") { + osmium::Box b; - SECTION("output undefined bottom left") { - b.top_right() = osmium::Location(1.2, 3.4); - std::stringstream out; - out << b; - REQUIRE(out.str() == "(undefined)"); - } + b.extend(osmium::Location{1.2, 3.4}); + b.extend(osmium::Location{5.6, 7.8}); - SECTION("output undefined top right") { - b.bottom_left() = osmium::Location(1.2, 3.4); - std::stringstream out; - out << b; - REQUIRE(out.str() == "(undefined)"); - } + std::stringstream out; + out << b; + REQUIRE(out.str() == "(1.2,3.4,5.6,7.8)"); + REQUIRE(b.size() == Approx(19.36).epsilon(0.000001)); +} + +TEST_CASE("Output of undefined Box") { + const osmium::Box b; + + std::stringstream out; + out << b; + + REQUIRE(out.str() == "(undefined)"); +} + +TEST_CASE("Output of undefined Box (bottom left)") { + osmium::Box b; + + b.top_right() = osmium::Location(1.2, 3.4); + std::stringstream out; + out << b; + REQUIRE(out.str() == "(undefined)"); +} + +TEST_CASE("Output of undefined Box (top right)") { + osmium::Box b; + + 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}}; + const 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}; + const 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") { - +TEST_CASE("Relationship between boxes: contains") { osmium::Box outer; - outer.extend(osmium::Location(1, 1)); - outer.extend(osmium::Location(10, 10)); + 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)); + 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)); + 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)); + REQUIRE( osmium::geom::contains(inner, outer)); + REQUIRE_FALSE(osmium::geom::contains(outer, inner)); + REQUIRE_FALSE(osmium::geom::contains(overlap, inner)); + REQUIRE_FALSE(osmium::geom::contains(inner, overlap)); +} + +TEST_CASE("Relationship between boxes: overlaps") { + 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}); + + osmium::Box outside; + overlap.extend(osmium::Location{30, 30}); + overlap.extend(osmium::Location{50, 50}); + + REQUIRE(osmium::geom::overlaps(inner, outer)); + REQUIRE(osmium::geom::overlaps(outer, inner)); + + REQUIRE(osmium::geom::overlaps(overlap, inner)); + REQUIRE(osmium::geom::overlaps(inner, overlap)); + + REQUIRE_FALSE(osmium::geom::overlaps(outside, inner)); + REQUIRE_FALSE(osmium::geom::overlaps(inner, outside)); } diff --git a/third_party/libosmium/test/t/osm/test_changeset.cpp b/third_party/libosmium/test/t/osm/test_changeset.cpp index 9be8fbbec..0c1fc4231 100644 --- a/third_party/libosmium/test/t/osm/test_changeset.cpp +++ b/third_party/libosmium/test/t/osm/test_changeset.cpp @@ -38,7 +38,7 @@ TEST_CASE("Build changeset") { crc32.update(cs1); REQUIRE(crc32().checksum() == 0x502e8c0e); - auto pos = osmium::builder::add_changeset(buffer, + const auto pos = osmium::builder::add_changeset(buffer, _cid(43), _created_at(time_t(120)), _num_changes(21), diff --git a/third_party/libosmium/test/t/osm/test_crc.cpp b/third_party/libosmium/test/t/osm/test_crc.cpp index 253c21763..97c3c4c75 100644 --- a/third_party/libosmium/test/t/osm/test_crc.cpp +++ b/third_party/libosmium/test/t/osm/test_crc.cpp @@ -4,65 +4,75 @@ #include -TEST_CASE("CRC of basic datatypes") { - +TEST_CASE("CRC of bool") { osmium::CRC crc32; - SECTION("Bool") { - crc32.update_bool(true); - crc32.update_bool(false); - - REQUIRE(crc32().checksum() == 0x58c223be); - } - - SECTION("Char") { - crc32.update_int8('x'); - crc32.update_int8('y'); - - 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); - - REQUIRE(crc32().checksum() == 0x9ef61f95); - } - - SECTION("Timestamp") { - osmium::Timestamp t("2015-07-12T13:10:46Z"); - crc32.update(t); - - REQUIRE(crc32().checksum() == 0x58a29d7); - } - - SECTION("Location") { - osmium::Location loc { 3.46, 2.001 }; - crc32.update(loc); - - REQUIRE(crc32().checksum() == 0xddee042c); - } + crc32.update_bool(true); + crc32.update_bool(false); + REQUIRE(crc32().checksum() == 0x58c223be); +} + +TEST_CASE("CRC of char") { + osmium::CRC crc32; + + crc32.update_int8('x'); + crc32.update_int8('y'); + + REQUIRE(crc32().checksum() == 0x8fe62899); +} + +TEST_CASE("CRC of int16") { + osmium::CRC crc32; + + crc32.update_int16(0x0123U); + crc32.update_int16(0x1234U); + + REQUIRE(crc32().checksum() == 0xda923744); +} + +TEST_CASE("CRC of int32") { + osmium::CRC crc32; + + crc32.update_int32(0x01234567UL); + crc32.update_int32(0x12345678UL); + + REQUIRE(crc32().checksum() == 0x9b4e2af3); +} + +TEST_CASE("CRC of int64") { + osmium::CRC crc32; + + crc32.update_int64(0x0123456789abcdefULL); + crc32.update_int64(0x123456789abcdef0ULL); + + REQUIRE(crc32().checksum() == 0x6d8b7267); +} + +TEST_CASE("CRC of string") { + osmium::CRC crc32; + + const char* str = "foobar"; + crc32.update_string(str); + + REQUIRE(crc32().checksum() == 0x9ef61f95); +} + +TEST_CASE("CRC of Timestamp") { + osmium::CRC crc32; + + const osmium::Timestamp t{"2015-07-12T13:10:46Z"}; + crc32.update(t); + + REQUIRE(crc32().checksum() == 0x58a29d7); +} + +TEST_CASE("CRC of Location") { + osmium::CRC crc32; + + const osmium::Location loc{3.46, 2.001}; + crc32.update(loc); + + REQUIRE(crc32().checksum() == 0xddee042c); } diff --git a/third_party/libosmium/test/t/osm/test_location.cpp b/third_party/libosmium/test/t/osm/test_location.cpp index 6aba91dbe..a47a49c6f 100644 --- a/third_party/libosmium/test/t/osm/test_location.cpp +++ b/third_party/libosmium/test/t/osm/test_location.cpp @@ -1,155 +1,156 @@ #include "catch.hpp" +#include #include #include #include -TEST_CASE("Location") { - // fails on MSVC and doesn't really matter // static_assert(std::is_literal_type::value, "osmium::Location not literal type"); - SECTION("instantiation_with_default_parameters") { - osmium::Location loc; - REQUIRE(!loc); - REQUIRE_THROWS_AS(loc.lon(), osmium::invalid_location); - REQUIRE_THROWS_AS(loc.lat(), osmium::invalid_location); - } +TEST_CASE("Location instantiation with default parameters") { + const osmium::Location loc; + REQUIRE_FALSE(loc); + REQUIRE_FALSE(loc.is_defined()); + REQUIRE(loc.is_undefined()); + REQUIRE_THROWS_AS(loc.lon(), const osmium::invalid_location&); + REQUIRE_THROWS_AS(loc.lat(), const osmium::invalid_location&); +} - SECTION("instantiation_with_double_parameters") { - osmium::Location loc1(1.2, 4.5); - REQUIRE(!!loc1); - REQUIRE(12000000 == loc1.x()); - REQUIRE(45000000 == loc1.y()); - REQUIRE(1.2 == loc1.lon()); - REQUIRE(4.5 == loc1.lat()); +TEST_CASE("Location instantiation with double parameters") { + const osmium::Location loc1{1.2, 4.5}; + REQUIRE(bool(loc1)); + REQUIRE(loc1.is_defined()); + REQUIRE_FALSE(loc1.is_undefined()); + REQUIRE(12000000 == loc1.x()); + REQUIRE(45000000 == loc1.y()); + REQUIRE(1.2 == Approx(loc1.lon())); + REQUIRE(4.5 == Approx(loc1.lat())); - osmium::Location loc2(loc1); - REQUIRE(4.5 == loc2.lat()); + const osmium::Location loc2{loc1}; + REQUIRE(4.5 == Approx(loc2.lat())); - osmium::Location loc3 = loc1; - REQUIRE(4.5 == loc3.lat()); - } + const osmium::Location loc3 = loc1; + REQUIRE(4.5 == Approx(loc3.lat())); +} - SECTION("instantiation_with_double_parameters_constructor_with_universal_initializer") { - osmium::Location loc { 2.2, 3.3 }; - REQUIRE(2.2 == loc.lon()); - REQUIRE(3.3 == loc.lat()); - } +TEST_CASE("Location instantiation with double parameters constructor with universal initializer") { + const osmium::Location loc{2.2, 3.3}; + REQUIRE(2.2 == Approx(loc.lon())); + REQUIRE(3.3 == Approx(loc.lat())); +} - SECTION("instantiation_with_double_parameters_constructor_with_initializer_list") { - osmium::Location loc({ 4.4, 5.5 }); - REQUIRE(4.4 == loc.lon()); - REQUIRE(5.5 == loc.lat()); - } +TEST_CASE("Location instantiation with double parameters constructor with initializer list") { + const osmium::Location loc({4.4, 5.5}); + REQUIRE(4.4 == Approx(loc.lon())); + REQUIRE(5.5 == Approx(loc.lat())); +} - SECTION("instantiation_with_double_parameters_operator_equal") { - osmium::Location loc = { 5.5, 6.6 }; - REQUIRE(5.5 == loc.lon()); - REQUIRE(6.6 == loc.lat()); - } +TEST_CASE("Location instantiation with double parameters operator equal") { + const osmium::Location loc = {5.5, 6.6}; + REQUIRE(5.5 == Approx(loc.lon())); + REQUIRE(6.6 == Approx(loc.lat())); +} - SECTION("equality") { - osmium::Location loc1(1.2, 4.5); - osmium::Location loc2(1.2, 4.5); - osmium::Location loc3(1.5, 1.5); - REQUIRE(loc1 == loc2); - REQUIRE(loc1 != loc3); - } +TEST_CASE("Location equality") { + const osmium::Location loc1{1.2, 4.5}; + const osmium::Location loc2{1.2, 4.5}; + const osmium::Location loc3{1.5, 1.5}; + REQUIRE(loc1 == loc2); + REQUIRE(loc1 != loc3); +} - SECTION("order") { - REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0)); - REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0)); +TEST_CASE("Location order") { + REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0)); + REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0)); - REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2)); - REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0)); - REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0)); - } + REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2)); + REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0)); + REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0)); +} - SECTION("validity") { - REQUIRE(osmium::Location(0.0, 0.0).valid()); - REQUIRE(osmium::Location(1.2, 4.5).valid()); - REQUIRE(osmium::Location(-1.2, 4.5).valid()); - REQUIRE(osmium::Location(-180.0, -90.0).valid()); - REQUIRE(osmium::Location(180.0, -90.0).valid()); - REQUIRE(osmium::Location(-180.0, 90.0).valid()); - REQUIRE(osmium::Location(180.0, 90.0).valid()); +TEST_CASE("Location validity") { + REQUIRE(osmium::Location(0.0, 0.0).valid()); + REQUIRE(osmium::Location(1.2, 4.5).valid()); + REQUIRE(osmium::Location(-1.2, 4.5).valid()); + REQUIRE(osmium::Location(-180.0, -90.0).valid()); + REQUIRE(osmium::Location(180.0, -90.0).valid()); + REQUIRE(osmium::Location(-180.0, 90.0).valid()); + REQUIRE(osmium::Location(180.0, 90.0).valid()); - REQUIRE(!osmium::Location(200.0, 4.5).valid()); - REQUIRE(!osmium::Location(-1.2, -100.0).valid()); - REQUIRE(!osmium::Location(-180.0, 90.005).valid()); - } + REQUIRE_FALSE(osmium::Location(200.0, 4.5).valid()); + REQUIRE_FALSE(osmium::Location(-1.2, -100.0).valid()); + REQUIRE_FALSE(osmium::Location(-180.0, 90.005).valid()); +} - SECTION("output_to_iterator_comma_separator") { - char buffer[100]; - osmium::Location loc(-3.2, 47.3); - *loc.as_string(buffer, ',') = 0; - REQUIRE(std::string("-3.2,47.3") == buffer); - } +TEST_CASE("Location output to iterator comma separator") { + char buffer[100]; + const osmium::Location loc{-3.2, 47.3}; + *loc.as_string(buffer, ',') = 0; + REQUIRE(std::string("-3.2,47.3") == buffer); +} - SECTION("output_to_iterator_space_separator") { - char buffer[100]; - osmium::Location loc(0.0, 7.0); - *loc.as_string(buffer, ' ') = 0; - REQUIRE(std::string("0 7") == buffer); - } +TEST_CASE("Location output to iterator space separator") { + char buffer[100]; + const osmium::Location loc{0.0, 7.0}; + *loc.as_string(buffer, ' ') = 0; + REQUIRE(std::string("0 7") == buffer); +} - SECTION("output_to_iterator_check_precision") { - char buffer[100]; - osmium::Location loc(-179.9999999, -90.0); - *loc.as_string(buffer, ' ') = 0; - REQUIRE(std::string("-179.9999999 -90") == buffer); - } +TEST_CASE("Location output to iterator check precision") { + char buffer[100]; + const osmium::Location loc{-179.9999999, -90.0}; + *loc.as_string(buffer, ' ') = 0; + REQUIRE(std::string("-179.9999999 -90") == buffer); +} - SECTION("output_to_iterator_undefined_location") { - char buffer[100]; - osmium::Location loc; - REQUIRE_THROWS_AS(loc.as_string(buffer, ','), osmium::invalid_location); - } +TEST_CASE("Location output to iterator undefined location") { + char buffer[100]; + const osmium::Location loc; + REQUIRE_THROWS_AS(loc.as_string(buffer, ','), const osmium::invalid_location&); +} - SECTION("output_to_string_comman_separator") { - std::string s; - osmium::Location loc(-3.2, 47.3); - loc.as_string(std::back_inserter(s), ','); - REQUIRE(s == "-3.2,47.3"); - } +TEST_CASE("Location output to string comman separator") { + std::string s; + const osmium::Location loc{-3.2, 47.3}; + loc.as_string(std::back_inserter(s), ','); + REQUIRE(s == "-3.2,47.3"); +} - SECTION("output_to_string_space_separator") { - std::string s; - osmium::Location loc(0.0, 7.0); - loc.as_string(std::back_inserter(s), ' '); - REQUIRE(s == "0 7"); - } +TEST_CASE("Location output to string space separator") { + std::string s; + const osmium::Location loc{0.0, 7.0}; + loc.as_string(std::back_inserter(s), ' '); + REQUIRE(s == "0 7"); +} - SECTION("output_to_string_check_precision") { - std::string s; - osmium::Location loc(-179.9999999, -90.0); - loc.as_string(std::back_inserter(s), ' '); - REQUIRE(s == "-179.9999999 -90"); - } +TEST_CASE("Location output to string check precision") { + std::string s; + const osmium::Location loc{-179.9999999, -90.0}; + loc.as_string(std::back_inserter(s), ' '); + REQUIRE(s == "-179.9999999 -90"); +} - SECTION("output_to_string_undefined_location") { - std::string s; - osmium::Location loc; - REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), osmium::invalid_location); - } +TEST_CASE("Location output to string undefined location") { + std::string s; + const osmium::Location loc; + REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), const osmium::invalid_location&); +} - SECTION("output_defined") { - osmium::Location p(-3.20, 47.30); - std::stringstream out; - out << p; - REQUIRE(out.str() == "(-3.2,47.3)"); - } - - SECTION("output_undefined") { - osmium::Location p; - std::stringstream out; - out << p; - REQUIRE(out.str() == "(undefined,undefined)"); - } +TEST_CASE("Location output defined") { + const osmium::Location loc{-3.20, 47.30}; + std::stringstream out; + out << loc; + REQUIRE(out.str() == "(-3.2,47.3)"); +} +TEST_CASE("Location output undefined") { + const osmium::Location loc; + std::stringstream out; + out << loc; + REQUIRE(out.str() == "(undefined,undefined)"); } TEST_CASE("Location hash") { @@ -188,10 +189,10 @@ void F(const char* s) { strm += s; const char* x = strm.c_str(); const char** data = &x; - REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), const osmium::invalid_location&); ++x; data = &x; - REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), const osmium::invalid_location&); } TEST_CASE("Parsing coordinates from strings") { @@ -260,6 +261,7 @@ TEST_CASE("Parsing coordinates from strings") { C("179.9999999", 1799999999); C("179.99999999", 1800000000); C("200.123", 2001230000); + C("214.7483647", 2147483647); C("8.109E-4" , 8109); C("8.1090E-4" , 8109); @@ -332,6 +334,12 @@ TEST_CASE("Parsing coordinates from strings") { C("1.1e2:", 1100000000, ":"); } +TEST_CASE("Parsing min coordinate from string") { + const char* minval = "-214.7483648"; + const char** data = &minval; + REQUIRE(osmium::detail::string_to_location_coordinate(data) == std::numeric_limits::min()); +} + TEST_CASE("Writing zero coordinate into string") { std::string buffer; osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); @@ -369,24 +377,41 @@ TEST_CASE("Writing coordinate into string") { CW( 40101010, "4.010101"); CW( 494561234, "49.4561234"); CW(1799999999, "179.9999999"); + + CW(2147483647, "214.7483647"); +} + +TEST_CASE("Writing min coordinate into string") { + std::string buffer; + + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), std::numeric_limits::min()); + REQUIRE(buffer == "-214.7483648"); } TEST_CASE("set lon/lat from string") { osmium::Location loc; + REQUIRE(loc.is_undefined()); + REQUIRE_FALSE(loc.is_defined()); + REQUIRE_FALSE(loc.valid()); + loc.set_lon("1.2"); + REQUIRE_FALSE(loc.is_undefined()); + REQUIRE(loc.is_defined()); + REQUIRE_FALSE(loc.valid()); + loc.set_lat("3.4"); + REQUIRE_FALSE(loc.is_undefined()); + REQUIRE(loc.is_defined()); + REQUIRE(loc.valid()); + REQUIRE(loc.lon() == Approx(1.2)); REQUIRE(loc.lat() == Approx(3.4)); } TEST_CASE("set lon/lat from string with trailing characters") { osmium::Location loc; - REQUIRE_THROWS_AS({ - loc.set_lon("1.2x"); - }, osmium::invalid_location); - REQUIRE_THROWS_AS({ - loc.set_lat("3.4e1 "); - }, osmium::invalid_location); + REQUIRE_THROWS_AS(loc.set_lon("1.2x"), const osmium::invalid_location&); + REQUIRE_THROWS_AS(loc.set_lat("3.4e1 "), const osmium::invalid_location&); } TEST_CASE("set lon/lat from string with trailing characters using partial") { diff --git a/third_party/libosmium/test/t/osm/test_node.cpp b/third_party/libosmium/test/t/osm/test_node.cpp index e5dbe8a7c..752e6af70 100644 --- a/third_party/libosmium/test/t/osm/test_node.cpp +++ b/third_party/libosmium/test/t/osm/test_node.cpp @@ -133,7 +133,7 @@ TEST_CASE("Setting attributes from bad data on strings should fail") { TEST_CASE("set large id") { osmium::memory::Buffer buffer{10000}; - int64_t id = 3000000000l; + const int64_t id = 3000000000l; osmium::builder::add_node(buffer, _id(id)); osmium::Node& node = buffer.get(0); @@ -164,3 +164,26 @@ TEST_CASE("set tags on node") { REQUIRE(std::string{"pub"} == node.get_value_by_key("amenity", "default")); } +TEST_CASE("Setting diff flags on node") { + osmium::memory::Buffer buffer{1000}; + + osmium::builder::add_node(buffer, _id(17)); + + osmium::Node& node = buffer.get(0); + + REQUIRE(node.diff() == osmium::diff_indicator_type::none); + REQUIRE(node.diff_as_char() == '*'); + + node.set_diff(osmium::diff_indicator_type::left); + REQUIRE(node.diff() == osmium::diff_indicator_type::left); + REQUIRE(node.diff_as_char() == '-'); + + node.set_diff(osmium::diff_indicator_type::right); + REQUIRE(node.diff() == osmium::diff_indicator_type::right); + REQUIRE(node.diff_as_char() == '+'); + + node.set_diff(osmium::diff_indicator_type::both); + REQUIRE(node.diff() == osmium::diff_indicator_type::both); + REQUIRE(node.diff_as_char() == ' '); +} + diff --git a/third_party/libosmium/test/t/osm/test_node_ref.cpp b/third_party/libosmium/test/t/osm/test_node_ref.cpp index 28a28a568..3494d2aee 100644 --- a/third_party/libosmium/test/t/osm/test_node_ref.cpp +++ b/third_party/libosmium/test/t/osm/test_node_ref.cpp @@ -6,30 +6,30 @@ #include TEST_CASE("Default construct a NodeRef") { - osmium::NodeRef node_ref; + const osmium::NodeRef node_ref; REQUIRE(node_ref.ref() == 0); REQUIRE(node_ref.location() == osmium::Location{}); } TEST_CASE("Construct a NodeRef with an id") { - osmium::NodeRef node_ref{7}; + const osmium::NodeRef node_ref{7}; REQUIRE(node_ref.ref() == 7); } TEST_CASE("Equality comparison fo NodeRefs") { - osmium::NodeRef node_ref1{7, {1.2, 3.4}}; - osmium::NodeRef node_ref2{7, {1.4, 3.1}}; - osmium::NodeRef node_ref3{9, {1.2, 3.4}}; + const osmium::NodeRef node_ref1{7, {1.2, 3.4}}; + const osmium::NodeRef node_ref2{7, {1.4, 3.1}}; + const osmium::NodeRef node_ref3{9, {1.2, 3.4}}; REQUIRE(node_ref1 == node_ref2); REQUIRE(node_ref1 != node_ref3); - REQUIRE(!osmium::location_equal()(node_ref1, node_ref2)); - REQUIRE(!osmium::location_equal()(node_ref2, node_ref3)); - REQUIRE(osmium::location_equal()(node_ref1, node_ref3)); + REQUIRE_FALSE(osmium::location_equal()(node_ref1, node_ref2)); + REQUIRE_FALSE(osmium::location_equal()(node_ref2, node_ref3)); + REQUIRE( osmium::location_equal()(node_ref1, node_ref3)); } TEST_CASE("Set location on a NodeRef") { osmium::NodeRef node_ref{7}; - REQUIRE(!node_ref.location().valid()); + REQUIRE_FALSE(node_ref.location().valid()); REQUIRE(node_ref.location() == osmium::Location()); node_ref.set_location(osmium::Location(13.5, -7.2)); REQUIRE(node_ref.location().lon() == 13.5); @@ -37,10 +37,10 @@ TEST_CASE("Set location on a NodeRef") { } TEST_CASE("Ordering of NodeRefs") { - osmium::NodeRef node_ref1{1, {1.0, 3.0}}; - osmium::NodeRef node_ref2{2, {1.4, 2.9}}; - osmium::NodeRef node_ref3{3, {1.2, 3.0}}; - osmium::NodeRef node_ref4{4, {1.2, 3.3}}; + const osmium::NodeRef node_ref1{1, {1.0, 3.0}}; + const osmium::NodeRef node_ref2{2, {1.4, 2.9}}; + const osmium::NodeRef node_ref3{3, {1.2, 3.0}}; + const osmium::NodeRef node_ref4{4, {1.2, 3.3}}; REQUIRE(node_ref1 < node_ref2); REQUIRE(node_ref2 < node_ref3); @@ -48,10 +48,10 @@ TEST_CASE("Ordering of NodeRefs") { REQUIRE(node_ref1 >= node_ref1); REQUIRE(osmium::location_less()(node_ref1, node_ref2)); - REQUIRE(!osmium::location_less()(node_ref2, node_ref3)); + REQUIRE_FALSE(osmium::location_less()(node_ref2, node_ref3)); REQUIRE(osmium::location_less()(node_ref1, node_ref3)); REQUIRE(osmium::location_less()(node_ref3, node_ref4)); - REQUIRE(!osmium::location_less()(node_ref1, node_ref1)); + REQUIRE_FALSE(osmium::location_less()(node_ref1, node_ref1)); } TEST_CASE("WayNodeList") { @@ -80,7 +80,7 @@ TEST_CASE("WayNodeList") { REQUIRE(nrl.size() == 3); REQUIRE(nrl[1].location() == osmium::Location(0.0, 1.0)); - nrl[1].set_location(osmium::Location(13.5, -7.2)); + nrl[1].set_location(osmium::Location{13.5, -7.2}); REQUIRE(nrl[1].location() == osmium::Location(13.5, -7.2)); } @@ -100,7 +100,7 @@ TEST_CASE("WayNodeList") { REQUIRE(nrl.ends_have_same_id()); REQUIRE(nrl.ends_have_same_location()); - osmium::Box envelope = nrl.envelope(); + const osmium::Box envelope = nrl.envelope(); REQUIRE(envelope.bottom_left().lon() == Approx(0)); REQUIRE(envelope.bottom_left().lat() == Approx(0)); REQUIRE(envelope.top_right().lon() == Approx(1)); diff --git a/third_party/libosmium/test/t/osm/test_object_comparisons.cpp b/third_party/libosmium/test/t/osm/test_object_comparisons.cpp index 42bcbff63..27ded917e 100644 --- a/third_party/libosmium/test/t/osm/test_object_comparisons.cpp +++ b/third_party/libosmium/test/t/osm/test_object_comparisons.cpp @@ -11,6 +11,72 @@ using namespace osmium::builder::attr; +TEST_CASE("Object ID comparisons") { + osmium::object_id_type a = 0; + osmium::object_id_type b = -1; + osmium::object_id_type c = -10; + osmium::object_id_type d = -11; + osmium::object_id_type e = 1; + osmium::object_id_type f = 11; + osmium::object_id_type g = 12; + + REQUIRE_FALSE(osmium::id_order{}(a, a)); + REQUIRE(osmium::id_order{}(a, b)); + REQUIRE(osmium::id_order{}(a, c)); + REQUIRE(osmium::id_order{}(a, d)); + REQUIRE(osmium::id_order{}(a, e)); + REQUIRE(osmium::id_order{}(a, f)); + REQUIRE(osmium::id_order{}(a, g)); + + REQUIRE_FALSE(osmium::id_order{}(b, a)); + REQUIRE_FALSE(osmium::id_order{}(b, b)); + REQUIRE(osmium::id_order{}(b, c)); + REQUIRE(osmium::id_order{}(b, d)); + REQUIRE(osmium::id_order{}(b, e)); + REQUIRE(osmium::id_order{}(b, f)); + REQUIRE(osmium::id_order{}(b, g)); + + REQUIRE_FALSE(osmium::id_order{}(c, a)); + REQUIRE_FALSE(osmium::id_order{}(c, b)); + REQUIRE_FALSE(osmium::id_order{}(c, c)); + REQUIRE(osmium::id_order{}(c, d)); + REQUIRE(osmium::id_order{}(c, e)); + REQUIRE(osmium::id_order{}(c, f)); + REQUIRE(osmium::id_order{}(c, g)); + + REQUIRE_FALSE(osmium::id_order{}(d, a)); + REQUIRE_FALSE(osmium::id_order{}(d, b)); + REQUIRE_FALSE(osmium::id_order{}(d, c)); + REQUIRE_FALSE(osmium::id_order{}(d, d)); + REQUIRE(osmium::id_order{}(d, e)); + REQUIRE(osmium::id_order{}(d, f)); + REQUIRE(osmium::id_order{}(d, g)); + + REQUIRE_FALSE(osmium::id_order{}(e, a)); + REQUIRE_FALSE(osmium::id_order{}(e, b)); + REQUIRE_FALSE(osmium::id_order{}(e, c)); + REQUIRE_FALSE(osmium::id_order{}(e, d)); + REQUIRE_FALSE(osmium::id_order{}(e, e)); + REQUIRE(osmium::id_order{}(e, f)); + REQUIRE(osmium::id_order{}(e, g)); + + REQUIRE_FALSE(osmium::id_order{}(f, a)); + REQUIRE_FALSE(osmium::id_order{}(f, b)); + REQUIRE_FALSE(osmium::id_order{}(f, c)); + REQUIRE_FALSE(osmium::id_order{}(f, d)); + REQUIRE_FALSE(osmium::id_order{}(f, e)); + REQUIRE_FALSE(osmium::id_order{}(f, f)); + REQUIRE(osmium::id_order{}(f, g)); + + REQUIRE_FALSE(osmium::id_order{}(g, a)); + REQUIRE_FALSE(osmium::id_order{}(g, b)); + REQUIRE_FALSE(osmium::id_order{}(g, c)); + REQUIRE_FALSE(osmium::id_order{}(g, d)); + REQUIRE_FALSE(osmium::id_order{}(g, e)); + REQUIRE_FALSE(osmium::id_order{}(g, f)); + REQUIRE_FALSE(osmium::id_order{}(g, g)); +} + TEST_CASE("Node comparisons") { osmium::memory::Buffer buffer(10 * 1000); @@ -38,12 +104,12 @@ TEST_CASE("Node comparisons") { REQUIRE_FALSE(nodes[0] > nodes[1]); } - SECTION("IDs are ordered by absolute value") { + SECTION("IDs are ordered by sign and then absolute value") { nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 0)))); - nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1)))); nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( -1)))); - nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10)))); nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id(-10)))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1)))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10)))); REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend())); } diff --git a/third_party/libosmium/test/t/osm/test_timestamp.cpp b/third_party/libosmium/test/t/osm/test_timestamp.cpp index 561a705b5..12d850173 100644 --- a/third_party/libosmium/test/t/osm/test_timestamp.cpp +++ b/third_party/libosmium/test/t/osm/test_timestamp.cpp @@ -1,79 +1,77 @@ #include "catch.hpp" #include +#include +#include #include -TEST_CASE("Timestamp") { +TEST_CASE("Timestamp can be default initialized to invalid value") { + const osmium::Timestamp t; + REQUIRE(0 == uint32_t(t)); + REQUIRE("" == t.to_iso()); + REQUIRE_FALSE(t.valid()); +} - SECTION("can be default initialized to invalid value") { - osmium::Timestamp t; - REQUIRE(0 == uint32_t(t)); - REQUIRE("" == t.to_iso()); - REQUIRE_FALSE(t.valid()); - } +TEST_CASE("Timestamp invalid value is zero") { + const osmium::Timestamp t{static_cast(0)}; + 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 == uint32_t(t)); - REQUIRE("" == t.to_iso()); - REQUIRE_FALSE(t.valid()); - } +TEST_CASE("Timestamp can be initialized from time_t") { + const osmium::Timestamp t{static_cast(1)}; + REQUIRE(1 == uint32_t(t)); + REQUIRE("1970-01-01T00:00:01Z" == t.to_iso()); + REQUIRE(t.valid()); +} - SECTION("can be initialized from time_t") { - osmium::Timestamp t(static_cast(1)); - REQUIRE(1 == uint32_t(t)); - REQUIRE("1970-01-01T00:00:01Z" == t.to_iso()); - REQUIRE(t.valid()); - } +TEST_CASE("Timestamp can be initialized from const char*") { + const 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 const char*") { - osmium::Timestamp t("2000-01-01T00:00:00Z"); - REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); - REQUIRE(t.valid()); - } +TEST_CASE("Timestamp can be initialized from string") { + const std::string s = "2000-01-01T00:00:00Z"; + const osmium::Timestamp t{s}; + REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); + REQUIRE(t.valid()); +} - SECTION("can be initialized from string") { - std::string s = "2000-01-01T00:00:00Z"; - osmium::Timestamp t(s); - REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); - REQUIRE(t.valid()); - } +TEST_CASE("Timestamp throws if initialized from bad string") { + REQUIRE_THROWS_AS(osmium::Timestamp("x"), const std::invalid_argument&); +} - SECTION("throws if initialized from bad string") { - REQUIRE_THROWS_AS(osmium::Timestamp("x"), std::invalid_argument); - } +TEST_CASE("Timestamp can be explicitly cast to time_t") { + const osmium::Timestamp t{4242}; + const time_t x = t.seconds_since_epoch(); + REQUIRE(x == 4242); +} - SECTION("can be explicitly cast to time_t") { - osmium::Timestamp t(4242); - time_t x = t.seconds_since_epoch(); - REQUIRE(x == 4242); - } +TEST_CASE("Timestamp uint32_t can be initialized from Timestamp") { + const osmium::Timestamp t{4242}; + const uint32_t x { t }; - SECTION("uint32_t can be initialized from Timestamp") { - osmium::Timestamp t(4242); - uint32_t x { t }; + REQUIRE(x == 4242); +} - REQUIRE(x == 4242); - } - - SECTION("can be compared") { - 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") { - std::stringstream ss; - osmium::Timestamp t(1); - ss << t; - REQUIRE("1970-01-01T00:00:01Z" == ss.str()); - } +TEST_CASE("Timestamps can be compared") { + const osmium::Timestamp t1{10}; + const 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()); +} +TEST_CASE("Timestamp can be written to stream") { + const osmium::Timestamp t{1}; + std::stringstream ss; + ss << t; + REQUIRE("1970-01-01T00:00:01Z" == ss.str()); } TEST_CASE("Valid timestamps") { @@ -88,34 +86,34 @@ TEST_CASE("Valid timestamps") { }; for (const auto& tc : test_cases) { - osmium::Timestamp t{tc}; + const osmium::Timestamp t{tc}; REQUIRE(tc == t.to_iso()); } } TEST_CASE("Invalid timestamps") { - REQUIRE_THROWS_AS(osmium::Timestamp{""}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"x"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"xxxxxxxxxxxxxxxxxxxx"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{""}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"x"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"xxxxxxxxxxxxxxxxxxxx"}, const std::invalid_argument&); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01x00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:00x"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01x00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:00x"}, const std::invalid_argument&); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000x01-01T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01x01T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00x00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00x00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000x01-01T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01x01T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00x00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00x00Z"}, const std::invalid_argument&); - REQUIRE_THROWS_AS(osmium::Timestamp{"0000-00-01T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-00-01T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-00T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T24:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:60:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:61Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"0000-00-01T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-00-01T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-00T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T24:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:60:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:61Z"}, const std::invalid_argument&); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-32T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-02-30T00:00:00Z"}, std::invalid_argument); - REQUIRE_THROWS_AS(osmium::Timestamp{"2000-03-32T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-32T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-02-30T00:00:00Z"}, const std::invalid_argument&); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-03-32T00:00:00Z"}, const std::invalid_argument&); } diff --git a/third_party/libosmium/test/t/osm/test_types_from_string.cpp b/third_party/libosmium/test/t/osm/test_types_from_string.cpp index 2866b8f74..0ea566e21 100644 --- a/third_party/libosmium/test/t/osm/test_types_from_string.cpp +++ b/third_party/libosmium/test/t/osm/test_types_from_string.cpp @@ -9,68 +9,68 @@ TEST_CASE("set ID from string") { REQUIRE(osmium::string_to_object_id("-17") == -17); REQUIRE(osmium::string_to_object_id("01") == 1); - REQUIRE_THROWS_AS(osmium::string_to_object_id(""), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id(" "), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id(" 22"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("x"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("0x1"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("12a"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("12345678901234567890"), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_object_id(""), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id(" "), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id(" 22"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("x"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("0x1"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("12a"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("12345678901234567890"), const std::range_error&); } TEST_CASE("set type and ID from string") { - auto n17 = osmium::string_to_object_id("n17", osmium::osm_entity_bits::nwr); + const auto n17 = osmium::string_to_object_id("n17", osmium::osm_entity_bits::nwr); REQUIRE(n17.first == osmium::item_type::node); REQUIRE(n17.second == 17); - auto w42 = osmium::string_to_object_id("w42", osmium::osm_entity_bits::nwr); + const auto w42 = osmium::string_to_object_id("w42", osmium::osm_entity_bits::nwr); REQUIRE(w42.first == osmium::item_type::way); REQUIRE(w42.second == 42); - auto r_2 = osmium::string_to_object_id("r-2", osmium::osm_entity_bits::nwr); + const auto r_2 = osmium::string_to_object_id("r-2", osmium::osm_entity_bits::nwr); REQUIRE(r_2.first == osmium::item_type::relation); REQUIRE(r_2.second == -2); - auto d3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr); + const auto d3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr); REQUIRE(d3.first == osmium::item_type::undefined); REQUIRE(d3.second == 3); - auto u3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::undefined); + const auto u3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::undefined); REQUIRE(u3.first == osmium::item_type::undefined); REQUIRE(u3.second == 3); - auto n3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::node); + const auto n3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::node); REQUIRE(n3.first == osmium::item_type::node); REQUIRE(n3.second == 3); - REQUIRE_THROWS_AS(osmium::string_to_object_id("", osmium::osm_entity_bits::nwr), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("n", osmium::osm_entity_bits::nwr), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("x3", osmium::osm_entity_bits::nwr), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("nx3", osmium::osm_entity_bits::nwr), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("n3", osmium::osm_entity_bits::way), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_id("n3a", osmium::osm_entity_bits::nwr), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_object_id("", osmium::osm_entity_bits::nwr), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("n", osmium::osm_entity_bits::nwr), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("x3", osmium::osm_entity_bits::nwr), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("nx3", osmium::osm_entity_bits::nwr), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("n3", osmium::osm_entity_bits::way), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_id("n3a", osmium::osm_entity_bits::nwr), const std::range_error&); } TEST_CASE("set object version from string") { REQUIRE(osmium::string_to_object_version("0") == 0); REQUIRE(osmium::string_to_object_version("1") == 1); - REQUIRE_THROWS_AS(osmium::string_to_object_version("-1"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_version(""), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_version(" "), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_version(" 22"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_object_version("x"), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_object_version("-1"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_version(""), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_version(" "), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_version(" 22"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_object_version("x"), const std::range_error&); } TEST_CASE("set changeset id from string") { REQUIRE(osmium::string_to_changeset_id("0") == 0); REQUIRE(osmium::string_to_changeset_id("1") == 1); - REQUIRE_THROWS_AS(osmium::string_to_changeset_id("-1"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_changeset_id(""), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" "), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" 22"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_changeset_id("x"), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_changeset_id("-1"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_changeset_id(""), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" "), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" 22"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_changeset_id("x"), const std::range_error&); } TEST_CASE("set user id from string") { @@ -78,21 +78,21 @@ TEST_CASE("set user id from string") { REQUIRE(osmium::string_to_user_id("1") == 1); REQUIRE(osmium::string_to_user_id("-1") == -1); - REQUIRE_THROWS_AS(osmium::string_to_user_id("-2"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_user_id(""), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_user_id(" "), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_user_id(" 22"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_user_id("x"), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_user_id("-2"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_user_id(""), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_user_id(" "), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_user_id(" 22"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_user_id("x"), const std::range_error&); } TEST_CASE("set num changes from string") { REQUIRE(osmium::string_to_num_changes("0") == 0); REQUIRE(osmium::string_to_num_changes("1") == 1); - REQUIRE_THROWS_AS(osmium::string_to_num_changes("-1"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_num_changes(""), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_num_changes(" "), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_num_changes(" 22"), std::range_error); - REQUIRE_THROWS_AS(osmium::string_to_num_changes("x"), std::range_error); + REQUIRE_THROWS_AS(osmium::string_to_num_changes("-1"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_num_changes(""), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_num_changes(" "), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_num_changes(" 22"), const std::range_error&); + REQUIRE_THROWS_AS(osmium::string_to_num_changes("x"), const std::range_error&); } diff --git a/third_party/libosmium/test/t/osm/test_way.cpp b/third_party/libosmium/test/t/osm/test_way.cpp index 699abe1b6..bbfcfc90f 100644 --- a/third_party/libosmium/test/t/osm/test_way.cpp +++ b/third_party/libosmium/test/t/osm/test_way.cpp @@ -42,7 +42,7 @@ TEST_CASE("Build way") { REQUIRE(1 == way.nodes()[0].ref()); REQUIRE(3 == way.nodes()[1].ref()); REQUIRE(2 == way.nodes()[2].ref()); - REQUIRE(! way.is_closed()); + REQUIRE_FALSE(way.is_closed()); osmium::CRC crc32; crc32.update(way); @@ -92,7 +92,7 @@ TEST_CASE("build way with helpers") { REQUIRE(22 == way.nodes()[0].ref()); REQUIRE(4.1 == Approx(way.nodes()[1].location().lon())); - osmium::Box envelope = way.envelope(); + const osmium::Box envelope = way.envelope(); REQUIRE(envelope.bottom_left().lon() == Approx(3.5)); REQUIRE(envelope.bottom_left().lat() == Approx(2.2)); REQUIRE(envelope.top_right().lon() == Approx(4.1)); diff --git a/third_party/libosmium/test/t/relations/data.osm b/third_party/libosmium/test/t/relations/data.osm new file mode 100644 index 000000000..796b98660 --- /dev/null +++ b/third_party/libosmium/test/t/relations/data.osm @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/t/relations/dupl_member.osm b/third_party/libosmium/test/t/relations/dupl_member.osm new file mode 100644 index 000000000..58683bcd0 --- /dev/null +++ b/third_party/libosmium/test/t/relations/dupl_member.osm @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/third_party/libosmium/test/t/relations/test_members_database.cpp b/third_party/libosmium/test/t/relations/test_members_database.cpp new file mode 100644 index 000000000..f2a4691ea --- /dev/null +++ b/third_party/libosmium/test/t/relations/test_members_database.cpp @@ -0,0 +1,194 @@ +#include "catch.hpp" + +#include +#include +#include +#include +#include + +osmium::memory::Buffer fill_buffer() { + using namespace osmium::builder::attr; + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; + + osmium::builder::add_relation(buffer, + _id(20), + _member(osmium::item_type::way, 10, "outer") + ); + + osmium::builder::add_relation(buffer, + _id(21), + _member(osmium::item_type::way, 11, "outer"), + _member(osmium::item_type::way, 12, "outer") + ); + + osmium::builder::add_relation(buffer, + _id(22), + _member(osmium::item_type::way, 13, "outer"), + _member(osmium::item_type::way, 10, "inner"), + _member(osmium::item_type::way, 14, "inner") + ); + + osmium::builder::add_way(buffer, _id(10)); + osmium::builder::add_way(buffer, _id(11)); + osmium::builder::add_way(buffer, _id(12)); + osmium::builder::add_way(buffer, _id(13)); + osmium::builder::add_way(buffer, _id(14)); + osmium::builder::add_way(buffer, _id(15)); + + return buffer; +} + +TEST_CASE("Fill member database") { + const auto buffer = fill_buffer(); + + osmium::ItemStash stash; + osmium::relations::RelationsDatabase rdb{stash}; + osmium::relations::MembersDatabase mdb{stash, rdb}; + + REQUIRE(mdb.used_memory() < 100); + + for (const auto& relation : buffer.select()) { + auto handle = rdb.add(relation); + int n = 0; + for (const auto& member : relation.members()) { + mdb.track(handle, member.ref(), n); + ++n; + } + } + + mdb.prepare_for_lookup(); + + int n = 0; + int match = 0; + for (const auto& way : buffer.select()) { + bool added = mdb.add(way, [&](osmium::relations::RelationHandle& rel_handle) { + ++match; + switch (n) { + case 0: // added w10 + REQUIRE(rel_handle->id() == 20); + break; + case 2: // added w11 and w12 + REQUIRE(rel_handle->id() == 21); + break; + case 4: // added w13 and w14 + REQUIRE(rel_handle->id() == 22); + break; + default: + REQUIRE(false); + break; + } + }); + + REQUIRE(added == (way.id() != 15)); + + if (way.id() == 11) { + const auto* way_ptr = mdb.get(way.id()); + REQUIRE(way_ptr); + REQUIRE(*way_ptr == way); + const auto* object = mdb.get_object(way.id()); + REQUIRE(object); + REQUIRE(object->id() == way.id()); + } + + ++n; + } + + REQUIRE(match == 3); + REQUIRE(mdb.used_memory() > 100); +} + +TEST_CASE("Member database with duplicate member in relation") { + using namespace osmium::builder::attr; + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; + + osmium::builder::add_relation(buffer, + _id(20), + _member(osmium::item_type::way, 10, "outer"), + _member(osmium::item_type::way, 11, "inner"), + _member(osmium::item_type::way, 12, "inner"), + _member(osmium::item_type::way, 11, "inner") + ); + + osmium::builder::add_way(buffer, _id(10)); + osmium::builder::add_way(buffer, _id(11)); + osmium::builder::add_way(buffer, _id(12)); + + osmium::ItemStash stash; + osmium::relations::RelationsDatabase rdb{stash}; + osmium::relations::MembersDatabase mdb{stash, rdb}; + + for (const auto& relation : buffer.select()) { + auto handle = rdb.add(relation); + int n = 0; + for (const auto& member : relation.members()) { + mdb.track(handle, member.ref(), n); + ++n; + } + } + + mdb.prepare_for_lookup(); + + REQUIRE(mdb.size() == 4); + { + const auto counts = mdb.count(); + REQUIRE(counts.tracked == 4); + REQUIRE(counts.available == 0); + REQUIRE(counts.removed == 0); + } + + int n = 0; + for (const auto& way : buffer.select()) { + mdb.add(way, [&](osmium::relations::RelationHandle& rel_handle) { + ++n; + REQUIRE(rel_handle->id() == 20); + { + const auto counts = mdb.count(); + REQUIRE(counts.tracked == 0); + REQUIRE(counts.available == 4); + REQUIRE(counts.removed == 0); + } + + // relation is complete here, normal code would handle it here + + for (const auto& member : rel_handle->members()) { + mdb.remove(member.ref(), rel_handle->id()); + } + rel_handle.remove(); + }); + } + + REQUIRE(n == 1); + + REQUIRE(rdb.size() == 1); + REQUIRE(rdb.count_relations() == 0); + + REQUIRE(mdb.size() == 4); + { + const auto counts = mdb.count(); + REQUIRE(counts.tracked == 0); + REQUIRE(counts.available == 0); + REQUIRE(counts.removed == 4); + } +} + +TEST_CASE("Remove non-existing object from members database doesn't do anything") { + const auto buffer = fill_buffer(); + + osmium::ItemStash stash; + osmium::relations::RelationsDatabase rdb{stash}; + osmium::relations::MembersDatabase mdb{stash, rdb}; + + for (const auto& relation : buffer.select()) { + auto handle = rdb.add(relation); + int n = 0; + for (const auto& member : relation.members()) { + mdb.track(handle, member.ref(), n); + ++n; + } + } + + REQUIRE(mdb.size() == 6); + mdb.remove(100, 100); + REQUIRE(mdb.size() == 6); +} + diff --git a/third_party/libosmium/test/t/relations/test_read_relations.cpp b/third_party/libosmium/test/t/relations/test_read_relations.cpp new file mode 100644 index 000000000..5e79133fc --- /dev/null +++ b/third_party/libosmium/test/t/relations/test_read_relations.cpp @@ -0,0 +1,77 @@ +#include "catch.hpp" +#include "utils.hpp" + +#include +#include +#include +#include + +class TestHandler : public osmium::handler::Handler { + +public: + + int count = 0; + bool prep = false; + + void relation(const osmium::Relation&) noexcept { + ++count; + } + + void prepare_for_lookup() noexcept { + prep = true; + } + +}; // class TestHandler + +TEST_CASE("Read relations with one handler") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + TestHandler handler; + + osmium::relations::read_relations(file, handler); + + REQUIRE(handler.count == 3); + REQUIRE(handler.prep); +} + +TEST_CASE("Read relations with two handlers") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + TestHandler handler1; + TestHandler handler2; + + osmium::relations::read_relations(file, handler1, handler2); + + REQUIRE(handler1.count == 3); + REQUIRE(handler2.count == 3); + REQUIRE(handler1.prep); + REQUIRE(handler2.prep); +} + +TEST_CASE("Read relations with progress bar and one handler") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + osmium::ProgressBar progress_bar{file.size(), false}; + + TestHandler handler; + + osmium::relations::read_relations(progress_bar, file, handler); + + REQUIRE(handler.count == 3); + REQUIRE(handler.prep); +} + +TEST_CASE("Read relations with progress bar and two handlers") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + osmium::ProgressBar progress_bar{file.size(), false}; + + TestHandler handler1; + TestHandler handler2; + + osmium::relations::read_relations(progress_bar, file, handler1, handler2); + + REQUIRE(handler1.count == 3); + REQUIRE(handler2.count == 3); + REQUIRE(handler1.prep); + REQUIRE(handler2.prep); +} + diff --git a/third_party/libosmium/test/t/relations/test_relations_database.cpp b/third_party/libosmium/test/t/relations/test_relations_database.cpp new file mode 100644 index 000000000..e352ea516 --- /dev/null +++ b/third_party/libosmium/test/t/relations/test_relations_database.cpp @@ -0,0 +1,105 @@ +#include "catch.hpp" + +#include +#include +#include +#include + +osmium::memory::Buffer fill_buffer() { + using namespace osmium::builder::attr; + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; + + osmium::builder::add_relation(buffer, + _id(1), + _member(osmium::item_type::way, 1, "outer") + ); + + osmium::builder::add_relation(buffer, + _id(2), + _member(osmium::item_type::way, 1, "outer"), + _member(osmium::item_type::way, 2, "outer") + ); + + osmium::builder::add_relation(buffer, + _id(3), + _member(osmium::item_type::way, 1, "outer"), + _member(osmium::item_type::way, 2, "inner"), + _member(osmium::item_type::way, 3, "inner") + ); + + return buffer; +} + +TEST_CASE("Fill relation database") { + const auto buffer = fill_buffer(); + + osmium::ItemStash stash; + osmium::relations::RelationsDatabase rdb{stash}; + + REQUIRE(rdb.size() == 0); + REQUIRE(rdb.used_memory() < 100); + + for (const auto& relation : buffer.select()) { + auto handle = rdb.add(relation); + handle.set_members(relation.cmembers().size()); + handle.decrement_members(); + REQUIRE(handle.has_all_members() == (relation.id() == 1)); + } + + REQUIRE(rdb.size() == 3); + + int n = 0; + rdb.for_each_relation([&](const osmium::relations::RelationHandle& rel_handle) { + ++n; + REQUIRE(rel_handle->members().size() == (*rel_handle).id()); + }); + REQUIRE(n == 3); +} + +TEST_CASE("Check need members and handle ops") { + const auto buffer = fill_buffer(); + + osmium::ItemStash stash; + osmium::relations::RelationsDatabase rdb{stash}; + + for (const auto& relation : buffer.select()) { + auto handle = rdb.add(relation); + REQUIRE(*handle == relation); + REQUIRE(handle->id() == relation.id()); + REQUIRE(handle.pos() + 1 == relation.positive_id()); + REQUIRE(rdb[handle.pos()].pos() == handle.pos()); + + for (auto i = relation.id(); i > 0; --i) { + handle.increment_members(); + } + + handle.decrement_members(); + REQUIRE(handle.has_all_members() == (relation.id() == 1)); + if (handle.has_all_members()) { + handle.remove(); + } + } + + REQUIRE(rdb.size() == 3); + + std::vector rels; + rdb.for_each_relation([&](const osmium::relations::RelationHandle& rel_handle) { + rels.push_back(&*rel_handle); + }); + + REQUIRE(rels.size() == 2); + + osmium::object_id_type n = 2; + for (const auto* rel : rels) { + REQUIRE(rel->id() == n); + ++n; + } + + REQUIRE(rdb[1]->id() == 2); + REQUIRE(rdb[2]->id() == 3); + + rdb[1].remove(); + + REQUIRE(rdb.count_relations() == 1); +} + diff --git a/third_party/libosmium/test/t/relations/test_relations_manager.cpp b/third_party/libosmium/test/t/relations/test_relations_manager.cpp new file mode 100644 index 000000000..d7ea12f50 --- /dev/null +++ b/third_party/libosmium/test/t/relations/test_relations_manager.cpp @@ -0,0 +1,248 @@ +#include "catch.hpp" +#include "utils.hpp" + +#include +#include +#include + +struct EmptyRM : public osmium::relations::RelationsManager { +}; + +struct TestRM : public osmium::relations::RelationsManager { + + std::size_t count_new_rels = 0; + std::size_t count_new_members = 0; + std::size_t count_complete_rels = 0; + std::size_t count_before = 0; + std::size_t count_not_in_any = 0; + std::size_t count_after = 0; + + bool new_relation(const osmium::Relation& /*relation*/) noexcept { + ++count_new_rels; + return true; + } + + bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& /*member*/, std::size_t /*n*/) noexcept { + ++count_new_members; + return true; + } + + void complete_relation(const osmium::Relation& /*relation*/) noexcept { + ++count_complete_rels; + } + + void before_node(const osmium::Node& /*node*/) noexcept { + ++count_before; + } + + void node_not_in_any_relation(const osmium::Node& /*node*/) noexcept { + ++count_not_in_any; + } + + void after_node(const osmium::Node& /*node*/) noexcept { + ++count_after; + } + + void before_way(const osmium::Way& /*way*/) noexcept { + ++count_before; + } + + void way_not_in_any_relation(const osmium::Way& /*way*/) noexcept { + ++count_not_in_any; + } + + void after_way(const osmium::Way& /*way*/) noexcept { + ++count_after; + } + + void before_relation(const osmium::Relation& /*relation*/) noexcept { + ++count_before; + } + + void relation_not_in_any_relation(const osmium::Relation& /*relation*/) noexcept { + ++count_not_in_any; + } + + void after_relation(const osmium::Relation& /*relation*/) noexcept { + ++count_after; + } + +}; + +struct CallbackRM : public osmium::relations::RelationsManager { + + std::size_t count_nodes = 0; + + bool new_relation(const osmium::Relation& /*relation*/) noexcept { + return true; + } + + bool new_member(const osmium::Relation& /*relation*/, const osmium::RelationMember& member, std::size_t /*n*/) noexcept { + return member.type() == osmium::item_type::node; + } + + void complete_relation(const osmium::Relation& relation) noexcept { + for (const auto& member : relation.members()) { + if (member.type() == osmium::item_type::node) { + ++count_nodes; + const auto* node = get_member_node(member.ref()); + REQUIRE(node); + buffer().add_item(*node); + buffer().commit(); + } + } + } + +}; + +TEST_CASE("Use RelationsManager without any overloaded functions in derived class") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + EmptyRM manager; + + osmium::relations::read_relations(file, manager); + + REQUIRE(manager.member_nodes_database().size() == 2); + REQUIRE(manager.member_ways_database().size() == 2); + REQUIRE(manager.member_relations_database().size() == 1); + + REQUIRE(manager.member_database(osmium::item_type::node).size() == 2); + REQUIRE(manager.member_database(osmium::item_type::way).size() == 2); + REQUIRE(manager.member_database(osmium::item_type::relation).size() == 1); + + const auto& m = manager; + REQUIRE(m.member_database(osmium::item_type::node).size() == 2); + REQUIRE(m.member_database(osmium::item_type::way).size() == 2); + REQUIRE(m.member_database(osmium::item_type::relation).size() == 1); + + osmium::io::Reader reader{file}; + osmium::apply(reader, manager.handler()); + reader.close(); +} + +TEST_CASE("Relations manager derived class") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + TestRM manager; + + osmium::relations::read_relations(file, manager); + + REQUIRE(manager.member_nodes_database().size() == 2); + REQUIRE(manager.member_ways_database().size() == 2); + REQUIRE(manager.member_relations_database().size() == 1); + + bool callback_called = false; + osmium::io::Reader reader{file}; + osmium::apply(reader, manager.handler([&](osmium::memory::Buffer&&) { + callback_called = true; + })); + reader.close(); + REQUIRE_FALSE(callback_called); + + REQUIRE(manager.count_new_rels == 3); + REQUIRE(manager.count_new_members == 5); + REQUIRE(manager.count_complete_rels == 2); + REQUIRE(manager.count_before == 10); + REQUIRE(manager.count_not_in_any == 6); + REQUIRE(manager.count_after == 10); + + int n = 0; + manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){ + ++n; + REQUIRE(handle->id() == 31); + for (const auto& member : handle->members()) { + const auto* obj = manager.get_member_object(member); + if (member.ref() == 22) { + REQUIRE_FALSE(obj); + } else { + REQUIRE(obj); + } + } + }); + REQUIRE(n == 1); +} + +TEST_CASE("Relations manager with callback") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + CallbackRM manager; + + osmium::relations::read_relations(file, manager); + + REQUIRE(manager.member_nodes_database().size() == 2); + REQUIRE(manager.member_ways_database().size() == 0); + REQUIRE(manager.member_relations_database().size() == 0); + + bool callback_called = false; + osmium::io::Reader reader{file}; + osmium::apply(reader, manager.handler([&](osmium::memory::Buffer&& buffer) { + callback_called = true; + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2); + })); + reader.close(); + REQUIRE(manager.count_nodes == 2); + REQUIRE(callback_called); +} + +TEST_CASE("Relations manager reading buffer without callback") { + osmium::io::File file{with_data_dir("t/relations/data.osm")}; + + CallbackRM manager; + + osmium::relations::read_relations(file, manager); + + REQUIRE(manager.member_nodes_database().size() == 2); + REQUIRE(manager.member_ways_database().size() == 0); + REQUIRE(manager.member_relations_database().size() == 0); + + osmium::io::Reader reader{file}; + osmium::apply(reader, manager.handler()); + reader.close(); + + auto buffer = manager.read(); + REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2); + + REQUIRE(manager.count_nodes == 2); +} + +TEST_CASE("Access members via RelationsManager") { + EmptyRM manager; + + manager.prepare_for_lookup(); + + REQUIRE(nullptr == manager.get_member_node(0)); + REQUIRE(nullptr == manager.get_member_way(0)); + REQUIRE(nullptr == manager.get_member_relation(0)); + + REQUIRE(nullptr == manager.get_member_node(17)); + REQUIRE(nullptr == manager.get_member_way(17)); + REQUIRE(nullptr == manager.get_member_relation(17)); +} + +TEST_CASE("Handle duplicate members correctly") { + osmium::io::File file{with_data_dir("t/relations/dupl_member.osm")}; + + TestRM manager; + + osmium::relations::read_relations(file, manager); + + auto c = manager.member_nodes_database().count(); + REQUIRE(c.tracked == 5); + REQUIRE(c.available == 0); + REQUIRE(c.removed == 0); + + osmium::io::Reader reader{file}; + osmium::apply(reader, manager.handler()); + reader.close(); + + c = manager.member_nodes_database().count(); + REQUIRE(c.tracked == 0); + REQUIRE(c.available == 0); + REQUIRE(c.removed == 5); + + REQUIRE(manager.count_new_rels == 2); + REQUIRE(manager.count_new_members == 5); + REQUIRE(manager.count_complete_rels == 2); + REQUIRE(manager.count_not_in_any == 2); // 2 relations +} + diff --git a/third_party/libosmium/test/t/storage/test_item_stash.cpp b/third_party/libosmium/test/t/storage/test_item_stash.cpp new file mode 100644 index 000000000..62d910efb --- /dev/null +++ b/third_party/libosmium/test/t/storage/test_item_stash.cpp @@ -0,0 +1,167 @@ + +#include + +#include + +#include +#include + +osmium::memory::Buffer generate_test_data() { + using namespace osmium::builder::attr; + + osmium::memory::Buffer buffer{1024 * 1024, osmium::memory::Buffer::auto_grow::yes}; + + const osmium::object_id_type num_nodes = 100; + const osmium::object_id_type num_ways = 50; + const osmium::object_id_type num_relations = 30; + + osmium::object_id_type id = 1; + for (; id <= num_nodes; ++id) { + osmium::builder::add_node(buffer, _id(id)); + } + + for (; id <= num_nodes + num_ways; ++id) { + osmium::builder::add_way(buffer, _id(id)); + } + + for (; id <= num_nodes + num_ways + num_relations; ++id) { + osmium::builder::add_relation(buffer, _id(id)); + } + + return buffer; +} + + +TEST_CASE("Item stash handle") { + const auto handle = osmium::ItemStash::handle_type{}; + REQUIRE_FALSE(handle.valid()); + + std::stringstream ss; + ss << handle; + REQUIRE(ss.str() == "-"); +} + +TEST_CASE("Item stash") { + const auto buffer = generate_test_data(); + + osmium::ItemStash stash; + REQUIRE(stash.size() == 0); + REQUIRE(stash.count_removed() == 0); + + std::vector handles; + for (const auto& item : buffer) { + auto handle = stash.add_item(item); + handles.push_back(handle); + } + + REQUIRE(stash.size() == 180); + REQUIRE(stash.count_removed() == 0); + + REQUIRE(stash.used_memory() > 1024 * 1024); + + osmium::object_id_type id = 1; + for (auto& handle : handles) { // must be reference because we will change it! + REQUIRE(handle.valid()); + const auto& item = stash.get_item(handle); + bool correct_type = item.type() == osmium::item_type::node || + item.type() == osmium::item_type::way || + item.type() == osmium::item_type::relation; + REQUIRE(correct_type); + const auto& obj = static_cast(item); + REQUIRE(obj.id() == id); + + std::stringstream ss; + ss << handle; + REQUIRE(ss.str() == std::to_string(id)); + + if (obj.id() % 3 == 0) { + stash.remove_item(handle); + handle = osmium::ItemStash::handle_type{}; + } + + id++; + } + + REQUIRE(stash.size() == 120); + REQUIRE(stash.count_removed() == 60); + + id = 1; + int count_valid = 0; + int count_invalid = 0; + for (auto handle : handles) { + if (handle.valid()) { + ++count_valid; + const auto& item = stash.get_item(handle); + const bool correct_type = item.type() == osmium::item_type::node || + item.type() == osmium::item_type::way || + item.type() == osmium::item_type::relation; + REQUIRE(correct_type); + const auto& obj = static_cast(item); + REQUIRE(obj.id() == id); + } else { + ++count_invalid; + } + id++; + } + + REQUIRE(count_valid == 120); + REQUIRE(count_invalid == 60); + + stash.garbage_collect(); + REQUIRE(stash.size() == 120); + REQUIRE(stash.count_removed() == 0); + + id = 1; + for (auto handle : handles) { + if (handle.valid()) { + const auto& item = stash.get_item(handle); + const bool correct_type = item.type() == osmium::item_type::node || + item.type() == osmium::item_type::way || + item.type() == osmium::item_type::relation; + REQUIRE(correct_type); + const auto& obj = static_cast(item); + REQUIRE(obj.id() == id); + } + id++; + } + + stash.clear(); + REQUIRE(stash.size() == 0); + REQUIRE(stash.count_removed() == 0); +} + +TEST_CASE("Fill item stash until it garbage collects") { + const auto buffer = generate_test_data(); + + osmium::ItemStash stash; + REQUIRE(stash.size() == 0); + REQUIRE(stash.count_removed() == 0); + + const auto& node = buffer.get(0); + + std::vector handles; + std::size_t num_items = 6 * 1000 * 1000; + for (std::size_t i = 0; i < num_items; ++i) { + auto handle = stash.add_item(node); + handles.push_back(handle); + } + + REQUIRE(stash.size() == num_items); + REQUIRE(stash.count_removed() == 0); + + for (std::size_t i = 0; i < num_items; ++i) { + if (i % 10 != 0) { + stash.remove_item(handles[i]); + } + } + + REQUIRE(stash.size() == num_items / 10); + REQUIRE(stash.count_removed() == num_items / 10 * 9); + + // trigger compaction + stash.add_item(node); + + REQUIRE(stash.size() == num_items / 10 + 1); + REQUIRE(stash.count_removed() == 0); +} + diff --git a/third_party/libosmium/test/t/tags/test_filter.cpp b/third_party/libosmium/test/t/tags/test_filter.cpp index eab8844a3..6c15a8048 100644 --- a/third_party/libosmium/test/t/tags/test_filter.cpp +++ b/third_party/libosmium/test/t/tags/test_filter.cpp @@ -26,7 +26,7 @@ void check_filter(const osmium::TagList& tag_list, } const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer, - std::initializer_list> tags) { + const std::initializer_list>& tags) { const auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); return buffer.get(pos); } @@ -131,9 +131,9 @@ TEST_CASE("KeyValueFilter") { { "source", "GPS" } }); - REQUIRE( osmium::tags::match_any_of(tag_list, filter)); - REQUIRE(!osmium::tags::match_all_of(tag_list, filter)); - REQUIRE(!osmium::tags::match_none_of(tag_list, filter)); + REQUIRE( osmium::tags::match_any_of(tag_list, filter)); + REQUIRE_FALSE(osmium::tags::match_all_of(tag_list, filter)); + REQUIRE_FALSE(osmium::tags::match_none_of(tag_list, filter)); } SECTION("KeyValueFilter matches against taglist with_all") { @@ -147,9 +147,9 @@ TEST_CASE("KeyValueFilter") { { "name", "Main Street" } }); - REQUIRE( osmium::tags::match_any_of(tag_list, filter)); - REQUIRE( osmium::tags::match_all_of(tag_list, filter)); - REQUIRE(!osmium::tags::match_none_of(tag_list, filter)); + REQUIRE( osmium::tags::match_any_of(tag_list, filter)); + REQUIRE( osmium::tags::match_all_of(tag_list, filter)); + REQUIRE_FALSE(osmium::tags::match_none_of(tag_list, filter)); } SECTION("KeyValueFilter matches against taglist with none") { @@ -163,9 +163,9 @@ TEST_CASE("KeyValueFilter") { { "name", "Main Street" } }); - REQUIRE(!osmium::tags::match_any_of(tag_list, filter)); - REQUIRE(!osmium::tags::match_all_of(tag_list, filter)); - REQUIRE( osmium::tags::match_none_of(tag_list, filter)); + REQUIRE_FALSE(osmium::tags::match_any_of(tag_list, filter)); + REQUIRE_FALSE(osmium::tags::match_all_of(tag_list, filter)); + REQUIRE( osmium::tags::match_none_of(tag_list, filter)); } SECTION("KeyValueFilter matches against taglist with any called with rvalue") { @@ -233,7 +233,7 @@ TEST_CASE("KeyPrefixFilter matches some keys") { } -TEST_CASE("Generic Filterw with regex matches some keys") { +TEST_CASE("Generic Filter with regex matches some keys") { osmium::memory::Buffer buffer{10240}; osmium::tags::Filter filter{false}; diff --git a/third_party/libosmium/test/t/tags/test_operators.cpp b/third_party/libosmium/test/t/tags/test_operators.cpp index 33a53c2a2..b90303ae8 100644 --- a/third_party/libosmium/test/t/tags/test_operators.cpp +++ b/third_party/libosmium/test/t/tags/test_operators.cpp @@ -6,56 +6,53 @@ #include #include -TEST_CASE("Operators") { - - SECTION("Equal") { - osmium::memory::Buffer buffer1(10240); - { - osmium::builder::TagListBuilder tl_builder(buffer1); - tl_builder.add_tag("highway", "primary"); - tl_builder.add_tag("name", "Main Street"); - tl_builder.add_tag("source", "GPS"); - } - buffer1.commit(); - - osmium::memory::Buffer buffer2(10240); - { - osmium::builder::TagListBuilder tl_builder(buffer2); - tl_builder.add_tag("highway", "primary"); - } - buffer2.commit(); - - const osmium::TagList& tl1 = buffer1.get(0); - const osmium::TagList& tl2 = buffer2.get(0); - - auto tagit1 = tl1.begin(); - auto tagit2 = tl2.begin(); - REQUIRE(*tagit1 == *tagit2); - ++tagit1; - REQUIRE(!(*tagit1 == *tagit2)); +TEST_CASE("Equality comparison of tags") { + osmium::memory::Buffer buffer1{10240}; + { + osmium::builder::TagListBuilder tl_builder{buffer1}; + tl_builder.add_tag("highway", "primary"); + tl_builder.add_tag("name", "Main Street"); + tl_builder.add_tag("source", "GPS"); } + buffer1.commit(); - SECTION("Order") { - osmium::memory::Buffer buffer(10240); - { - osmium::builder::TagListBuilder tl_builder(buffer); - tl_builder.add_tag("highway", "residential"); - tl_builder.add_tag("highway", "primary"); - tl_builder.add_tag("name", "Main Street"); - tl_builder.add_tag("amenity", "post_box"); - } - buffer.commit(); - - const osmium::TagList& tl = buffer.get(0); - const osmium::Tag& t1 = *(tl.begin()); - const osmium::Tag& t2 = *(std::next(tl.begin(), 1)); - const osmium::Tag& t3 = *(std::next(tl.begin(), 2)); - const osmium::Tag& t4 = *(std::next(tl.begin(), 3)); - - REQUIRE(t2 < t1); - REQUIRE(t1 < t3); - REQUIRE(t2 < t3); - REQUIRE(t4 < t1); + osmium::memory::Buffer buffer2{10240}; + { + osmium::builder::TagListBuilder tl_builder{buffer2}; + tl_builder.add_tag("highway", "primary"); } + buffer2.commit(); + const osmium::TagList& tl1 = buffer1.get(0); + const osmium::TagList& tl2 = buffer2.get(0); + + auto tagit1 = tl1.begin(); + auto tagit2 = tl2.begin(); + REQUIRE(*tagit1 == *tagit2); + ++tagit1; + REQUIRE_FALSE(*tagit1 == *tagit2); } + +TEST_CASE("Ordering of tags") { + osmium::memory::Buffer buffer{10240}; + { + osmium::builder::TagListBuilder tl_builder{buffer}; + tl_builder.add_tag("highway", "residential"); + tl_builder.add_tag("highway", "primary"); + tl_builder.add_tag("name", "Main Street"); + tl_builder.add_tag("amenity", "post_box"); + } + buffer.commit(); + + const osmium::TagList& tl = buffer.get(0); + const osmium::Tag& t1 = *(tl.begin()); + const osmium::Tag& t2 = *(std::next(tl.begin(), 1)); + const osmium::Tag& t3 = *(std::next(tl.begin(), 2)); + const osmium::Tag& t4 = *(std::next(tl.begin(), 3)); + + REQUIRE(t2 < t1); + REQUIRE(t1 < t3); + REQUIRE(t2 < t3); + REQUIRE(t4 < t1); +} + 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 3bdf5e967..db3f16543 100644 --- a/third_party/libosmium/test/t/tags/test_tag_list.cpp +++ b/third_party/libosmium/test/t/tags/test_tag_list.cpp @@ -11,7 +11,7 @@ using namespace osmium::builder::attr; TEST_CASE("create tag list") { - osmium::memory::Buffer buffer(10240); + osmium::memory::Buffer buffer{10240}; SECTION("with TagListBuilder from char*") { { @@ -160,13 +160,6 @@ TEST_CASE("create tag list") { 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"); - tlb.add_tag("name", "Main Street"); - }); - } - const osmium::TagList& tl = *buffer.select().cbegin(); REQUIRE(osmium::item_type::tag_list == tl.type()); REQUIRE(2 == tl.size()); @@ -194,9 +187,9 @@ TEST_CASE("create tag list") { } TEST_CASE("empty keys and values are okay") { - osmium::memory::Buffer buffer(10240); + osmium::memory::Buffer buffer{10240}; - auto pos = osmium::builder::add_tag_list(buffer, + const auto pos = osmium::builder::add_tag_list(buffer, _tag("empty value", ""), _tag("", "empty key") ); @@ -219,8 +212,8 @@ TEST_CASE("empty keys and values are okay") { } TEST_CASE("tag key or value is too long") { - osmium::memory::Buffer buffer(10240); - osmium::builder::TagListBuilder builder(buffer); + osmium::memory::Buffer buffer{10240}; + osmium::builder::TagListBuilder builder{buffer}; const char kv[2000] = ""; builder.add_tag(kv, 1, kv, 1000); diff --git a/third_party/libosmium/test/t/tags/test_tag_matcher.cpp b/third_party/libosmium/test/t/tags/test_tag_matcher.cpp new file mode 100644 index 000000000..d4980620a --- /dev/null +++ b/third_party/libosmium/test/t/tags/test_tag_matcher.cpp @@ -0,0 +1,93 @@ +#include "catch.hpp" + +#include + +#include +#include +#include + +static_assert(std::is_default_constructible::value, "TagMatcher should be default constructible"); +static_assert(std::is_copy_constructible::value, "TagMatcher should be copy constructible"); +static_assert(std::is_move_constructible::value, "TagMatcher should be move constructible"); +static_assert(std::is_copy_assignable::value, "TagMatcher should be copyable"); +static_assert(std::is_move_assignable::value, "TagMatcher should be moveable"); + +TEST_CASE("Tag matcher") { + osmium::memory::Buffer buffer{10240}; + + const auto pos = osmium::builder::add_tag_list(buffer, + osmium::builder::attr::_tags({ + { "highway", "primary" }, + { "name", "Main Street" }, + { "source", "GPS" } + })); + const osmium::TagList& tag_list = buffer.get(pos); + + SECTION("Matching keys only") { + osmium::TagMatcher m{osmium::StringMatcher::equal{"highway"}}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching keys only with shortcut const char*") { + osmium::TagMatcher m{"highway"}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching keys only with shortcut std::string") { + std::string s{"highway"}; + osmium::TagMatcher m{s}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching key and value") { + osmium::TagMatcher m{osmium::StringMatcher::equal{"highway"}, + osmium::StringMatcher::equal{"primary"}}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching key and value with shortcut") { + osmium::TagMatcher m{"highway", "primary", false}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching key and value") { + osmium::TagMatcher m{osmium::StringMatcher::equal{"highway"}, + osmium::StringMatcher::equal{"secondary"}}; + REQUIRE_FALSE(m(tag_list)); + } + + SECTION("Matching key and value inverted") { + osmium::TagMatcher m{osmium::StringMatcher::equal{"highway"}, + osmium::StringMatcher::equal{"secondary"}, + true}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } + + SECTION("Matching key and value list") { + osmium::TagMatcher m{osmium::StringMatcher::equal{"highway"}, + osmium::StringMatcher::list{{"primary", "secondary"}}}; + REQUIRE(m(tag_list)); + + REQUIRE(m(*tag_list.begin())); + REQUIRE_FALSE(m(*std::next(tag_list.begin()))); + } +} + diff --git a/third_party/libosmium/test/t/tags/test_tags_filter.cpp b/third_party/libosmium/test/t/tags/test_tags_filter.cpp new file mode 100644 index 000000000..a537228c5 --- /dev/null +++ b/third_party/libosmium/test/t/tags/test_tags_filter.cpp @@ -0,0 +1,79 @@ +#include "catch.hpp" + +#include + +#include +#include +#include + +TEST_CASE("Tags filter") { + osmium::memory::Buffer buffer{10240}; + + const auto pos1 = osmium::builder::add_tag_list(buffer, + osmium::builder::attr::_tags({ + { "highway", "primary" }, + { "name", "Main Street" }, + { "source", "GPS" } + })); + const auto pos2 = osmium::builder::add_tag_list(buffer, + osmium::builder::attr::_tags({ + { "amenity", "restaurant" }, + { "name", "The Golden Goose" } + })); + const osmium::TagList& tag_list1 = buffer.get(pos1); + const osmium::TagList& tag_list2 = buffer.get(pos2); + + SECTION("Filter based on key only: okay") { + osmium::TagsFilter filter; + filter.add_rule(true, osmium::TagMatcher{osmium::StringMatcher::equal{"highway"}}); + filter.add_rule(true, osmium::TagMatcher{osmium::StringMatcher::equal{"amenity"}}); + REQUIRE(filter(*tag_list1.begin())); + REQUIRE(filter(*tag_list2.begin())); + REQUIRE_FALSE(filter(*std::next(tag_list1.begin()))); + REQUIRE_FALSE(filter(*std::next(tag_list2.begin()))); + } + + SECTION("Filter based string: shortcut") { + osmium::TagsFilter filter; + filter.add_rule(true, "highway"); + filter.add_rule(true, "amenity", "restaurant"); + REQUIRE(filter(*tag_list1.begin())); + REQUIRE(filter(*tag_list2.begin())); + REQUIRE_FALSE(filter(*std::next(tag_list1.begin()))); + REQUIRE_FALSE(filter(*std::next(tag_list2.begin()))); + } + + SECTION("Filter based on key only: fail") { + osmium::TagsFilter filter; + filter.add_rule(true, osmium::StringMatcher::equal{"foo"}); + filter.add_rule(true, osmium::StringMatcher::equal{"bar"}); + REQUIRE_FALSE(filter(*tag_list1.begin())); + REQUIRE_FALSE(filter(*tag_list2.begin())); + REQUIRE_FALSE(filter(*std::next(tag_list1.begin()))); + REQUIRE_FALSE(filter(*std::next(tag_list2.begin()))); + } + + SECTION("KeyFilter iterator filters tags") { + osmium::TagsFilter filter; + filter.add_rule(true, osmium::StringMatcher::equal{"highway"}) + .add_rule(true, osmium::StringMatcher::equal{"source"}); + + osmium::TagsFilter::iterator it{std::cref(filter), tag_list1.begin(), + tag_list1.end()}; + + const osmium::TagsFilter::iterator end{std::cref(filter), tag_list1.end(), + tag_list1.end()}; + + REQUIRE(2 == std::distance(it, end)); + + REQUIRE(it != end); + REQUIRE(std::string("highway") == it->key()); + REQUIRE(std::string("primary") == it->value()); + ++it; + REQUIRE(std::string("source") == it->key()); + REQUIRE(std::string("GPS") == it->value()); + REQUIRE(++it == end); + } + +} + diff --git a/third_party/libosmium/test/t/thread/test_pool.cpp b/third_party/libosmium/test/t/thread/test_pool.cpp index c1047db49..567d8de0a 100644 --- a/third_party/libosmium/test/t/thread/test_pool.cpp +++ b/third_party/libosmium/test/t/thread/test_pool.cpp @@ -15,7 +15,7 @@ struct test_job_with_result { struct test_job_throw { OSMIUM_NORETURN void operator()() const { - throw std::runtime_error("exception in pool thread"); + throw std::runtime_error{"exception in pool thread"}; } }; @@ -43,29 +43,75 @@ TEST_CASE("number of threads in pool") { 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); + REQUIRE(osmium::thread::detail::get_pool_size(-100, 0, 16) == 1); + REQUIRE(osmium::thread::detail::get_pool_size(1000, 0, 16) == 32); } +TEST_CASE("if zero number of threads requested, threads configured") { + osmium::thread::Pool pool{0}; + REQUIRE(pool.num_threads() > 0); +} + +TEST_CASE("if any negative number of threads requested, threads configured") { + osmium::thread::Pool pool{-1}; + REQUIRE(pool.num_threads() > 0); +} + +TEST_CASE("if outlier negative number of threads requested, threads configured") { + osmium::thread::Pool pool{-100}; + REQUIRE(pool.num_threads() > 0); +} + +TEST_CASE("if outlier positive number of threads requested, threads configured") { + osmium::thread::Pool pool{1000}; + REQUIRE(pool.num_threads() > 0); +} + TEST_CASE("thread") { - auto& pool = osmium::thread::Pool::instance(); + auto& pool = osmium::thread::Pool::default_instance(); SECTION("can get access to thread pool") { REQUIRE(pool.queue_empty()); } SECTION("can send job to thread pool") { - auto future = pool.submit(test_job_with_result {}); + auto future = pool.submit(test_job_with_result{}); REQUIRE(future.get() == 42); } SECTION("can throw from job in thread pool") { - auto future = pool.submit(test_job_throw {}); + auto future = pool.submit(test_job_throw{}); - REQUIRE_THROWS_AS(future.get(), std::runtime_error); + REQUIRE_THROWS_AS(future.get(), const std::runtime_error&); + } + +} + +TEST_CASE("thread (user-provided pool)") { + + osmium::thread::Pool pool{7}; + + SECTION("can get access to thread pool") { + REQUIRE(pool.queue_empty()); + } + + SECTION("can access user-provided number of threads") { + REQUIRE(pool.num_threads() == 7); + } + + SECTION("can send job to thread pool") { + auto future = pool.submit(test_job_with_result{}); + + REQUIRE(future.get() == 42); + } + + SECTION("can throw from job in thread pool") { + auto future = pool.submit(test_job_throw{}); + + REQUIRE_THROWS_AS(future.get(), const std::runtime_error&); } } diff --git a/third_party/libosmium/test/t/thread/test_util.cpp b/third_party/libosmium/test/t/thread/test_util.cpp new file mode 100644 index 000000000..419329e02 --- /dev/null +++ b/third_party/libosmium/test/t/thread/test_util.cpp @@ -0,0 +1,37 @@ +#include + +#include + +#include + +TEST_CASE("check_for_exception") { + std::promise p; + auto f = p.get_future(); + + SECTION("not ready") { + osmium::thread::check_for_exception(f); + } + SECTION("ready") { + p.set_value(3); + osmium::thread::check_for_exception(f); + } + SECTION("no shared state") { + p.set_value(3); + REQUIRE(f.get() == 3); + osmium::thread::check_for_exception(f); + } +} + +TEST_CASE("check_for_exception with exception") { + std::promise p; + auto f = p.get_future(); + + try { + throw std::runtime_error{"TEST"}; + } catch(...) { + p.set_exception(std::current_exception()); + } + + REQUIRE_THROWS_AS(osmium::thread::check_for_exception(f), const std::runtime_error&); +} + diff --git a/third_party/libosmium/test/t/util/.gitattributes b/third_party/libosmium/test/t/util/.gitattributes new file mode 100644 index 000000000..a22ace47c --- /dev/null +++ b/third_party/libosmium/test/t/util/.gitattributes @@ -0,0 +1 @@ +known_file_size text eol=lf diff --git a/third_party/libosmium/test/t/util/known_file_size b/third_party/libosmium/test/t/util/known_file_size new file mode 100644 index 000000000..1369e1e14 --- /dev/null +++ b/third_party/libosmium/test/t/util/known_file_size @@ -0,0 +1 @@ +this file has size 22 diff --git a/third_party/libosmium/test/t/util/test_cast_with_assert.cpp b/third_party/libosmium/test/t/util/test_cast_with_assert.cpp index 044176eef..e9aaef7c7 100644 --- a/third_party/libosmium/test/t/util/test_cast_with_assert.cpp +++ b/third_party/libosmium/test/t/util/test_cast_with_assert.cpp @@ -6,84 +6,79 @@ struct assert_error : public std::runtime_error { explicit assert_error(const char* what_arg) : std::runtime_error(what_arg) { } }; -#define assert(x) if (!(x)) { throw(assert_error(#x)); } +#define assert(x) if (!(x)) { throw assert_error{#x}; } #include -TEST_CASE("static_cast_with_assert") { - - SECTION("same types is always okay") { - int f = 10; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("casting to larger type is always okay") { - int16_t f = 10; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - - SECTION("cast int32_t -> int_16t should not trigger assert for small int") { - int32_t f = 100; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast int32_t -> int_16t should trigger assert for large int") { - int32_t f = 100000; - REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), assert_error); - } - - - SECTION("cast int16_t -> uint16_t should not trigger assert for zero") { - int16_t f = 0; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast int16_t -> uint16_t should not trigger assert for positive int") { - int16_t f = 1; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast int16_t -> uint16_t should trigger assert for negative int") { - int16_t f = -1; - REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), assert_error); - } - - - SECTION("cast uint32_t -> uint_16t should not trigger assert for zero") { - uint32_t f = 0; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast uint32_t -> uint_16t should not trigger assert for small int") { - uint32_t f = 100; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast int32_t -> int_16t should trigger assert for large int") { - uint32_t f = 100000; - REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), assert_error); - } - - - SECTION("cast uint16_t -> int16_t should not trigger assert for small int") { - uint16_t f = 1; - auto t = osmium::static_cast_with_assert(f); - REQUIRE(t == f); - } - - SECTION("cast uint16_t -> int16_t should trigger assert for large int") { - uint16_t f = 65000; - REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), assert_error); - } - - +TEST_CASE("static_cast_with_assert: same types is always okay") { + const int f = 10; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: casting to larger type is always okay") { + const int16_t f = 10; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + + +TEST_CASE("static_cast_with_assert: cast int32_t -> int16_t should not trigger assert for small int") { + const int32_t f = 100; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast int32_t -> int16_t should trigger assert for large int") { + const int32_t f = 100000; + REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), const assert_error&); +} + + +TEST_CASE("static_cast_with_assert: cast int16_t -> uint16_t should not trigger assert for zero") { + const int16_t f = 0; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast int16_t -> uint16_t should not trigger assert for positive int") { + const int16_t f = 1; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast int16_t -> uint16_t should trigger assert for negative int") { + const int16_t f = -1; + REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), const assert_error&); +} + + +TEST_CASE("static_cast_with_assert: cast uint32_t -> uint16_t should not trigger assert for zero") { + const uint32_t f = 0; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast uint32_t -> uint16_t should not trigger assert for small int") { + const uint32_t f = 100; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast uint32_t -> uint16_t should trigger assert for large int") { + const uint32_t f = 100000; + REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), const assert_error&); +} + + +TEST_CASE("static_cast_with_assert: cast uint16_t -> int16_t should not trigger assert for small int") { + const uint16_t f = 1; + const auto t = osmium::static_cast_with_assert(f); + REQUIRE(t == f); +} + +TEST_CASE("static_cast_with_assert: cast uint16_t -> int16_t should trigger assert for large int") { + const uint16_t f = 65000; + REQUIRE_THROWS_AS(osmium::static_cast_with_assert(f), const assert_error&); } diff --git a/third_party/libosmium/test/t/util/test_config.cpp b/third_party/libosmium/test/t/util/test_config.cpp new file mode 100644 index 000000000..1f168d163 --- /dev/null +++ b/third_party/libosmium/test/t/util/test_config.cpp @@ -0,0 +1,75 @@ +#include "catch.hpp" + +#include + +const char* env = nullptr; +std::string name; + +const char* fake_getenv(const char* env_var) { + name = env_var; + return env; +} + +#define getenv fake_getenv + +#include + +TEST_CASE("get_pool_threads") { + env = nullptr; + REQUIRE(osmium::config::get_pool_threads() == 0); + REQUIRE(name == "OSMIUM_POOL_THREADS"); + env = ""; + REQUIRE(osmium::config::get_pool_threads() == 0); + env = "2"; + REQUIRE(osmium::config::get_pool_threads() == 2); +} + +TEST_CASE("use_pool_threads_for_pbf_parsing") { + env = nullptr; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + REQUIRE(name == "OSMIUM_USE_POOL_THREADS_FOR_PBF_PARSING"); + env = ""; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + + env = "off"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "OFF"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "false"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "no"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "No"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "0"; + REQUIRE_FALSE(osmium::config::use_pool_threads_for_pbf_parsing()); + + env = "on"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "ON"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "true"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "yes"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "Yes"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); + env = "1"; + REQUIRE(osmium::config::use_pool_threads_for_pbf_parsing()); +} + +TEST_CASE("get_max_queue_size") { + env = nullptr; + REQUIRE(osmium::config::get_max_queue_size("NAME", 0) == 0); + REQUIRE(name == "OSMIUM_MAX_NAME_QUEUE_SIZE"); + + REQUIRE(osmium::config::get_max_queue_size("NAME", 7) == 7); + + env = ""; + REQUIRE(osmium::config::get_max_queue_size("NAME", 7) == 7); + env = "0"; + REQUIRE(osmium::config::get_max_queue_size("NAME", 7) == 7); + env = "3"; + REQUIRE(osmium::config::get_max_queue_size("NAME", 7) == 3); +} + diff --git a/third_party/libosmium/test/t/util/test_delta.cpp b/third_party/libosmium/test/t/util/test_delta.cpp index 27bd8be72..f6f2a5484 100644 --- a/third_party/libosmium/test/t/util/test_delta.cpp +++ b/third_party/libosmium/test/t/util/test_delta.cpp @@ -1,60 +1,74 @@ #include "catch.hpp" +#include #include #include 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); - } + REQUIRE(x.update(17) == 17); + REQUIRE(x.value() == 17); + REQUIRE(x.update(10) == -7); + REQUIRE(x.value() == 10); + REQUIRE(x.update(-10) == -20); + REQUIRE(x.value() == -10); + x.clear(); + REQUIRE(x.value() == 0); +} +TEST_CASE("delta encode int with int32") { + osmium::util::DeltaEncode x; + + REQUIRE(x.update(17) == 17); + REQUIRE(x.value() == 17); + REQUIRE(x.update(10) == -7); + REQUIRE(x.value() == 10); + REQUIRE(x.update(-10) == -20); + REQUIRE(x.value() == -10); + x.clear(); + REQUIRE(x.value() == 0); } 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); - } + REQUIRE(x.update(17) == 17); + REQUIRE(x.update(10) == 27); + REQUIRE(x.update(-40) == -13); + x.clear(); + REQUIRE(x.update(17) == 17); +} +TEST_CASE("delta decode int with int32") { + osmium::util::DeltaDecode x; + + REQUIRE(x.update(17) == 17); + REQUIRE(x.update(10) == 27); + REQUIRE(x.update(-40) == -13); + x.clear(); + REQUIRE(x.update(17) == 17); } 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); - } - + 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); - } - + REQUIRE(x.update(17) == 17); + REQUIRE(x.update(10) == 27); + REQUIRE(x.update(-15) == 12); } TEST_CASE("delta encode and decode") { - - std::vector a = { 5, -9, 22, 13, 0, 23 }; + const std::vector a = { 5, -9, 22, 13, 0, 23 }; osmium::util::DeltaEncode de; std::vector b; @@ -68,5 +82,6 @@ TEST_CASE("delta encode and decode") { c.push_back(dd.update(x)); } + REQUIRE(a == c); } diff --git a/third_party/libosmium/test/t/util/test_double.cpp b/third_party/libosmium/test/t/util/test_double.cpp index 6cc87a03a..3ffaa93f8 100644 --- a/third_party/libosmium/test/t/util/test_double.cpp +++ b/third_party/libosmium/test/t/util/test_double.cpp @@ -2,32 +2,29 @@ #include -TEST_CASE("Double") { +TEST_CASE("Check double2string function") { + std::string s1; + osmium::util::double2string(s1, 1.123, 7); + REQUIRE(s1 == "1.123"); - SECTION("double2string") { - std::string s1; - osmium::util::double2string(s1, 1.123, 7); - REQUIRE(s1 == "1.123"); + std::string s2; + osmium::util::double2string(s2, 1.000, 7); + REQUIRE(s2 == "1"); - std::string s2; - osmium::util::double2string(s2, 1.000, 7); - REQUIRE(s2 == "1"); + std::string s3; + osmium::util::double2string(s3, 0.0, 7); + REQUIRE(s3 == "0"); - std::string s3; - osmium::util::double2string(s3, 0.0, 7); - REQUIRE(s3 == "0"); + std::string s4; + osmium::util::double2string(s4, 0.020, 7); + REQUIRE(s4 == "0.02"); - std::string s4; - osmium::util::double2string(s4, 0.020, 7); - REQUIRE(s4 == "0.02"); + std::string s5; + osmium::util::double2string(s5, -0.020, 7); + REQUIRE(s5 == "-0.02"); - std::string s5; - osmium::util::double2string(s5, -0.020, 7); - REQUIRE(s5 == "-0.02"); - - std::string s6; - osmium::util::double2string(s6, -0.0, 7); - REQUIRE(s6 == "-0"); - } + std::string s6; + osmium::util::double2string(s6, -0.0, 7); + REQUIRE(s6 == "-0"); } diff --git a/third_party/libosmium/test/t/util/test_file.cpp b/third_party/libosmium/test/t/util/test_file.cpp index 475f28596..a5d65ce6a 100644 --- a/third_party/libosmium/test/t/util/test_file.cpp +++ b/third_party/libosmium/test/t/util/test_file.cpp @@ -1,71 +1,51 @@ #include "catch.hpp" +#include "utils.hpp" +#include #include -#ifdef _WIN32 -#include -// https://msdn.microsoft.com/en-us/library/ksazx244.aspx -// https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx -class DoNothingInvalidParameterHandler { - - static void invalid_parameter_handler( - const wchar_t* expression, - const wchar_t* function, - const wchar_t* file, - unsigned int line, - uintptr_t pReserved - ) { - // do nothing - } - - _invalid_parameter_handler old_handler; - -public: - - DoNothingInvalidParameterHandler() : - old_handler(_set_invalid_parameter_handler(invalid_parameter_handler)) { - _CrtSetReportMode(_CRT_ASSERT, 0); - } - - ~DoNothingInvalidParameterHandler() { - _set_invalid_parameter_handler(old_handler); - } - -}; // class InvalidParameterHandler -#endif - - -TEST_CASE("file_size") { - -#ifdef _WIN32 - DoNothingInvalidParameterHandler handler; -#endif - - SECTION("illegal fd should throw") { - REQUIRE_THROWS_AS(osmium::util::file_size(-1), std::system_error); - } - - SECTION("unused fd should throw") { - // its unlikely that fd 1000 is open... - REQUIRE_THROWS_AS(osmium::util::file_size(1000), std::system_error); - } - +TEST_CASE("file_size(int) and file_offset() of known file") { + std::string file_name{with_data_dir("t/util/known_file_size")}; + const int fd = osmium::io::detail::open_for_reading(file_name); + REQUIRE(fd > 0); + REQUIRE(osmium::util::file_size(fd) == 22); + REQUIRE(osmium::util::file_offset(fd) == 0); + REQUIRE_FALSE(osmium::util::isatty(fd)); } -TEST_CASE("resize_file") { - -#ifdef _WIN32 - DoNothingInvalidParameterHandler handler; -#endif - - SECTION("illegal fd should throw") { - REQUIRE_THROWS_AS(osmium::util::resize_file(-1, 10), std::system_error); - } - - SECTION("unused fd should throw") { - // its unlikely that fd 1000 is open... - REQUIRE_THROWS_AS(osmium::util::resize_file(1000, 10), std::system_error); - } - +TEST_CASE("file_size(std::string) of known file") { + std::string file_name{with_data_dir("t/util/known_file_size")}; + REQUIRE(osmium::util::file_size(file_name) == 22); +} + +TEST_CASE("file_size(const char*) of known file") { + std::string file_name{with_data_dir("t/util/known_file_size")}; + REQUIRE(osmium::util::file_size(file_name.c_str()) == 22); +} + +TEST_CASE("file_size() with illegal fd should throw") { + REQUIRE_THROWS_AS(osmium::util::file_size(-1), const std::system_error&); +} + +TEST_CASE("file_size() with unused fd should throw") { + // its unlikely that fd 1000 is open... + REQUIRE_THROWS_AS(osmium::util::file_size(1000), const std::system_error&); +} + +TEST_CASE("file_size() of unknown file should throw") { + REQUIRE_THROWS_AS(osmium::util::file_size("unknown file"), const std::system_error&); +} + +TEST_CASE("resize_file() with illegal fd should throw") { + REQUIRE_THROWS_AS(osmium::util::resize_file(-1, 10), const std::system_error&); +} + +TEST_CASE("resize_file() with unused fd should throw") { + // its unlikely that fd 1000 is open... + REQUIRE_THROWS_AS(osmium::util::resize_file(1000, 10), const std::system_error&); +} + +TEST_CASE("get_pagesize()") { + REQUIRE(osmium::util::get_pagesize() > 0); } diff --git a/third_party/libosmium/test/t/util/test_memory_mapping.cpp b/third_party/libosmium/test/t/util/test_memory_mapping.cpp index 647d2a05d..7330aaf6f 100644 --- a/third_party/libosmium/test/t/util/test_memory_mapping.cpp +++ b/third_party/libosmium/test/t/util/test_memory_mapping.cpp @@ -1,7 +1,8 @@ #include "catch.hpp" -#include +#include #include +#include #include #include @@ -12,403 +13,398 @@ static const size_t huge = std::numeric_limits::max(); -TEST_CASE("anonymous mapping") { +TEST_CASE("Anonymous mapping: simple memory mapping should work") { + osmium::util::MemoryMapping mapping{1000, osmium::util::MemoryMapping::mapping_mode::write_private}; + REQUIRE(mapping.get_addr() != nullptr); - SECTION("simple memory mapping should work") { - osmium::util::MemoryMapping mapping(1000, osmium::util::MemoryMapping::mapping_mode::write_private); - REQUIRE(mapping.get_addr() != nullptr); + REQUIRE(mapping.size() >= 1000); - REQUIRE(mapping.size() >= 1000); + volatile int* addr = mapping.get_addr(); - volatile int* addr = mapping.get_addr(); + REQUIRE(mapping.writable()); - REQUIRE(mapping.writable()); + *addr = 42; + REQUIRE(*addr == 42); - *addr = 42; - REQUIRE(*addr == 42); + REQUIRE(!!mapping); + mapping.unmap(); + REQUIRE(!mapping); + mapping.unmap(); // second unmap is okay +} - REQUIRE(!!mapping); - mapping.unmap(); - REQUIRE(!mapping); - mapping.unmap(); // second unmap is okay - } +TEST_CASE("Anonymous mapping: memory mapping of zero length should result in memory mapping of pagesize length") { + osmium::util::MemoryMapping mapping{0, osmium::util::MemoryMapping::mapping_mode::write_private}; + REQUIRE(mapping.size() == osmium::util::get_pagesize()); +} - SECTION("memory mapping of zero length should fail") { - REQUIRE_THROWS({ - osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private); - }); - } +TEST_CASE("Anonymous mapping: moving a memory mapping should work") { + osmium::util::MemoryMapping mapping1{1000, osmium::util::MemoryMapping::mapping_mode::write_private}; + int* addr1 = mapping1.get_addr(); + *addr1 = 42; - SECTION("moving a memory mapping should work") { - osmium::util::MemoryMapping mapping1(1000, osmium::util::MemoryMapping::mapping_mode::write_private); - int* addr1 = mapping1.get_addr(); - *addr1 = 42; + REQUIRE(!!mapping1); + osmium::util::MemoryMapping mapping2{std::move(mapping1)}; + REQUIRE(!!mapping2); + REQUIRE(!mapping1); + mapping1.unmap(); - REQUIRE(!!mapping1); - osmium::util::MemoryMapping mapping2(std::move(mapping1)); - REQUIRE(!!mapping2); - REQUIRE(!mapping1); - mapping1.unmap(); + int* addr2 = mapping2.get_addr(); + REQUIRE(*addr2 == 42); - int* addr2 = mapping2.get_addr(); - REQUIRE(*addr2 == 42); + mapping2.unmap(); + REQUIRE(!mapping2); +} - mapping2.unmap(); - REQUIRE(!mapping2); - } +TEST_CASE("Anonymous mapping: move assignment should work") { + osmium::util::MemoryMapping mapping1{1000, osmium::util::MemoryMapping::mapping_mode::write_private}; + osmium::util::MemoryMapping mapping2{1000, osmium::util::MemoryMapping::mapping_mode::write_private}; - SECTION("move assignment should work") { - osmium::util::MemoryMapping mapping1(1000, osmium::util::MemoryMapping::mapping_mode::write_private); - osmium::util::MemoryMapping mapping2(1000, osmium::util::MemoryMapping::mapping_mode::write_private); + REQUIRE(!!mapping1); + REQUIRE(!!mapping2); - REQUIRE(!!mapping1); - REQUIRE(!!mapping2); + int* addr1 = mapping1.get_addr(); + *addr1 = 42; - int* addr1 = mapping1.get_addr(); - *addr1 = 42; + mapping2 = std::move(mapping1); + REQUIRE(!!mapping2); + REQUIRE(!mapping1); - mapping2 = std::move(mapping1); - REQUIRE(!!mapping2); - REQUIRE(!mapping1); + int* addr2 = mapping2.get_addr(); + REQUIRE(*addr2 == 42); - int* addr2 = mapping2.get_addr(); - REQUIRE(*addr2 == 42); - - mapping2.unmap(); - REQUIRE(!mapping2); - } + mapping2.unmap(); + REQUIRE(!mapping2); +} #ifdef __linux__ - SECTION("remapping to larger size should work") { - osmium::util::MemoryMapping mapping(1000, osmium::util::MemoryMapping::mapping_mode::write_private); - REQUIRE(mapping.size() >= 1000); +TEST_CASE("Anonymous mapping: remapping to larger size should work") { + osmium::util::MemoryMapping mapping{1000, osmium::util::MemoryMapping::mapping_mode::write_private}; + REQUIRE(mapping.size() >= 1000); - size_t size1 = mapping.size(); + const size_t size1 = mapping.size(); - int* addr1 = mapping.get_addr(); - *addr1 = 42; + int* addr1 = mapping.get_addr(); + *addr1 = 42; - mapping.resize(8000); - REQUIRE(mapping.size() > size1); + mapping.resize(8000); + REQUIRE(mapping.size() > size1); - int* addr2 = mapping.get_addr(); - REQUIRE(*addr2 == 42); + int* addr2 = mapping.get_addr(); + REQUIRE(*addr2 == 42); +} + +TEST_CASE("Anonymous mapping: remapping to smaller size should work") { + osmium::util::MemoryMapping mapping{8000, osmium::util::MemoryMapping::mapping_mode::write_private}; + REQUIRE(mapping.size() >= 1000); + + const size_t size1 = mapping.size(); + + int* addr1 = mapping.get_addr(); + *addr1 = 42; + + mapping.resize(500); + REQUIRE(mapping.size() < size1); + + int* addr2 = mapping.get_addr(); + REQUIRE(*addr2 == 42); +} +#endif + +TEST_CASE("File-based mapping: writing to a mapped file should work") { + char filename[] = "test_mmap_write_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); + + osmium::util::resize_file(fd, 100); + + { + osmium::util::MemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd}; + REQUIRE(mapping.writable()); + + REQUIRE(!!mapping); + REQUIRE(mapping.size() >= 100); + + *mapping.get_addr() = 1234; + + mapping.unmap(); } - SECTION("remapping to smaller size should work") { - osmium::util::MemoryMapping mapping(8000, osmium::util::MemoryMapping::mapping_mode::write_private); - REQUIRE(mapping.size() >= 1000); + REQUIRE(osmium::util::file_size(fd) == 100); - size_t size1 = mapping.size(); + { + osmium::util::MemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::readonly, fd}; + REQUIRE(!mapping.writable()); + + REQUIRE(!!mapping); + REQUIRE(mapping.size() >= 100); + REQUIRE(*mapping.get_addr() == 1234); + + mapping.unmap(); + } + + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); +} + +TEST_CASE("File-based mapping: Reading from a zero-sized mapped file should work") { + char filename[] = "test_mmap_read_zero_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); + + { + osmium::util::MemoryMapping mapping{0, osmium::util::MemoryMapping::mapping_mode::readonly, fd}; + REQUIRE(mapping.size() > 0); + mapping.unmap(); + } + + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); +} + +TEST_CASE("File-based mapping: writing to a privately mapped file should work") { + char filename[] = "test_mmap_write_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); + + osmium::util::resize_file(fd, 100); + + { + osmium::util::MemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::write_private, fd}; + REQUIRE(mapping.writable()); + + REQUIRE(!!mapping); + REQUIRE(mapping.size() >= 100); + + *mapping.get_addr() = 1234; + + mapping.unmap(); + } + + REQUIRE(osmium::util::file_size(fd) == 100); + + { + osmium::util::MemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::readonly, fd}; + REQUIRE(!mapping.writable()); + + REQUIRE(!!mapping); + REQUIRE(mapping.size() >= 100); + REQUIRE(*mapping.get_addr() == 0); // should not see the value set above + + mapping.unmap(); + } + + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); +} + +TEST_CASE("File-based mapping: remapping to larger size should work") { + char filename[] = "test_mmap_grow_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); + + osmium::util::MemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd}; + REQUIRE(mapping.size() >= 100); + const size_t size1 = mapping.size(); + + int* addr1 = mapping.get_addr(); + *addr1 = 42; + + mapping.resize(8000); + REQUIRE(mapping.size() >= 8000); + REQUIRE(mapping.size() > size1); + + int* addr2 = mapping.get_addr(); + REQUIRE(*addr2 == 42); + + mapping.unmap(); + + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); +} + +TEST_CASE("File-based mapping: remapping to smaller size should work") { + char filename[] = "test_mmap_shrink_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); + + { + osmium::util::MemoryMapping mapping{8000, osmium::util::MemoryMapping::mapping_mode::write_shared, fd}; + REQUIRE(mapping.size() >= 8000); + const size_t size1 = mapping.size(); int* addr1 = mapping.get_addr(); *addr1 = 42; - mapping.resize(500); + mapping.resize(50); + REQUIRE(mapping.size() >= 50); REQUIRE(mapping.size() < size1); int* addr2 = mapping.get_addr(); REQUIRE(*addr2 == 42); } -#endif + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); } -TEST_CASE("file-based mapping") { +TEST_CASE("Typed anonymous mapping: simple memory mapping should work") { + osmium::util::TypedMemoryMapping mapping{1000}; + volatile uint32_t* addr = mapping.begin(); - SECTION("writing to a mapped file should work") { - char filename[] = "test_mmap_write_XXXXXX"; - const int fd = mkstemp(filename); - REQUIRE(fd > 0); + REQUIRE(mapping.writable()); - osmium::util::resize_file(fd, 100); + *addr = 42; + REQUIRE(*addr == 42); - { - osmium::util::MemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd); - REQUIRE(mapping.writable()); + REQUIRE(!!mapping); + mapping.unmap(); + REQUIRE(!mapping); + mapping.unmap(); // second unmap is okay +} - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); +TEST_CASE("Typed anonymous mapping: moving a memory mapping should work") { + osmium::util::TypedMemoryMapping mapping1{1000}; + uint32_t* addr1 = mapping1.begin(); + *addr1 = 42; - *mapping.get_addr() = 1234; + REQUIRE(!!mapping1); + osmium::util::TypedMemoryMapping mapping2{std::move(mapping1)}; + REQUIRE(!!mapping2); + REQUIRE(!mapping1); + mapping1.unmap(); - mapping.unmap(); - } + const auto addr2 = mapping2.begin(); + REQUIRE(*addr2 == 42); - REQUIRE(osmium::util::file_size(fd) == 100); + mapping2.unmap(); + REQUIRE(!mapping2); +} - { - osmium::util::MemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::readonly, fd); - REQUIRE(!mapping.writable()); +TEST_CASE("Typed anonymous mapping: move assignment should work") { + osmium::util::TypedMemoryMapping mapping1{1000}; + osmium::util::TypedMemoryMapping mapping2{1000}; - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); - REQUIRE(*mapping.get_addr() == 1234); + REQUIRE(!!mapping1); + REQUIRE(!!mapping2); - mapping.unmap(); - } + auto addr1 = mapping1.begin(); + *addr1 = 42; - REQUIRE(0 == close(fd)); - REQUIRE(0 == unlink(filename)); - } + mapping2 = std::move(mapping1); + REQUIRE(!!mapping2); + REQUIRE(!mapping1); - SECTION("writing to a privately mapped file should work") { - char filename[] = "test_mmap_write_XXXXXX"; - const int fd = mkstemp(filename); - REQUIRE(fd > 0); + const auto addr2 = mapping2.begin(); + REQUIRE(*addr2 == 42); - osmium::util::resize_file(fd, 100); + mapping2.unmap(); + REQUIRE(!mapping2); +} - { - osmium::util::MemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::write_private, fd); - REQUIRE(mapping.writable()); +#ifdef __linux__ +TEST_CASE("Typed anonymous mapping: remapping to larger size should work") { + osmium::util::TypedMemoryMapping mapping{1000}; + REQUIRE(mapping.size() >= 1000); - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); + auto addr1 = mapping.begin(); + *addr1 = 42; - *mapping.get_addr() = 1234; + mapping.resize(8000); - mapping.unmap(); - } + const auto addr2 = mapping.begin(); + REQUIRE(*addr2 == 42); +} - REQUIRE(osmium::util::file_size(fd) == 100); +TEST_CASE("Typed anonymous mapping: remapping to smaller size should work") { + osmium::util::TypedMemoryMapping mapping{8000}; + REQUIRE(mapping.size() >= 8000); - { - osmium::util::MemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::readonly, fd); - REQUIRE(!mapping.writable()); + auto addr1 = mapping.begin(); + *addr1 = 42; - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); - REQUIRE(*mapping.get_addr() == 0); // should not see the value set above + mapping.resize(500); - mapping.unmap(); - } + const auto addr2 = mapping.begin(); + REQUIRE(*addr2 == 42); +} +#endif - REQUIRE(0 == close(fd)); - REQUIRE(0 == unlink(filename)); - } +TEST_CASE("Typed file-based mapping: writing to a mapped file should work") { + char filename[] = "test_mmap_file_size_XXXXXX"; + const int fd = mkstemp(filename); + REQUIRE(fd > 0); - SECTION("remapping to larger size should work") { - char filename[] = "test_mmap_grow_XXXXXX"; - const int fd = mkstemp(filename); - REQUIRE(fd > 0); + osmium::util::resize_file(fd, 100); - osmium::util::MemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd); + { + osmium::util::TypedMemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd}; + REQUIRE(mapping.writable()); + + REQUIRE(!!mapping); REQUIRE(mapping.size() >= 100); - size_t size1 = mapping.size(); - int* addr1 = mapping.get_addr(); - *addr1 = 42; - - mapping.resize(8000); - REQUIRE(mapping.size() >= 8000); - REQUIRE(mapping.size() > size1); - - int* addr2 = mapping.get_addr(); - REQUIRE(*addr2 == 42); + *mapping.begin() = 1234; mapping.unmap(); - - REQUIRE(0 == close(fd)); - REQUIRE(0 == unlink(filename)); } - SECTION("remapping to smaller size should work") { - char filename[] = "test_mmap_shrink_XXXXXX"; - const int fd = mkstemp(filename); - REQUIRE(fd > 0); - - { - osmium::util::MemoryMapping mapping(8000, osmium::util::MemoryMapping::mapping_mode::write_shared, fd); - REQUIRE(mapping.size() >= 8000); - size_t size1 = mapping.size(); - - int* addr1 = mapping.get_addr(); - *addr1 = 42; - - mapping.resize(50); - REQUIRE(mapping.size() >= 50); - REQUIRE(mapping.size() < size1); - - int* addr2 = mapping.get_addr(); - REQUIRE(*addr2 == 42); - } - - REQUIRE(0 == close(fd)); - REQUIRE(0 == unlink(filename)); - } -} - -TEST_CASE("typed anonymous mapping") { - - SECTION("simple memory mapping should work") { - osmium::util::TypedMemoryMapping mapping(1000); - volatile uint32_t* addr = mapping.begin(); - - REQUIRE(mapping.writable()); - - *addr = 42; - REQUIRE(*addr == 42); + { + osmium::util::TypedMemoryMapping mapping{100, osmium::util::MemoryMapping::mapping_mode::readonly, fd}; + REQUIRE(!mapping.writable()); REQUIRE(!!mapping); + REQUIRE(mapping.size() >= 100); + REQUIRE(*mapping.begin() == 1234); + mapping.unmap(); - REQUIRE(!mapping); - mapping.unmap(); // second unmap is okay } - SECTION("moving a memory mapping should work") { - osmium::util::TypedMemoryMapping mapping1(1000); - uint32_t* addr1 = mapping1.begin(); - *addr1 = 42; + REQUIRE(0 == close(fd)); + REQUIRE(0 == unlink(filename)); +} - REQUIRE(!!mapping1); - osmium::util::TypedMemoryMapping mapping2(std::move(mapping1)); - REQUIRE(!!mapping2); - REQUIRE(!mapping1); - mapping1.unmap(); +TEST_CASE("Anonymous memory mapping class: simple memory mapping should work") { + osmium::util::AnonymousMemoryMapping mapping{1000}; + REQUIRE(mapping.get_addr() != nullptr); - auto addr2 = mapping2.begin(); - REQUIRE(*addr2 == 42); + volatile int* addr = mapping.get_addr(); - mapping2.unmap(); - REQUIRE(!mapping2); - } + REQUIRE(mapping.writable()); - SECTION("move assignment should work") { - osmium::util::TypedMemoryMapping mapping1(1000); - osmium::util::TypedMemoryMapping mapping2(1000); + *addr = 42; + REQUIRE(*addr == 42); - REQUIRE(!!mapping1); - REQUIRE(!!mapping2); - - auto addr1 = mapping1.begin(); - *addr1 = 42; - - mapping2 = std::move(mapping1); - REQUIRE(!!mapping2); - REQUIRE(!mapping1); - - auto addr2 = mapping2.begin(); - REQUIRE(*addr2 == 42); - - mapping2.unmap(); - REQUIRE(!mapping2); - } + REQUIRE(!!mapping); + mapping.unmap(); + REQUIRE(!mapping); + mapping.unmap(); // second unmap is okay +} #ifdef __linux__ - SECTION("remapping to larger size should work") { - osmium::util::TypedMemoryMapping mapping(1000); - REQUIRE(mapping.size() >= 1000); +TEST_CASE("Anonymous memory mapping class: remapping to larger size should work") { + osmium::util::AnonymousMemoryMapping mapping{1000}; + REQUIRE(mapping.size() >= 1000); - auto addr1 = mapping.begin(); - *addr1 = 42; + int* addr1 = mapping.get_addr(); + *addr1 = 42; - mapping.resize(8000); + mapping.resize(2000); - auto addr2 = mapping.begin(); - REQUIRE(*addr2 == 42); - } + const int* addr2 = mapping.get_addr(); + REQUIRE(*addr2 == 42); +} - SECTION("remapping to smaller size should work") { - osmium::util::TypedMemoryMapping mapping(8000); - REQUIRE(mapping.size() >= 8000); +TEST_CASE("Anonymous memory mapping class: remapping to smaller size should work") { + osmium::util::AnonymousMemoryMapping mapping{2000}; + REQUIRE(mapping.size() >= 2000); - auto addr1 = mapping.begin(); - *addr1 = 42; + int* addr1 = mapping.get_addr(); + *addr1 = 42; - mapping.resize(500); + mapping.resize(500); - auto addr2 = mapping.begin(); - REQUIRE(*addr2 == 42); - } + const int* addr2 = mapping.get_addr(); + REQUIRE(*addr2 == 42); +} #endif -} - -TEST_CASE("typed file-based mapping") { - - SECTION("writing to a mapped file should work") { - char filename[] = "test_mmap_file_size_XXXXXX"; - const int fd = mkstemp(filename); - REQUIRE(fd > 0); - - osmium::util::resize_file(fd, 100); - - { - osmium::util::TypedMemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::write_shared, fd); - REQUIRE(mapping.writable()); - - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); - - *mapping.begin() = 1234; - - mapping.unmap(); - } - - { - osmium::util::TypedMemoryMapping mapping(100, osmium::util::MemoryMapping::mapping_mode::readonly, fd); - REQUIRE(!mapping.writable()); - - REQUIRE(!!mapping); - REQUIRE(mapping.size() >= 100); - REQUIRE(*mapping.begin() == 1234); - - mapping.unmap(); - } - - REQUIRE(0 == close(fd)); - REQUIRE(0 == unlink(filename)); - } - -} - -TEST_CASE("anonymous memory mapping class") { - - SECTION("simple memory mapping should work") { - osmium::util::AnonymousMemoryMapping mapping(1000); - REQUIRE(mapping.get_addr() != nullptr); - - volatile int* addr = mapping.get_addr(); - - REQUIRE(mapping.writable()); - - *addr = 42; - REQUIRE(*addr == 42); - - REQUIRE(!!mapping); - mapping.unmap(); - REQUIRE(!mapping); - mapping.unmap(); // second unmap is okay - } - -#ifdef __linux__ - SECTION("remapping to larger size should work") { - osmium::util::AnonymousMemoryMapping mapping(1000); - REQUIRE(mapping.size() >= 1000); - - int* addr1 = mapping.get_addr(); - *addr1 = 42; - - mapping.resize(2000); - - int* addr2 = mapping.get_addr(); - REQUIRE(*addr2 == 42); - } - - SECTION("remapping to smaller size should work") { - osmium::util::AnonymousMemoryMapping mapping(2000); - REQUIRE(mapping.size() >= 2000); - - int* addr1 = mapping.get_addr(); - *addr1 = 42; - - mapping.resize(500); - - int* addr2 = mapping.get_addr(); - REQUIRE(*addr2 == 42); - } -#endif - -} - diff --git a/third_party/libosmium/test/t/util/test_minmax.cpp b/third_party/libosmium/test/t/util/test_minmax.cpp index 8b40f856e..f84d4e4ba 100644 --- a/third_party/libosmium/test/t/util/test_minmax.cpp +++ b/third_party/libosmium/test/t/util/test_minmax.cpp @@ -3,66 +3,57 @@ #include #include -TEST_CASE("minmax numeric") { +TEST_CASE("min_op numeric") { + osmium::min_op x; + REQUIRE(x() == std::numeric_limits::max()); - SECTION("min") { - osmium::min_op x; - REQUIRE(x() == std::numeric_limits::max()); + x.update(17); + REQUIRE(x() == 17); - x.update(17); - REQUIRE(x() == 17); - - x.update(10); - REQUIRE(x() == 10); - - x.update(22); - REQUIRE(x() == 10); - } - - SECTION("max") { - osmium::max_op x; - REQUIRE(x() == 0); - - x.update(17); - REQUIRE(x() == 17); - - x.update(10); - REQUIRE(x() == 17); - - x.update(22); - REQUIRE(x() == 22); - } + x.update(10); + REQUIRE(x() == 10); + x.update(22); + REQUIRE(x() == 10); } -TEST_CASE("minmax timestamp") { +TEST_CASE("max_op numeric") { + osmium::max_op x; + REQUIRE(x() == 0); - SECTION("min") { - osmium::min_op x; + x.update(17); + REQUIRE(x() == 17); - x.update(osmium::Timestamp("2010-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); - - x.update(osmium::Timestamp("2015-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); - - x.update(osmium::Timestamp("2000-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2000-01-01T00:00:00Z"); - } - - SECTION("max") { - osmium::max_op x; - - x.update(osmium::Timestamp("2010-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); - - x.update(osmium::Timestamp("2015-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z"); - - x.update(osmium::Timestamp("2000-01-01T00:00:00Z")); - REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z"); - - } + x.update(10); + REQUIRE(x() == 17); + x.update(22); + REQUIRE(x() == 22); +} + +TEST_CASE("min_op timestamp") { + osmium::min_op x; + + x.update(osmium::Timestamp("2010-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); + + x.update(osmium::Timestamp("2015-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); + + x.update(osmium::Timestamp("2000-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2000-01-01T00:00:00Z"); +} + +TEST_CASE("max_op timestamp") { + osmium::max_op x; + + x.update(osmium::Timestamp("2010-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z"); + + x.update(osmium::Timestamp("2015-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z"); + + x.update(osmium::Timestamp("2000-01-01T00:00:00Z")); + REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z"); } diff --git a/third_party/libosmium/test/t/util/test_options.cpp b/third_party/libosmium/test/t/util/test_options.cpp index 8cba0950b..618fa8949 100644 --- a/third_party/libosmium/test/t/util/test_options.cpp +++ b/third_party/libosmium/test/t/util/test_options.cpp @@ -1,65 +1,64 @@ #include "catch.hpp" -#include - #include -TEST_CASE("Options") { - +TEST_CASE("Set a single option value from string") { 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")); + 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_FALSE(o.is_true("foo")); + REQUIRE_FALSE(o.is_true("empty")); - REQUIRE(o.is_not_false("foo")); - REQUIRE(o.is_not_false("empty")); + REQUIRE(o.is_not_false("foo")); + REQUIRE(o.is_not_false("empty")); - REQUIRE(1 == o.size()); - } + REQUIRE(1 == o.size()); +} - 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")); +TEST_CASE("Set option values from booleans") { + osmium::util::Options o; - REQUIRE(o.is_true("t")); - REQUIRE(!o.is_true("f")); + 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_not_false("t")); - REQUIRE(!o.is_not_false("f")); + REQUIRE(o.is_true("t")); + REQUIRE_FALSE(o.is_true("f")); - REQUIRE(2 == o.size()); - } + REQUIRE(o.is_not_false("t")); + REQUIRE_FALSE(o.is_not_false("f")); - SECTION("set value from string with equal sign") { - o.set("foo=bar"); - REQUIRE("bar" == o.get("foo")); - REQUIRE(1 == o.size()); - } + REQUIRE(2 == o.size()); +} - SECTION("set value from string without equal sign") { - o.set("foo"); - REQUIRE("true" == o.get("foo")); +TEST_CASE("Set option value from string with equal sign") { + osmium::util::Options o; - REQUIRE(o.is_true("foo")); - REQUIRE(o.is_not_false("foo")); + o.set("foo=bar"); + REQUIRE("bar" == o.get("foo")); + REQUIRE(1 == o.size()); +} - REQUIRE(1 == o.size()); - } +TEST_CASE("Set option value from string without equal sign") { + osmium::util::Options o; + 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" } }; + osmium::util::Options o{{ "foo", "true" }, { "bar", "17" }}; REQUIRE(o.get("foo") == "true"); REQUIRE(o.get("bar") == "17"); @@ -79,3 +78,41 @@ TEST_CASE("Options with initializer list") { } } +TEST_CASE("Iterating over options") { + /*not const*/ osmium::util::Options o{{ "foo", "true" }, { "bar", "17" }}; + + auto it = o.begin(); + REQUIRE(it->first == "bar"); + REQUIRE(it->second == "17"); + ++it; + REQUIRE(it->first == "foo"); + REQUIRE(it->second == "true"); + ++it; + REQUIRE(it == o.end()); +} + +TEST_CASE("Const iterating over options") { + const osmium::util::Options o{{ "foo", "true" }, { "bar", "17" }}; + + SECTION("begin/end") { + auto it = o.begin(); + REQUIRE(it->first == "bar"); + REQUIRE(it->second == "17"); + ++it; + REQUIRE(it->first == "foo"); + REQUIRE(it->second == "true"); + ++it; + REQUIRE(it == o.end()); + } + SECTION("cbegin/cend") { + auto it = o.cbegin(); + REQUIRE(it->first == "bar"); + REQUIRE(it->second == "17"); + ++it; + REQUIRE(it->first == "foo"); + REQUIRE(it->second == "true"); + ++it; + REQUIRE(it == o.cend()); + } +} + diff --git a/third_party/libosmium/test/t/util/test_string.cpp b/third_party/libosmium/test/t/util/test_string.cpp index 0960afe9a..95e0e9a35 100644 --- a/third_party/libosmium/test/t/util/test_string.cpp +++ b/third_party/libosmium/test/t/util/test_string.cpp @@ -2,67 +2,84 @@ #include -TEST_CASE("split_string") { - - SECTION("split_string string") { - std::string str { "foo,baramba,baz" }; - std::vector result = {"foo", "baramba", "baz"}; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(result == osmium::split_string(str, ',', true)); - } - - SECTION("split_string string without sep") { - std::string str { "foo" }; - std::vector result = {"foo"}; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(result == osmium::split_string(str, ',', true)); - } - - SECTION("split_string string with empty at end") { - std::string str { "foo,bar," }; - std::vector result = {"foo", "bar", ""}; - std::vector resultc = {"foo", "bar"}; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(resultc == osmium::split_string(str, ',', true)); - } - - SECTION("split_string string with empty in middle") { - std::string str { "foo,,bar" }; - std::vector result = {"foo", "", "bar"}; - std::vector resultc = {"foo", "bar"}; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(resultc == osmium::split_string(str, ',', true)); - } - - SECTION("split_string string with empty at start") { - std::string str { ",bar,baz" }; - std::vector result = {"", "bar", "baz"}; - std::vector resultc = {"bar", "baz"}; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(resultc == osmium::split_string(str, ',', true)); - } - - SECTION("split_string sep") { - std::string str { "," }; - std::vector result = {"", ""}; - std::vector resultc; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(resultc == osmium::split_string(str, ',', true)); - } - - SECTION("split_string empty string") { - std::string str { "" }; - std::vector result; - - REQUIRE(result == osmium::split_string(str, ',')); - REQUIRE(result == osmium::split_string(str, ',', true)); - } +TEST_CASE("split_string string") { + const std::string str{"foo,baramba,baz"}; + const std::vector result = {"foo", "baramba", "baz"}; + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(result == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ",;")); + REQUIRE(result == osmium::split_string(str, ",;", true)); +} + +TEST_CASE("split_string string without sep") { + const std::string str{"foo"}; + const std::vector result = {"foo"}; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(result == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ",;")); + REQUIRE(result == osmium::split_string(str, ",;", true)); +} + +TEST_CASE("split_string string with empty at end") { + const std::string str{"foo,bar,"}; + const std::vector result = {"foo", "bar", ""}; + const std::vector resultc = {"foo", "bar"}; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(resultc == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ";,")); + REQUIRE(resultc == osmium::split_string(str, ";,", true)); +} + +TEST_CASE("split_string string with empty in middle") { + const std::string str{"foo,,bar"}; + const std::vector result = {"foo", "", "bar"}; + const std::vector resultc = {"foo", "bar"}; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(resultc == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ",;")); + REQUIRE(resultc == osmium::split_string(str, ";,", true)); +} + +TEST_CASE("split_string string with empty at start") { + const std::string str{",bar,baz"}; + const std::vector result = {"", "bar", "baz"}; + const std::vector resultc = {"bar", "baz"}; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(resultc == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ";,")); + REQUIRE(resultc == osmium::split_string(str, ",;", true)); +} + +TEST_CASE("split_string sep") { + const std::string str{","}; + const std::vector result = {"", ""}; + const std::vector resultc; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(resultc == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ",;")); + REQUIRE(resultc == osmium::split_string(str, ",;", true)); +} + +TEST_CASE("split_string empty string") { + const std::string str{""}; + const std::vector result; + + REQUIRE(result == osmium::split_string(str, ',')); + REQUIRE(result == osmium::split_string(str, ',', true)); + REQUIRE(result == osmium::split_string(str, ",;")); + REQUIRE(result == osmium::split_string(str, ",;", true)); +} + +TEST_CASE("split_string string with multiple sep characters") { + const std::string str{"foo,bar;baz-,blub"}; + const std::vector result = {"foo", "bar", "baz", "", "blub"}; + + REQUIRE(result == osmium::split_string(str, ";,-")); } diff --git a/third_party/libosmium/test/t/util/test_string_matcher.cpp b/third_party/libosmium/test/t/util/test_string_matcher.cpp new file mode 100644 index 000000000..64b2f2806 --- /dev/null +++ b/third_party/libosmium/test/t/util/test_string_matcher.cpp @@ -0,0 +1,189 @@ +#include "catch.hpp" + +#include +#include + +#include + +static_assert(std::is_default_constructible::value, "StringMatcher should be default constructible"); +static_assert(std::is_copy_constructible::value, "StringMatcher should be copy constructible"); +static_assert(std::is_move_constructible::value, "StringMatcher should be move constructible"); +static_assert(std::is_copy_assignable::value, "StringMatcher should be copyable"); +static_assert(std::is_move_assignable::value, "StringMatcher should be moveable"); + +template +std::string print(const T& matcher) { + std::stringstream ss; + ss << matcher; + return ss.str(); +} + +TEST_CASE("String matcher: always false") { + osmium::StringMatcher::always_false m; + REQUIRE_FALSE(m.match("foo")); +} + +TEST_CASE("String matcher: always true") { + osmium::StringMatcher::always_true m; + REQUIRE(m.match("foo")); +} + +TEST_CASE("String matcher: equal") { + osmium::StringMatcher::equal m{"foo"}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE_FALSE(m.match("foobar")); +} + +TEST_CASE("String matcher: prefix from const char*") { + osmium::StringMatcher::prefix m{"foo"}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE_FALSE(m.match("")); +} + +TEST_CASE("String matcher: prefix from std::string") { + const std::string v{"foo"}; + osmium::StringMatcher::prefix m{v}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE_FALSE(m.match("")); +} + +TEST_CASE("String matcher: substring from const char*") { + osmium::StringMatcher::substring m{"foo"}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE(m.match("barfoo")); + REQUIRE(m.match("xfoox")); +} + +TEST_CASE("String matcher: substring from std::string") { + const std::string v{"foo"}; + osmium::StringMatcher::substring m{v}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE(m.match("barfoo")); + REQUIRE(m.match("xfoox")); +} + +TEST_CASE("String matcher: empty prefix") { + osmium::StringMatcher::prefix m{""}; + REQUIRE(m.match("foo")); + REQUIRE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE(m.match("")); +} + +#ifdef OSMIUM_WITH_REGEX +TEST_CASE("String matcher: regex prefix") { + osmium::StringMatcher::regex m{std::regex{"^foo", std::regex::optimize}}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE_FALSE(m.match("")); +} + +TEST_CASE("String matcher: regex substr") { + osmium::StringMatcher::regex m{std::regex{"foo", std::regex::optimize}}; + REQUIRE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + REQUIRE(m.match("foobar")); + REQUIRE(m.match("xfoox")); + REQUIRE_FALSE(m.match("")); +} +#endif + +TEST_CASE("String matcher: list") { + osmium::StringMatcher::list m{{"foo", "bar"}}; + REQUIRE(m.match("foo")); + REQUIRE(m.match("bar")); + REQUIRE_FALSE(m.match("foobar")); + REQUIRE_FALSE(m.match("baz")); + REQUIRE_FALSE(m.match("")); +} + +TEST_CASE("String matcher: list with add") { + osmium::StringMatcher::list m; + REQUIRE_FALSE(m.match("foo")); + REQUIRE_FALSE(m.match("bar")); + m.add_string("foo"); + REQUIRE(m.match("foo")); + m.add_string(std::string{"bar"}); + REQUIRE(m.match("bar")); + REQUIRE_FALSE(m.match("foobar")); + REQUIRE_FALSE(m.match("baz")); + REQUIRE_FALSE(m.match("")); +} + +TEST_CASE("Default constructed StringMatcher matches nothing") { + osmium::StringMatcher m; + REQUIRE_FALSE(m("foo")); + REQUIRE_FALSE(m("bar")); + REQUIRE(print(m) == "always_false"); +} + +TEST_CASE("Construct StringMatcher from bool") { + osmium::StringMatcher m1{false}; + REQUIRE_FALSE(m1("foo")); + REQUIRE_FALSE(m1("bar")); + REQUIRE(print(m1) == "always_false"); + + osmium::StringMatcher m2{true}; + REQUIRE(m2("foo")); + REQUIRE(m2("bar")); + REQUIRE(print(m2) == "always_true"); +} + +TEST_CASE("Construct StringMatcher from string") { + osmium::StringMatcher m{"foo"}; + REQUIRE(m("foo")); + REQUIRE_FALSE(m("bar")); + REQUIRE(print(m) == "equal[foo]"); +} + +#ifdef OSMIUM_WITH_REGEX +TEST_CASE("Construct StringMatcher from regex") { + osmium::StringMatcher m{std::regex{"^foo"}}; + REQUIRE(m("foo")); + REQUIRE_FALSE(m("bar")); + REQUIRE(print(m) == "regex"); +} +#endif + +TEST_CASE("Construct StringMatcher from list") { + std::vector v{"foo", "xxx"}; + osmium::StringMatcher m{v}; + REQUIRE(m("foo")); + REQUIRE_FALSE(m("bar")); + REQUIRE(print(m) == "list[[foo][xxx]]"); +} + +TEST_CASE("Construct StringMatcher") { + osmium::StringMatcher m{osmium::StringMatcher::equal{"foo"}}; + REQUIRE(print(m) == "equal[foo]"); + REQUIRE(m("foo")); + REQUIRE_FALSE(m("bar")); + + m = osmium::StringMatcher::list{{"foo", "bar"}}; + REQUIRE(m("foo")); + REQUIRE(m(std::string{"bar"})); + REQUIRE(print(m) == "list[[foo][bar]]"); + + m = osmium::StringMatcher::prefix{"foo"}; + REQUIRE(m("foo")); + REQUIRE(m("foobar")); + REQUIRE_FALSE(m("barfoo")); + REQUIRE(print(m) == "prefix[foo]"); + + m = osmium::StringMatcher::substring{"foo"}; + REQUIRE(m("foo")); + REQUIRE(m("foobar")); + REQUIRE(m(std::string{"barfoo"})); + REQUIRE(print(m) == "substring[foo]"); +} + diff --git a/third_party/libosmium/test/t/util/test_timer_disabled.cpp b/third_party/libosmium/test/t/util/test_timer_disabled.cpp new file mode 100644 index 000000000..2bf01ffd7 --- /dev/null +++ b/third_party/libosmium/test/t/util/test_timer_disabled.cpp @@ -0,0 +1,15 @@ +#include "catch.hpp" + +#include +#include + +#include + +TEST_CASE("timer") { + osmium::Timer timer; + timer.start(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + timer.stop(); + REQUIRE(timer.elapsed_microseconds() == 0); +} + diff --git a/third_party/libosmium/test/t/util/test_timer_enabled.cpp b/third_party/libosmium/test/t/util/test_timer_enabled.cpp new file mode 100644 index 000000000..c1fb66537 --- /dev/null +++ b/third_party/libosmium/test/t/util/test_timer_enabled.cpp @@ -0,0 +1,16 @@ +#include "catch.hpp" + +#include +#include + +#define OSMIUM_WITH_TIMER +#include + +TEST_CASE("timer") { + osmium::Timer timer; + timer.start(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + timer.stop(); + REQUIRE(timer.elapsed_microseconds() > 900); +} +