diff --git a/third_party/libosmium/.gitignore b/third_party/libosmium/.gitignore index 50139035b7c..79e5e65d5c0 100644 --- a/third_party/libosmium/.gitignore +++ b/third_party/libosmium/.gitignore @@ -1,2 +1,4 @@ *.swp .ycm_extra_conf.pyc +/build +/libosmium-deps diff --git a/third_party/libosmium/.travis.yml b/third_party/libosmium/.travis.yml index ac0d270e2a4..9918112603e 100644 --- a/third_party/libosmium/.travis.yml +++ b/third_party/libosmium/.travis.yml @@ -4,24 +4,34 @@ # #----------------------------------------------------------------------------- -language: cpp +language: generic sudo: false +cache: + directories: + - $HOME/.ccache + +env: + global: + - CCACHE_TEMPDIR=/tmp/.ccache-temp + - CCACHE_COMPRESS=1 + - CASHER_TIME_OUT=1000 + matrix: include: # 1/ Linux Clang Builds - os: linux - compiler: clang + compiler: linux-clang35-release addons: apt: sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + packages: ['clang-3.5', 'cmake', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] env: COMPILER='clang++-3.5' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang35-dev addons: apt: sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] @@ -30,42 +40,42 @@ matrix: - os: linux - compiler: clang + compiler: linux-clang37-release addons: apt: - sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.6' BUILD_TYPE='Release' + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang37-dev addons: apt: - sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.6' BUILD_TYPE='Dev' + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' - os: linux - compiler: clang + compiler: linux-clang38-release addons: apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Release' + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.8' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang38-dev addons: apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.8' BUILD_TYPE='Dev' # 2/ Linux GCC Builds - os: linux - compiler: gcc + compiler: linux-gcc48-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -73,7 +83,7 @@ matrix: env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc48-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -82,7 +92,7 @@ matrix: - os: linux - compiler: gcc + compiler: linux-gcc49-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -90,7 +100,7 @@ matrix: env: COMPILER='g++-4.9' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc49-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -99,7 +109,7 @@ matrix: - os: linux - compiler: gcc + compiler: linux-gcc50-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -107,7 +117,7 @@ matrix: env: COMPILER='g++-5' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc50-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -118,24 +128,24 @@ matrix: # 3/ OSX Clang Builds - os: osx osx_image: xcode6.4 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Dev' + compiler: xcode64-clang-release + env: COMPILER='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode6.4 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Release' + compiler: xcode64-clang-dev + env: COMPILER='clang++' BUILD_TYPE='Dev' - os: osx osx_image: xcode7 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Dev' + compiler: xcode7-clang-release + env: COMPILER='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode7 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Release' + compiler: xcode7-clang-dev + env: COMPILER='clang++' BUILD_TYPE='Dev' install: @@ -145,15 +155,15 @@ install: - | if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew remove gdal - brew install cmake boost google-sparsehash gdal + brew install cmake boost google-sparsehash gdal || true fi + - cmake --version before_script: - cd ${TRAVIS_BUILD_DIR} - mkdir build && cd build - - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" + - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_WITH_CCACHE=1 -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" script: - - make VERBOSE=1 - - ctest --output-on-failure + - make VERBOSE=1 && ctest --output-on-failure diff --git a/third_party/libosmium/.ycm_extra_conf.py b/third_party/libosmium/.ycm_extra_conf.py index 2b8730616a0..6fc19376d5e 100644 --- a/third_party/libosmium/.ycm_extra_conf.py +++ b/third_party/libosmium/.ycm_extra_conf.py @@ -29,6 +29,11 @@ '-x', 'c++', +# workaround for https://github.com/Valloric/YouCompleteMe/issues/303 +# also see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800618 +'-isystem', +'/usr/lib/ycmd/clang_includes/', + # libosmium include dirs '-I%s/include' % basedir, '-I%s/test/include' % basedir, diff --git a/third_party/libosmium/CHANGELOG.md b/third_party/libosmium/CHANGELOG.md index 394644890e9..0d132ef5916 100644 --- a/third_party/libosmium/CHANGELOG.md +++ b/third_party/libosmium/CHANGELOG.md @@ -13,6 +13,140 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [2.9.0] - 2016-09-15 + +### Added + +- Support for reading OPL files. +- For diff output OSM objects in buffers can be marked as only in one or the + other file. The OPL and debug output formats support diff output based on + this. +- Add documentation and range checks to `Tile` struct. +- More documentation. +- More examples and more extensive comments on examples. +- Support for a progress report in `osmium::io::Reader()` and a `ProgressBar` + utility class to use it. +- New `OSMObject::set_timestamp(const char*)` function. + +### Changed + +- Parse coordinates in scientific notations ourselves. +- Updated included protozero version to 1.4.2. +- Lots of one-argument constructors are now explicit. +- Timestamp parser now uses our own implementation instead of strptime. + This is faster and independant of locale settings. +- More cases of invalid areas with duplicate segments are reported as + errors. + +### Fixed + +- Fixed a problem limiting cache file sizes on Windows to 32 bit. +- Fixed includes. +- Exception messages for invalid areas do not report "area contains no rings" + any more, but "invalid area". + + +## [2.8.0] - 2016-08-04 + +### Added + +- EWKT support. +- Track `pop` type calls and queue underruns when `OSMIUM_DEBUG_QUEUE_SIZE` + environment variable is set. + +### Changed + +- Switched to newest protozero v1.4.0. This should deliver some speedups + when parsing PBF files. This also removes the DeltaEncodeIterator class, + which isn't needed any more. +- Uses `std::unordered_map` instead of `std::map` in PBF string table code + speeding up writing of PBF files considerably. +- Uses less memory when writing PBF files (smaller string table by default). +- Removes dependency on sparsehash and boost program options libraries for + examples. +- Cleaned up threaded queue code. + +### Fixed + +- A potentially very bad bug was fixed: When there are many and/or long strings + in tag keys and values and/or user names and/or relation roles, the string + table inside a PBF block would overflow. I have never seen this happen for + normal OSM data, but that doesn't mean it can't happen. The result is that + the strings will all be mixed up, keys for values, values for user names or + whatever. +- Automatically set correct SRID when creating WKB and GEOS geometries. + Note that this changes the behaviour of libosmium when creating GEOS + geometries. Before we created them with -1 as SRID unless set otherwise. + Manual setting of the SRID on the GEOSGeometryFactory is now deprecated. +- Allow coordinates of nodes in scientific notation when reading XML files. + This shouldn't be used really, but sometimes you can find them. + + +## [2.7.2] - 2016-06-08 + +### Changed + +- Much faster output of OSM files in XML, OPL, or debug formats. + +### Fixed + +- Parsing and output of coordinates now faster and always uses decimal dot + independant of locale setting. +- Do not output empty discussion elements in changeset XML output. +- Data corruption regression in mmap based indexes. + + +## [2.7.1] - 2016-06-01 + +### Fixes + +- Update version number in version.hpp. + + +## [2.7.0] - 2016-06-01 + +### Added + +- New functions for iterating over specific item types in buffers + (`osmium::memory::Buffer::select()`), over specific subitems + (`osmium::OSMObject::subitems()`), and for iterating over all rings of + an area (`osmium::Areas::outer_rings(`), `inner_rings()`). +- Debug output optionally prints CRC32 when `add_crc32` file option is set. + +### Changed + +- XML parser will not allow any XML entities which are usually not used in OSM + files anyway. This can help avoiding DOS attacks. +- Removed SortedQueue implementation which was never used. +- Also incorporate Locations in NodeRefs into CRC32 checksums. This means + all checksums will be different compared to earlier versions of libosmium. +- The completely new algorithm for assembling multipolygons is much faster, + has better error reporting, generates statistics and can build more complex + multipolygons correctly. The ProblemReporter classes have changed to make + this happen, if you have written your own, you have to fix it. +- Sparse node location stores are now only sorted if needed, ie. when nodes + come in unordered. + +### Fixed + +- Output operator for Location shows full precision. +- Undefined behaviour in WKB writer and `types_from_string()` function. +- Fix unsigned overflow in pool.hpp. +- OSM objects are now ordered by type (nodes, then ways, then relations), + then ID, then version, then timestamp. Ordering by timestamp is normally + not necessary, because there can't be two objects with same type, ID, and + version but different timestamp. But this can happen when diffs are + created from OSM extracts, so we check for this here. This change also + makes sure IDs are always ordered by absolute IDs, positives first, so + order is 0, 1, -1, 2, -2, ... +- Data corruption bug fixed in disk based indexes (used for the node + location store for instance). This only affected you, if you created + and index, closed it, and re-opened it (possibly in a different process) + and if there were missing nodes. If you looked up those nodes, you got + location (0,0) back instead of an error. +- Memory corruption bug showing up with GDAL 2. + + ## [2.6.1] - 2016-02-22 ### Added @@ -276,7 +410,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.6.1...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.9.0...HEAD +[2.9.0]: https://github.com/osmcode/libosmium/compare/v2.8.0...v2.9.0 +[2.8.0]: https://github.com/osmcode/libosmium/compare/v2.7.2...v2.8.0 +[2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2 +[2.7.1]: https://github.com/osmcode/libosmium/compare/v2.7.0...v2.7.1 +[2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0 [2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1 [2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0 [2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4 diff --git a/third_party/libosmium/CMakeLists.txt b/third_party/libosmium/CMakeLists.txt index bc9b12ea5d8..095137b3975 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 6) -set(LIBOSMIUM_VERSION_PATCH 1) +set(LIBOSMIUM_VERSION_MINOR 9) +set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") @@ -61,6 +61,27 @@ option(INSTALL_UTFCPP "also install utfcpp headers" OFF) option(WITH_PROFILING "add flags needed for profiling" OFF) +#----------------------------------------------------------------------------- +# +# CCache support +# +#----------------------------------------------------------------------------- + +option(BUILD_WITH_CCACHE "build using ccache" OFF) + +if(BUILD_WITH_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "CCACHE_CPP2=1 ${CCACHE_PROGRAM}") + + # workaround for some clang versions + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_definitions(-Qunused-arguments) + endif() + endif() +endif() + + #----------------------------------------------------------------------------- # # Coverage support diff --git a/third_party/libosmium/CONTRIBUTING.md b/third_party/libosmium/CONTRIBUTING.md index 1064b94de98..a054f362e6f 100644 --- a/third_party/libosmium/CONTRIBUTING.md +++ b/third_party/libosmium/CONTRIBUTING.md @@ -1,143 +1,12 @@ -# Notes for Developers +Some rules for contributing to this project: -Read this if you want to contribute to Libosmium. +* Please open a separate issue for each problem, question, or comment you have. + Do not re-use existing issues for other topics, even if they are similar. This + keeps issues small and manageable and makes it much easier to follow through + and make sure each problem is taken care of. - -## Versioning - -Osmium is currently considered in beta and doesn't use versioning yet. Proper -versions will be introduced as soon as it is somewhat stable. - - -## Namespace - -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. - -One drawback ist that you can't have static data in classes, because there -is no place to put this data. - -All free functions must be declared `inline`. - - -## Coding Conventions - -These coding conventions have been changing over time and some code is still -different. - -* All include files have `#ifdef` guards around them, macros are the path name - in all uppercase where the slashes (`/`) have been changed to underscore (`_`). -* Class names begin with uppercase chars and use CamelCase. Smaller helper - classes are usually defined as struct and have lowercase names. -* Macros (and only macros) are all uppercase. Use macros sparingly, usually - a simple (maybe constexpr) inline function is better. Undef macros after use - if possible. -* Macros should only be used for controlling which parts of the code should be - included when compiling or to avoid major code repetitions. -* Variables, attributes, and function names are lowercase with - `underscores_between_words`. -* Class attribute names start with `m_` (member). -* Use `descriptive_variable_names`, exceptions are well-established conventions - like `i` for a loop variable. Iterators are usually called `it`. -* Declare variables where they are first used (C++ style), not at the beginning - of a function (old C style). -* Names from external namespaces (even `std`) are always mentioned explicitly. - Do not use `using` (except for `std::swap`). This way we can't even by - accident pollute the namespace of the code using Osmium. -* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`. -* `#include` directives appear in three "blocks" after the copyright notice. - The blocks are separated by blank lines. First block contains `#include`s for - standard C/C++ includes, second block for any external libs used, third - block for osmium internal includes. Within each block `#include`s are usually - sorted by path name. All `#include`s use `<>` syntax not `""`. -* Names not to be used from outside the library should be in a namespace - called `detail` under the namespace where they would otherwise appear. If - whole include files are never meant to be included from outside they should - be in a subdirectory called `detail`. -* All files have suffix `.hpp`. -* Closing } of all classes and namespaces should have a trailing comment - with the name of the class/namespace. -* All constructors with one (or more arguments if they have a default) should - be declared "explicit" unless there is a reason for them not to be. Document - that reason. -* If a class has any of the special methods (copy/move constructor/assigment, - destructor) it should have all of them, possibly marking them as default or - deleted. -* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should - use the new `using foo_type = bar` syntax instead of the old - `typedef bar foo_type`. -* Template parameters are single uppercase letters or start with uppercase `T` - and use CamelCase. -* Always use `typename` in templates, not `class`: `template `. -* The ellipsis in variadic template never has a space to the left of it and - always has a space to the right: `template ` etc. - -Keep to the indentation and other styles used in the code. Use `make indent` -in the toplevel directory to fix indentation and styling. It calls `astyle` -with the right parameters. This program is in the `astyle` Debian package. - - -## C++11 - -Osmium uses C++11 and you can use its features such as auto, lambdas, -threading, etc. There are a few features we do not use, because even modern -compilers don't support them yet. This list might change as we get more data -about which compilers support which feature and what operating system versions -or distributions have which versions of these compilers installed. - -GCC 4.6 - too old, not supported (Ubuntu 12.04 LTS) -GCC 4.7.2 - can probably not be supported (Debian wheezy) -GCC 4.7.3 - probably works -GCC 4.8 - works and is supported from here on -clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS) -clang 3.2 - probably works -clang 3.5 - works and is supported from here on - -Use `include/osmium/util/compatibility.hpp` if there are compatibility problems -between compilers due to different C++11 support. - - -## Checking your code - -The Osmium makefiles use pretty draconian warning options for the compiler. -This is good. Code MUST never produce any warnings, even with those settings. -If absolutely necessary pragmas can be used to disable certain warnings in -specific areas of the code. - -If the static code checker `cppcheck` is installed, the CMake configuration -will add a new build target `cppcheck` that will check all `.cpp` and `.hpp` -files. Cppcheck finds some bugs that gcc/clang doesn't. But take the result -with a grain of salt, it also sometimes produces wrong warnings. - -Set `BUILD_HEADERS=ON` in the CMake config to enable compiling all include -files on their own to check whether dependencies are all okay. All include -files MUST include all other include files they depend on. - -Call `cmake/iwyu.sh` to check for proper includes and forward declarations. -This uses the clang-based `include-what-you-use` program. Note that it does -produce some false reports and crashes often. The `osmium.imp` file can be -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. - - -## Documenting the code - -All namespaces, classes, functions, attributes, etc. should be documented. - -Osmium uses the Doxygen (www.doxygen.org) source code documentation system. -If it is installed, the CMake configuration will add a new build target, so -you can build it with `make doc`. +* We'd love for you to send pull requests for fixes you have made or new features + you have added. Please read the [notes for developers](NOTES_FOR_DEVELOPERS.md) + beforehand which contain some coding guidelines. diff --git a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md new file mode 100644 index 00000000000..3c1f73b788d --- /dev/null +++ b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md @@ -0,0 +1,143 @@ + +# Notes for Developers + +Read this if you want to contribute to Libosmium. + + +## Namespace + +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. + +One drawback ist that you can't have static data in classes, because there +is no place to put this data. + +All free functions must be declared `inline`. + + +## Coding Conventions + +These coding conventions have been changing over time and some code is still +different. + +* All include files have `#ifdef` guards around them, macros are the path name + in all uppercase where the slashes (`/`) have been changed to underscore (`_`). +* Class names begin with uppercase chars and use CamelCase. Smaller helper + classes are usually defined as struct and have lowercase names. +* Macros (and only macros) are all uppercase. Use macros sparingly, usually + a simple (maybe constexpr) inline function is better. Undef macros after use + if possible. +* Macros should only be used for controlling which parts of the code should be + included when compiling or to avoid major code repetitions. +* Variables, attributes, and function names are lowercase with + `underscores_between_words`. +* Class attribute names start with `m_` (member). +* Use `descriptive_variable_names`, exceptions are well-established conventions + like `i` for a loop variable. Iterators are usually called `it`. +* Declare variables where they are first used (C++ style), not at the beginning + of a function (old C style). +* Names from external namespaces (even `std`) are always mentioned explicitly. + Do not use `using` (except for `std::swap`). This way we can't even by + accident pollute the namespace of the code using Osmium. +* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`. +* `#include` directives appear in three "blocks" after the copyright notice. + The blocks are separated by blank lines. First block contains `#include`s for + standard C/C++ includes, second block for any external libs used, third + block for osmium internal includes. Within each block `#include`s are usually + sorted by path name. All `#include`s use `<>` syntax not `""`. +* Names not to be used from outside the library should be in a namespace + called `detail` under the namespace where they would otherwise appear. If + whole include files are never meant to be included from outside they should + be in a subdirectory called `detail`. +* All files have suffix `.hpp`. +* Closing } of all classes and namespaces should have a trailing comment + with the name of the class/namespace. +* All constructors with one (or more arguments if they have a default) should + be declared "explicit" unless there is a reason for them not to be. Document + that reason. +* If a class has any of the special methods (copy/move constructor/assigment, + destructor) it should have all of them, possibly marking them as default or + deleted. +* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should + use the new `using foo_type = bar` syntax instead of the old + `typedef bar foo_type`. +* Template parameters are single uppercase letters or start with uppercase `T` + and use CamelCase. +* Always use `typename` in templates, not `class`: `template `. +* The ellipsis in variadic template never has a space to the left of it and + always has a space to the right: `template ` etc. + +Keep to the indentation and other styles used in the code. Use `make indent` +in the toplevel directory to fix indentation and styling. It calls `astyle` +with the right parameters. This program is in the `astyle` Debian package. + + +## C++11 + +Osmium uses C++11 and you can use its features such as auto, lambdas, +threading, etc. There are a few features we do not use, because even modern +compilers don't support them yet. This list might change as we get more data +about which compilers support which feature and what operating system versions +or distributions have which versions of these compilers installed. + +GCC 4.6 - too old, not supported (Ubuntu 12.04 LTS) +GCC 4.7.2 - can probably not be supported (Debian wheezy) +GCC 4.7.3 - probably works +GCC 4.8 - works and is supported from here on +clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS) +clang 3.2 - probably works +clang 3.5 - works and is supported from here on + +Use `include/osmium/util/compatibility.hpp` if there are compatibility problems +between compilers due to different C++11 support. + + +## Operating systems + +Usually all code must work on Linux, OSX, and Windows. Execptions are allowed +for some minor functionality, but please discuss this first. + + +## Checking your code + +The Osmium makefiles use pretty draconian warning options for the compiler. +This is good. Code MUST never produce any warnings, even with those settings. +If absolutely necessary pragmas can be used to disable certain warnings in +specific areas of the code. + +If the static code checker `cppcheck` is installed, the CMake configuration +will add a new build target `cppcheck` that will check all `.cpp` and `.hpp` +files. Cppcheck finds some bugs that gcc/clang doesn't. But take the result +with a grain of salt, it also sometimes produces wrong warnings. + +Set `BUILD_HEADERS=ON` in the CMake config to enable compiling all include +files on their own to check whether dependencies are all okay. All include +files MUST include all other include files they depend on. + +Call `cmake/iwyu.sh` to check for proper includes and forward declarations. +This uses the clang-based `include-what-you-use` program. Note that it does +produce some false reports and crashes often. The `osmium.imp` file can be +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. + + +## Documenting the code + +All namespaces, classes, functions, attributes, etc. should be documented. + +Osmium uses the Doxygen (www.doxygen.org) source code documentation system. +If it is installed, the CMake configuration will add a new build target, so +you can build it with `make doc`. + diff --git a/third_party/libosmium/appveyor.yml b/third_party/libosmium/appveyor.yml index 8244d98e775..7e6060a7ff6 100644 --- a/third_party/libosmium/appveyor.yml +++ b/third_party/libosmium/appveyor.yml @@ -22,88 +22,5 @@ clone_folder: c:\projects\libosmium platform: x64 -install: - # show all available env vars - - set - - echo cmake on AppVeyor - - cmake -version - - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 - - set PATH=c:\projects\libosmium\cmake-3.1.0-win32-x86\bin;%PATH% - - set LODEPSDIR=c:\projects\libosmium\libosmium-deps - - set PROJ_LIB=%LODEPSDIR%\proj\share - - set GDAL_DATA=%LODEPSDIR%\gdal\data - #geos.dll - - set PATH=%LODEPSDIR%\geos\lib;%PATH% - #gdal.dll - - set PATH=%LODEPSDIR%\gdal\lib;%PATH% - #libexpat.dll - - set PATH=%LODEPSDIR%\expat\lib;%PATH% - #libtiff.dll - - set PATH=%LODEPSDIR%\libtiff\lib;%PATH% - #jpeg.dll - - set PATH=%LODEPSDIR%\jpeg\lib;%PATH% - #zlibwapi.dll - - set PATH=%LODEPSDIR%\zlib\lib;%PATH% - #convert backslashes in bzip2 path to forward slashes - #cmake cannot find it otherwise - - set LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib - - set LIBBZIP2=%LIBBZIP2:\=/% - - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/cmake-3.1.0-win32-x86.7z -FileName cm.7z - - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -FileName lodeps.7z - - 7z x cm.7z | %windir%\system32\find "ing archive" - - 7z x lodeps.7z | %windir%\system32\find "ing archive" - - echo %LODEPSDIR% - - dir %LODEPSDIR% - - echo our own cmake - - cmake -version - - cd c:\projects - - git clone --depth 1 https://github.com/osmcode/osm-testdata.git - build_script: - - cd c:\projects\libosmium - - mkdir build - - cd build - - echo %config% - # This will produce lots of LNK4099 warnings which can be ignored. - # Unfortunately they can't be disabled, see - # http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings - - cmake -LA -G "Visual Studio 14 Win64" - -DOsmium_DEBUG=TRUE - -DCMAKE_BUILD_TYPE=%config% - -DBUILD_HEADERS=OFF - -DBOOST_ROOT=%LODEPSDIR%\boost - -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_58.lib - -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib - -DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% - -DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt - .. - - msbuild libosmium.sln /p:Configuration=%config% /toolsversion:14.0 /p:Platform=x64 /p:PlatformToolset=v140 - #- cmake .. -LA -G "NMake Makefiles" - # -DOsmium_DEBUG=TRUE - # -DCMAKE_BUILD_TYPE=%config% - # -DBOOST_ROOT=%LODEPSDIR%\boost - # -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib - # -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib - # -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include - # -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib - # -DEXPAT_INCLUDE_DIR=%LODEPSDIR%\expat\include - # -DBZIP2_LIBRARIES=%LIBBZIP2% - # -DBZIP2_INCLUDE_DIR=%LODEPSDIR%\bzip2\include - # -DGDAL_LIBRARY=%LODEPSDIR%\gdal\lib\gdal_i.lib - # -DGDAL_INCLUDE_DIR=%LODEPSDIR%\gdal\include - # -DGEOS_LIBRARY=%LODEPSDIR%\geos\lib\geos.lib - # -DGEOS_INCLUDE_DIR=%LODEPSDIR%\geos\include - # -DPROJ_LIBRARY=%LODEPSDIR%\proj\lib\proj.lib - # -DPROJ_INCLUDE_DIR=%LODEPSDIR%\proj\include - # -DSPARSEHASH_INCLUDE_DIR=%LODEPSDIR%\sparsehash\include - # -DGETOPT_LIBRARY=%LODEPSDIR%\wingetopt\lib\wingetopt.lib - # -DGETOPT_INCLUDE_DIR=%LODEPSDIR%\wingetopt\include - #- nmake - -test_script: - # "-E testdata-overview" exempts one test we know fails on Appveyor - # because we currently don't have spatialite support. - - ctest --output-on-failure - -C %config% - -E testdata-overview - + - build-appveyor.bat diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp index d50c53dc814..1a16275f0df 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp @@ -5,7 +5,9 @@ */ #include +#include #include +#include #include #include @@ -35,12 +37,12 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; CountHandler handler; osmium::apply(reader, handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp index 8fa696a4ee6..6062eccebc9 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp @@ -5,7 +5,9 @@ */ #include +#include #include +#include #include #include @@ -38,12 +40,12 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; CountHandler handler; osmium::apply(reader, handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp index 02578261808..2cf550ca9c5 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp @@ -4,7 +4,9 @@ */ +#include #include +#include #include #include @@ -13,24 +15,24 @@ #include #include -typedef osmium::index::map::Map index_type; +using index_type = osmium::index::map::Map; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE FORMAT\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; - std::string location_store = argv[2]; + const std::string input_filename{argv[1]}; + const std::string location_store{argv[2]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; const auto& map_factory = osmium::index::MapFactory::instance(); std::unique_ptr index = map_factory.create_map(location_store); - location_handler_type location_handler(*index); + location_handler_type location_handler{*index}; location_handler.ignore_errors(); osmium::apply(reader, location_handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp index 66e2a0bd918..e9f950c3168 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -29,23 +31,23 @@ #include #include -typedef osmium::index::map::SparseMemArray static_index_type; -const std::string location_store="sparse_mem_array"; +using static_index_type = osmium::index::map::SparseMemArray; +const std::string location_store{"sparse_mem_array"}; -typedef osmium::index::map::Map dynamic_index_type; +using dynamic_index_type = osmium::index::map::Map; -typedef osmium::handler::NodeLocationsForWays static_location_handler_type; -typedef osmium::handler::NodeLocationsForWays dynamic_location_handler_type; +using static_location_handler_type = osmium::handler::NodeLocationsForWays; +using dynamic_location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::memory::Buffer buffer = osmium::io::read_file(input_filename); + osmium::memory::Buffer buffer{osmium::io::read_file(input_filename)}; const auto& map_factory = osmium::index::MapFactory::instance(); @@ -67,20 +69,20 @@ int main(int argc, char* argv[]) { { // static index - osmium::memory::Buffer tmp_buffer(buffer.committed()); + osmium::memory::Buffer tmp_buffer{buffer.committed()}; for (const auto& item : buffer) { tmp_buffer.add_item(item); tmp_buffer.commit(); } static_index_type static_index; - static_location_handler_type static_location_handler(static_index); + static_location_handler_type static_location_handler{static_index}; - auto start = std::chrono::steady_clock::now(); + const auto start = std::chrono::steady_clock::now(); osmium::apply(tmp_buffer, static_location_handler); - auto end = std::chrono::steady_clock::now(); + const auto end = std::chrono::steady_clock::now(); - double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end-start).count(); if (duration < static_min) static_min = duration; if (duration > static_max) static_max = duration; @@ -89,21 +91,21 @@ int main(int argc, char* argv[]) { { // dynamic index - osmium::memory::Buffer tmp_buffer(buffer.committed()); + osmium::memory::Buffer tmp_buffer{buffer.committed()}; for (const auto& item : buffer) { tmp_buffer.add_item(item); tmp_buffer.commit(); } std::unique_ptr index = map_factory.create_map(location_store); - dynamic_location_handler_type dynamic_location_handler(*index); + dynamic_location_handler_type dynamic_location_handler{*index}; dynamic_location_handler.ignore_errors(); - auto start = std::chrono::steady_clock::now(); + const auto start = std::chrono::steady_clock::now(); osmium::apply(tmp_buffer, dynamic_location_handler); - auto end = std::chrono::steady_clock::now(); + const auto end = std::chrono::steady_clock::now(); - double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end-start).count(); if (duration < dynamic_min) dynamic_min = duration; if (duration > dynamic_max) dynamic_max = duration; @@ -111,21 +113,21 @@ int main(int argc, char* argv[]) { } } - double static_avg = static_sum/runs; - double dynamic_avg = dynamic_sum/runs; + const double static_avg = static_sum/runs; + const double dynamic_avg = dynamic_sum/runs; std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n"; std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n"; - double rfactor = 100.0; - double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; - double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; - double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; + const double rfactor = 100.0; + const double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; + const double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; + const double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; - double prfactor = 10.0; - double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; - double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; - double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; + const double prfactor = 10.0; + const double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; + const double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; + const double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; std::cout << "difference:"; std::cout << " min=" << diff_min << "ms (" << percent_min << "%)"; diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp index 869f3a8f849..632dd26ebd9 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp @@ -4,8 +4,9 @@ */ -#include -#include +#include +#include +#include #include #include @@ -13,16 +14,16 @@ int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " INPUT-FILE OUTPUT-FILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; - std::string output_filename = argv[2]; + std::string input_filename{argv[1]}; + std::string output_filename{argv[2]}; - osmium::io::Reader reader(input_filename); - osmium::io::File output_file(output_filename, "pbf"); + osmium::io::Reader reader{input_filename}; + osmium::io::File output_file{output_filename, "pbf"}; osmium::io::Header header; - osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow); + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; while (osmium::memory::Buffer buffer = reader.read()) { writer(std::move(buffer)); diff --git a/third_party/libosmium/build-appveyor.bat b/third_party/libosmium/build-appveyor.bat new file mode 100644 index 00000000000..27d387c33a3 --- /dev/null +++ b/third_party/libosmium/build-appveyor.bat @@ -0,0 +1,123 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO ~~~~~~ %~f0 ~~~~~~ + +SET CUSTOM_CMAKE=cmake-3.6.2-win64-x64 +::show all available env vars +SET +ECHO cmake on AppVeyor +cmake -version + +ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +SET lodir=%CD% +SET PATH=%lodir%\%CUSTOM_CMAKE%\bin;%PATH% +SET LODEPSDIR=%lodir%\libosmium-deps +SET PROJ_LIB=%LODEPSDIR%\proj\share +SET GDAL_DATA=%LODEPSDIR%\gdal\data +::gdal.dll +SET PATH=%LODEPSDIR%\gdal\lib;%PATH% +::geos.dll +SET PATH=%LODEPSDIR%\geos\lib;%PATH% +::libtiff.dll +SET PATH=%LODEPSDIR%\libtiff\lib;%PATH% +::jpeg.dll +SET PATH=%LODEPSDIR%\jpeg\lib;%PATH% +::libexpat.dll +SET PATH=%LODEPSDIR%\expat\lib;%PATH% +::zlibwapi.dll +SET PATH=%LODEPSDIR%\zlib\lib;%PATH% +::convert backslashes in bzip2 path to forward slashes +::cmake cannot find it otherwise +SET LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib +SET LIBBZIP2=%LIBBZIP2:\=/% + +IF NOT EXIST cm.7z ECHO downloading cmake %CUSTOM_CMAKE% ... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/%CUSTOM_CMAKE%.7z -OutFile cm.7z +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST lodeps.7z ECHO downloading binary dependencies... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -OutFile lodeps.7z +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST %CUSTOM_CMAKE% ECHO extracting cmake... && 7z x cm.7z | %windir%\system32\find "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST %LODEPSDIR% ECHO extracting binary dependencies... && 7z x lodeps.7z | %windir%\system32\find "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO %LODEPSDIR% +DIR %LODEPSDIR% +::TREE %LODEPSDIR% + +::powershell (Get-ChildItem $env:LODEPSDIR\boost\lib -Filter *boost*.dll)[0].BaseName.split('_')[-1] +FOR /F "tokens=1 usebackq" %%i in (`powershell ^(Get-ChildItem %LODEPSDIR%\boost\lib -Filter *boost*.dll^)[0].BaseName.split^('_'^)[-1]`) DO SET BOOST_VERSION=%%i +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO BOOST_VERSION^: %BOOST_VERSION% + +ECHO our own cmake +cmake -version + +CD %lodir%\.. + +IF NOT EXIST osm-testdata ECHO cloning osm-testdata && git clone --depth 1 https://github.com/osmcode/osm-testdata.git +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +CD osm-testdata +git fetch +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +git pull +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +CD %lodir% +IF EXIST build ECHO deleting build dir... && RD /Q /S build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +MKDIR build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +CD build +ECHO config^: %config% + +::This will produce lots of LNK4099 warnings which can be ignored. +::Unfortunately they can't be disabled, see +::http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings +SET CMAKE_CMD=cmake .. ^ +-LA -G "Visual Studio 14 Win64" ^ +-DOsmium_DEBUG=TRUE ^ +-DCMAKE_BUILD_TYPE=%config% ^ +-DBUILD_HEADERS=OFF ^ +-DBOOST_ROOT=%LODEPSDIR%\boost ^ +-DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib ^ +-DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% ^ +-DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt + +ECHO calling^: %CMAKE_CMD% +%CMAKE_CMD% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +msbuild libosmium.sln ^ +/p:Configuration=%config% ^ +/toolsversion:14.0 ^ +/p:Platform=x64 ^ +/p:PlatformToolset=v140 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ctest --output-on-failure ^ +-C %config% ^ +-E testdata-overview +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + + +GOTO DONE + +:ERROR +ECHO ~~~~~~ ERROR %~f0 ~~~~~~ +SET EL=%ERRORLEVEL% + +:DONE +IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. +ECHO ~~~~~~ DONE %~f0 ~~~~~~ + +EXIT /b %EL% diff --git a/third_party/libosmium/build-local.bat b/third_party/libosmium/build-local.bat new file mode 100644 index 00000000000..202d0b2fa68 --- /dev/null +++ b/third_party/libosmium/build-local.bat @@ -0,0 +1,43 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO ~~~~~~ %~f0 ~~~~~~ + +ECHO. +ECHO build-local ["config=Dev"] +ECHO default config^: RelWithDebInfo +ECHO. + +SET platform=x64 +SET config=RelWithDebInfo + +:: OVERRIDE PARAMETERS >>>>>>>> +:NEXT-ARG + +IF '%1'=='' GOTO ARGS-DONE +ECHO setting %1 +SET %1 +SHIFT +GOTO NEXT-ARG + +:ARGS-DONE +::<<<<< OVERRIDE PARAMETERS + +WHERE 7z +IF %ERRORLEVEL% NEQ 0 ECHO 7zip not on PATH && GOTO ERROR + +CALL build-appveyor.bat +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +GOTO DONE + +:ERROR +ECHO ~~~~~~ ERROR %~f0 ~~~~~~ +SET EL=%ERRORLEVEL% + +:DONE +IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. +ECHO ~~~~~~ DONE %~f0 ~~~~~~ + +EXIT /b %EL% diff --git a/third_party/libosmium/cmake/iwyu.sh b/third_party/libosmium/cmake/iwyu.sh index ceea106c37b..b386159c444 100755 --- a/third_party/libosmium/cmake/iwyu.sh +++ b/third_party/libosmium/cmake/iwyu.sh @@ -6,6 +6,11 @@ # TODO: This script should be integrated with cmake in some way... # +# If these are set, the wrong compiler is used by iwyu and there will be +# errors about missing includes. +unset CC +unset CXX + cmdline="iwyu -Xiwyu --mapping_file=osmium.imp -std=c++11 -I include" log=build/iwyu.log diff --git a/third_party/libosmium/examples/CMakeLists.txt b/third_party/libosmium/examples/CMakeLists.txt index a04a843f247..b47cfdcf7b1 100644 --- a/third_party/libosmium/examples/CMakeLists.txt +++ b/third_party/libosmium/examples/CMakeLists.txt @@ -12,13 +12,17 @@ set(EXAMPLES area_test convert count - create_node_cache debug filter_discussions index + location_cache_create + location_cache_use + pub_names read + read_with_progress + road_length serdump - use_node_cache + tiles CACHE STRING "Example programs" ) @@ -28,7 +32,7 @@ set(EXAMPLES # Examples depending on wingetopt # #----------------------------------------------------------------------------- -set(GETOPT_EXAMPLES area_test convert serdump) +set(GETOPT_EXAMPLES area_test convert index serdump) if(NOT GETOPT_MISSING) foreach(example ${GETOPT_EXAMPLES}) list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY}) @@ -42,36 +46,6 @@ else() endif() -#----------------------------------------------------------------------------- -# -# Examples depending on SparseHash -# -#----------------------------------------------------------------------------- -if(NOT SPARSEHASH_FOUND) - list(REMOVE_ITEM EXAMPLES area_test) - message(STATUS "Configuring examples - Skipping examples because Google SparseHash not found:") - message(STATUS " - osmium_area_test") -endif() - - -#----------------------------------------------------------------------------- -# -# Examples depending on Boost Program Options -# -#----------------------------------------------------------------------------- -unset(Boost_LIBRARIES) -unset(Boost_FOUND) -find_package(Boost 1.38 COMPONENTS program_options) - -if(Boost_PROGRAM_OPTIONS_FOUND) - list(APPEND EXAMPLE_LIBS_index ${Boost_PROGRAM_OPTIONS_LIBRARY}) -else() - list(REMOVE_ITEM EXAMPLES index) - message(STATUS "Configuring examples - Skipping examples because Boost program_options not found:") - message(STATUS " - osmium_index") -endif() - - #----------------------------------------------------------------------------- # # Configure examples diff --git a/third_party/libosmium/examples/README.md b/third_party/libosmium/examples/README.md new file mode 100644 index 00000000000..e2910329252 --- /dev/null +++ b/third_party/libosmium/examples/README.md @@ -0,0 +1,32 @@ + +# Osmium example programs + +The programs in this directory are intended as examples for developers. They +contain extensive comments explaining what's going on. Note that the examples +only cover a small part of what Osmium can do, you should also read the +documentation and API documentation. + +All programs can be run without arguments and they will tell you how to run +them. + +## Very simple examples + +* `osmium_read` +* `osmium_count` +* `osmium_debug` +* `osmium_tiles` + +## Still reasonably simple examples + +* `osmium_filter_discussions` +* `osmium_convert` + +## More advanced examples + +* `osmium_area_test` + +## License + +The code in these example files is released into the Public Domain. Feel free +to copy the code and build on it. + diff --git a/third_party/libosmium/examples/osmium_area_test.cpp b/third_party/libosmium/examples/osmium_area_test.cpp index e9b7a18add1..030337454cf 100644 --- a/third_party/libosmium/examples/osmium_area_test.cpp +++ b/third_party/libosmium/examples/osmium_area_test.cpp @@ -1,48 +1,81 @@ /* - This is an example tool that creates multipolygons from OSM data - and dumps them to stdout. - + EXAMPLE osmium_area_test + + Create multipolygons from OSM data and dump them to stdout in one of two + formats: WKT or using the built-in Dump format. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * the MultipolygonCollector 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 + * the DynamicHandler + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_debug + + LICENSE The code in this example file is released into the Public Domain. */ -#include - -#include +#include // for std::exit +#include // for getopt_long +#include // for std::cout, std::cerr +// For assembling multipolygons #include #include + +// For the DynamicHandler class #include + +// For the WKT factory #include + +// For the Dump handler #include + +// For the NodeLocationForWays handler #include -#include -#include + +// Allow any format of input files (XML, PBF, ...) #include + +// For osmium::apply() #include -typedef osmium::index::map::Dummy index_neg_type; -typedef osmium::index::map::SparseMemArray index_pos_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +// For the location index. There are different types of indexes available. +// This will work for small and medium sized input files. +#include -class WKTDump : public osmium::handler::Handler { +// The type of index used. This must match the include file above +using index_type = osmium::index::map::SparseMemArray; - osmium::geom::WKTFactory<> m_factory ; +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; - std::ostream& m_out; +// This handler writes all area geometries out in WKT (Well Known Text) format. +class WKTDump : public osmium::handler::Handler { -public: + // This factory is used to create a geometry in WKT format from OSM + // objects. The template parameter is empty here, because we output WGS84 + // coordinates, but could be used for a projection. + osmium::geom::WKTFactory<> m_factory; - WKTDump(std::ostream& out) : - m_out(out) { - } +public: + // This callback is called by osmium::apply for each area in the data. void area(const osmium::Area& area) { try { - m_out << m_factory.create_multipolygon(area) << "\n"; - } catch (osmium::geometry_error& e) { - m_out << "GEOMETRY ERROR: " << e.what() << "\n"; + std::cout << m_factory.create_multipolygon(area) << "\n"; + } catch (const osmium::geometry_error& e) { + std::cout << "GEOMETRY ERROR: " << e.what() << "\n"; } } @@ -65,8 +98,13 @@ int main(int argc, char* argv[]) { {0, 0, 0, 0} }; + // Initialize an empty DynamicHandler. Later it will be associated + // with one of the handlers. You can think of the DynamicHandler as + // a kind of "variant handler" or a "pointer handler" pointing to the + // real handler. osmium::handler::DynamicHandler handler; + // Read options from command line. while (true) { int c = getopt_long(argc, argv, "hwo", long_options, 0); if (c == -1) { @@ -76,54 +114,81 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); case 'w': - handler.set(std::cout); + handler.set(); break; case 'o': handler.set(std::cout); break; default: - exit(1); + std::exit(1); } } int remaining_args = argc - optind; if (remaining_args != 1) { std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[optind]); + osmium::io::File input_file{argv[optind]}; + // Configuration for the multipolygon assembler. Here the default settings + // are used, but you could change multiple settings. osmium::area::Assembler::config_type assembler_config; - osmium::area::MultipolygonCollector collector(assembler_config); + // Initialize the MultipolygonCollector. 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}; + + // We read the input file twice. In the first pass, only relations are + // read and fed into the multipolygon collector. std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1(infile, osmium::osm_entity_bits::relation); + osmium::io::Reader reader1{input_file, osmium::osm_entity_bits::relation}; collector.read_relations(reader1); reader1.close(); 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(); - index_pos_type index_pos; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); // XXX + // The index storing all node locations. + index_type index; + + // The handler that stores all node locations in the index and adds them + // to the ways. + location_handler_type location_handler{index}; + + // If a location is not available in the index, we ignore it. It might + // not be needed (if it is not part of a multipolygon relation), so why + // create an error? + location_handler.ignore_errors(); + // On the second pass we read all objects and run them first through the + // node location handler and then the multipolygon collector. The collector + // 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(infile); + osmium::io::Reader reader2{input_file}; osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) { osmium::apply(buffer, handler); })); reader2.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(); + // 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::cerr << "Warning! Some member ways missing for these multipolygon relations:"; diff --git a/third_party/libosmium/examples/osmium_convert.cpp b/third_party/libosmium/examples/osmium_convert.cpp index 4f2ba33185f..48a0823db82 100644 --- a/third_party/libosmium/examples/osmium_convert.cpp +++ b/third_party/libosmium/examples/osmium_convert.cpp @@ -1,16 +1,32 @@ /* + EXAMPLE osmium_convert + Convert OSM files from one format into another. + DEMONSTRATES USE OF: + * file input and output + * file types + * Osmium buffers + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE The code in this example file is released into the Public Domain. */ -#include -#include +#include // for std::exit +#include // for std::exception +#include // for getopt_long +#include // for std::cout, std::cerr +#include // for std::string +// Allow any format of input files (XML, PBF, ...) #include +// Allow any format of output files (XML, PBF, ...) #include void print_help() { @@ -43,9 +59,13 @@ int main(int argc, char* argv[]) { {0, 0, 0, 0} }; + // Input and output format are empty by default. Later this will mean that + // the format should be taken from the input and output file suffix, + // respectively. std::string input_format; std::string output_format; + // Read options from command line. while (true) { int c = getopt_long(argc, argv, "dhf:t:", long_options, 0); if (c == -1) { @@ -55,7 +75,7 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); case 'f': input_format = optarg; break; @@ -63,49 +83,73 @@ int main(int argc, char* argv[]) { output_format = optarg; break; default: - exit(1); + std::exit(1); } } - std::string input; - std::string output; int remaining_args = argc - optind; if (remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl; - exit(1); - } else if (remaining_args == 2) { - input = argv[optind]; - output = argv[optind+1]; - } else if (remaining_args == 1) { - input = argv[optind]; + std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n"; + std::exit(1); } - osmium::io::File infile(input, input_format); + // Get input file name from command line. + std::string input_file_name; + if (remaining_args >= 1) { + input_file_name = argv[optind]; + } - osmium::io::File outfile(output, output_format); + // Get output file name from command line. + std::string output_file_name; + if (remaining_args == 2) { + output_file_name = argv[optind+1]; + } - if (infile.has_multiple_object_versions() && !outfile.has_multiple_object_versions()) { + // 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}; + + // Input and output files can be OSM data files (without history) or + // OSM history files. History files are detected if they use the '.osh' + // file suffix. + if ( input_file.has_multiple_object_versions() && + !output_file.has_multiple_object_versions()) { std::cerr << "Warning! You are converting from an OSM file with (potentially) several versions of the same object to one that is not marked as such.\n"; } - int exit_code = 0; - try { - osmium::io::Reader reader(infile); + // Initialize Reader + osmium::io::Reader reader{input_file}; + + // Get header from input file and change the "generator" setting to + // outselves. osmium::io::Header header = reader.header(); header.set("generator", "osmium_convert"); - osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow); + // 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, header, osmium::io::overwrite::allow); + + // Copy the contents from the input to the output file one buffer at + // a time. This is much easier and faster than copying each object + // in the file. Buffers are moved around, so there is no cost for + // copying in memory. while (osmium::memory::Buffer buffer = reader.read()) { writer(std::move(buffer)); } + + // Explicitly close the writer and reader. Will throw an exception if + // there is a problem. If you wait for the destructor to close the writer + // and reader, you will not notice the problem, because destructors must + // not throw. writer.close(); reader.close(); - } catch (std::exception& e) { + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. std::cerr << e.what() << "\n"; - exit_code = 1; + std::exit(1); } - - return exit_code; } diff --git a/third_party/libosmium/examples/osmium_count.cpp b/third_party/libosmium/examples/osmium_count.cpp index baea153b88f..7de1b6b7c6e 100644 --- a/third_party/libosmium/examples/osmium_count.cpp +++ b/third_party/libosmium/examples/osmium_count.cpp @@ -1,56 +1,95 @@ /* - This is a small tool that counts the number of nodes, ways, and relations in - the input file. + EXAMPLE osmium_count + Counts the number of nodes, ways, and relations in the input file. + + DEMONSTRATES USE OF: + * OSM file input + * your own handler + * the memory usage utility class + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE The code in this example file is released into the Public Domain. */ -#include -#include +#include // for std::uint64_t +#include // for std::exit +#include // for std::cout, std::cerr +// Allow any format of input files (XML, PBF, ...) #include + +// We want to use the handler interface #include + +// Utility class gives us access to memory usage information +#include + +// For osmium::apply() #include +// Handler derive from the osmium::handler::Handler base class. Usually you +// overwrite functions node(), way(), and relation(). Other functions are +// available, too. Read the API documentation for details. struct CountHandler : public osmium::handler::Handler { - uint64_t nodes = 0; - uint64_t ways = 0; - uint64_t relations = 0; + std::uint64_t nodes = 0; + std::uint64_t ways = 0; + std::uint64_t relations = 0; - void node(osmium::Node&) { + // This callback is called by osmium::apply for each node in the data. + void node(const osmium::Node&) noexcept { ++nodes; } - void way(osmium::Way&) { + // This callback is called by osmium::apply for each way in the data. + void way(const osmium::Way&) noexcept { ++ways; } - void relation(osmium::Relation&) { + // This callback is called by osmium::apply for each relation in the data. + void relation(const osmium::Relation&) noexcept { ++relations; } -}; +}; // struct CountHandler int main(int argc, char* argv[]) { - if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[1]); - osmium::io::Reader reader(infile); + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + // Create an instance of our own CountHandler and push the data from the + // input file through it. CountHandler handler; osmium::apply(reader, handler); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); std::cout << "Nodes: " << handler.nodes << "\n"; std::cout << "Ways: " << handler.ways << "\n"; std::cout << "Relations: " << handler.relations << "\n"; + + // Because of the huge amount of OSM data, some Osmium-based programs + // (though not this one) can use huge amounts of data. So checking actual + // memore usage is often useful and can be done easily with this class. + // (Currently only works on Linux, not OSX and Windows.) + osmium::MemoryUsage memory; + + std::cout << "\nMemory used: " << memory.peak() << " MBytes\n"; } diff --git a/third_party/libosmium/examples/osmium_create_node_cache.cpp b/third_party/libosmium/examples/osmium_create_node_cache.cpp deleted file mode 100644 index 359fa1978a8..00000000000 --- a/third_party/libosmium/examples/osmium_create_node_cache.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - - This reads an OSM file and writes out the node locations to a cache - file. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; -//typedef osmium::index::map::DenseMmapArray index_pos_type; -typedef osmium::index::map::DenseFileArray index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -int main(int argc, char* argv[]) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - return 1; - } - - std::string input_filename(argv[1]); - osmium::io::Reader reader(input_filename, osmium::osm_entity_bits::node); - - int fd = open(argv[2], O_RDWR | O_CREAT, 0666); - if (fd == -1) { - std::cerr << "Can not open node cache file '" << argv[2] << "': " << strerror(errno) << "\n"; - return 1; - } - - index_pos_type index_pos {fd}; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); - - osmium::apply(reader, location_handler); - reader.close(); - - return 0; -} - diff --git a/third_party/libosmium/examples/osmium_debug.cpp b/third_party/libosmium/examples/osmium_debug.cpp index 365fc729754..813349190d2 100644 --- a/third_party/libosmium/examples/osmium_debug.cpp +++ b/third_party/libosmium/examples/osmium_debug.cpp @@ -1,27 +1,46 @@ /* - This is a small tool to dump the contents of the input file. + EXAMPLE osmium_debug + Dump the contents of the input file in a debug format. + + DEMONSTRATES USE OF: + * file input reading only some types + * the dump handler + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE The code in this example file is released into the Public Domain. */ -#include +#include // for std::exit +#include // for std::cout, std::cerr +#include // for std::string +// The Dump handler #include + +// Allow any format of input files (XML, PBF, ...) #include int main(int argc, char* argv[]) { + // Speed up output (not Osmium-specific) std::ios_base::sync_with_stdio(false); if (argc < 2 || argc > 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE [TYPES]\n"; std::cerr << "TYPES can be any combination of 'n', 'w', 'r', and 'c' to indicate what types of OSM entities you want (default: all).\n"; - exit(1); + std::exit(1); } + // Default is all entity types: nodes, ways, relations, and changesets osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all; + // Get entity types from command line if there is a 2nd argument. if (argc == 3) { read_types = osmium::osm_entity_bits::nothing; std::string types = argv[2]; @@ -31,20 +50,27 @@ int main(int argc, char* argv[]) { if (types.find('c') != std::string::npos) read_types |= osmium::osm_entity_bits::changeset; } - osmium::io::Reader reader(argv[1], read_types); - osmium::io::Header header = reader.header(); + // Initialize Reader with file name and the types of entities we want to + // read. + osmium::io::Reader reader{argv[1], read_types}; + // The file header can contain metadata such as the program that generated + // the file and the bounding box of the data. + osmium::io::Header header = reader.header(); std::cout << "HEADER:\n generator=" << header.get("generator") << "\n"; - for (auto& bbox : header.boxes()) { + for (const auto& bbox : header.boxes()) { std::cout << " bbox=" << bbox << "\n"; } - osmium::handler::Dump dump(std::cout); - while (osmium::memory::Buffer buffer = reader.read()) { - osmium::apply(buffer, dump); - } + // Initialize Dump handler. + osmium::handler::Dump dump{std::cout}; + + // Read from input and send everything to Dump handler. + osmium::apply(reader, dump); + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); } diff --git a/third_party/libosmium/examples/osmium_filter_discussions.cpp b/third_party/libosmium/examples/osmium_filter_discussions.cpp index bba25b75284..633498199c8 100644 --- a/third_party/libosmium/examples/osmium_filter_discussions.cpp +++ b/third_party/libosmium/examples/osmium_filter_discussions.cpp @@ -1,53 +1,73 @@ /* + EXAMPLE osmium_filter_discussions + Read OSM changesets with discussions from a changeset dump like the one you get from http://planet.osm.org/planet/discussions-latest.osm.bz2 and write out only those changesets which have discussions (ie comments). + DEMONSTRATES USE OF: + * file input and output + * setting file formats using the osmium::io::File class + * OSM file headers + * input and output iterators + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE The code in this example file is released into the Public Domain. */ #include // for std::copy_if -#include // for std::cout, std::cerr +#include // for std::exit +#include // for std::cout, std::cerr -// we want to read OSM files in XML format -// (other formats don't support full changesets, so only XML is needed here) +// We want to read OSM files in XML format +// (other formats don't support full changesets, so only XML is needed here). #include -#include -// we want to write OSM files in XML format +// We want to write OSM files in XML format. #include + +// We want to use input and output iterators for easy integration with the +// algorithms of the standard library. +#include #include -// we want to support any compressioon (.gz2 and .bz2) +// We want to support any compression (none, gzip, and bzip2). #include int main(int argc, char* argv[]) { if (argc != 3) { std::cout << "Usage: " << argv[0] << " INFILE OUTFILE\n"; - exit(1); + std::exit(1); } - // The input file, deduce file format from file suffix - osmium::io::File infile(argv[1]); + // The input file, deduce file format from file suffix. + osmium::io::File input_file{argv[1]}; - // The output file, force class XML OSM file format - osmium::io::File outfile(argv[2], "osm"); + // The output file, force XML OSM file format. + osmium::io::File output_file{argv[2], "osm"}; // Initialize Reader for the input file. // Read only changesets (will ignore nodes, ways, and // relations if there are any). - osmium::io::Reader reader(infile, osmium::osm_entity_bits::changeset); + osmium::io::Reader reader{input_file, osmium::osm_entity_bits::changeset}; - // Get the header from the input file + // Get the header from the input file. osmium::io::Header header = reader.header(); + // Set the "generator" on the header to ourselves. + header.set("generator", "osmium_filter_discussions"); + // Initialize writer for the output file. Use the header from the input // file for the output file. This will copy over some header information. // The last parameter will tell the writer that it is allowed to overwrite // an existing file. Without it, it will refuse to do so. - osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow); + osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow); // Create range of input iterators that will iterator over all changesets // delivered from input file through the "reader". diff --git a/third_party/libosmium/examples/osmium_index.cpp b/third_party/libosmium/examples/osmium_index.cpp index b61214097b0..478b6c6b605 100644 --- a/third_party/libosmium/examples/osmium_index.cpp +++ b/third_party/libosmium/examples/osmium_index.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ class IndexSearch { void dump_dense() { dense_index_type index(m_fd); - for (size_t i = 0; i < index.size(); ++i) { + for (std::size_t i = 0; i < index.size(); ++i) { if (index.get(i) != TValue()) { std::cout << i << " " << index.get(i) << "\n"; } @@ -51,9 +51,9 @@ class IndexSearch { try { TValue value = index.get(key); - std::cout << key << " " << value << std::endl; + std::cout << key << " " << value << "\n"; } catch (...) { - std::cout << key << " not found" << std::endl; + std::cout << key << " not found\n"; return false; } @@ -69,7 +69,7 @@ class IndexSearch { return lhs.first < rhs.first; }); if (positions.first == positions.second) { - std::cout << key << " not found" << std::endl; + std::cout << key << " not found\n"; return false; } @@ -103,7 +103,7 @@ class IndexSearch { } } - bool search(std::vector keys) { + bool search(const std::vector& keys) { bool found_all = true; for (const auto key : keys) { @@ -124,82 +124,105 @@ enum return_code : int { fatal = 3 }; -namespace po = boost::program_options; - class Options { - po::variables_map vm; + std::vector m_ids; + std::string m_type; + std::string m_filename; + bool m_dump = false; + bool m_array_format = false; + bool m_list_format = false; + + void print_help() { + std::cout << "Usage: osmium_index [OPTIONS]\n\n" + << "-h, --help Print this help message\n" + << "-a, --array=FILE Read given index file in array format\n" + << "-l, --list=FILE Read given index file in list format\n" + << "-d, --dump Dump contents of index file to STDOUT\n" + << "-s, --search=ID Search for given id (Option can appear multiple times)\n" + << "-t, --type=TYPE Type of value ('location' or 'offset')\n" + ; + } public: Options(int argc, char* argv[]) { - try { - po::options_description desc("Allowed options"); - desc.add_options() - ("help,h", "Print this help message") - ("array,a", po::value(), "Read given index file in array format") - ("list,l", po::value(), "Read given index file in list format") - ("dump,d", "Dump contents of index file to STDOUT") - ("search,s", po::value>(), "Search for given id (Option can appear multiple times)") - ("type,t", po::value(), "Type of value ('location' or 'offset')") - ; - - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << desc << "\n"; - exit(return_code::okay); + 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} + }; + + while (true) { + int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0); + if (c == -1) { + break; } - if (vm.count("array") && vm.count("list")) { - std::cerr << "Only option --array or --list allowed." << std::endl; - exit(return_code::fatal); - } - - if (!vm.count("array") && !vm.count("list")) { - std::cerr << "Need one of option --array or --list." << std::endl; - exit(return_code::fatal); + switch (c) { + case 'a': + m_array_format = true; + m_filename = optarg; + break; + case 'd': + m_dump = true; + break; + case 'h': + print_help(); + std::exit(return_code::okay); + case 'l': + m_list_format = true; + m_filename = optarg; + break; + case 's': + m_ids.push_back(std::atoll(optarg)); + break; + case 't': + m_type = optarg; + if (m_type != "location" && m_type != "offset") { + std::cerr << "Unknown type '" << m_type << "'. Must be 'location' or 'offset'.\n"; + std::exit(return_code::fatal); + } + break; + default: + std::exit(return_code::fatal); } + } - if (!vm.count("type")) { - std::cerr << "Need --type argument." << std::endl; - exit(return_code::fatal); - } + if (m_array_format == m_list_format) { + std::cerr << "Need option --array or --list, but not both\n"; + std::exit(return_code::fatal); + } - const std::string& type = vm["type"].as(); - if (type != "location" && type != "offset") { - std::cerr << "Unknown type '" << type << "'. Must be 'location' or 'offset'." << std::endl; - exit(return_code::fatal); - } - } catch (boost::program_options::error& e) { - std::cerr << "Error parsing command line: " << e.what() << std::endl; - exit(return_code::fatal); + if (m_type.empty()) { + std::cerr << "Need --type argument.\n"; + std::exit(return_code::fatal); } + } - const std::string& filename() const { - if (vm.count("array")) { - return vm["array"].as(); - } else { - return vm["list"].as(); - } + const std::string& filename() const noexcept { + return m_filename; } - bool dense_format() const { - return vm.count("array") != 0; + bool dense_format() const noexcept { + return m_array_format; } - bool do_dump() const { - return vm.count("dump") != 0; + bool do_dump() const noexcept { + return m_dump; } - std::vector search_keys() const { - return vm["search"].as>(); + const std::vector& search_keys() const noexcept { + return m_ids; } - bool type_is(const char* type) const { - return vm["type"].as() == type; + bool type_is(const char* type) const noexcept { + return m_type == type; } }; // class Options @@ -232,6 +255,6 @@ int main(int argc, char* argv[]) { } } - exit(result_okay ? return_code::okay : return_code::not_found); + std::exit(result_okay ? return_code::okay : return_code::not_found); } diff --git a/third_party/libosmium/examples/osmium_location_cache_create.cpp b/third_party/libosmium/examples/osmium_location_cache_create.cpp new file mode 100644 index 00000000000..9de41d12f4d --- /dev/null +++ b/third_party/libosmium/examples/osmium_location_cache_create.cpp @@ -0,0 +1,87 @@ +/* + + EXAMPLE osmium_location_cache_create + + Reads nodes from an OSM file and writes out their locations to a cache + file. The cache file can then be read with osmium_location_cache_use. + + Warning: The locations cache file will get huge (>32GB) if you are using + the DenseFileArray index even if the input file is small, because + it depends on the *largest* node ID, not the number of nodes. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * location indexes on disk + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_road_length + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for errno +#include // for std::exit +#include // for strerror +#include // for open +#include // for std::cout, std::cerr +#include // for std::string +#include // for open +#include // for open + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the location index. There are different types of index implementation +// available. These implementations put the index on disk. See below. +#include +#include + +// For the NodeLocationForWays handler +#include + +// For osmium::apply() +#include + +// Chose one of these two. "sparse" is best used for small and medium extracts, +// the "dense" index for large extracts or the whole planet. +using index_type = osmium::index::map::SparseFileArray; +//using index_type = osmium::index::map::DenseFileArray; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; + std::exit(1); + } + + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; + + // Construct Reader reading only nodes + 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); + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + std::exit(1); + } + index_type index{fd}; + + // The handler that stores all node locations in the index. + location_handler_type location_handler{index}; + + // Feed all nodes through the location handler. + osmium::apply(reader, location_handler); + + // Explicitly close input so we get notified of any errors. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_location_cache_use.cpp b/third_party/libosmium/examples/osmium_location_cache_use.cpp new file mode 100644 index 00000000000..f5db0becf27 --- /dev/null +++ b/third_party/libosmium/examples/osmium_location_cache_use.cpp @@ -0,0 +1,101 @@ +/* + + EXAMPLE osmium_location_cache_use + + This reads ways from an OSM file and writes out the way node locations + it got from a location cache generated with osmium_location_cache_create. + + Warning: The locations cache file will get huge (>32GB) if you are using + the DenseFileArray index even if the input file is small, because + it depends on the *largest* node ID, not the number of nodes. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * location indexes on disk + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_road_length + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for errno +#include // for std::exit +#include // for strerror +#include // for open +#include // for std::cout, std::cerr +#include // for std::string +#include // for open +#include // for open + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the location index. There are different types of index implementation +// available. These implementations put the index on disk. See below. +#include +#include + +// For the NodeLocationForWays handler +#include + +// For osmium::apply() +#include + +// Chose one of these two. "sparse" is best used for small and medium extracts, +// the "dense" index for large extracts or the whole planet. +using index_type = osmium::index::map::SparseFileArray; +//using index_type = osmium::index::map::DenseFileArray; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +// This handler only implements the way() function which prints out the way +// ID and all nodes IDs and locations in those ways. +struct MyHandler : public osmium::handler::Handler { + + void way(const osmium::Way& way) { + std::cout << "way " << way.id() << "\n"; + for (const auto& nr : way.nodes()) { + std::cout << " node " << nr.ref() << " " << nr.location() << "\n"; + } + } + +}; // struct MyHandler + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; + std::exit(1); + } + + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; + + // Construct Reader reading only ways + 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); + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + return 1; + } + index_type index{fd}; + + // The handler that adds node locations from the index to the ways. + location_handler_type location_handler{index}; + + // Feed all ways through the location handler and then our own handler. + MyHandler handler; + osmium::apply(reader, location_handler, handler); + + // Explicitly close input so we get notified of any errors. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_pub_names.cpp b/third_party/libosmium/examples/osmium_pub_names.cpp new file mode 100755 index 00000000000..dbc37c3328e --- /dev/null +++ b/third_party/libosmium/examples/osmium_pub_names.cpp @@ -0,0 +1,89 @@ +/* + + EXAMPLE osmium_pub_names + + Show the names and addresses of all pubs found in an OSM file. + + DEMONSTRATES USE OF: + * file input + * your own handler + * access to tags + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::strncmp +#include // for std::cout, std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// We want to use the handler interface +#include + +// For osmium::apply() +#include + +class NamesHandler : public osmium::handler::Handler { + + void output_pubs(const osmium::OSMObject& object) { + const osmium::TagList& tags = object.tags(); + if (tags.has_tag("amenity", "pub")) { + + // Print name of the pub if it is set. + const char* name = tags["name"]; + if (name) { + std::cout << name << "\n"; + } else { + std::cout << "pub with unknown name\n"; + } + + // Iterate over all tags finding those which start with "addr:" + // and print them. + for (const osmium::Tag& tag : tags) { + if (!std::strncmp(tag.key(), "addr:", 5)) { + std::cout << " " << tag.key() << ": " << tag.value() << "\n"; + } + } + } + } + +public: + + // Nodes can be tagged amenity=pub. + void node(const osmium::Node& node) { + output_pubs(node); + } + + // Ways can be tagged amenity=pub, too (typically buildings). + void way(const osmium::Way& way) { + output_pubs(way); + } + +}; // class NamesHandler + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // Construct the handler defined above + NamesHandler names_handler; + + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. We are ignoring multipolygon + // relations in this simple example. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + + // Apply input data to our own handler + osmium::apply(reader, names_handler); +} + diff --git a/third_party/libosmium/examples/osmium_read.cpp b/third_party/libosmium/examples/osmium_read.cpp index 653600684a1..9f391c874f0 100644 --- a/third_party/libosmium/examples/osmium_read.cpp +++ b/third_party/libosmium/examples/osmium_read.cpp @@ -1,30 +1,42 @@ /* - This is a small tool that reads and discards the contents of the input file. - (Used for timing.) + EXAMPLE osmium_read + Reads and discards the contents of the input file. + (It can be used for timing.) + + DEMONSTRATES USE OF: + * file input + + LICENSE The code in this example file is released into the Public Domain. */ -#include +#include // for std::exit +#include // for std::cerr +// Allow any format of input files (XML, PBF, ...) #include int main(int argc, char* argv[]) { - if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[1]); - osmium::io::Reader reader(infile); + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + // OSM data comes in buffers, read until there are no more. while (osmium::memory::Buffer buffer = reader.read()) { // do nothing } + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); } diff --git a/third_party/libosmium/examples/osmium_read_with_progress.cpp b/third_party/libosmium/examples/osmium_read_with_progress.cpp new file mode 100644 index 00000000000..81437a665df --- /dev/null +++ b/third_party/libosmium/examples/osmium_read_with_progress.cpp @@ -0,0 +1,56 @@ +/* + + EXAMPLE osmium_read_with_progress + + Reads the contents of the input file showing a progress bar. + + DEMONSTRATES USE OF: + * file input + * ProgressBar utility function + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// Get access to isatty utility function and progress bar utility class. +#include +#include + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + + // Initialize progress bar, enable it only if STDERR is a TTY. + osmium::ProgressBar progress{reader.file_size(), osmium::util::isatty(2)}; + + // OSM data comes in buffers, read until there are no more. + while (osmium::memory::Buffer buffer = reader.read()) { + // Update progress bar for each buffer. + progress.update(reader.offset()); + } + + // Progress bar is done. + progress.done(); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_road_length.cpp b/third_party/libosmium/examples/osmium_road_length.cpp new file mode 100755 index 00000000000..2e1be908037 --- /dev/null +++ b/third_party/libosmium/examples/osmium_road_length.cpp @@ -0,0 +1,92 @@ +/* + + EXAMPLE osmium_road_length + + Calculate the length of the road network (everything tagged `highway=*`) + from the given OSM file. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * length calculation on the earth using the haversine function + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_pub_names + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::cout, std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the osmium::geom::haversine::distance() function +#include + +// For osmium::apply() +#include + +// For the location index. There are different types of indexes available. +// This will work for small and medium sized input files. +#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; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +// This handler only implements the way() function, we are not interested in +// any other objects. +struct RoadLengthHandler : public osmium::handler::Handler { + + double length = 0; + + // If the way has a "highway" tag, find its length and add it to the + // overall length. + void way(const osmium::Way& way) { + const char* highway = way.tags()["highway"]; + if (highway) { + length += osmium::geom::haversine::distance(way.nodes()); + } + } + +}; // struct RoadLengthHandler + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + + // The index to hold node locations. + index_type index; + + // The location handler will add the node locations to the index and then + // to the ways + location_handler_type location_handler{index}; + + // Our handler defined above + RoadLengthHandler road_length_handler; + + // Apply input data to first the location handler and then our own handler + osmium::apply(reader, location_handler, road_length_handler); + + // Output the length. The haversine function calculates it in meters, + // so we first devide by 1000 to get kilometers. + std::cout << "Length: " << road_length_handler.length / 1000 << " km\n"; +} + diff --git a/third_party/libosmium/examples/osmium_serdump.cpp b/third_party/libosmium/examples/osmium_serdump.cpp index 9ab26e4eeee..81a6d0cc575 100644 --- a/third_party/libosmium/examples/osmium_serdump.cpp +++ b/third_party/libosmium/examples/osmium_serdump.cpp @@ -69,9 +69,9 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); default: - exit(2); + std::exit(2); } } @@ -79,7 +79,7 @@ int main(int argc, char* argv[]) { if (remaining_args != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n"; - exit(2); + std::exit(2); } std::string dir(argv[optind+1]); @@ -90,14 +90,14 @@ int main(int argc, char* argv[]) { #endif if (result == -1 && errno != EEXIST) { std::cerr << "Problem creating directory '" << dir << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } std::string data_file(dir + "/data.osm.ser"); int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (data_fd < 0) { std::cerr << "Can't open data file '" << data_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } offset_index_type node_index; @@ -127,7 +127,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open nodes index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } node_index.dump_as_list(fd); close(fd); @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open ways index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } way_index.dump_as_list(fd); close(fd); @@ -149,7 +149,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open relations index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } relation_index.dump_as_list(fd); close(fd); @@ -161,7 +161,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open node->way map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_node2way.dump_as_list(fd); close(fd); @@ -173,7 +173,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open node->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_node2relation.dump_as_list(fd); close(fd); @@ -185,7 +185,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open way->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_way2relation.dump_as_list(fd); close(fd); @@ -197,7 +197,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open rel->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_relation2relation.dump_as_list(fd); close(fd); diff --git a/third_party/libosmium/examples/osmium_tiles.cpp b/third_party/libosmium/examples/osmium_tiles.cpp new file mode 100644 index 00000000000..7dbeb3e29ae --- /dev/null +++ b/third_party/libosmium/examples/osmium_tiles.cpp @@ -0,0 +1,72 @@ +/* + + EXAMPLE osmium_tiles + + Convert WGS84 longitude and latitude to Mercator coordinates and tile + coordinates. + + DEMONSTRATES USE OF: + * the Location and Coordinates classes + * the Mercator projection function + * the Tile class + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit, std::atoi, std::atof +#include // for std::cout, std::cerr + +// The Location contains a longitude and latitude and is usually used inside +// a node to store its location in the world. +#include + +// Needed for the Mercator projection function. Osmium supports the Mercator +// projection out of the box, or pretty much any projection using the Proj.4 +// library (with the osmium::geom::Projection class). +#include + +// The Tile class handles tile coordinates and zoom levels. +#include + +int main(int argc, char* argv[]) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " ZOOM LON LAT\n"; + std::exit(1); + } + + const int zoom = std::atoi(argv[1]); + + if (zoom < 0 || zoom > 30) { + std::cerr << "ERROR: Zoom must be between 0 and 30\n"; + std::exit(1); + } + + const double lon = std::atof(argv[2]); + const double lat = std::atof(argv[3]); + + // Create location from WGS84 coordinates. In Osmium the order of + // coordinate values is always x/longitude first, then y/latitude. + const osmium::Location location{lon, lat}; + + std::cout << "WGS84: lon=" << lon << " lat=" << lat << "\n"; + + // A location can store some invalid locations, ie locations outside the + // -180 to 180 and -90 to 90 degree range. This function checks for that. + if (!location.valid()) { + std::cerr << "ERROR: Location is invalid\n"; + std::exit(1); + } + + // Project the coordinates using a helper function. You can also use the + // osmium::geom::MercatorProjection class. + const osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(location); + std::cout << "Mercator: x=" << c.x << " y=" << c.y << "\n"; + + // Create a tile at this location. This will also internally use the + // Mercator projection and then calculate the tile coordinates. + const osmium::geom::Tile tile{uint32_t(zoom), location}; + std::cout << "Tile: zoom=" << tile.z << " x=" << tile.x << " y=" << tile.y << "\n"; +} + diff --git a/third_party/libosmium/examples/osmium_use_node_cache.cpp b/third_party/libosmium/examples/osmium_use_node_cache.cpp deleted file mode 100644 index cfee6df4683..00000000000 --- a/third_party/libosmium/examples/osmium_use_node_cache.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - - This reads ways from an OSM file and writes out the node locations - it got from a node cache generated with osmium_create_node_cache. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; -//typedef osmium::index::map::DenseMmapArray index_pos_type; -typedef osmium::index::map::DenseFileArray index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -class MyHandler : public osmium::handler::Handler { - -public: - - void way(osmium::Way& way) { - for (auto& nr : way.nodes()) { - std::cout << nr << "\n"; - } - } - -}; // class MyHandler - -int main(int argc, char* argv[]) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - return 1; - } - - std::string input_filename(argv[1]); - osmium::io::Reader reader(input_filename, osmium::osm_entity_bits::way); - - int fd = open(argv[2], O_RDWR); - if (fd == -1) { - std::cerr << "Can not open node cache file '" << argv[2] << "': " << strerror(errno) << "\n"; - return 1; - } - - index_pos_type index_pos {fd}; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); - - MyHandler handler; - osmium::apply(reader, location_handler, handler); - reader.close(); - - return 0; -} - diff --git a/third_party/libosmium/include/gdalcpp.hpp b/third_party/libosmium/include/gdalcpp.hpp index 1502f2f0cb6..4f3d480961a 100644 --- a/third_party/libosmium/include/gdalcpp.hpp +++ b/third_party/libosmium/include/gdalcpp.hpp @@ -110,8 +110,12 @@ namespace gdalcpp { namespace detail { struct init_wrapper { +#if GDAL_VERSION_MAJOR >= 2 + init_wrapper() { GDALAllRegister(); } +#else init_wrapper() { OGRRegisterAll(); } ~init_wrapper() { OGRCleanupAll(); } +#endif }; struct init_library { @@ -237,6 +241,8 @@ namespace gdalcpp { detail::Options m_options; SRS m_srs; std::unique_ptr m_dataset; + uint64_t m_edit_count = 0; + uint64_t m_max_edit_count = 0; public: @@ -255,6 +261,15 @@ namespace gdalcpp { } } + ~Dataset() { + try { + if (m_edit_count > 0) { + commit_transaction(); + } + } catch (...) { + } + } + const std::string& driver_name() const { return m_driver_name; } @@ -282,10 +297,14 @@ namespace gdalcpp { exec(sql.c_str()); } - Dataset& start_transaction() { #if GDAL_VERSION_MAJOR >= 2 m_dataset->StartTransaction(); +#else + OGRLayer* layer = m_dataset->GetLayer(0); + if (layer) { + layer->StartTransaction(); + } #endif return *this; } @@ -293,7 +312,38 @@ namespace gdalcpp { Dataset& commit_transaction() { #if GDAL_VERSION_MAJOR >= 2 m_dataset->CommitTransaction(); +#else + OGRLayer* layer = m_dataset->GetLayer(0); + if (layer) { + layer->CommitTransaction(); + } #endif + m_edit_count = 0; + return *this; + } + + void prepare_edit() { + if (m_max_edit_count != 0 && m_edit_count == 0) { + start_transaction(); + } + } + + void finalize_edit() { + if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) { + commit_transaction(); + } + } + + Dataset& enable_auto_transactions(uint64_t edits = 100000) { + m_max_edit_count = edits; + return *this; + } + + Dataset& disable_auto_transactions() { + if (m_max_edit_count != 0 && m_edit_count > 0) { + commit_transaction(); + } + m_max_edit_count = 0; return *this; } @@ -346,19 +396,32 @@ namespace gdalcpp { return *this; } + void create_feature(OGRFeature* feature) { + dataset().prepare_edit(); + OGRErr result = m_layer->CreateFeature(feature); + if (result != OGRERR_NONE) { + throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name()); + } + dataset().finalize_edit(); + } + Layer& start_transaction() { +#if GDAL_VERSION_MAJOR < 2 OGRErr result = m_layer->StartTransaction(); if (result != OGRERR_NONE) { throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); } +#endif return *this; } Layer& commit_transaction() { +#if GDAL_VERSION_MAJOR < 2 OGRErr result = m_layer->CommitTransaction(); if (result != OGRERR_NONE) { throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); } +#endif return *this; } @@ -366,36 +429,44 @@ namespace gdalcpp { class Feature { + struct ogr_feature_deleter { + + void operator()(OGRFeature* feature) { + OGRFeature::DestroyFeature(feature); + } + + }; // struct ogr_feature_deleter + Layer& m_layer; - OGRFeature m_feature; + std::unique_ptr m_feature; public: Feature(Layer& layer, std::unique_ptr&& geometry) : m_layer(layer), - m_feature(m_layer.get().GetLayerDefn()) { - OGRErr result = m_feature.SetGeometryDirectly(geometry.release()); + m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) { + if (!m_feature) { + throw std::bad_alloc(); + } + OGRErr result = m_feature->SetGeometryDirectly(geometry.release()); if (result != OGRERR_NONE) { throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name()); } } void add_to_layer() { - OGRErr result = m_layer.get().CreateFeature(&m_feature); - if (result != OGRERR_NONE) { - throw gdal_error(std::string("creating feature in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name()); - } + m_layer.create_feature(m_feature.get()); } template Feature& set_field(int n, T&& arg) { - m_feature.SetField(n, std::forward(arg)); + m_feature->SetField(n, std::forward(arg)); return *this; } template Feature& set_field(const char* name, T&& arg) { - m_feature.SetField(name, std::forward(arg)); + m_feature->SetField(name, std::forward(arg)); return *this; } diff --git a/third_party/libosmium/include/osmium/area/assembler.hpp b/third_party/libosmium/include/osmium/area/assembler.hpp index a2d1c8ebd85..d5bf8d8daae 100644 --- a/third_party/libosmium/include/osmium/area/assembler.hpp +++ b/third_party/libosmium/include/osmium/area/assembler.hpp @@ -35,6 +35,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include #include #include @@ -42,95 +44,251 @@ 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 namespace osmium { namespace area { - using osmium::area::detail::ProtoRing; - + /** + * 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 { - osmium::area::ProblemReporter* problem_reporter; + /** + * 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; - // Enables debug output to stderr - bool debug; + AssemblerConfig() noexcept = default; - explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d = false) : + /** + * Constructor + * @deprecated Use default constructor and set values afterwards. + */ + explicit AssemblerConfig(osmium::area::ProblemReporter* pr, bool d = false) : problem_reporter(pr), - debug(d) { + debug_level(d) { } /** * Enable or disable debug output to stderr. This is for Osmium * developers only. + * + * @deprecated Set debug_level directly. */ - void enable_debug_output(bool d = true) { - debug = d; + 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& a, const location_to_ring_map& b) noexcept { + return a.location == b.location; + } + + inline bool operator<(const location_to_ring_map& a, const location_to_ring_map& b) noexcept { + return a.location < b.location; + } + + } // namespace detail + /** - * Assembles area objects from multipolygon relations and their - * members. This is called by the MultipolygonCollector object - * after all members have been collected. + * Assembles area objects from closed ways or multipolygon relations + * and their members. */ class Assembler { - const AssemblerConfig m_config; + 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 - // The way segments + // 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 way segments - std::list m_rings; + // The rings we are building from the segments + std::list m_rings; + + // All node locations + std::vector m_locations; - std::vector m_outer_rings; - std::vector m_inner_rings; + // All locations where more than two segments start/end + std::vector m_split_locations; - int m_inner_outer_mismatches { 0 }; + // Statistics + area_stats m_stats; - bool debug() const { - return m_config.debug; + // The number of members the multipolygon relation has + size_t m_num_members = 0; + + bool debug() const noexcept { + return m_config.debug_level > 1; } - /** - * Checks whether the given NodeRefs have the same location. - * Uses the actual location for the test, not the id. If both - * have the same location, but not the same id, a problem - * point will be added to the list of problem points. - */ - bool has_same_location(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) { - if (nr1.location() != nr2.location()) { + bool report_ways() const noexcept { + if (!m_config.problem_reporter) { return false; } - if (nr1.ref() != nr2.ref()) { - if (m_config.problem_reporter) { - m_config.problem_reporter->report_duplicate_node(nr1.ref(), nr2.ref(), nr1.location()); - } - } - return true; + 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 { - osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder); - for (const osmium::Tag& tag : way.tags()) { - tl_builder.add_tag(tag.key(), tag.value()); - } + builder.add_item(&way.tags()); } void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set& ways) const { @@ -144,13 +302,13 @@ namespace osmium { } } - size_t num_ways = ways.size(); + 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) { - size_t len = std::strlen(t_c.first.c_str()); + 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); } } @@ -169,13 +327,22 @@ namespace osmium { }; // struct MPFilter - static MPFilter& filter() { - static MPFilter filter; + 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 { - const auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter()); + static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { + osmium::builder::TagListBuilder tl_builder(builder.buffer(), &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"; @@ -186,29 +353,27 @@ namespace osmium { std::cerr << " use tags from relation\n"; } - // write out all tags except type=* - osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder); - for (const osmium::Tag& tag : relation.tags()) { - if (strcmp(tag.key(), "type")) { - tl_builder.add_tag(tag.key(), tag.value()); - } + 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_outer_rings) { - ring->get_ways(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"; } - osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder); - for (const osmium::Tag& tag : (*ways.begin())->tags()) { - tl_builder.add_tag(tag.key(), tag.value()); - } + builder.add_item(&(*ways.cbegin())->tags()); } else { if (debug()) { std::cerr << " multiple outer ways, get common tags\n"; @@ -219,494 +384,1087 @@ namespace osmium { } } + template + static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) { + TBuilder ring_builder(builder.buffer(), &builder); + ring_builder.add_node_ref(ring.get_node_ref_start()); + for (const auto& segment : ring.segments()) { + ring_builder.add_node_ref(segment->stop()); + } + } + /** - * Go through all the rings and find rings that are not closed. - * Problems are reported through the problem reporter. - * - * @returns true if any rings were not closed, false otherwise + * Append each outer ring together with its inner rings to the + * area in the buffer. */ - bool check_for_open_rings() { - bool open_rings = false; - - for (const auto& ring : m_rings) { - if (!ring.closed()) { - open_rings = true; - if (m_config.problem_reporter) { - m_config.problem_reporter->report_ring_not_closed(ring.get_node_ref_front().location(), ring.get_node_ref_back().location()); + 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); } } } - - return open_rings; } - /** - * Check whether there are any rings that can be combined with the - * given ring to one larger ring by appending the other ring to - * the end of this ring. - * If the rings can be combined they are and the function returns - * true. - */ - bool possibly_combine_rings_back(ProtoRing& ring) { - const osmium::NodeRef& nr = ring.get_node_ref_back(); - + void check_inner_outer_roles() { if (debug()) { - std::cerr << " possibly_combine_rings_back()\n"; + std::cerr << " Checking inner/outer roles\n"; } - for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { - if (&*it != &ring && !it->closed()) { - if (has_same_location(nr, it->get_node_ref_front())) { + + 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 << " ring.last=it->first\n"; + std::cerr << " Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name() + << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n"; } - ring.merge_ring(*it, debug()); - m_rings.erase(it); - return true; - } - if (has_same_location(nr, it->get_node_ref_back())) { - if (debug()) { - std::cerr << " ring.last=it->last\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()); + } } - ring.merge_ring_reverse(*it, debug()); - m_rings.erase(it); - return true; } + + auto& r = way_rings[segment->way()]; + if (!r) { + r = ˚ + } else if (r != &ring) { + ways_in_multiple_rings.insert(segment->way()); + } + } } - return false; + + 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); + } + } + } - /** - * Check whether there are any rings that can be combined with the - * given ring to one larger ring by prepending the other ring to - * the start of this ring. - * If the rings can be combined they are and the function returns - * true. - */ - bool possibly_combine_rings_front(ProtoRing& ring) { - const osmium::NodeRef& nr = ring.get_node_ref_front(); + 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& a, const slocation& b) { + return a.location(m_segment_list, location) < b.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 { + + int32_t m_y; + detail::ProtoRing* m_ring_ptr; + + public: + + rings_stack_element(int32_t y, detail::ProtoRing* ring_ptr) : + m_y(y), + m_ring_ptr(ring_ptr) { + } + + int32_t 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 ring_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 << " possibly_combine_rings_front()\n"; + std::cerr << " Looking for ring enclosing " << *segment << "\n"; } - for (auto it = m_rings.begin(); it != m_rings.end(); ++it) { - if (&*it != &ring && !it->closed()) { - if (has_same_location(nr, it->get_node_ref_back())) { + + 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 << " ring.first=it->last\n"; + std::cerr << " Segment is below (nesting=" << nesting << ")\n"; } - ring.swap_segments(*it); - ring.merge_ring(*it, debug()); - m_rings.erase(it); - return true; + 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"; } - if (has_same_location(nr, it->get_node_ref_front())) { + + 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 << " ring.first=it->first\n"; + std::cerr << " Segment is below (nesting=" << nesting << ")\n"; + } + if (segment->ring()->is_outer()) { + if (debug()) { + std::cerr << " Segment belongs to outer ring\n"; + } + const int32_t y = int32_t(ay + (by - ay) * (lx - ax) / (bx - ax)); + outer_rings.emplace_back(y, segment->ring()); } - ring.reverse(); - ring.merge_ring(*it, debug()); - m_rings.erase(it); - return true; } } + --segment; } - return false; - } - void split_off_subring(osmium::area::detail::ProtoRing& ring, osmium::area::detail::ProtoRing::segments_type::iterator it, osmium::area::detail::ProtoRing::segments_type::iterator it_begin, osmium::area::detail::ProtoRing::segments_type::iterator it_end) { - if (debug()) { - std::cerr << " subring found at: " << *it << "\n"; + 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(); } - ProtoRing new_ring(it_begin, it_end); - ring.remove_segments(it_begin, it_end); + } + + 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 << " split into two rings:\n"; - std::cerr << " " << new_ring << "\n"; - std::cerr << " " << ring << "\n"; + std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; } - m_rings.push_back(std::move(new_ring)); - } - bool has_closed_subring_back(ProtoRing& ring, const NodeRef& nr) { - if (ring.segments().size() < 3) { - return false; + if (node.reverse) { + segment->reverse(); } - if (debug()) { - std::cerr << " has_closed_subring_back()\n"; + + detail::ProtoRing* outer_ring = nullptr; + + if (segment != &m_segment_list.front()) { + outer_ring = find_enclosing_ring(segment); } - const auto end = ring.segments().end(); - for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) { - if (has_same_location(nr, it->first())) { - split_off_subring(ring, it, it, end); - return true; + 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"; } - return false; - } - bool has_closed_subring_front(ProtoRing& ring, const NodeRef& nr) { - if (ring.segments().size() < 3) { - return false; + 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 << " has_closed_subring_front()\n"; + std::cerr << " Completed ring: " << *ring << "\n"; } - const auto end = ring.segments().end(); - for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) { - if (has_same_location(nr, it->second())) { - split_off_subring(ring, it, ring.segments().begin(), it+1); - return true; - } - } - return false; + + return nodes; } - bool check_for_closed_subring(ProtoRing& ring) { + uint32_t add_new_ring_complex(slocation& node) { + detail::NodeRefSegment* segment = &m_segment_list[node.item]; + assert(!segment->is_done()); + if (debug()) { - std::cerr << " check_for_closed_subring()\n"; + std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n"; } - osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size()); - std::copy(ring.segments().cbegin(), ring.segments().cend(), segments.begin()); - std::sort(segments.begin(), segments.end()); - const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) { - return has_same_location(s1.first(), s2.first()); - }); - if (it == segments.end()) { - return false; + 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(); } - const auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1); - assert(r1 != ring.segments().end()); - const auto r2 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it+1, it+2); - assert(r2 != ring.segments().end()); if (debug()) { - std::cerr << " found subring in ring " << ring << " at " << it->first() << "\n"; + if (first_location == last_location) { + std::cerr << " Completed ring: " << *ring << "\n"; + } else { + std::cerr << " Completed partial ring: " << *ring << "\n"; + } } - const auto m = std::minmax(r1, r2); + return nodes; + } - ProtoRing new_ring(m.first, m.second); - ring.remove_segments(m.first, m.second); + void create_locations_list() { + m_locations.reserve(m_segment_list.size() * 2); - if (debug()) { - std::cerr << " split ring1=" << new_ring << "\n"; - std::cerr << " split ring2=" << ring << "\n"; + for (uint32_t n = 0; n < m_segment_list.size(); ++n) { + m_locations.emplace_back(n, false); + m_locations.emplace_back(n, true); } - m_rings.emplace_back(new_ring); + std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& a, const slocation& b) { + return a.location(m_segment_list) < b.location(m_segment_list); + }); + } - return true; + 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 combine_rings_front(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) { + void find_inner_outer_complex() { if (debug()) { - std::cerr << " => match at front of ring\n"; + 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); + } } - ring.add_segment_front(segment); - has_closed_subring_front(ring, segment.first()); - if (possibly_combine_rings_front(ring)) { - check_for_closed_subring(ring); + + if (rings.empty()) { + return; } - } - void combine_rings_back(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) { + 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 << " => match at back of ring\n"; + std::cerr << " First ring is outer: " << *rings.front() << "\n"; } - ring.add_segment_back(segment); - has_closed_subring_back(ring, segment.second()); - if (possibly_combine_rings_back(ring)) { - check_for_closed_subring(ring); + 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"; + } } } /** - * Append each outer ring together with its inner rings to the - * area in the buffer. + * 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. */ - void add_rings_to_area(osmium::builder::AreaBuilder& builder) const { - for (const ProtoRing* ring : m_outer_rings) { - if (debug()) { - std::cerr << " ring " << *ring << " is outer\n"; - } - { - osmium::builder::OuterRingBuilder ring_builder(builder.buffer(), &builder); - ring_builder.add_node_ref(ring->get_node_ref_front()); - for (const auto& segment : ring->segments()) { - ring_builder.add_node_ref(segment.second()); + 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; } } - for (ProtoRing* inner : ring->inner_rings()) { - osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder); - ring_builder.add_node_ref(inner->get_node_ref_front()); - for (const auto& segment : inner->segments()) { - ring_builder.add_node_ref(segment.second()); + 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; } } } } - bool add_to_existing_ring(osmium::area::detail::NodeRefSegment segment) { - int n = 0; - for (auto& ring : m_rings) { + 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 << " check against ring " << n << " " << ring; + std::cerr << " Ring: " << **it << "\n"; } - if (ring.closed()) { - if (debug()) { - std::cerr << " => ring CLOSED\n"; - } - } else { - if (has_same_location(ring.get_node_ref_back(), segment.first())) { - combine_rings_back(segment, ring); - return true; - } - if (has_same_location(ring.get_node_ref_back(), segment.second())) { - segment.swap_locations(); - combine_rings_back(segment, ring); - return true; - } - if (has_same_location(ring.get_node_ref_front(), segment.first())) { - segment.swap_locations(); - combine_rings_front(segment, ring); - return true; - } - if (has_same_location(ring.get_node_ref_front(), segment.second())) { - combine_rings_front(segment, ring); - return true; - } + 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) { + auto& r1 = *m1.ring_it; + auto& 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); + } + + m_rings.erase(r2); + open_ring_its.remove(r2); + + if (r1->closed()) { + open_ring_its.remove(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 << " => no match\n"; + 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; } - - ++n; } + return false; } - void check_inner_outer(ProtoRing& ring) { - const osmium::NodeRef& min_node = ring.min_node(); + 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 << " check_inner_outer min_node=" << min_node << "\n"; + 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"; + } } - int count = 0; - int above = 0; + const auto connections = make_range(std::equal_range(xrings.cbegin(), + xrings.cend(), + location_to_ring_map{cand.stop_location})); - for (auto it = m_segment_list.begin(); it != m_segment_list.end() && it->first().location().x() <= min_node.location().x(); ++it) { - if (!ring.contains(*it)) { + 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 << " segments for count: " << *it; + 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 (it->to_left_of(min_node.location())) { - ++count; + if (c.closed()) { if (debug()) { - std::cerr << " counted\n"; + std::cerr << " found candidate\n"; } - } else { + candidates.push_back(c); + } else if (loc_done.count(c.stop_location) == 0) { if (debug()) { - std::cerr << " not counted\n"; + std::cerr << " recurse...\n"; } - } - if (it->first().location() == min_node.location()) { - if (it->second().location().y() > min_node.location().y()) { - ++above; + loc_done.insert(c.stop_location); + find_candidates(candidates, loc_done, xrings, c); + if (debug()) { + std::cerr << " ...back\n"; } + } else if (debug()) { + std::cerr << " loop found\n"; } - if (it->second().location() == min_node.location()) { - if (it->first().location().y() > min_node.location().y()) { - ++above; + } + } + } + + /** + * 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& a, const location_to_ring_map& b) { + return a.ring().min_segment() < b.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 << " count=" << count << " above=" << above << "\n"; + 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"; + } + } } - count += above % 2; + // 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& a, const candidate& b) { + return std::abs(a.sum) < std::abs(b.sum); + }) : + std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) { + return std::abs(a.sum) < std::abs(b.sum); + }); - if (count % 2) { - ring.set_inner(); + 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; + for (auto it = chosen_cand->rings.begin() + 1; it != chosen_cand->rings.end(); ++it) { + merge_two_rings(open_ring_its, first_ring, it->first); } - } - void check_inner_outer_roles() { if (debug()) { - std::cerr << " check_inner_outer_roles\n"; + std::cerr << " Merged to " << first_ring.ring() << "\n"; } - for (const auto ringptr : m_outer_rings) { - for (const auto& segment : ringptr->segments()) { - if (!segment.role_outer()) { - ++m_inner_outer_mismatches; - if (debug()) { - std::cerr << " segment " << segment << " from way " << segment.way()->id() << " should have role 'outer'\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& a, const slocation& b) { + return a.location(m_segment_list, location) < b.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; } - if (m_config.problem_reporter) { - m_config.problem_reporter->report_role_should_be_outer(segment.way()->id(), segment.first().location(), segment.second().location()); + } + } + } + + // 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; } } } } - for (const auto ringptr : m_inner_rings) { - for (const auto& segment : ringptr->segments()) { - if (!segment.role_inner()) { - ++m_inner_outer_mismatches; + + // 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 << " segment " << segment << " from way " << segment.way()->id() << " should have role 'inner'\n"; + std::cerr << " After joining obvious cases there are still " << open_ring_its.size() << " open rings\n"; } - if (m_config.problem_reporter) { - m_config.problem_reporter->report_role_should_be_inner(segment.way()->id(), segment.first().location(), segment.second().location()); + 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(); - m_segment_list.erase_duplicate_segments(); + 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. - if (m_segment_list.find_intersections(m_config.problem_reporter)) { + 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; } - // Now iterator over all segments and add them to rings. Each segment - // is tacked on to either end of an existing ring if possible, or a - // new ring is started with it. - for (const auto& segment : m_segment_list) { + // 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 << " checking segment " << segment << "\n"; + std::cerr << " Found split locations:\n"; } - if (!add_to_existing_ring(segment)) { + 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& a, const slocation& b) { + return a.location(m_segment_list, location) < b.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 << " new ring for segment " << segment << "\n"; + std::cerr << " " << location << "\n"; } - m_rings.emplace_back(segment); } } - if (debug()) { - std::cerr << " Rings:\n"; - for (const auto& ring : m_rings) { - std::cerr << " " << ring; - if (ring.closed()) { - std::cerr << " (closed)"; - } - std::cerr << "\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; - if (check_for_open_rings()) { + timer_simple_case.start(); + create_rings_simple_case(); + timer_simple_case.stop(); + } else { if (debug()) { - std::cerr << " not all rings are closed\n"; + std::cerr << " Found split locations -> using complex algorithm\n"; } - return false; + ++m_stats.area_touching_rings_case; + + timer_complex_case.start(); + if (!create_rings_complex_case()) { + return false; + } + timer_complex_case.stop(); } - if (debug()) { - std::cerr << " Find inner/outer...\n"; + // 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(); } - if (m_rings.size() == 1) { - m_outer_rings.push_back(&m_rings.front()); + 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 { - for (auto& ring : m_rings) { - check_inner_outer(ring); - if (ring.outer()) { - if (!ring.is_cw()) { - ring.reverse(); - } - m_outer_rings.push_back(&ring); - } else { - if (ring.is_cw()) { - ring.reverse(); - } - m_inner_rings.push_back(&ring); - } - } - - if (m_outer_rings.size() == 1) { - for (auto inner : m_inner_rings) { - m_outer_rings.front()->add_inner_ring(inner); - } - } else { - // sort outer rings by size, smallest first - std::sort(m_outer_rings.begin(), m_outer_rings.end(), [](ProtoRing* a, ProtoRing* b) { - return a->area() < b->area(); - }); - for (auto inner : m_inner_rings) { - for (auto outer : m_outer_rings) { - if (inner->is_in(outer)) { - outer->add_inner_ring(inner); - break; - } - } - } - } + std::cout << " 0" << + ' ' << timer_complex_case.elapsed_microseconds(); } - check_inner_outer_roles(); + 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 + + 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) { + add_rings_to_area(builder); + } + + if (report_ways()) { + m_config.problem_reporter->report_way(way); + } + + return area_okay || m_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(); + 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) { + add_rings_to_area(builder); + } + + if (report_ways()) { + for (const osmium::Way* way : members) { + m_config.problem_reporter->report_way(*way); + } + } + + return area_okay || m_config.create_empty_areas; + } + public: - typedef osmium::area::AssemblerConfig config_type; + using config_type = osmium::area::AssemblerConfig; explicit Assembler(const config_type& config) : m_config(config), - m_segment_list(config.debug) { + m_segment_list(config.debug_level > 1) { +#ifdef OSMIUM_WITH_TIMER + init_header(); +#endif } - ~Assembler() = default; + ~Assembler() noexcept = default; /** * Assemble an area from the given way. * The resulting area is put into the out_buffer. */ void operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) { + if (!m_config.create_way_polygons) { + return; + } + + 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()); + } + + // Ignore (but count) ways without segments. + if (way.nodes().size() < 2) { + ++m_stats.short_ways; + return; } 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()); } } - m_segment_list.extract_segments_from_way(way, "outer"); + ++m_stats.from_ways; + m_stats.duplicate_nodes += m_segment_list.extract_segments_from_way(m_config.problem_reporter, way); - if (debug()) { - std::cerr << "\nBuild way id()=" << way.id() << " segments.size()=" << m_segment_list.size() << "\n"; + if (m_config.debug_level > 0) { + std::cerr << "\nAssembling way " << way.id() << " containing " << m_segment_list.size() << " nodes\n"; } // Now create the Area object and add the attributes and tags // from the way. - { - osmium::builder::AreaBuilder builder(out_buffer); - builder.initialize_from_object(way); + if (create_area(out_buffer, way)) { + out_buffer.commit(); + } else { + out_buffer.rollback(); + } - if (create_rings()) { - add_tags_to_area(builder, way); - add_rings_to_area(builder); - } + if (debug()) { + std::cerr << "Done: " << m_stats << "\n"; } - out_buffer.commit(); } /** @@ -714,32 +1472,62 @@ namespace osmium { * 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. */ - void operator()(const osmium::Relation& relation, const std::vector& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) { + 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); + } + 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()); + if (m_config.problem_reporter) { m_config.problem_reporter->set_object(osmium::item_type::relation, relation.id()); } - m_segment_list.extract_segments_from_ways(relation, members, in_buffer); + if (relation.members().empty()) { + ++m_stats.no_way_in_mp_relation; + return; + } - if (debug()) { - std::cerr << "\nBuild relation id()=" << relation.id() << " members.size()=" << members.size() << " segments.size()=" << m_segment_list.size() << "\n"; + ++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(); + + if (m_stats.member_ways == 1) { + ++m_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"; } - size_t area_offset = out_buffer.committed(); + const size_t area_offset = out_buffer.committed(); // Now create the Area object and add the attributes and tags // from the relation. - { - osmium::builder::AreaBuilder builder(out_buffer); - builder.initialize_from_object(relation); - - if (create_rings()) { - add_tags_to_area(builder, relation); - add_rings_to_area(builder); + 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(); } + } else { + out_buffer.rollback(); } - out_buffer.commit(); const osmium::TagList& area_tags = out_buffer.get(area_offset).tags(); // tags of the area we just built @@ -748,27 +1536,33 @@ namespace osmium { // just built, add them to a list and later build areas for // them, too. std::vector ways_that_should_be_areas; - if (m_inner_outer_mismatches == 0) { - auto memit = relation.members().begin(); - for (size_t offset : members) { - if (!std::strcmp(memit->role(), "inner")) { - const osmium::Way& way = in_buffer.get(offset); + 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) { - auto d = std::count_if(way.tags().begin(), way.tags().end(), filter()); + 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().begin(), way.tags().end()); - osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().end(), way.tags().end()); - osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.begin(), area_tags.end()); - osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.end(), area_tags.end()); + 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); + } } } } } - ++memit; - } + }); + } + + if (debug()) { + std::cerr << "Done: " << m_stats << "\n"; } // Now build areas for all ways found in the last step. @@ -778,6 +1572,14 @@ namespace osmium { } } + /** + * 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; + } + }; // class Assembler } // namespace area 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 7c1555a33e1..b131a431756 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 @@ -34,11 +34,12 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include #include +#include #include #include @@ -53,108 +54,178 @@ namespace osmium { */ namespace detail { + class ProtoRing; + + enum class role_type : uint8_t { + unknown = 0, + outer = 1, + inner = 2, + empty = 3 + }; + /** - * This helper class for the Assembler class models a segment. - * Segments are the connection between - * two nodes and they all have their smaller coordinate at the - * beginning of the segment. Smaller, in this case, means smaller x - * coordinate, and if they are the same smaller y coordinate. + * This helper class for the Assembler class models a segment, + * the connection between two nodes. + * + * Internally segments have their smaller coordinate at the + * beginning of the segment. Smaller, in this case, means smaller + * x coordinate, and, if they are the same, smaller y coordinate. */ class NodeRefSegment { + // First node in order described above. osmium::NodeRef m_first; - osmium::NodeRef m_second; - /// Role of the member this segment was from. - const char* m_role; + // Second node in order described above. + osmium::NodeRef m_second; - /// Way this segment was from. + // Way this segment was from. const osmium::Way* m_way; - public: + // The ring this segment is part of. Initially nullptr, this + // will be filled in once we know which ring the segment is in. + ProtoRing* m_ring; - void swap_locations() { - using std::swap; - swap(m_first, m_second); - } + // The role of this segment from the member role. + role_type m_role; + + // Nodes have to be reversed to get the intended order. + bool m_reverse = false; - explicit NodeRefSegment() noexcept : + // We found the right direction for this segment in the ring. + // (This depends on whether it is an inner or outer ring.) + bool m_direction_done = false; + + public: + + NodeRefSegment() noexcept : m_first(), m_second(), - m_role(nullptr), - m_way(nullptr) { + m_way(nullptr), + m_ring(nullptr), + m_role(role_type::unknown) { } - explicit NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, const char* role, const osmium::Way* way) : + NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role = role_type::unknown, const osmium::Way* way = nullptr) noexcept : m_first(nr1), m_second(nr2), - m_role(role), - m_way(way) { + m_way(way), + m_ring(nullptr), + m_role(role) { if (nr2.location() < nr1.location()) { - swap_locations(); + using std::swap; + swap(m_first, m_second); } } - NodeRefSegment(const NodeRefSegment&) = default; - NodeRefSegment(NodeRefSegment&&) = default; + /** + * The ring this segment is a part of. nullptr if we don't + * have the ring yet. + */ + ProtoRing* ring() const noexcept { + return m_ring; + } - NodeRefSegment& operator=(const NodeRefSegment&) = default; - NodeRefSegment& operator=(NodeRefSegment&&) = default; + /** + * Returns true if the segment has already been placed in a + * ring. + */ + bool is_done() const noexcept { + return m_ring != nullptr; + } - ~NodeRefSegment() = default; + void set_ring(ProtoRing* ring) noexcept { + assert(ring); + m_ring = ring; + } - /// Return first NodeRef of Segment according to sorting order (bottom left to top right). + bool is_reverse() const noexcept { + return m_reverse; + } + + void reverse() noexcept { + m_reverse = !m_reverse; + } + + bool is_direction_done() const noexcept { + return m_direction_done; + } + + void mark_direction_done() noexcept { + m_direction_done = true; + } + + void mark_direction_not_done() noexcept { + m_direction_done = false; + } + + /** + * Return first NodeRef of Segment according to sorting + * order (bottom left to top right). + */ const osmium::NodeRef& first() const noexcept { return m_first; } - /// Return second NodeRef of Segment according to sorting order (bottom left to top right). + /** + * Return second NodeRef of Segment according to sorting + * order (bottom left to top right). + */ const osmium::NodeRef& second() const noexcept { return m_second; } - bool to_left_of(const osmium::Location& location) const { - // std::cerr << "segment " << first() << "--" << second() << " to_left_of(" << location << "\n"; - - if (first().location() == location || second().location() == location) { - return false; - } - - const std::pair mm = std::minmax(first().location(), second().location(), [](const osmium::Location a, const osmium::Location b) { - return a.y() < b.y(); - }); - - if (mm.first.y() >= location.y() || mm.second.y() < location.y() || first().location().x() > location.x()) { - // std::cerr << " false\n"; - return false; - } + /** + * Return real first NodeRef of Segment. + */ + const osmium::NodeRef& start() const noexcept { + return m_reverse ? m_second : m_first; + } - int64_t ax = mm.first.x(); - int64_t bx = mm.second.x(); - int64_t lx = location.x(); - int64_t ay = mm.first.y(); - int64_t by = mm.second.y(); - int64_t ly = location.y(); - return ((bx - ax)*(ly - ay) - (by - ay)*(lx - ax)) <= 0; + /** + * Return real second NodeRef of Segment. + */ + const osmium::NodeRef& stop() const noexcept { + return m_reverse ? m_first : m_second; } bool role_outer() const noexcept { - return !strcmp(m_role, "outer"); + return m_role == role_type::outer; } bool role_inner() const noexcept { - return !strcmp(m_role, "inner"); + return m_role == role_type::inner; + } + + bool role_empty() const noexcept { + return m_role == role_type::empty; + } + + const char* role_name() const noexcept { + static const char* names[] = { "unknown", "outer", "inner", "empty" }; + return names[int(m_role)]; } const osmium::Way* way() const noexcept { return m_way; } + /** + * The "determinant" of this segment. Used for calculating + * the winding order or a ring. + */ + int64_t det() const noexcept { + const vec a{start()}; + const vec b{stop()}; + return a * b; + } + }; // class NodeRefSegment /// NodeRefSegments are equal if both their locations are equal inline bool operator==(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return lhs.first().location() == rhs.first().location() && lhs.second().location() == rhs.second().location(); + return lhs.first().location() == rhs.first().location() && + lhs.second().location() == rhs.second().location(); } inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { @@ -162,12 +233,33 @@ namespace osmium { } /** - * NodeRefSegments are "smaller" if they are to the left and down of another - * segment. The first() location is checked first() and only if they have the - * same first() location the second() location is taken into account. + * A NodeRefSegment is "smaller" if the first point is to the + * left and down of the first point of the second segment. + * If both first points are the same, the segment with the higher + * slope comes first. If the slope is the same, the shorter + * segment comes first. */ inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return (lhs.first().location() == rhs.first().location() && lhs.second().location() < rhs.second().location()) || lhs.first().location() < rhs.first().location(); + if (lhs.first().location() == rhs.first().location()) { + const vec p0{lhs.first().location()}; + const vec p1{lhs.second().location()}; + const vec q0{rhs.first().location()}; + const vec q1{rhs.second().location()}; + const vec p = p1 - p0; + const vec q = q1 - q0; + + if (p.x == 0 && q.x == 0) { + return p.y < q.y; + } + + const auto a = p.y * q.x; + const auto b = q.y * p.x; + if (a == b) { + return p.x < q.x; + } + return a > b; + } + return lhs.first().location() < rhs.first().location(); } inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { @@ -184,7 +276,10 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& out, const NodeRefSegment& segment) { - return out << segment.first() << "--" << segment.second(); + return out << segment.start() << "--" << segment.stop() + << "[" << (segment.is_reverse() ? 'R' : '_') + << (segment.is_done() ? 'd' : '_') + << (segment.is_direction_done() ? 'D' : '_') << "]"; } inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { @@ -194,7 +289,7 @@ namespace osmium { return false; } - inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) { + 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) { @@ -210,55 +305,94 @@ namespace osmium { * might be slightly different than the numerically correct * location. * - * This function uses integer arithmentic as much as possible and + * This function uses integer arithmetic as much as possible and * will not work if the segments are longer than about half the * planet. This shouldn't happen with real data, so it isn't a big * problem. * - * If the segments touch in one of their endpoints, it doesn't - * count as an intersection. + * If the segments touch in one or both of their endpoints, it + * doesn't count as an intersection. * * If the segments intersect not in a single point but in multiple - * points, ie if they overlap, this is NOT detected. + * points, ie if they are collinear and overlap, the smallest + * of the endpoints that is in the overlapping section is returned. * * @returns Undefined osmium::Location if there is no intersection * or a defined Location if the segments intersect. */ - inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) { - if (s1.first().location() == s2.first().location() || - s1.first().location() == s2.second().location() || - s1.second().location() == s2.first().location() || - s1.second().location() == s2.second().location()) { + inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { + // See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect + // for some hints about how the algorithm works. + const vec p0{s1.first()}; + const vec p1{s1.second()}; + const vec q0{s2.first()}; + const vec q1{s2.second()}; + + if ((p0 == q0 && p1 == q1) || + (p0 == q1 && p1 == q0)) { + // segments are the same return osmium::Location(); } - int64_t s1ax = s1.first().x(); - int64_t s1ay = s1.first().y(); - int64_t s1bx = s1.second().x(); - int64_t s1by = s1.second().y(); - int64_t s2ax = s2.first().x(); - int64_t s2ay = s2.first().y(); - int64_t s2bx = s2.second().x(); - int64_t s2by = s2.second().y(); - - int64_t d = (s2by - s2ay) * (s1bx - s1ax) - - (s2bx - s2ax) * (s1by - s1ay); + const vec pd = p1 - p0; + const int64_t d = pd * (q1 - q0); if (d != 0) { - int64_t na = (s2bx - s2ax) * (s1ay - s2ay) - - (s2by - s2ay) * (s1ax - s2ax); + // segments are not collinear + + if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) { + // touching at an end point + return osmium::Location(); + } + + // intersection in a point - int64_t nb = (s1bx - s1ax) * (s1ay - s2ay) - - (s1by - s1ay) * (s1ax - s2ax); + const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) - + (q1.y - q0.y) * (p0.x - q0.x); + + const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) - + (p1.y - p0.y) * (p0.x - q0.x); if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) || (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(); + } - double ua = double(na) / d; - int32_t ix = int32_t(s1ax + ua*(s1bx - s1ax)); - int32_t iy = int32_t(s1ay + ua*(s1by - s1ay)); + // segments are collinear + + if (pd * (q0 - p0) == 0) { + // segments are on the same line + + struct seg_loc { + int segment; + osmium::Location location; + }; + + seg_loc sl[4]; + sl[0] = {0, s1.first().location() }; + sl[1] = {0, s1.second().location()}; + sl[2] = {1, s2.first().location() }; + sl[3] = {1, s2.second().location()}; + + std::sort(sl, sl+4, [](const seg_loc& a, const seg_loc& b) { + return a.location < b.location; + }); + + if (sl[1].location == sl[2].location) { + return osmium::Location(); + } - return osmium::Location(ix, iy); + if (sl[0].segment != sl[1].segment) { + if (sl[0].location == sl[1].location) { + return sl[2].location; + } else { + return sl[1].location; + } } } 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 c4edf40183f..bce681711fb 100644 --- a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp +++ b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp @@ -34,10 +34,9 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include -#include #include #include @@ -47,6 +46,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Way; + namespace area { namespace detail { @@ -58,214 +59,155 @@ namespace osmium { public: - typedef std::vector segments_type; + using segments_type = std::vector; private: - // segments in this ring + // Segments in this ring. segments_type m_segments; - bool m_outer {true}; - - // if this is an outer ring, these point to it's inner rings (if any) + // If this is an outer ring, these point to it's inner rings + // (if any). std::vector m_inner; - public: - - explicit ProtoRing(const NodeRefSegment& segment) noexcept : - m_segments() { - add_segment_back(segment); - } - - explicit ProtoRing(segments_type::const_iterator sbegin, segments_type::const_iterator send) : - m_segments(static_cast(std::distance(sbegin, send))) { - std::copy(sbegin, send, m_segments.begin()); - } - - bool outer() const noexcept { - return m_outer; - } + // The smallest segment. Will be kept current whenever a new + // segment is added to the ring. + NodeRefSegment* m_min_segment; - void set_inner() noexcept { - m_outer = false; - } - - segments_type& segments() noexcept { - return m_segments; - } + // If this is an inner ring, points to the outer ring. + ProtoRing* m_outer_ring; - const segments_type& segments() const noexcept { - return m_segments; - } + int64_t m_sum; - void remove_segments(segments_type::iterator sbegin, segments_type::iterator send) { - m_segments.erase(sbegin, send); - } + public: - void add_segment_front(const NodeRefSegment& segment) { - m_segments.insert(m_segments.begin(), segment); + explicit ProtoRing(NodeRefSegment* segment) noexcept : + m_segments(), + m_inner(), + m_min_segment(segment), + m_outer_ring(nullptr), + m_sum(0) { + add_segment_back(segment); } - void add_segment_back(const NodeRefSegment& segment) { + void add_segment_back(NodeRefSegment* segment) { + assert(segment); + if (*segment < *m_min_segment) { + m_min_segment = segment; + } m_segments.push_back(segment); + segment->set_ring(this); + m_sum += segment->det(); } - const NodeRefSegment& get_segment_front() const { - return m_segments.front(); + NodeRefSegment* min_segment() const noexcept { + return m_min_segment; } - NodeRefSegment& get_segment_front() { - return m_segments.front(); + ProtoRing* outer_ring() const noexcept { + return m_outer_ring; } - const NodeRef& get_node_ref_front() const { - return get_segment_front().first(); + void set_outer_ring(ProtoRing* outer_ring) noexcept { + assert(outer_ring); + assert(m_inner.empty()); + m_outer_ring = outer_ring; } - const NodeRefSegment& get_segment_back() const { - return m_segments.back(); + const std::vector& inner_rings() const noexcept { + return m_inner; } - NodeRefSegment& get_segment_back() { - return m_segments.back(); + void add_inner_ring(ProtoRing* ring) { + assert(ring); + assert(!m_outer_ring); + m_inner.push_back(ring); } - const NodeRef& get_node_ref_back() const { - return get_segment_back().second(); + bool is_outer() const noexcept { + return !m_outer_ring; } - bool closed() const { - return m_segments.front().first().location() == m_segments.back().second().location(); + const segments_type& segments() const noexcept { + return m_segments; } - int64_t sum() const { - int64_t sum = 0; - - for (const auto& segment : m_segments) { - sum += static_cast(segment.first().location().x()) * static_cast(segment.second().location().y()) - - static_cast(segment.second().location().x()) * static_cast(segment.first().location().y()); - } + const NodeRef& get_node_ref_start() const noexcept { + return m_segments.front()->start(); + } - return sum; + const NodeRef& get_node_ref_stop() const noexcept { + return m_segments.back()->stop(); } - bool is_cw() const { - return sum() <= 0; + bool closed() const noexcept { + return get_node_ref_start().location() == get_node_ref_stop().location(); } - int64_t area() const { - return std::abs(sum()) / 2; + void reverse() { + std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) { + segment->reverse(); + }); + std::reverse(m_segments.begin(), m_segments.end()); + m_sum = -m_sum; } - void swap_segments(ProtoRing& other) { - using std::swap; - swap(m_segments, other.m_segments); + void mark_direction_done() { + std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) { + segment->mark_direction_done(); + }); } - void add_inner_ring(ProtoRing* ring) { - m_inner.push_back(ring); + bool is_cw() const noexcept { + return m_sum <= 0; } - const std::vector& inner_rings() const { - return m_inner; + int64_t sum() const noexcept { + return m_sum; } - void print(std::ostream& out) const { - out << "["; - bool first = true; - for (const auto& segment : m_segments) { - if (first) { - out << segment.first().ref(); - } - out << ',' << segment.second().ref(); - first = false; + void fix_direction() noexcept { + if (is_cw() == is_outer()) { + reverse(); } - out << "]"; } - void reverse() { - std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment& segment) { - segment.swap_locations(); + void reset() { + m_inner.clear(); + m_outer_ring = nullptr; + std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) { + segment->mark_direction_not_done(); }); - std::reverse(m_segments.begin(), m_segments.end()); } - /** - * Merge other ring to end of this ring. - */ - void merge_ring(const ProtoRing& other, bool debug) { - if (debug) { - std::cerr << " MERGE rings "; - print(std::cerr); - std::cerr << " to "; - other.print(std::cerr); - std::cerr << "\n"; - } - m_segments.insert(m_segments.end(), other.m_segments.begin(), other.m_segments.end()); - if (debug) { - std::cerr << " result ring: "; - print(std::cerr); - std::cerr << "\n"; + void get_ways(std::set& ways) const { + for (const auto& segment : m_segments) { + ways.insert(segment->way()); } } - void merge_ring_reverse(const ProtoRing& other, bool debug) { - if (debug) { - std::cerr << " MERGE rings (reverse) "; - print(std::cerr); - std::cerr << " to "; - other.print(std::cerr); - std::cerr << "\n"; - } - size_t n = m_segments.size(); - m_segments.resize(n + other.m_segments.size()); - std::transform(other.m_segments.rbegin(), other.m_segments.rend(), m_segments.begin() + static_cast(n), [](NodeRefSegment segment) { - segment.swap_locations(); - return segment; - }); - if (debug) { - std::cerr << " result ring: "; - print(std::cerr); - std::cerr << "\n"; + void join_forward(ProtoRing& other) { + for (NodeRefSegment* segment : other.m_segments) { + add_segment_back(segment); } } - const NodeRef& min_node() const { - auto it = std::min_element(m_segments.begin(), m_segments.end()); - if (location_less()(it->first(), it->second())) { - return it->first(); - } else { - return it->second(); + void join_backward(ProtoRing& other) { + for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) { + (*it)->reverse(); + add_segment_back(*it); } } - bool is_in(ProtoRing* outer) { - osmium::Location testpoint = segments().front().first().location(); - bool is_in = false; - - for (size_t i = 0, j = outer->segments().size()-1; i < outer->segments().size(); j = i++) { - if (((outer->segments()[i].first().location().y() > testpoint.y()) != (outer->segments()[j].first().location().y() > testpoint.y())) && - (testpoint.x() < (outer->segments()[j].first().location().x() - outer->segments()[i].first().location().x()) * (testpoint.y() - outer->segments()[i].first().location().y()) / (outer->segments()[j].first().location().y() - outer->segments()[i].first().location().y()) + outer->segments()[i].first().location().x()) ) { - is_in = !is_in; - } + void print(std::ostream& out) const { + out << "["; + if (!m_segments.empty()) { + out << m_segments.front()->start().ref(); } - - return is_in; - } - - void get_ways(std::set& ways) { for (const auto& segment : m_segments) { - ways.insert(segment.way()); - } - } - - bool contains(const NodeRefSegment& segment) const { - for (const auto& s : m_segments) { - if (s == segment || (s.first() == segment.second() && s.second() == segment.first())) { - return true; - } + out << ',' << segment->stop().ref(); } - return false; + out << "]-" << (is_outer() ? "OUTER" : "INNER"); } }; // class ProtoRing 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 05e0cd8d481..a4361e0e130 100644 --- a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp +++ b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp @@ -35,12 +35,16 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include +#include +#include #include -#include #include -#include +#include +#include #include #include #include @@ -52,6 +56,24 @@ namespace osmium { namespace detail { + /** + * Iterate over all relation members and the vector of ways at the + * same time and call given function with the relation member and + * way as parameter. This takes into account that there might be + * non-way members in the relation. + */ + template + inline void for_each_member(const osmium::Relation& relation, const std::vector& ways, F&& func) { + auto way_it = ways.cbegin(); + for (const osmium::RelationMember& member : relation.members()) { + if (member.type() == osmium::item_type::way) { + assert(way_it != ways.cend()); + func(member, **way_it); + ++way_it; + } + } + } + /** * This is a helper class for the area assembler. It models * a list of segments. @@ -64,6 +86,51 @@ namespace osmium { bool m_debug; + static role_type parse_role(const char* role) noexcept { + if (role[0] == '\0') { + return role_type::empty; + } else if (!std::strcmp(role, "outer")) { + return role_type::outer; + } else if (!std::strcmp(role, "inner")) { + return role_type::inner; + } + return role_type::unknown; + } + + /** + * 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(), 0, [](size_t sum, const osmium::Way* way) { + if (way->nodes().empty()) { + return sum; + } else { + 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; + + osmium::NodeRef previous_nr; + for (const osmium::NodeRef& nr : way.nodes()) { + if (previous_nr.location()) { + if (previous_nr.location() != nr.location()) { + m_segments.emplace_back(previous_nr, nr, role, &way); + } else { + ++duplicate_nodes; + if (problem_reporter) { + problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location()); + } + } + } + previous_nr = nr; + } + + return duplicate_nodes; + } + public: explicit SegmentList(bool debug) noexcept : @@ -84,12 +151,31 @@ namespace osmium { return m_segments.size(); } + /// Is the segment list empty? bool empty() const noexcept { return m_segments.empty(); } - typedef slist_type::const_iterator const_iterator; - typedef slist_type::iterator iterator; + using const_iterator = slist_type::const_iterator; + using iterator = slist_type::iterator; + + NodeRefSegment& front() { + return m_segments.front(); + } + + NodeRefSegment& back() { + return m_segments.back(); + } + + const NodeRefSegment& operator[](size_t n) const noexcept { + assert(n < m_segments.size()); + return m_segments[n]; + } + + NodeRefSegment& operator[](size_t n) noexcept { + assert(n < m_segments.size()); + return m_segments[n]; + } iterator begin() noexcept { return m_segments.begin(); @@ -115,11 +201,6 @@ namespace osmium { m_debug = debug; } - /// Clear the list of segments. All segments are removed. - void clear() { - m_segments.clear(); - } - /// Sort the list of segments. void sort() { std::sort(m_segments.begin(), m_segments.end()); @@ -128,32 +209,37 @@ namespace osmium { /** * Extract segments from given way and add them to the list. * - * Segments connecting two nodes with the same location (ie same - * node or different node with same location) are removed. - * - * XXX should two nodes with same location be reported? + * Segments connecting two nodes with the same location (ie + * same node or different nodes with same location) are + * removed after reporting the duplicate node. */ - void extract_segments_from_way(const osmium::Way& way, const char* role) { - osmium::NodeRef last_nr; - for (const osmium::NodeRef& nr : way.nodes()) { - if (last_nr.location() && last_nr.location() != nr.location()) { - m_segments.emplace_back(last_nr, nr, role, &way); - } - last_nr = nr; + uint32_t extract_segments_from_way(osmium::area::ProblemReporter* problem_reporter, 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); } /** * Extract all segments from all ways that make up this * multipolygon relation and add them to the list. */ - void extract_segments_from_ways(const osmium::Relation& relation, const std::vector& members, const osmium::memory::Buffer& in_buffer) { - auto member_it = relation.members().begin(); - for (size_t offset : members) { - const osmium::Way& way = in_buffer.get(offset); - extract_segments_from_way(way, member_it->role()); - ++member_it; + 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()); + + const 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())); + }); + + return duplicate_nodes; } /** @@ -162,17 +248,35 @@ namespace osmium { * same segment. So if there are three, for instance, two will * be removed and one will be left. */ - void erase_duplicate_segments() { + uint32_t erase_duplicate_segments(osmium::area::ProblemReporter* problem_reporter) { + uint32_t duplicate_segments = 0; + while (true) { auto it = std::adjacent_find(m_segments.begin(), m_segments.end()); if (it == m_segments.end()) { - return; + break; } if (m_debug) { std::cerr << " erase duplicate segment: " << *it << "\n"; } + + // Only count and report duplicate segments if they + // belong to the same way or if they don't both have + // the role "inner". Those cases are definitely wrong. + // If the duplicate segments belong to different + // "inner" ways, they could be touching inner rings + // which are perfectly okay. Note that for this check + // the role has to be correct in the member data. + if (it->way() == std::next(it)->way() || !it->role_inner() || !std::next(it)->role_inner()) { + ++duplicate_segments; + if (problem_reporter) { + problem_reporter->report_duplicate_segment(it->first(), it->second()); + } + } m_segments.erase(it, it+2); } + + return duplicate_segments; } /** @@ -182,14 +286,14 @@ namespace osmium { * reported to this object. * @returns true if there are intersections. */ - bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const { + uint32_t find_intersections(osmium::area::ProblemReporter* problem_reporter) const { if (m_segments.empty()) { - return false; + return 0; } - bool found_intersections = false; + uint32_t found_intersections = 0; - for (auto it1 = m_segments.begin(); it1 != m_segments.end()-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; @@ -203,12 +307,13 @@ namespace osmium { if (y_range_overlap(s1, s2)) { osmium::Location intersection = calculate_intersection(s1, s2); if (intersection) { - found_intersections = true; + ++found_intersections; if (m_debug) { std::cerr << " segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n"; } if (problem_reporter) { - problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(), s2.way()->id(), s2.first().location(), s2.second().location(), intersection); + problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(), + s2.way()->id(), s2.first().location(), s2.second().location(), intersection); } } } diff --git a/third_party/libosmium/include/osmium/area/detail/vector.hpp b/third_party/libosmium/include/osmium/area/detail/vector.hpp new file mode 100644 index 00000000000..44983cc7547 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/detail/vector.hpp @@ -0,0 +1,121 @@ +#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP +#define OSMIUM_AREA_DETAIL_VECTOR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +#include +#include + +namespace osmium { + + namespace area { + + namespace detail { + + /** + * This helper class models a 2D vector in the mathematical sense. + * It uses 64 bit integers internally which has enough precision + * for most operations with inputs based on 32 bit locations. + */ + struct vec { + + int64_t x; + int64_t y; + + constexpr vec(int64_t a, int64_t b) noexcept : + x(a), + y(b) { + } + + constexpr explicit vec(const osmium::Location& l) noexcept : + x(l.x()), + y(l.y()) { + } + + constexpr explicit vec(const osmium::NodeRef& nr) noexcept : + x(nr.x()), + y(nr.y()) { + } + + }; // struct vec + + // addition + constexpr inline vec operator+(const vec& a, const vec& b) noexcept { + return vec{a.x + b.x, a.y + b.y}; + } + + // subtraction + constexpr inline vec operator-(const vec& a, const vec& b) noexcept { + return vec{a.x - b.x, a.y - b.y}; + } + + // cross product + constexpr inline int64_t operator*(const vec& a, const vec& b) noexcept { + return a.x * b.y - a.y * b.x; + } + + // scale vector + constexpr inline vec operator*(double s, const vec& v) noexcept { + return vec{int64_t(s * v.x), int64_t(s * v.y)}; + } + + // scale vector + constexpr inline vec operator*(const vec& v, double s) noexcept { + return vec{int64_t(s * v.x), int64_t(s * v.y)}; + } + + // equality + constexpr inline bool operator==(const vec& a, const vec& b) noexcept { + return a.x == b.x && a.y == b.y; + } + + // inequality + constexpr inline bool operator!=(const vec& a, const vec& b) noexcept { + return !(a == b); + } + + template + inline std::basic_ostream& operator<<(std::basic_ostream& out, const vec& v) { + return out << '(' << v.x << ',' << v.y << ')'; + } + + } // namespace detail + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP diff --git a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp index 81a28b24b97..8b370521b87 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp @@ -34,11 +34,11 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include +#include #include #include #include @@ -47,7 +47,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include namespace osmium { @@ -74,13 +73,15 @@ namespace osmium { template class MultipolygonCollector : public osmium::relations::Collector, false, true, false> { - typedef typename osmium::relations::Collector, false, true, false> collector_type; + using collector_type = osmium::relations::Collector, false, true, false>; - typedef typename TAssembler::config_type assembler_config_type; + using assembler_config_type = typename TAssembler::config_type; const assembler_config_type m_assembler_config; osmium::memory::Buffer m_output_buffer; + osmium::area::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; @@ -107,6 +108,10 @@ namespace osmium { m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) { } + const osmium::area::area_stats& stats() const noexcept { + return m_stats; + } + /** * We are interested in all relations tagged with type=multipolygon * or type=boundary. @@ -121,7 +126,7 @@ namespace osmium { return false; } - if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) { + if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) { return true; } @@ -155,26 +160,32 @@ namespace osmium { // way is closed and has enough nodes, build simple multipolygon TAssembler assembler(m_assembler_config); assembler(way, m_output_buffer); + m_stats += assembler.stats(); possibly_flush_output_buffer(); } - } catch (osmium::invalid_location&) { + } catch (const osmium::invalid_location&) { // XXX ignore } } void complete_relation(osmium::relations::RelationMeta& relation_meta) { const osmium::Relation& relation = this->get_relation(relation_meta); - std::vector offsets; + const osmium::memory::Buffer& buffer = this->members_buffer(); + + std::vector ways; for (const auto& member : relation.members()) { if (member.ref() != 0) { - offsets.push_back(this->get_offset(member.type(), member.ref())); + const size_t offset = this->get_offset(member.type(), member.ref()); + ways.push_back(&buffer.get(offset)); } } + try { TAssembler assembler(m_assembler_config); - assembler(relation, offsets, this->members_buffer(), m_output_buffer); + assembler(relation, ways, m_output_buffer); + m_stats += assembler.stats(); possibly_flush_output_buffer(); - } catch (osmium::invalid_location&) { + } catch (const osmium::invalid_location&) { // XXX ignore } } diff --git a/third_party/libosmium/include/osmium/area/problem_reporter.hpp b/third_party/libosmium/include/osmium/area/problem_reporter.hpp index 1dde85ba9d8..6c562314e1d 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter.hpp @@ -33,12 +33,17 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include #include #include namespace osmium { + class NodeRef; + class Way; + namespace area { /** @@ -62,6 +67,9 @@ namespace osmium { // ID of the relation/way we are currently working on osmium::object_id_type m_object_id; + // Number of nodes in the area + size_t m_nodes; + public: ProblemReporter() = default; @@ -79,6 +87,10 @@ namespace osmium { m_object_id = object_id; } + void set_nodes(size_t nodes) noexcept { + m_nodes = nodes; + } + // Disable "unused-parameter" warning, so that the compiler will not complain. // We can't remove the parameter names, because then doxygen will complain. #pragma GCC diagnostic push @@ -87,13 +99,23 @@ namespace osmium { /** * Report a duplicate node, ie. two nodes with the same location. * - * @param node_id1 ID of the first node. - * @param node_id2 ID of the second node. - * @param location Location of both nodes. + * @param node_id1 ID of the first node. + * @param node_id2 ID of the second node. + * @param location Location of both nodes. */ virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) { } + /** + * Report a node/location where rings touch. This is often wrong, + * but not necessarily so. + * + * @param node_id ID of the node. + * @param location Location of the node. + */ + virtual void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) { + } + /** * Report an intersection between two segments. * @@ -109,21 +131,33 @@ namespace osmium { osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) { } + /** + * 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_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) { + } + /** * Report an open ring. * - * @param end1 Location of the first open end. - * @param end2 Location of the second open end. + * @param nr NodeRef of one end of the ring. + * @param way Optional pointer to way the end node is in. */ - virtual void report_ring_not_closed(osmium::Location end1, osmium::Location end2) { + virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) { } /** * Report a segment that should have role "outer", but has a different role. * - * @param way_id ID of the way this segment is in. - * @param seg_start Start of the segment with the wrong role. - * @param seg_end End of the segment with the wrong role. + * @param way_id ID of the way this segment is in. + * @param seg_start Start of the segment with the wrong role. + * @param seg_end End of the segment with the wrong role. */ virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) { } @@ -138,6 +172,32 @@ namespace osmium { virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) { } + /** + * Report a way that is in multiple rings. + * + * @param way The way. + */ + virtual void report_way_in_multiple_rings(const osmium::Way& way) { + } + + /** + * Report a way with role inner that has the same tags as the + * relation or outer ways. + * + * @param way The way. + */ + virtual void report_inner_with_same_tags(const osmium::Way& way) { + } + + /** + * In addition to reporting specific problems, this is used to + * report all ways belonging to areas having problems. + * + * @param way The way + */ + virtual void report_way(const osmium::Way& way) { + } + #pragma GCC diagnostic pop }; // class ProblemReporter 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 abadd965363..009a5f4baf2 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp @@ -42,6 +42,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class NodeRef; + class Way; + namespace area { class ProblemReporterException : public ProblemReporterStream { @@ -62,6 +65,12 @@ namespace osmium { throw std::runtime_error(m_sstream.str()); } + void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override { + m_sstream.str(); + ProblemReporterStream::report_touching_ring(node_id, location); + 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(); @@ -69,9 +78,15 @@ namespace osmium { throw std::runtime_error(m_sstream.str()); } - void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override { + void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + m_sstream.str(); + ProblemReporterStream::report_duplicate_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(); - ProblemReporterStream::report_ring_not_closed(end1, end2); + ProblemReporterStream::report_ring_not_closed(nr, way); throw std::runtime_error(m_sstream.str()); } @@ -87,6 +102,18 @@ namespace osmium { throw std::runtime_error(m_sstream.str()); } + void report_way_in_multiple_rings(const osmium::Way& way) override { + m_sstream.str(); + ProblemReporterStream::report_way_in_multiple_rings(way); + throw std::runtime_error(m_sstream.str()); + } + + void report_inner_with_same_tags(const osmium::Way& way) override { + m_sstream.str(); + ProblemReporterStream::report_inner_with_same_tags(way); + throw std::runtime_error(m_sstream.str()); + } + }; // class ProblemReporterException } // namespace area 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 d58fe559cca..0889a580916 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp @@ -49,8 +49,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include +#include +#include #include +#include namespace osmium { @@ -66,26 +70,34 @@ namespace osmium { gdalcpp::Layer m_layer_perror; gdalcpp::Layer m_layer_lerror; + gdalcpp::Layer m_layer_ways; + + void set_object(gdalcpp::Feature& feature) { + 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)); - feature.set_field("id1", static_cast(id1)); - feature.set_field("id2", static_cast(id2)); - feature.set_field("problem_type", problem_type); + set_object(feature); + feature.set_field("id1", double(id1)); + feature.set_field("id2", double(id2)); + feature.set_field("problem", problem_type); feature.add_to_layer(); } void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) { - std::unique_ptr ogr_point1 = m_ogr_factory.create_point(loc1); - std::unique_ptr ogr_point2 = m_ogr_factory.create_point(loc2); - std::unique_ptr ogr_linestring = std::unique_ptr(new OGRLineString()); - ogr_linestring->addPoint(ogr_point1.get()); - ogr_linestring->addPoint(ogr_point2.get()); + auto ogr_linestring = std::unique_ptr{new OGRLineString{}}; + ogr_linestring->addPoint(loc1.lon(), loc1.lat()); + ogr_linestring->addPoint(loc2.lon(), loc2.lat()); 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)); - feature.set_field("problem_type", problem_type); + feature.set_field("problem", problem_type); feature.add_to_layer(); } @@ -93,15 +105,36 @@ namespace osmium { explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) : m_layer_perror(dataset, "perrors", wkbPoint), - m_layer_lerror(dataset, "lerrors", wkbLineString) { - - m_layer_perror.add_field("id1", OFTReal, 10); - m_layer_perror.add_field("id2", OFTReal, 10); - m_layer_perror.add_field("problem_type", OFTString, 30); - - m_layer_lerror.add_field("id1", OFTReal, 10); - m_layer_lerror.add_field("id2", OFTReal, 10); - m_layer_lerror.add_field("problem_type", OFTString, 30); + m_layer_lerror(dataset, "lerrors", wkbLineString), + m_layer_ways(dataset, "ways", wkbLineString) { + + // 64bit integers are not supported in GDAL < 2, so we + // are using a workaround here in fields where we expect + // node IDs, we use real numbers. + m_layer_perror + .add_field("obj_type", OFTString, 1) + .add_field("obj_id", OFTInteger, 10) + .add_field("nodes", OFTInteger, 8) + .add_field("id1", OFTReal, 12, 1) + .add_field("id2", OFTReal, 12, 1) + .add_field("problem", OFTString, 30) + ; + + m_layer_lerror + .add_field("obj_type", OFTString, 1) + .add_field("obj_id", OFTInteger, 10) + .add_field("nodes", OFTInteger, 8) + .add_field("id1", OFTReal, 12, 1) + .add_field("id2", OFTReal, 12, 1) + .add_field("problem", OFTString, 30) + ; + + m_layer_ways + .add_field("obj_type", OFTString, 1) + .add_field("obj_id", OFTInteger, 10) + .add_field("way_id", OFTInteger, 10) + .add_field("nodes", OFTInteger, 8) + ; } ~ProblemReporterOGR() override = default; @@ -110,24 +143,82 @@ namespace osmium { write_point("duplicate_node", node_id1, node_id2, location); } + void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override { + write_point("touching_ring", node_id, 0, location); + } + 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 { - write_point("intersection", m_object_id, 0, intersection); - write_line("intersection", m_object_id, way1_id, way1_seg_start, way1_seg_end); - write_line("intersection", m_object_id, way2_id, way2_seg_start, way2_seg_end); + write_point("intersection", way1_id, way2_id, intersection); + write_line("intersection", way1_id, way2_id, way1_seg_start, way1_seg_end); + write_line("intersection", way2_id, way1_id, way2_seg_start, way2_seg_end); } - void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override { - write_point("ring_not_closed", m_object_id, 0, end1); - write_point("ring_not_closed", m_object_id, 0, end2); + void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + write_line("duplicate_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()); } void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override { - write_line("role_should_be_outer", m_object_id, way_id, seg_start, seg_end); + write_line("role_should_be_outer", way_id, 0, seg_start, seg_end); } void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override { - write_line("role_should_be_inner", m_object_id, way_id, seg_start, seg_end); + write_line("role_should_be_inner", way_id, 0, seg_start, seg_end); + } + + void report_way_in_multiple_rings(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", "way_in_multiple_rings"); + feature.add_to_layer(); + } catch (const osmium::geometry_error&) { + // XXX + } + } + + void report_inner_with_same_tags(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", "inner_with_same_tags"); + feature.add_to_layer(); + } catch (const osmium::geometry_error&) { + // XXX + } + } + + void report_way(const osmium::Way& way) override { + if (way.nodes().empty()) { + return; + } + if (way.nodes().size() == 1) { + const auto& first_nr = way.nodes()[0]; + write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location()); + return; + } + try { + 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(); + } catch (const osmium::geometry_error&) { + // XXX + } } }; // class ProblemReporterOGR 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 ffd94a605f3..c601867b263 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp @@ -38,7 +38,9 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include +#include namespace osmium { @@ -57,7 +59,7 @@ namespace osmium { ~ProblemReporterStream() override = default; void header(const char* msg) { - *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << ": "; + *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): "; } void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { @@ -65,6 +67,11 @@ namespace osmium { *m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " location=" << location << "\n"; } + void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override { + header("touching ring"); + *m_out << "node_id=" << node_id << " location=" << location << "\n"; + } + 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 { header("intersection"); @@ -72,9 +79,19 @@ namespace osmium { << " way2_id=" << way2_id << " way2_seg_start=" << way2_seg_start << " way2_seg_end=" << way2_seg_end << " intersection=" << intersection << "\n"; } - void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override { + void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override { + header("duplicate 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 << "end1=" << end1 << " end2=" << end2 << "\n"; + *m_out << "node_id=" << nr.ref() << " location=" << nr.location(); + if (way) { + *m_out << " on way " << way->id(); + } + *m_out << "\n"; } void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override { @@ -87,6 +104,16 @@ namespace osmium { *m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n"; } + void report_way_in_multiple_rings(const osmium::Way& way) override { + header("way in multiple rings"); + *m_out << "way_id=" << way.id() << '\n'; + } + + void report_inner_with_same_tags(const osmium::Way& way) override { + header("inner way with same tags as relation or outer"); + *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 new file mode 100644 index 00000000000..79cc0026ab8 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/stats.hpp @@ -0,0 +1,128 @@ +#ifndef OSMIUM_AREA_STATS_HPP +#define OSMIUM_AREA_STATS_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +namespace osmium { + + namespace area { + + /** + * These statistics are generated by the area assembler code. They + * tell the user of the assembler a lot about the objects this area + * is made out of, what happened during the assembly, and what errors + * there were. + */ + struct area_stats { + uint64_t area_really_complex_case = 0; ///< Most difficult case with rings touching in multiple points + uint64_t area_simple_case = 0; ///< Simple case, no touching rings + 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 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 + uint64_t inner_with_same_tags = 0; ///< Number of inner ways with same tags as area + uint64_t intersections = 0; ///< Number of intersections between segments + uint64_t member_ways = 0; ///< Number of ways in the area + uint64_t no_tags_on_relation = 0; ///< No tags on relation (old-style multipolygon with tags on outer ways) + uint64_t no_way_in_mp_relation = 0; ///< Multipolygon relation with no way members + 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 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) + + area_stats& operator+=(const area_stats& other) noexcept { + area_really_complex_case += other.area_really_complex_case; + area_simple_case += other.area_simple_case; + area_touching_rings_case += other.area_touching_rings_case; + duplicate_nodes += other.duplicate_nodes; + duplicate_segments += other.duplicate_segments; + from_relations += other.from_relations; + from_ways += other.from_ways; + inner_rings += other.inner_rings; + inner_with_same_tags += other.inner_with_same_tags; + intersections += other.intersections; + member_ways += other.member_ways; + no_tags_on_relation += other.no_tags_on_relation; + no_way_in_mp_relation += other.no_way_in_mp_relation; + nodes += other.nodes; + open_rings += other.open_rings; + outer_rings += other.outer_rings; + short_ways += other.short_ways; + single_way_in_mp_relation += other.single_way_in_mp_relation; + touching_rings += other.touching_rings; + ways_in_multiple_rings += other.ways_in_multiple_rings; + wrong_role += other.wrong_role; + return *this; + } + + }; // struct area_stats + + template + inline std::basic_ostream& operator<<(std::basic_ostream& out, const area_stats& s) { + return out << " area_really_complex_case=" << s.area_really_complex_case + << " area_simple_case=" << s.area_simple_case + << " area_touching_rings_case=" << s.area_touching_rings_case + << " duplicate_nodes=" << s.duplicate_nodes + << " duplicate_segments=" << s.duplicate_segments + << " from_relations=" << s.from_relations + << " from_ways=" << s.from_ways + << " inner_rings=" << s.inner_rings + << " inner_with_same_tags=" << s.inner_with_same_tags + << " intersections=" << s.intersections + << " member_ways=" << s.member_ways + << " no_tags_on_relation=" << s.no_tags_on_relation + << " no_way_in_mp_relation=" << s.no_way_in_mp_relation + << " nodes=" << s.nodes + << " open_rings=" << s.open_rings + << " outer_rings=" << s.outer_rings + << " short_ways=" << s.short_ways + << " 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; + } + + } // namespace area + +} // namespace osmium + +#endif // OSMIUM_AREA_STATS_HPP diff --git a/third_party/libosmium/include/osmium/builder/attr.hpp b/third_party/libosmium/include/osmium/builder/attr.hpp index d9831c28f3f..2a5b6907048 100644 --- a/third_party/libosmium/include/osmium/builder/attr.hpp +++ b/third_party/libosmium/include/osmium/builder/attr.hpp @@ -46,8 +46,15 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include namespace osmium { @@ -261,6 +268,34 @@ namespace osmium { }; // class member_type + class member_type_string { + + osmium::item_type m_type; + osmium::object_id_type m_ref; + std::string m_role; + + public: + + member_type_string(osmium::item_type type, osmium::object_id_type ref, std::string&& role) : + m_type(type), + m_ref(ref), + m_role(std::move(role)) { + } + + osmium::item_type type() const noexcept { + return m_type; + } + + osmium::object_id_type ref() const noexcept { + return m_ref; + } + + const char* role() const noexcept { + return m_role.c_str(); + } + + }; // class member_type_string + class comment_type { osmium::Timestamp m_date; diff --git a/third_party/libosmium/include/osmium/builder/builder.hpp b/third_party/libosmium/include/osmium/builder/builder.hpp index 869fe44e532..1b274ada562 100644 --- a/third_party/libosmium/include/osmium/builder/builder.hpp +++ b/third_party/libosmium/include/osmium/builder/builder.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -101,7 +100,7 @@ namespace osmium { * */ void add_padding(bool self = false) { - auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); + const auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); if (padding != osmium::memory::align_bytes) { std::fill_n(m_buffer.reserve_space(padding), padding, 0); if (self) { diff --git a/third_party/libosmium/include/osmium/builder/builder_helper.hpp b/third_party/libosmium/include/osmium/builder/builder_helper.hpp index e1b7c52ff92..5e0f2181d47 100644 --- a/third_party/libosmium/include/osmium/builder/builder_helper.hpp +++ b/third_party/libosmium/include/osmium/builder/builder_helper.hpp @@ -56,7 +56,7 @@ namespace osmium { * Use osmium::builder::add_way_node_list() instead. */ OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list& nodes) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::WayNodeListBuilder wnl_builder(buffer); for (const auto& node_ref : nodes) { @@ -72,7 +72,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); for (const auto& p : tags) { @@ -88,7 +88,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map& tags) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); for (const auto& p : tags) { @@ -104,7 +104,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function func) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); func(tl_builder); 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 d4b54f8fc11..e7a82988b88 100644 --- a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp +++ b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp @@ -34,7 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include @@ -44,16 +43,23 @@ DEALINGS IN THE SOFTWARE. #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 @@ -186,9 +192,9 @@ namespace osmium { }; // class NodeRefListBuilder - typedef NodeRefListBuilder WayNodeListBuilder; - typedef NodeRefListBuilder OuterRingBuilder; - typedef NodeRefListBuilder InnerRingBuilder; + using WayNodeListBuilder = NodeRefListBuilder; + using OuterRingBuilder = NodeRefListBuilder; + using InnerRingBuilder = NodeRefListBuilder; class RelationMemberListBuilder : public ObjectBuilder { @@ -353,8 +359,8 @@ namespace osmium { }; // class OSMObjectBuilder - typedef OSMObjectBuilder NodeBuilder; - typedef OSMObjectBuilder RelationBuilder; + using NodeBuilder = OSMObjectBuilder; + using RelationBuilder = OSMObjectBuilder; class WayBuilder : public OSMObjectBuilder { @@ -398,7 +404,7 @@ namespace osmium { }; // class AreaBuilder - typedef ObjectBuilder ChangesetBuilder; + using ChangesetBuilder = ObjectBuilder; } // namespace builder diff --git a/third_party/libosmium/include/osmium/diff_iterator.hpp b/third_party/libosmium/include/osmium/diff_iterator.hpp index 6e0ba4bbf62..fc92b36ce43 100644 --- a/third_party/libosmium/include/osmium/diff_iterator.hpp +++ b/third_party/libosmium/include/osmium/diff_iterator.hpp @@ -34,8 +34,10 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include +#include #include @@ -49,7 +51,7 @@ namespace osmium { * underlying OSMObjects. */ template - class DiffIterator : public std::iterator { + class DiffIterator { static_assert(std::is_base_of::value, "TBasicIterator::value_type must derive from osmium::OSMObject"); @@ -76,6 +78,12 @@ namespace osmium { public: + using iterator_category = std::input_iterator_tag; + using value_type = const osmium::DiffObject; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + DiffIterator(TBasicIterator begin, TBasicIterator end) : m_prev(begin), m_curr(begin), diff --git a/third_party/libosmium/include/osmium/dynamic_handler.hpp b/third_party/libosmium/include/osmium/dynamic_handler.hpp index e7c9900fa34..01d15546b2a 100644 --- a/third_party/libosmium/include/osmium/dynamic_handler.hpp +++ b/third_party/libosmium/include/osmium/dynamic_handler.hpp @@ -36,11 +36,16 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include namespace osmium { + class Node; + class Way; + class Relation; + class Area; + class Changeset; + namespace handler { namespace detail { @@ -143,7 +148,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> class DynamicHandler : public osmium::handler::Handler { - typedef std::unique_ptr impl_ptr; + using impl_ptr = std::unique_ptr; impl_ptr m_impl; public: diff --git a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp index 8059ea32a4a..0a2a6683749 100644 --- a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp +++ b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp @@ -34,11 +34,12 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include -#include +#include // IWYU pragma: keep #include #include #include diff --git a/third_party/libosmium/include/osmium/geom/factory.hpp b/third_party/libosmium/include/osmium/geom/factory.hpp index 394228eb920..14c51dfdf96 100644 --- a/third_party/libosmium/include/osmium/geom/factory.hpp +++ b/third_party/libosmium/include/osmium/geom/factory.hpp @@ -46,6 +46,8 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include namespace osmium { @@ -148,7 +150,7 @@ namespace osmium { /** * Add all points of an outer or inner ring to a multipolygon. */ - void add_points(const osmium::OuterRing& nodes) { + void add_points(const osmium::NodeRefList& nodes) { osmium::Location last_location; for (const osmium::NodeRef& node_ref : nodes) { if (last_location != node_ref.location()) { @@ -169,7 +171,7 @@ namespace osmium { template explicit GeometryFactory(TArgs&&... args) : m_projection(), - m_impl(std::forward(args)...) { + m_impl(m_projection.epsg(), std::forward(args)...) { } /** @@ -179,15 +181,16 @@ namespace osmium { template explicit GeometryFactory(TProjection&& projection, TArgs&&... args) : m_projection(std::move(projection)), - m_impl(std::forward(args)...) { + m_impl(m_projection.epsg(), std::forward(args)...) { } - typedef TProjection projection_type; - typedef typename TGeomImpl::point_type point_type; - typedef typename TGeomImpl::linestring_type linestring_type; - typedef typename TGeomImpl::polygon_type polygon_type; - typedef typename TGeomImpl::multipolygon_type multipolygon_type; - typedef typename TGeomImpl::ring_type ring_type; + using projection_type = TProjection; + + using point_type = typename TGeomImpl::point_type; + using linestring_type = typename TGeomImpl::linestring_type; + using polygon_type = typename TGeomImpl::polygon_type; + using multipolygon_type = typename TGeomImpl::multipolygon_type; + using ring_type = typename TGeomImpl::ring_type; int epsg() const { return m_projection.epsg(); @@ -280,13 +283,13 @@ namespace osmium { } if (num_points < 2) { - throw osmium::geometry_error("need at least two points for linestring"); + throw osmium::geometry_error{"need at least two points for linestring"}; } return linestring_finish(num_points); } - linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_linestring(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { @@ -354,13 +357,13 @@ namespace osmium { } if (num_points < 4) { - throw osmium::geometry_error("need at least four points for polygon"); + throw osmium::geometry_error{"need at least four points for polygon"}; } return polygon_finish(num_points); } - polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_polygon(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { @@ -378,8 +381,8 @@ namespace osmium { m_impl.multipolygon_start(); for (auto it = area.cbegin(); it != area.cend(); ++it) { - const osmium::OuterRing& ring = static_cast(*it); if (it->type() == osmium::item_type::outer_ring) { + auto& ring = static_cast(*it); if (num_polygons > 0) { m_impl.multipolygon_polygon_finish(); } @@ -390,6 +393,7 @@ namespace osmium { ++num_rings; ++num_polygons; } else if (it->type() == osmium::item_type::inner_ring) { + auto& ring = static_cast(*it); m_impl.multipolygon_inner_ring_start(); add_points(ring); m_impl.multipolygon_inner_ring_finish(); @@ -399,7 +403,7 @@ namespace osmium { // if there are no rings, this area is invalid if (num_rings == 0) { - throw osmium::geometry_error("area contains no rings"); + throw osmium::geometry_error{"invalid area"}; } m_impl.multipolygon_polygon_finish(); diff --git a/third_party/libosmium/include/osmium/geom/geojson.hpp b/third_party/libosmium/include/osmium/geom/geojson.hpp index 044a89f865c..e9f722fa951 100644 --- a/third_party/libosmium/include/osmium/geom/geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/geojson.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include @@ -53,13 +54,13 @@ namespace osmium { public: - typedef std::string point_type; - typedef std::string linestring_type; - typedef std::string polygon_type; - typedef std::string multipolygon_type; - typedef std::string ring_type; + using point_type = std::string; + using linestring_type = std::string; + using polygon_type = std::string; + using multipolygon_type = std::string; + using ring_type = std::string; - GeoJSONFactoryImpl(int precision = 7) : + GeoJSONFactoryImpl(int /* srid */, int precision = 7) : m_precision(precision) { } diff --git a/third_party/libosmium/include/osmium/geom/geos.hpp b/third_party/libosmium/include/osmium/geom/geos.hpp index f9026e5a99b..f406076a184 100644 --- a/third_party/libosmium/include/osmium/geom/geos.hpp +++ b/third_party/libosmium/include/osmium/geom/geos.hpp @@ -42,9 +42,14 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libgeos`. */ +#include +#include +#include +#include #include #include #include +#include #include #include @@ -59,11 +64,13 @@ DEALINGS IN THE SOFTWARE. #include #include +#include // MSVC doesn't support throw_with_nested yet #ifdef _MSC_VER # define THROW throw #else +# include # define THROW std::throw_with_nested #endif @@ -71,8 +78,8 @@ namespace osmium { struct geos_geometry_error : public geometry_error { - geos_geometry_error(const char* message) : - geometry_error(std::string("geometry creation failed in GEOS library: ") + message) { + explicit geos_geometry_error(const char* message) : + geometry_error(std::string{"geometry creation failed in GEOS library: "} + message) { } }; // struct geos_geometry_error @@ -93,19 +100,29 @@ namespace osmium { public: - typedef std::unique_ptr point_type; - typedef std::unique_ptr linestring_type; - typedef std::unique_ptr polygon_type; - typedef std::unique_ptr multipolygon_type; - typedef std::unique_ptr ring_type; + using point_type = std::unique_ptr; + using linestring_type = std::unique_ptr; + using polygon_type = std::unique_ptr; + using multipolygon_type = std::unique_ptr; + using ring_type = std::unique_ptr; - explicit GEOSFactoryImpl(geos::geom::GeometryFactory& geos_factory) : + explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) : m_precision_model(nullptr), m_our_geos_factory(nullptr), m_geos_factory(&geos_factory) { } - explicit GEOSFactoryImpl(int srid = -1) : + /** + * @deprecated Do not set SRID explicitly. It will be set to the + * correct value automatically. + */ + 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_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_geos_factory(m_our_geos_factory.get()) { @@ -116,7 +133,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))); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -126,7 +143,7 @@ namespace osmium { void linestring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -134,7 +151,7 @@ namespace osmium { void linestring_add_location(const osmium::geom::Coordinates& xy) { try { m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -142,7 +159,7 @@ namespace osmium { linestring_type linestring_finish(size_t /* num_points */) { try { return linestring_type(m_geos_factory->createLineString(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -166,7 +183,7 @@ namespace osmium { }); m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings)); m_rings.clear(); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -174,7 +191,7 @@ namespace osmium { void multipolygon_outer_ring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -182,7 +199,7 @@ namespace osmium { void multipolygon_outer_ring_finish() { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -190,7 +207,7 @@ namespace osmium { void multipolygon_inner_ring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -198,7 +215,7 @@ namespace osmium { void multipolygon_inner_ring_finish() { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -206,7 +223,7 @@ namespace osmium { void multipolygon_add_location(const osmium::geom::Coordinates& xy) { try { m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -219,7 +236,7 @@ namespace osmium { }); m_polygons.clear(); return multipolygon_type(m_geos_factory->createMultiPolygon(polygons)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } diff --git a/third_party/libosmium/include/osmium/geom/haversine.hpp b/third_party/libosmium/include/osmium/geom/haversine.hpp index 632bc16129a..ef3f1ff787d 100644 --- a/third_party/libosmium/include/osmium/geom/haversine.hpp +++ b/third_party/libosmium/include/osmium/geom/haversine.hpp @@ -56,7 +56,7 @@ namespace osmium { namespace haversine { /// @brief Earth's quadratic mean radius for WGS84 - constexpr double EARTH_RADIUS_IN_METERS = 6372797.560856; + constexpr const double EARTH_RADIUS_IN_METERS = 6372797.560856; /** * Calculate distance in meters between two sets of coordinates. diff --git a/third_party/libosmium/include/osmium/geom/ogr.hpp b/third_party/libosmium/include/osmium/geom/ogr.hpp index b8e9ef3ab30..a91fbe5c564 100644 --- a/third_party/libosmium/include/osmium/geom/ogr.hpp +++ b/third_party/libosmium/include/osmium/geom/ogr.hpp @@ -62,33 +62,34 @@ namespace osmium { public: - typedef std::unique_ptr point_type; - typedef std::unique_ptr linestring_type; - typedef std::unique_ptr polygon_type; - typedef std::unique_ptr multipolygon_type; - typedef std::unique_ptr ring_type; + using point_type = std::unique_ptr; + using linestring_type = std::unique_ptr; + using polygon_type = std::unique_ptr; + using multipolygon_type = std::unique_ptr; + using ring_type = std::unique_ptr; private: - linestring_type m_linestring; - multipolygon_type m_multipolygon; - polygon_type m_polygon; - ring_type m_ring; + linestring_type m_linestring{nullptr}; + multipolygon_type m_multipolygon{nullptr}; + polygon_type m_polygon{nullptr}; + ring_type m_ring{nullptr}; public: - OGRFactoryImpl() = default; + explicit OGRFactoryImpl(int /* srid */) { + } /* Point */ point_type make_point(const osmium::geom::Coordinates& xy) const { - return point_type(new OGRPoint(xy.x, xy.y)); + return point_type{new OGRPoint{xy.x, xy.y}}; } /* LineString */ void linestring_start() { - m_linestring = std::unique_ptr(new OGRLineString()); + m_linestring.reset(new OGRLineString{}); } void linestring_add_location(const osmium::geom::Coordinates& xy) { @@ -97,13 +98,14 @@ namespace osmium { } linestring_type linestring_finish(size_t /* num_points */) { + assert(!!m_linestring); return std::move(m_linestring); } /* Polygon */ void polygon_start() { - m_ring = std::unique_ptr(new OGRLinearRing()); + m_ring.reset(new OGRLinearRing{}); } void polygon_add_location(const osmium::geom::Coordinates& xy) { @@ -112,7 +114,7 @@ namespace osmium { } polygon_type polygon_finish(size_t /* num_points */) { - std::unique_ptr polygon = std::unique_ptr(new OGRPolygon()); + auto polygon = std::unique_ptr{new OGRPolygon{}}; polygon->addRingDirectly(m_ring.release()); return polygon; } @@ -120,11 +122,11 @@ namespace osmium { /* MultiPolygon */ void multipolygon_start() { - m_multipolygon.reset(new OGRMultiPolygon()); + m_multipolygon.reset(new OGRMultiPolygon{}); } void multipolygon_polygon_start() { - m_polygon.reset(new OGRPolygon()); + m_polygon.reset(new OGRPolygon{}); } void multipolygon_polygon_finish() { @@ -134,7 +136,7 @@ namespace osmium { } void multipolygon_outer_ring_start() { - m_ring.reset(new OGRLinearRing()); + m_ring.reset(new OGRLinearRing{}); } void multipolygon_outer_ring_finish() { @@ -144,7 +146,7 @@ namespace osmium { } void multipolygon_inner_ring_start() { - m_ring.reset(new OGRLinearRing()); + m_ring.reset(new OGRLinearRing{}); } void multipolygon_inner_ring_finish() { diff --git a/third_party/libosmium/include/osmium/geom/projection.hpp b/third_party/libosmium/include/osmium/geom/projection.hpp index 42d95b27e0c..eaa9b53f361 100644 --- a/third_party/libosmium/include/osmium/geom/projection.hpp +++ b/third_party/libosmium/include/osmium/geom/projection.hpp @@ -70,15 +70,19 @@ namespace osmium { public: - CRS(const std::string& crs) : + explicit CRS(const std::string& crs) : m_crs(pj_init_plus(crs.c_str()), 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())); } } - CRS(int epsg) : - CRS(std::string("+init=epsg:") + std::to_string(epsg)) { + explicit CRS(const char* crs) : + CRS(std::string{crs}) { + } + + explicit CRS(int epsg) : + CRS(std::string{"+init=epsg:"} + std::to_string(epsg)) { } /** @@ -127,13 +131,19 @@ namespace osmium { public: - Projection(const std::string& proj_string) : + explicit Projection(const std::string& proj_string) : + m_epsg(-1), + m_proj_string(proj_string), + m_crs_user(proj_string) { + } + + explicit Projection(const char* proj_string) : m_epsg(-1), m_proj_string(proj_string), m_crs_user(proj_string) { } - Projection(int epsg) : + explicit Projection(int epsg) : m_epsg(epsg), m_proj_string(std::string("+init=epsg:") + std::to_string(epsg)), m_crs_user(epsg) { diff --git a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp index 87e13110feb..7817389c96d 100644 --- a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include #include @@ -53,13 +55,13 @@ namespace osmium { public: - typedef void point_type; - typedef void linestring_type; - typedef void polygon_type; - typedef void multipolygon_type; - typedef void ring_type; + using point_type = void; + using linestring_type = void; + using polygon_type = void; + using multipolygon_type = void; + using ring_type = void; - RapidGeoJSONFactoryImpl(TWriter& writer) : + RapidGeoJSONFactoryImpl(int /* srid */, TWriter& writer) : m_writer(&writer) { } diff --git a/third_party/libosmium/include/osmium/geom/tile.hpp b/third_party/libosmium/include/osmium/geom/tile.hpp index e35c2ee59e5..672ae5416db 100644 --- a/third_party/libosmium/include/osmium/geom/tile.hpp +++ b/third_party/libosmium/include/osmium/geom/tile.hpp @@ -33,9 +33,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include +#include namespace osmium { @@ -57,24 +60,64 @@ namespace osmium { */ struct Tile { + /// x coordinate uint32_t x; + + /// y coordinate uint32_t y; + + /// Zoom level uint32_t z; + /** + * Create a tile with the given zoom level and x any y tile + * coordinates. + * + * The values are not checked for validity. + * + * @pre @code zoom <= 30 && x < 2^zoom && y < 2^zoom @endcode + */ explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept : x(tx), y(ty), z(zoom) { + assert(zoom <= 30u); + assert(x < (1u << zoom)); + assert(y < (1u << zoom)); } + /** + * Create a tile with the given zoom level that contains the given + * location. + * + * The values are not checked for validity. + * + * @pre @code location.valid() && zoom <= 30 @endcode + */ explicit Tile(uint32_t zoom, const osmium::Location& location) : z(zoom) { - osmium::geom::Coordinates c = lonlat_to_mercator(location); + assert(zoom <= 30u); + assert(location.valid()); + const osmium::geom::Coordinates c = lonlat_to_mercator(location); const int32_t n = 1 << zoom; const double scale = detail::max_coordinate_epsg3857 * 2 / n; x = uint32_t(detail::restrict_to_range(int32_t((c.x + detail::max_coordinate_epsg3857) / scale), 0, n-1)); y = uint32_t(detail::restrict_to_range(int32_t((detail::max_coordinate_epsg3857 - c.y) / scale), 0, n-1)); } + /** + * Check whether this tile is valid. For a tile to be valid the + * zoom level must be between 0 and 30 and the coordinates must + * each be between 0 and 2^zoom-1. + */ + bool valid() const noexcept { + if (z > 30) { + return false; + } + const uint32_t max = 1 << z; + return x < max && y < max; + } + }; // struct Tile + /// Tiles are equal if all their attributes are equal. inline bool operator==(const Tile& a, const Tile& b) { return a.z == b.z && a.x == b.x && a.y == b.y; } diff --git a/third_party/libosmium/include/osmium/geom/util.hpp b/third_party/libosmium/include/osmium/geom/util.hpp index fa9c8bc3687..b5682f92f29 100644 --- a/third_party/libosmium/include/osmium/geom/util.hpp +++ b/third_party/libosmium/include/osmium/geom/util.hpp @@ -44,11 +44,11 @@ namespace osmium { */ struct projection_error : public std::runtime_error { - projection_error(const std::string& what) : + explicit projection_error(const std::string& what) : std::runtime_error(what) { } - projection_error(const char* what) : + explicit projection_error(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/geom/wkb.hpp b/third_party/libosmium/include/osmium/geom/wkb.hpp index 19c12165ee0..d6b779879c0 100644 --- a/third_party/libosmium/include/osmium/geom/wkb.hpp +++ b/third_party/libosmium/include/osmium/geom/wkb.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -60,14 +61,13 @@ namespace osmium { template inline void str_push(std::string& str, T data) { - size_t size = str.size(); - str.resize(size + sizeof(T)); - std::copy_n(reinterpret_cast(&data), sizeof(T), &str[size]); + str.append(reinterpret_cast(&data), sizeof(T)); } inline std::string convert_to_hex(const std::string& str) { static const char* lookup_hex = "0123456789ABCDEF"; std::string out; + out.reserve(str.size() * 2); for (char c : str) { out += lookup_hex[(c >> 4) & 0xf]; @@ -79,9 +79,6 @@ namespace osmium { class WKBFactoryImpl { - /// OSM data always uses SRID 4326 (WGS84). - static constexpr uint32_t srid = 4326; - /** * Type of WKB geometry. * These definitions are from @@ -112,6 +109,7 @@ namespace osmium { std::string m_data; uint32_t m_points {0}; + int m_srid; wkb_type m_wkb_type; out_type m_out_type; @@ -130,11 +128,11 @@ namespace osmium { #endif if (m_wkb_type == wkb_type::ewkb) { str_push(str, type | wkbSRID); - str_push(str, srid); + str_push(str, m_srid); } else { str_push(str, type); } - size_t offset = str.size(); + const size_t offset = str.size(); if (add_length) { str_push(str, static_cast(0)); } @@ -142,18 +140,20 @@ namespace osmium { } void set_size(const size_t offset, const size_t size) { - *reinterpret_cast(&m_data[offset]) = static_cast_with_assert(size); + uint32_t s = static_cast_with_assert(size); + std::copy_n(reinterpret_cast(&s), sizeof(uint32_t), &m_data[offset]); } public: - typedef std::string point_type; - typedef std::string linestring_type; - typedef std::string polygon_type; - typedef std::string multipolygon_type; - typedef std::string ring_type; + using point_type = std::string; + using linestring_type = std::string; + using polygon_type = std::string; + using multipolygon_type = std::string; + using ring_type = std::string; - explicit WKBFactoryImpl(wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) : + explicit WKBFactoryImpl(int srid, wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) : + m_srid(srid), m_wkb_type(wtype), m_out_type(otype) { } diff --git a/third_party/libosmium/include/osmium/geom/wkt.hpp b/third_party/libosmium/include/osmium/geom/wkt.hpp index ae73b960407..e360f712b53 100644 --- a/third_party/libosmium/include/osmium/geom/wkt.hpp +++ b/third_party/libosmium/include/osmium/geom/wkt.hpp @@ -45,29 +45,44 @@ namespace osmium { namespace geom { + enum class wkt_type : bool { + wkt = false, + ewkt = true + }; // enum class wkt_type + namespace detail { class WKTFactoryImpl { + std::string m_srid_prefix; std::string m_str; int m_precision; + wkt_type m_wkt_type; public: - typedef std::string point_type; - typedef std::string linestring_type; - typedef std::string polygon_type; - typedef std::string multipolygon_type; - typedef std::string ring_type; - - WKTFactoryImpl(int precision = 7) : - m_precision(precision) { + using point_type = std::string; + using linestring_type = std::string; + using polygon_type = std::string; + using multipolygon_type = std::string; + using ring_type = std::string; + + WKTFactoryImpl(int srid, int precision = 7, wkt_type wtype = wkt_type::wkt) : + m_srid_prefix(), + m_precision(precision), + m_wkt_type(wtype) { + if (m_wkt_type == wkt_type::ewkt) { + m_srid_prefix = "SRID="; + m_srid_prefix += std::to_string(srid); + m_srid_prefix += ';'; + } } /* Point */ point_type make_point(const osmium::geom::Coordinates& xy) const { - std::string str {"POINT"}; + std::string str {m_srid_prefix}; + str += "POINT"; xy.append_to_string(str, '(', ' ', ')', m_precision); return str; } @@ -75,7 +90,8 @@ namespace osmium { /* LineString */ void linestring_start() { - m_str = "LINESTRING("; + m_str = m_srid_prefix; + m_str += "LINESTRING("; } void linestring_add_location(const osmium::geom::Coordinates& xy) { @@ -97,7 +113,8 @@ namespace osmium { /* MultiPolygon */ void multipolygon_start() { - m_str = "MULTIPOLYGON("; + m_str = m_srid_prefix; + m_str += "MULTIPOLYGON("; } void multipolygon_polygon_start() { diff --git a/third_party/libosmium/include/osmium/handler.hpp b/third_party/libosmium/include/osmium/handler.hpp index 3620d654697..1263910d4ef 100644 --- a/third_party/libosmium/include/osmium/handler.hpp +++ b/third_party/libosmium/include/osmium/handler.hpp @@ -33,56 +33,82 @@ DEALINGS IN THE SOFTWARE. */ -#include - namespace osmium { + class Area; + class Changeset; + class ChangesetDiscussion; + class InnerRing; + class Node; + class OSMObject; + class OuterRing; + class Relation; + class RelationMemberList; + class TagList; + class Way; + class WayNodeList; + /** * @brief Osmium handlers provide callbacks for OSM objects */ namespace handler { + /** + * Handler base class. Never used directly. Derive your own class from + * this class and "overwrite" the functions. Your functions must be + * named the same, but don't have to be const or noexcept or take + * their argument as const. + * + * Usually you will overwrite the node(), way(), and relation() + * functions. If your program supports multipolygons, also the area() + * function. You can also use the osm_object() function which is + * called for all OSM objects (nodes, ways, relations, and areas) + * right before each of their specific callbacks is called. + * + * If you are working with changesets, implement the changeset() + * function. + */ class Handler { public: - void osm_object(const osmium::OSMObject&) const { + void osm_object(const osmium::OSMObject&) const noexcept { } - void node(const osmium::Node&) const { + void node(const osmium::Node&) const noexcept { } - void way(const osmium::Way&) const { + void way(const osmium::Way&) const noexcept { } - void relation(const osmium::Relation&) const { + void relation(const osmium::Relation&) const noexcept { } - void area(const osmium::Area&) const { + void area(const osmium::Area&) const noexcept { } - void changeset(const osmium::Changeset&) const { + void changeset(const osmium::Changeset&) const noexcept { } - void tag_list(const osmium::TagList&) const { + void tag_list(const osmium::TagList&) const noexcept { } - void way_node_list(const osmium::WayNodeList&) const { + void way_node_list(const osmium::WayNodeList&) const noexcept { } - void relation_member_list(const osmium::RelationMemberList&) const { + void relation_member_list(const osmium::RelationMemberList&) const noexcept { } - void outer_ring(const osmium::OuterRing&) const { + void outer_ring(const osmium::OuterRing&) const noexcept { } - void inner_ring(const osmium::InnerRing&) const { + void inner_ring(const osmium::InnerRing&) const noexcept { } - void changeset_discussion(const osmium::ChangesetDiscussion&) const { + void changeset_discussion(const osmium::ChangesetDiscussion&) const noexcept { } - void flush() const { + void flush() const noexcept { } }; // class Handler diff --git a/third_party/libosmium/include/osmium/handler/chain.hpp b/third_party/libosmium/include/osmium/handler/chain.hpp index e59eb05b9b2..109e3537acb 100644 --- a/third_party/libosmium/include/osmium/handler/chain.hpp +++ b/third_party/libosmium/include/osmium/handler/chain.hpp @@ -67,7 +67,7 @@ namespace osmium { template class ChainHandler : public osmium::handler::Handler { - typedef std::tuple handlers_type; + using handlers_type = std::tuple; handlers_type m_handlers; template diff --git a/third_party/libosmium/include/osmium/handler/check_order.hpp b/third_party/libosmium/include/osmium/handler/check_order.hpp index 143794bd57f..a9b6834a434 100644 --- a/third_party/libosmium/include/osmium/handler/check_order.hpp +++ b/third_party/libosmium/include/osmium/handler/check_order.hpp @@ -51,11 +51,11 @@ namespace osmium { */ struct out_of_order_error : public std::runtime_error { - out_of_order_error(const std::string& what) : + explicit out_of_order_error(const std::string& what) : std::runtime_error(what) { } - out_of_order_error(const char* what) : + explicit out_of_order_error(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/handler/disk_store.hpp b/third_party/libosmium/include/osmium/handler/disk_store.hpp index b8ab229a314..4e955734a7c 100644 --- a/third_party/libosmium/include/osmium/handler/disk_store.hpp +++ b/third_party/libosmium/include/osmium/handler/disk_store.hpp @@ -57,7 +57,7 @@ namespace osmium { */ class DiskStore : public osmium::handler::Handler { - typedef osmium::index::map::Map offset_index_type; + using offset_index_type = osmium::index::map::Map; size_t m_offset = 0; int m_data_fd; 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 f62a4db7cd6..a490f9e28eb 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 @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include @@ -50,7 +51,7 @@ namespace osmium { namespace handler { - typedef osmium::index::map::Dummy dummy_type; + using dummy_type = osmium::index::map::Dummy; /** * Handler to retrieve locations from nodes and add them to ways. @@ -69,8 +70,8 @@ namespace osmium { public: - typedef TStoragePosIDs index_pos_type; - typedef TStorageNegIDs index_neg_type; + using index_pos_type = TStoragePosIDs; + using index_neg_type = TStorageNegIDs; private: @@ -80,6 +81,8 @@ 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}; + bool m_ignore_errors {false}; bool m_must_sort {false}; @@ -115,7 +118,11 @@ namespace osmium { * Store the location of the node in the storage. */ void node(const osmium::Node& node) { - m_must_sort = true; + if (node.positive_id() < m_last_id) { + m_must_sort = true; + } + m_last_id = node.positive_id(); + const osmium::object_id_type id = node.id(); if (id >= 0) { m_storage_pos.set(static_cast( id), node.location()); @@ -144,6 +151,7 @@ namespace osmium { m_storage_pos.sort(); m_storage_neg.sort(); m_must_sort = false; + m_last_id = std::numeric_limits::max(); } bool error = false; for (auto& node_ref : way.nodes()) { @@ -152,7 +160,7 @@ namespace osmium { if (!node_ref.location()) { error = true; } - } catch (osmium::not_found&) { + } catch (const osmium::not_found&) { error = true; } } diff --git a/third_party/libosmium/include/osmium/handler/object_relations.hpp b/third_party/libosmium/include/osmium/handler/object_relations.hpp index 4afcf6a6d2a..279365d08ef 100644 --- a/third_party/libosmium/include/osmium/handler/object_relations.hpp +++ b/third_party/libosmium/include/osmium/handler/object_relations.hpp @@ -52,7 +52,7 @@ namespace osmium { */ class ObjectRelations : public osmium::handler::Handler { - typedef osmium::index::multimap::Multimap index_type; + using index_type = osmium::index::multimap::Multimap; index_type& m_index_n2w; index_type& m_index_n2r; 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 640b3c693ac..72b326a1b14 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 @@ -54,9 +54,9 @@ namespace osmium { } assert(config.size() > 1); const std::string& filename = config[1]; - int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); + 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 + "': " + strerror(errno)); + throw std::runtime_error(std::string("can't open file '") + filename + "': " + std::strerror(errno)); } return new T(fd); } diff --git a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp index aadeef0b543..8f52e98495d 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp @@ -33,10 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include // IWYU pragma: keep #include +#include #include namespace osmium { @@ -63,22 +66,26 @@ namespace osmium { mmap_vector_base(int fd, size_t capacity, size_t size = 0) : m_size(size), m_mapping(capacity, osmium::util::MemoryMapping::mapping_mode::write_shared, fd) { + assert(size <= capacity); + std::fill(data() + size, data() + capacity, osmium::index::empty_value()); + shrink_to_fit(); } explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) : m_size(0), m_mapping(capacity) { + std::fill_n(data(), capacity, osmium::index::empty_value()); } ~mmap_vector_base() noexcept = default; - typedef T value_type; - typedef T& reference; - typedef const T& const_reference; - typedef T* pointer; - typedef const T* const_pointer; - typedef T* iterator; - typedef const T* const_iterator; + using value_type = T; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = value_type*; + using const_iterator = const value_type*; void close() { m_mapping.unmap(); @@ -105,6 +112,7 @@ namespace osmium { } T& operator[](size_t n) { + assert(n < m_size); return data()[n]; } @@ -120,20 +128,21 @@ namespace osmium { } void shrink_to_fit() { - // XXX do something here + while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value()) { + --m_size; + } } void push_back(const T& value) { - if (m_size >= capacity()) { - resize(m_size+1); - } - data()[m_size] = value; - ++m_size; + resize(m_size+1); + data()[m_size-1] = value; } void reserve(size_t new_capacity) { if (new_capacity > capacity()) { + const size_t old_capacity = capacity(); m_mapping.resize(new_capacity); + std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value()); } } @@ -141,9 +150,6 @@ namespace osmium { if (new_size > capacity()) { reserve(new_size + osmium::detail::mmap_vector_size_increment); } - if (new_size > size()) { - new (data() + size()) T[new_size - size()]; - } m_size = new_size; } 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 666a409d4b1..9e72f5a8cea 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 @@ -33,6 +33,11 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include + #include #include #include @@ -48,6 +53,16 @@ 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); + + if (size % sizeof(T) != 0) { + throw std::runtime_error("Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ")."); + } + + return size / sizeof(T); + } + public: mmap_vector_file() : @@ -59,8 +74,8 @@ namespace osmium { explicit mmap_vector_file(int fd) : mmap_vector_base( fd, - osmium::util::file_size(fd) / sizeof(T), - osmium::util::file_size(fd) / sizeof(T)) { + std::max(osmium::detail::mmap_vector_size_increment, filesize(fd)), + filesize(fd)) { } ~mmap_vector_file() noexcept = default; 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 0fa59dd4882..ac87c2fc918 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_map.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_map.hpp @@ -55,10 +55,10 @@ namespace osmium { public: - typedef TValue element_type; - typedef TVector vector_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + using element_type = TValue; + using vector_type = TVector; + using iterator = typename vector_type::iterator; + using const_iterator = typename vector_type::const_iterator; VectorBasedDenseMap() : m_vector() { @@ -88,7 +88,7 @@ namespace osmium { not_found_error(id); } return value; - } catch (std::out_of_range&) { + } catch (const std::out_of_range&) { not_found_error(id); } } @@ -146,10 +146,10 @@ namespace osmium { public: - typedef typename std::pair element_type; - typedef TVector vector_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + using element_type = typename std::pair; + using vector_type = TVector; + using iterator = typename vector_type::iterator; + using const_iterator = typename vector_type::const_iterator; private: diff --git a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp index 35a4cc182b8..f859f5b4559 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp @@ -52,10 +52,10 @@ namespace osmium { public: - typedef typename std::pair element_type; - typedef TVector vector_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + using element_type = typename std::pair; + using vector_type = TVector; + using iterator = typename vector_type::iterator; + using const_iterator = typename vector_type::const_iterator; private: @@ -127,7 +127,7 @@ namespace osmium { } void remove(const TId id, const TValue value) { - auto r = get_all(id); + const auto r = get_all(id); for (auto it = r.first; it != r.second; ++it) { if (it->second == value) { it->second = 0; diff --git a/third_party/libosmium/include/osmium/index/index.hpp b/third_party/libosmium/include/osmium/index/index.hpp index 37d022d1bd2..c3deeadf80c 100644 --- a/third_party/libosmium/include/osmium/index/index.hpp +++ b/third_party/libosmium/include/osmium/index/index.hpp @@ -49,11 +49,11 @@ namespace osmium { */ struct not_found : public std::runtime_error { - not_found(const std::string& what) : + explicit not_found(const std::string& what) : std::runtime_error(what) { } - not_found(const char* what) : + explicit not_found(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/index/map.hpp b/third_party/libosmium/include/osmium/index/map.hpp index f90a895d16a..1d2d5aa3421 100644 --- a/third_party/libosmium/include/osmium/index/map.hpp +++ b/third_party/libosmium/include/osmium/index/map.hpp @@ -98,10 +98,10 @@ namespace osmium { public: /// The "key" type, usually osmium::unsigned_object_id_type. - typedef TId key_type; + using key_type = TId; /// The "value" type, usually a Location or size_t. - typedef TValue value_type; + using value_type = TValue; Map() = default; @@ -171,10 +171,10 @@ namespace osmium { public: - typedef TId id_type; - typedef TValue value_type; - typedef osmium::index::map::Map map_type; - typedef std::function&)> create_map_func; + using id_type = TId; + using value_type = TValue; + using map_type = osmium::index::map::Map; + using create_map_func = std::function&)>; private: @@ -207,7 +207,7 @@ namespace osmium { } bool has_map_type(const std::string& map_type_name) const { - return m_callbacks.count(map_type_name); + return m_callbacks.count(map_type_name) != 0; } std::vector map_types() const { 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 41351c86551..1e3c58cc66a 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 @@ -37,7 +37,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include @@ -98,7 +97,7 @@ namespace osmium { } void dump_as_list(const int fd) final { - typedef typename std::map::value_type t; + using t = typename std::map::value_type; std::vector v; v.reserve(m_elements.size()); std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v)); diff --git a/third_party/libosmium/include/osmium/index/multimap.hpp b/third_party/libosmium/include/osmium/index/multimap.hpp index de6aa1eb436..9361d6a7695 100644 --- a/third_party/libosmium/include/osmium/index/multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap.hpp @@ -52,7 +52,7 @@ namespace osmium { static_assert(std::is_integral::value && std::is_unsigned::value, "TId template parameter for class Multimap must be unsigned integral type"); - typedef typename std::pair element_type; + using element_type = typename std::pair; Multimap(const Multimap&) = delete; Multimap& operator=(const Multimap&) = delete; @@ -65,10 +65,10 @@ namespace osmium { public: /// The "key" type, usually osmium::unsigned_object_id_type. - typedef TId key_type; + using key_type = TId; /// The "value" type, usually a Location or size_t. - typedef TValue value_type; + using value_type = TValue; Multimap() = default; @@ -77,7 +77,7 @@ namespace osmium { /// Set the field with id to value. virtual void set(const TId id, const TValue value) = 0; - typedef element_type* iterator; + using iterator = element_type*; // virtual std::pair get_all(const TId id) const = 0; diff --git a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp index ba9430232db..06a5f6e7b8c 100644 --- a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp @@ -50,10 +50,10 @@ namespace osmium { template class HybridIterator { - typedef SparseMemArray main_map_type; - typedef SparseMemMultimap extra_map_type; + using main_map_type = SparseMemArray; + using extra_map_type = SparseMemMultimap; - typedef typename std::pair element_type; + using element_type = typename std::pair; typename main_map_type::iterator m_begin_main; typename main_map_type::iterator m_end_main; @@ -120,16 +120,16 @@ namespace osmium { template class Hybrid : public Multimap { - typedef SparseMemArray main_map_type; - typedef SparseMemMultimap extra_map_type; + using main_map_type = SparseMemArray; + using extra_map_type = SparseMemMultimap; main_map_type m_main; extra_map_type m_extra; public: - typedef HybridIterator iterator; - typedef const HybridIterator const_iterator; + using iterator = HybridIterator; + using const_iterator = const HybridIterator; Hybrid() : m_main(), diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp index 0859fca21a8..7a67cc7ccbc 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp @@ -63,12 +63,11 @@ namespace osmium { public: - typedef typename std::multimap collection_type; - typedef typename collection_type::iterator iterator; - typedef typename collection_type::const_iterator const_iterator; - typedef typename collection_type::value_type value_type; - - typedef typename std::pair element_type; + using collection_type = typename std::multimap; + using iterator = typename collection_type::iterator; + using const_iterator = typename collection_type::const_iterator; + using value_type = typename collection_type::value_type; + using element_type = typename std::pair; private: diff --git a/third_party/libosmium/include/osmium/io/any_input.hpp b/third_party/libosmium/include/osmium/io/any_input.hpp index dd8445132e7..e4617e8b5a2 100644 --- a/third_party/libosmium/include/osmium/io/any_input.hpp +++ b/third_party/libosmium/include/osmium/io/any_input.hpp @@ -45,8 +45,9 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export #endif // OSMIUM_IO_ANY_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp index e5cad0be3a6..63b4cadf923 100644 --- a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp +++ b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp @@ -43,10 +43,9 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include -#include #include +#include #include @@ -55,6 +54,7 @@ DEALINGS IN THE SOFTWARE. #endif #include +#include #include #include #include @@ -109,7 +109,7 @@ namespace osmium { explicit Bzip2Compressor(int fd, fsync sync) : Compressor(sync), - m_file(fdopen(dup(fd), "wb")), + m_file(fdopen(::dup(fd), "wb")), m_bzerror(BZ_OK), m_bzfile(::BZ2_bzWriteOpen(&m_bzerror, m_file, 6, 0, 0)) { if (!m_bzfile) { @@ -165,7 +165,7 @@ namespace osmium { explicit Bzip2Decompressor(int fd) : Decompressor(), - m_file(fdopen(dup(fd), "rb")), + m_file(fdopen(::dup(fd), "rb")), m_bzerror(BZ_OK), m_bzfile(::BZ2_bzReadOpen(&m_bzerror, m_file, 0, 0, nullptr, 0)) { if (!m_bzfile) { @@ -215,6 +215,8 @@ namespace osmium { buffer.resize(static_cast(nread)); } + set_offset(size_t(ftell(m_file))); + return buffer; } diff --git a/third_party/libosmium/include/osmium/io/compression.hpp b/third_party/libosmium/include/osmium/io/compression.hpp index 7d95661bdae..e7f93bd5ea0 100644 --- a/third_party/libosmium/include/osmium/io/compression.hpp +++ b/third_party/libosmium/include/osmium/io/compression.hpp @@ -33,11 +33,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include #include -#include #include #include #include @@ -54,6 +55,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -86,6 +88,9 @@ namespace osmium { class Decompressor { + std::atomic m_file_size {0}; + std::atomic m_offset {0}; + public: static constexpr unsigned int input_buffer_size = 1024 * 1024; @@ -105,6 +110,22 @@ namespace osmium { virtual void close() = 0; + size_t file_size() const noexcept { + return m_file_size; + } + + void set_file_size(size_t size) noexcept { + m_file_size = size; + } + + size_t offset() const noexcept { + return m_offset; + } + + void set_offset(size_t offset) noexcept { + m_offset = offset; + } + }; // class Decompressor /** @@ -118,16 +139,16 @@ namespace osmium { public: - typedef std::function create_compressor_type; - typedef std::function create_decompressor_type_fd; - typedef std::function create_decompressor_type_buffer; + using create_compressor_type = std::function; + using create_decompressor_type_fd = std::function; + using create_decompressor_type_buffer = std::function; private: - typedef std::map> compression_map_type; + using compression_map_type = std::map>; compression_map_type m_callbacks; @@ -182,7 +203,9 @@ namespace osmium { auto it = m_callbacks.find(compression); if (it != m_callbacks.end()) { - return std::unique_ptr(std::get<1>(it->second)(fd)); + auto p = std::unique_ptr(std::get<1>(it->second)(fd)); + p->set_file_size(osmium::util::file_size(fd)); + return p; } error(compression); @@ -241,6 +264,7 @@ namespace osmium { int m_fd; const char *m_buffer; size_t m_buffer_size; + size_t m_offset = 0; public: @@ -284,6 +308,9 @@ namespace osmium { buffer.resize(std::string::size_type(nread)); } + m_offset += buffer.size(); + set_offset(m_offset); + return buffer; } 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 3f7537ea624..dc5323ad3ec 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 @@ -34,29 +34,35 @@ 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 @@ -66,8 +72,6 @@ namespace osmium { namespace io { - class File; - namespace detail { constexpr const char* color_bold = "\x1b[1m"; @@ -80,6 +84,10 @@ namespace osmium { constexpr const char* color_magenta = "\x1b[35m"; constexpr const char* color_cyan = "\x1b[36m"; constexpr const char* color_white = "\x1b[37m"; + + constexpr const char* color_backg_red = "\x1b[41m"; + constexpr const char* color_backg_green = "\x1b[42m"; + constexpr const char* color_reset = "\x1b[0m"; struct debug_output_options { @@ -90,6 +98,11 @@ namespace osmium { /// Output with ANSI colors? bool use_color; + /// Add CRC32 checksum to each object? + bool add_crc32; + + /// Write in form of a diff file? + bool format_as_diff; }; /** @@ -102,16 +115,47 @@ namespace osmium { const char* m_utf8_prefix = ""; const char* m_utf8_suffix = ""; + char m_diff_char = '\0'; + void append_encoded_string(const char* data) { append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix); } + template + void output_formatted(const char* format, TArgs&&... args) { + append_printf_formatted_string(*m_out, format, std::forward(args)...); + } + void write_color(const char* color) { if (m_options.use_color) { *m_out += color; } } + void write_diff() { + if (!m_diff_char) { + return; + } + if (m_options.use_color) { + if (m_diff_char == '-') { + *m_out += color_backg_red; + *m_out += color_white; + *m_out += color_bold; + *m_out += '-'; + *m_out += color_reset; + return; + } else if (m_diff_char == '+') { + *m_out += color_backg_green; + *m_out += color_white; + *m_out += color_bold; + *m_out += '+'; + *m_out += color_reset; + return; + } + } + *m_out += m_diff_char; + } + void write_string(const char* string) { *m_out += '"'; write_color(color_blue); @@ -121,6 +165,7 @@ namespace osmium { } void write_object_type(const char* object_type, bool visible = true) { + write_diff(); if (visible) { write_color(color_bold); } else { @@ -132,6 +177,7 @@ namespace osmium { } void write_fieldname(const char* name) { + write_diff(); *m_out += " "; write_color(color_cyan); *m_out += name; @@ -161,7 +207,9 @@ namespace osmium { void write_timestamp(const osmium::Timestamp& timestamp) { if (timestamp.valid()) { *m_out += timestamp.to_iso(); - output_formatted(" (%d)", timestamp.seconds_since_epoch()); + *m_out += " ("; + output_int(timestamp.seconds_since_epoch()); + *m_out += ')'; } else { write_error("NOT SET"); } @@ -169,53 +217,63 @@ namespace osmium { } void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64 "\n", object.id()); + output_int(object.id()); + *m_out += '\n'; if (m_options.add_metadata) { write_fieldname("version"); - output_formatted(" %d", object.version()); + *m_out += " "; + output_int(object.version()); if (object.visible()) { *m_out += " visible\n"; } else { write_error(" deleted\n"); } write_fieldname("changeset"); - output_formatted("%d\n", object.changeset()); + output_int(object.changeset()); + *m_out += '\n'; write_fieldname("timestamp"); write_timestamp(object.timestamp()); write_fieldname("user"); - output_formatted(" %d ", object.uid()); + *m_out += " "; + output_int(object.uid()); + *m_out += ' '; write_string(object.user()); *m_out += '\n'; } } void write_tags(const osmium::TagList& tags, const char* padding="") { - if (!tags.empty()) { - write_fieldname("tags"); - *m_out += padding; - output_formatted(" %d\n", tags.size()); - - osmium::max_op max; - for (const auto& tag : tags) { - max.update(std::strlen(tag.key())); - } - for (const auto& tag : tags) { - *m_out += " "; - write_string(tag.key()); - auto spacing = max() - std::strlen(tag.key()); - while (spacing--) { - *m_out += " "; - } - *m_out += " = "; - write_string(tag.value()); - *m_out += '\n'; + if (tags.empty()) { + return; + } + write_fieldname("tags"); + *m_out += padding; + *m_out += " "; + output_int(tags.size()); + *m_out += '\n'; + + osmium::max_op max; + for (const auto& tag : tags) { + max.update(std::strlen(tag.key())); + } + for (const auto& tag : tags) { + write_diff(); + *m_out += " "; + write_string(tag.key()); + auto spacing = max() - std::strlen(tag.key()); + while (spacing--) { + *m_out += " "; } + *m_out += " = "; + write_string(tag.value()); + *m_out += '\n'; } } void write_location(const osmium::Location& location) { write_fieldname("lon/lat"); - output_formatted(" %.7f,%.7f", location.lon_without_check(), location.lat_without_check()); + *m_out += " "; + location.as_string_without_check(std::back_inserter(*m_out)); if (!location.valid()) { write_error(" INVALID LOCATION!"); } @@ -230,13 +288,30 @@ namespace osmium { } const auto& bl = box.bottom_left(); const auto& tr = box.top_right(); - output_formatted("%.7f,%.7f %.7f,%.7f", bl.lon_without_check(), bl.lat_without_check(), tr.lon_without_check(), tr.lat_without_check()); + bl.as_string(std::back_inserter(*m_out)); + *m_out += ' '; + tr.as_string(std::back_inserter(*m_out)); if (!box.valid()) { write_error(" INVALID BOX!"); } *m_out += '\n'; } + template + void write_crc32(const T& object) { + write_fieldname("crc32"); + osmium::CRC crc32; + crc32.update(object); + output_formatted(" %x\n", crc32().checksum()); + } + + void write_crc32(const osmium::Changeset& object) { + write_fieldname("crc32"); + osmium::CRC crc32; + crc32.update(object); + output_formatted(" %x\n", crc32().checksum()); + } + public: DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) : @@ -265,6 +340,8 @@ namespace osmium { } void node(const osmium::Node& node) { + m_diff_char = m_options.format_as_diff ? node.diff_as_char() : '\0'; + write_object_type("node", node.visible()); write_meta(node); @@ -274,17 +351,24 @@ namespace osmium { write_tags(node.tags()); + if (m_options.add_crc32) { + write_crc32(node); + } + *m_out += '\n'; } void way(const osmium::Way& way) { + m_diff_char = m_options.format_as_diff ? way.diff_as_char() : '\0'; + write_object_type("way", way.visible()); write_meta(way); write_tags(way.tags()); write_fieldname("nodes"); - output_formatted(" %d", way.nodes().size()); + *m_out += " "; + output_int(way.nodes().size()); if (way.nodes().size() < 2) { write_error(" LESS THAN 2 NODES!\n"); } else if (way.nodes().size() > 2000) { @@ -295,32 +379,45 @@ namespace osmium { *m_out += " (open)\n"; } - int width = int(log10(way.nodes().size())) + 1; + const int width = int(std::log10(way.nodes().size())) + 1; int n = 0; for (const auto& node_ref : way.nodes()) { + write_diff(); write_counter(width, n++); output_formatted("%10" PRId64, node_ref.ref()); if (node_ref.location().valid()) { - output_formatted(" (%.7f,%.7f)", node_ref.location().lon_without_check(), node_ref.location().lat_without_check()); + *m_out += " ("; + node_ref.location().as_string(std::back_inserter(*m_out)); + *m_out += ')'; } *m_out += '\n'; } + if (m_options.add_crc32) { + write_crc32(way); + } + *m_out += '\n'; } void relation(const osmium::Relation& relation) { static const char* short_typename[] = { "node", "way ", "rel " }; + + m_diff_char = m_options.format_as_diff ? relation.diff_as_char() : '\0'; + write_object_type("relation", relation.visible()); write_meta(relation); write_tags(relation.tags()); write_fieldname("members"); - output_formatted(" %d\n", relation.members().size()); + *m_out += " "; + output_int(relation.members().size()); + *m_out += '\n'; - int width = int(log10(relation.members().size())) + 1; + const int width = int(std::log10(relation.members().size())) + 1; int n = 0; for (const auto& member : relation.members()) { + write_diff(); write_counter(width, n++); *m_out += short_typename[item_type_to_nwr_index(member.type())]; output_formatted(" %10" PRId64 " ", member.ref()); @@ -328,15 +425,20 @@ namespace osmium { *m_out += '\n'; } + if (m_options.add_crc32) { + write_crc32(relation); + } + *m_out += '\n'; } void changeset(const osmium::Changeset& changeset) { write_object_type("changeset"); - output_formatted("%d\n", changeset.id()); + output_int(changeset.id()); + *m_out += '\n'; write_fieldname("num changes"); - output_formatted("%d", changeset.num_changes()); + output_int(changeset.num_changes()); if (changeset.num_changes() == 0) { write_error(" NO CHANGES!"); } @@ -355,7 +457,9 @@ namespace osmium { } write_fieldname("user"); - output_formatted(" %d ", changeset.uid()); + *m_out += " "; + output_int(changeset.uid()); + *m_out += ' '; write_string(changeset.user()); *m_out += '\n'; @@ -364,9 +468,11 @@ namespace osmium { if (changeset.num_comments() > 0) { write_fieldname("comments"); - output_formatted(" %d\n", changeset.num_comments()); + *m_out += " "; + output_int(changeset.num_comments()); + *m_out += '\n'; - int width = int(log10(changeset.num_comments())) + 1; + const int width = int(std::log10(changeset.num_comments())) + 1; int n = 0; for (const auto& comment : changeset.discussion()) { write_counter(width, n++); @@ -376,7 +482,8 @@ namespace osmium { output_formatted(" %*s", width, ""); write_comment_field("user"); - output_formatted("%d ", comment.uid()); + output_int(comment.uid()); + *m_out += ' '; write_string(comment.user()); output_formatted("\n %*s", width, ""); @@ -386,6 +493,10 @@ namespace osmium { } } + if (m_options.add_crc32) { + write_crc32(changeset); + } + *m_out += '\n'; } @@ -412,8 +523,10 @@ namespace osmium { DebugOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : OutputFormat(output_queue), m_options() { - m_options.add_metadata = file.is_not_false("add_metadata"); - m_options.use_color = file.is_true("color"); + m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.use_color = file.is_true("color"); + m_options.add_crc32 = file.is_true("add_crc32"); + m_options.format_as_diff = file.is_true("diff"); } DebugOutputFormat(const DebugOutputFormat&) = delete; @@ -422,6 +535,10 @@ namespace osmium { ~DebugOutputFormat() noexcept final = default; void write_header(const osmium::io::Header& header) final { + if (m_options.format_as_diff) { + return; + } + std::string out; if (m_options.use_color) { @@ -439,9 +556,9 @@ namespace osmium { out += '\n'; for (const auto& box : header.boxes()) { out += " "; - box.bottom_left().as_string(std::back_inserter(out), ','); - out += " "; - box.top_right().as_string(std::back_inserter(out), ','); + box.bottom_left().as_string(std::back_inserter(out)); + out += ' '; + box.top_right().as_string(std::back_inserter(out)); out += '\n'; } write_fieldname(out, "options"); 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 0d788513161..98081e3c984 100644 --- a/third_party/libosmium/include/osmium/io/detail/input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/input_format.hpp @@ -38,11 +38,11 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include +#include #include #include #include @@ -154,18 +154,14 @@ namespace osmium { public: - typedef std::function< - std::unique_ptr( - future_string_queue_type&, - future_buffer_queue_type&, - std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities - ) - > create_parser_type; + using create_parser_type = std::function(future_string_queue_type&, + future_buffer_queue_type&, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities)>; private: - typedef std::map map_type; + using map_type = std::map; map_type m_callbacks; 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 7293cfa7324..d6428790a9e 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 @@ -42,9 +42,9 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include -#include #include #include #include @@ -52,19 +52,26 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include #include +#include #include +#include +#include #include +#include #include #include #include namespace osmium { + namespace builder { + class Builder; + } // namespace builder + /** * Exception thrown when the o5m deocder failed. The exception contains * (if available) information about the place where the error happened @@ -529,7 +536,7 @@ namespace osmium { uint64_t length = 0; try { length = protozero::decode_varint(&m_data, m_end); - } catch (protozero::end_of_buffer_exception&) { + } catch (const protozero::end_of_buffer_exception&) { throw o5m_error("premature end of file"); } 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 new file mode 100644 index 00000000000..15a31f38a5f --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp @@ -0,0 +1,156 @@ +#ifndef OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP +#define OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osmium { + + namespace io { + + namespace detail { + + 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() { + if (m_buffer.committed() > 800*1024) { + osmium::memory::Buffer buffer{1024*1024}; + using std::swap; + swap(m_buffer, buffer); + send_to_output_queue(std::move(buffer)); + + } + } + + 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::osm_entity_bits::type read_types) : + Parser(input_queue, output_queue, header_promise, read_types) { + set_header_value(osmium::io::Header{}); + } + + ~OPLParser() noexcept final = default; + + 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 (m_buffer.committed() > 0) { + send_to_output_queue(std::move(m_buffer)); + } + } + + }; // class OPLParser + + // we want the register_parser() function to run, setting + // 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::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new OPLParser(input_queue, output_queue, header_promise, read_which_entities)); + }); + + // dummy function to silence the unused variable warning from above + inline bool get_registered_opl_parser() noexcept { + return registered_opl_parser; + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + + +#endif // OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp index 3fedf7e9c7d..acdfe29afb1 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 @@ -33,26 +33,26 @@ 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 @@ -65,8 +65,6 @@ namespace osmium { namespace io { - class File; - namespace detail { struct opl_output_options { @@ -74,6 +72,12 @@ namespace osmium { /// Should metadata of objects be added? bool add_metadata; + /// Should node locations be added to ways? + bool locations_on_ways; + + /// Write in form of a diff file? + bool format_as_diff; + }; /** @@ -87,38 +91,71 @@ namespace osmium { osmium::io::detail::append_utf8_encoded_string(*m_out, data); } + void write_field_int(char c, int64_t value) { + *m_out += c; + output_int(value); + } + + void write_field_timestamp(char c, const osmium::Timestamp& timestamp) { + *m_out += c; + *m_out += timestamp.to_iso(); + } + + void write_tags(const osmium::TagList& tags) { + *m_out += " T"; + + if (tags.empty()) { + return; + } + + auto it = tags.begin(); + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + + for (++it; it != tags.end(); ++it) { + *m_out += ','; + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + } + } + void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64, object.id()); + output_int(object.id()); if (m_options.add_metadata) { - output_formatted(" v%d d", object.version()); + *m_out += ' '; + write_field_int('v', object.version()); + *m_out += " d"; *m_out += (object.visible() ? 'V' : 'D'); - output_formatted(" c%d t", object.changeset()); - *m_out += object.timestamp().to_iso(); - output_formatted(" i%d u", object.uid()); + *m_out += ' '; + write_field_int('c', object.changeset()); + *m_out += ' '; + write_field_timestamp('t', object.timestamp()); + *m_out += ' '; + write_field_int('i', object.uid()); + *m_out += " u"; append_encoded_string(object.user()); } - *m_out += " T"; - bool first = true; - for (const auto& tag : object.tags()) { - if (first) { - first = false; - } else { - *m_out += ','; - } - append_encoded_string(tag.key()); - *m_out += '='; - append_encoded_string(tag.value()); - } + write_tags(object.tags()); } void write_location(const osmium::Location& location, const char x, const char y) { + *m_out += ' '; + *m_out += x; if (location) { - output_formatted(" %c%.7f %c%.7f", x, location.lon_without_check(), y, location.lat_without_check()); - } else { - *m_out += ' '; - *m_out += x; - *m_out += ' '; - *m_out += y; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.x()); + } + *m_out += ' '; + *m_out += y; + if (location) { + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.y()); + } + } + + void write_diff(const osmium::OSMObject& object) { + if (m_options.format_as_diff) { + *m_out += object.diff_as_char(); } } @@ -148,70 +185,93 @@ namespace osmium { } void node(const osmium::Node& node) { + write_diff(node); *m_out += 'n'; write_meta(node); write_location(node.location(), 'x', 'y'); *m_out += '\n'; } + void write_field_ref(const osmium::NodeRef& node_ref) { + write_field_int('n', node_ref.ref()); + *m_out += 'x'; + if (node_ref.location()) { + node_ref.location().as_string(std::back_inserter(*m_out), 'y'); + } else { + *m_out += 'y'; + } + } + void way(const osmium::Way& way) { + write_diff(way); *m_out += 'w'; write_meta(way); *m_out += " N"; - bool first = true; - for (const auto& node_ref : way.nodes()) { - if (first) { - first = false; + + if (!way.nodes().empty()) { + auto it = way.nodes().begin(); + if (m_options.locations_on_ways) { + write_field_ref(*it); + for (++it; it != way.nodes().end(); ++it) { + *m_out += ','; + write_field_ref(*it); + } } else { - *m_out += ','; + write_field_int('n', it->ref()); + for (++it; it != way.nodes().end(); ++it) { + *m_out += ','; + write_field_int('n', it->ref()); + } } - output_formatted("n%" PRId64, node_ref.ref()); } + *m_out += '\n'; } + void relation_member(const osmium::RelationMember& member) { + *m_out += item_type_to_char(member.type()); + output_int(member.ref()); + *m_out += '@'; + append_encoded_string(member.role()); + } + void relation(const osmium::Relation& relation) { + write_diff(relation); *m_out += 'r'; write_meta(relation); *m_out += " M"; - bool first = true; - for (const auto& member : relation.members()) { - if (first) { - first = false; - } else { + + if (!relation.members().empty()) { + auto it = relation.members().begin(); + relation_member(*it); + for (++it; it != relation.members().end(); ++it) { *m_out += ','; + relation_member(*it); } - *m_out += item_type_to_char(member.type()); - output_formatted("%" PRId64 "@", member.ref()); - append_encoded_string(member.role()); } + *m_out += '\n'; } void changeset(const osmium::Changeset& changeset) { - output_formatted("c%d k%d s", changeset.id(), changeset.num_changes()); - *m_out += changeset.created_at().to_iso(); - *m_out += " e"; - *m_out += changeset.closed_at().to_iso(); - output_formatted(" d%d i%d u", changeset.num_comments(), changeset.uid()); + write_field_int('c', changeset.id()); + *m_out += ' '; + write_field_int('k', changeset.num_changes()); + *m_out += ' '; + write_field_timestamp('s', changeset.created_at()); + *m_out += ' '; + write_field_timestamp('e', changeset.closed_at()); + *m_out += ' '; + write_field_int('d', changeset.num_comments()); + *m_out += ' '; + write_field_int('i', changeset.uid()); + *m_out += " u"; append_encoded_string(changeset.user()); write_location(changeset.bounds().bottom_left(), 'x', 'y'); write_location(changeset.bounds().top_right(), 'X', 'Y'); - *m_out += " T"; - bool first = true; - for (const auto& tag : changeset.tags()) { - if (first) { - first = false; - } else { - *m_out += ','; - } - append_encoded_string(tag.key()); - *m_out += '='; - append_encoded_string(tag.value()); - } - + write_tags(changeset.tags()); *m_out += '\n'; } @@ -226,7 +286,9 @@ namespace osmium { OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : OutputFormat(output_queue), m_options() { - m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.locations_on_ways = file.is_true("locations_on_ways"); + m_options.format_as_diff = file.is_true("diff"); } OPLOutputFormat(const OPLOutputFormat&) = delete; 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 new file mode 100644 index 00000000000..97c59275a09 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp @@ -0,0 +1,747 @@ +#ifndef OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP +#define OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osmium { + + namespace builder { + class Builder; + } // namespace builder + + /** + * Exception thrown when there was a problem with parsing the OPL format + * of a file. + */ + struct opl_error : public io_error { + + uint64_t line = 0; + uint64_t column = 0; + const char* data; + std::string msg; + + explicit opl_error(const std::string& what, const char* d = nullptr) : + io_error(std::string("OPL error: ") + what), + data(d), + msg("OPL error: ") { + msg.append(what); + } + + explicit opl_error(const char* what, const char* d = nullptr) : + io_error(std::string("OPL error: ") + what), + data(d), + msg("OPL error: ") { + msg.append(what); + } + + void set_pos(uint64_t l, uint64_t col) { + line = l; + column = col; + msg.append(" on line "); + msg.append(std::to_string(line)); + msg.append(" column "); + msg.append(std::to_string(column)); + } + + const char* what() const noexcept override { + return msg.c_str(); + } + + }; // struct opl_error + + namespace io { + + namespace detail { + + /** + * Consume consecutive space and tab characters. There must be + * at least one. + */ + inline void opl_parse_space(const char** s) { + if (**s != ' ' && **s != '\t') { + throw opl_error{"expected space or tab character", *s}; + } + do { + ++*s; + } while (**s == ' ' || **s == '\t'); + } + + /** + * Check whether s points to something else than the end of the + * string or a space or tab. + */ + inline bool opl_non_empty(const char *s) { + return *s != '\0' && *s != ' ' && *s != '\t'; + } + + /** + * Skip to the next space or tab character or the end of the + * string. + */ + inline const char* opl_skip_section(const char** s) noexcept { + while (opl_non_empty(*s)) { + ++*s; + } + return *s; + } + + /** + * Parse OPL-escaped strings with hex code with a '%' at the end. + * Appends resulting unicode character to the result string. + * + * Returns a pointer to next character that needs to be consumed. + */ + inline void opl_parse_escaped(const char** data, std::string& result) { + const char* s = *data; + uint32_t value = 0; + const int max_length = sizeof(value) * 2 /* hex chars per byte */; + int length = 0; + while (++length <= max_length) { + if (*s == '\0') { + throw opl_error{"eol", s}; + } + if (*s == '%') { + ++s; + utf8::utf32to8(&value, &value + 1, std::back_inserter(result)); + *data = s; + return; + } + value <<= 4; + if (*s >= '0' && *s <= '9') { + value += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + value += *s - 'a' + 10; + } else if (*s >= 'A' && *s <= 'F') { + value += *s - 'A' + 10; + } else { + throw opl_error{"not a hex char", s}; + } + ++s; + } + throw opl_error{"hex escape too long", s}; + } + + /** + * Parse a string up to end of string or next space, tab, comma, or + * equal sign. + * + * Appends characters to the result string. + * + * Returns a pointer to next character that needs to be consumed. + */ + inline void opl_parse_string(const char** data, std::string& result) { + const char* s = *data; + while (true) { + if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',' || *s == '=') { + break; + } else if (*s == '%') { + ++s; + opl_parse_escaped(&s, result); + } else { + result += *s; + ++s; + } + } + *data = s; + } + + // Arbitrary limit how long integers can get + constexpr const int max_int_len = 16; + + template + inline T opl_parse_int(const char** s) { + if (**s == '\0') { + throw opl_error{"expected integer", *s}; + } + const bool negative = (**s == '-'); + if (negative) { + ++*s; + } + + int64_t value = 0; + + int n = max_int_len; + while (**s >= '0' && **s <= '9') { + if (--n == 0) { + throw opl_error{"integer too long", *s}; + } + value *= 10; + value += **s - '0'; + ++*s; + } + + if (n == max_int_len) { + throw opl_error{"expected integer", *s}; + } + + if (negative) { + value = -value; + if (value < std::numeric_limits::min()) { + throw opl_error{"integer too long", *s}; + } + } else { + if (value > std::numeric_limits::max()) { + throw opl_error{"integer too long", *s}; + } + } + + return T(value); + } + + inline osmium::object_id_type opl_parse_id(const char** s) { + return opl_parse_int(s); + } + + inline osmium::changeset_id_type opl_parse_changeset_id(const char** s) { + return opl_parse_int(s); + } + + inline osmium::object_version_type opl_parse_version(const char** s) { + return opl_parse_int(s); + } + + inline bool opl_parse_visible(const char** data) { + if (**data == 'V') { + ++*data; + return true; + } + if (**data == 'D') { + ++*data; + return false; + } + throw opl_error{"invalid visible flag", *data}; + } + + inline osmium::user_id_type opl_parse_uid(const char** s) { + return opl_parse_int(s); + } + + inline osmium::Timestamp opl_parse_timestamp(const char** s) { + try { + if (**s == '\0' || **s == ' ' || **s == '\t') { + return osmium::Timestamp{}; + } + osmium::Timestamp timestamp{*s}; + *s += 20; + return timestamp; + } catch (const std::invalid_argument&) { + throw opl_error{"can not parse timestamp", *s}; + } + } + + /** + * Check if data points to given character and consume it. + * Throw error otherwise. + */ + inline void opl_parse_char(const char** data, char c) { + if (**data == c) { + ++*data; + return; + } + std::string msg = "expected '"; + msg += c; + msg += "'"; + throw opl_error{msg, *data}; + } + + /** + * Parse a list of tags in the format 'key=value,key=value,...' + * + * Tags will be added to the buffer using a TagListBuilder. + */ + inline void opl_parse_tags(const char* s, osmium::memory::Buffer& buffer, osmium::builder::Builder* parent_builder = nullptr) { + osmium::builder::TagListBuilder builder{buffer, parent_builder}; + std::string key; + std::string value; + while (true) { + opl_parse_string(&s, key); + opl_parse_char(&s, '='); + opl_parse_string(&s, value); + builder.add_tag(key, value); + if (*s == ' ' || *s == '\t' || *s == '\0') { + break; + } + opl_parse_char(&s, ','); + key.clear(); + value.clear(); + } + } + + /** + * Parse a number of nodes in the format "nID,nID,nID..." + * + * Nodes will be added to the buffer using a WayNodeListBuilder. + */ + inline void opl_parse_way_nodes(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::WayBuilder* parent_builder = nullptr) { + if (s == e) { + return; + } + osmium::builder::WayNodeListBuilder builder{buffer, parent_builder}; + + while (s < e) { + opl_parse_char(&s, 'n'); + if (s == e) { + throw opl_error{"expected integer", s}; + } + + const osmium::object_id_type ref = opl_parse_id(&s); + if (s == e) { + builder.add_node_ref(ref); + return; + } + + osmium::Location location; + if (*s == 'x') { + ++s; + location.set_lon_partial(&s); + if (*s == 'y') { + ++s; + location.set_lat_partial(&s); + } + } + + builder.add_node_ref(ref, location); + + if (s == e) { + return; + } + + opl_parse_char(&s, ','); + } + } + + inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::NodeBuilder builder{buffer}; + osmium::Node& node = builder.object(); + + node.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + std::string user; + osmium::Location location; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + node.set_version(opl_parse_version(data)); + break; + case 'd': + node.set_visible(opl_parse_visible(data)); + break; + case 'c': + node.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + node.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + node.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'x': + if (opl_non_empty(*data)) { + location.set_lon_partial(data); + } + break; + case 'y': + if (opl_non_empty(*data)) { + location.set_lat_partial(data); + } + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + if (location.valid()) { + node.set_location(location); + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + buffer.commit(); + } + + inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::WayBuilder builder{buffer}; + osmium::Way& way = builder.object(); + + way.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + const char* nodes_begin = nullptr; + const char* nodes_end = nullptr; + + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + way.set_version(opl_parse_version(data)); + break; + case 'd': + way.set_visible(opl_parse_visible(data)); + break; + case 'c': + way.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + way.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + way.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'N': + nodes_begin = *data; + nodes_end = opl_skip_section(data); + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder); + + buffer.commit(); + } + + inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) { + if (s == e) { + return; + } + osmium::builder::RelationMemberListBuilder builder{buffer, parent_builder}; + + while (s < e) { + osmium::item_type type = osmium::char_to_item_type(*s); + if (type != osmium::item_type::node && + type != osmium::item_type::way && + type != osmium::item_type::relation) { + throw opl_error{"unknown object type", s}; + } + ++s; + + if (s == e) { + throw opl_error{"expected integer", s}; + } + osmium::object_id_type ref = opl_parse_id(&s); + opl_parse_char(&s, '@'); + if (s == e) { + builder.add_member(type, ref, ""); + return; + } + std::string role; + opl_parse_string(&s, role); + builder.add_member(type, ref, role); + + if (s == e) { + return; + } + opl_parse_char(&s, ','); + } + } + + inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::RelationBuilder builder{buffer}; + osmium::Relation& relation = builder.object(); + + relation.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + const char* members_begin = nullptr; + const char* members_end = nullptr; + + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + relation.set_version(opl_parse_version(data)); + break; + case 'd': + relation.set_visible(opl_parse_visible(data)); + break; + case 'c': + relation.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + relation.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + relation.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'M': + members_begin = *data; + members_end = opl_skip_section(data); + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + if (members_begin != members_end) { + opl_parse_relation_members(members_begin, members_end, buffer, &builder); + } + + buffer.commit(); + } + + inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::ChangesetBuilder builder{buffer}; + osmium::Changeset& changeset = builder.object(); + + changeset.set_id(opl_parse_changeset_id(data)); + + const char* tags_begin = nullptr; + + osmium::Location location1; + osmium::Location location2; + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'k': + changeset.set_num_changes(opl_parse_int(data)); + break; + case 's': + changeset.set_created_at(opl_parse_timestamp(data)); + break; + case 'e': + changeset.set_closed_at(opl_parse_timestamp(data)); + break; + case 'd': + changeset.set_num_comments(opl_parse_int(data)); + break; + case 'i': + changeset.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'x': + if (opl_non_empty(*data)) { + location1.set_lon_partial(data); + } + break; + case 'y': + if (opl_non_empty(*data)) { + location1.set_lat_partial(data); + } + break; + case 'X': + if (opl_non_empty(*data)) { + location2.set_lon_partial(data); + } + break; + case 'Y': + if (opl_non_empty(*data)) { + location2.set_lat_partial(data); + } + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + + } + + if (location1.valid() && location2.valid()) { + changeset.bounds().extend(location1); + changeset.bounds().extend(location2); + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + buffer.commit(); + } + + inline bool opl_parse_line(uint64_t line_count, + const char* data, + osmium::memory::Buffer& buffer, + osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) { + const char* start_of_line = data; + try { + switch (*data) { + case '\0': + // ignore empty lines + break; + case '#': + // ignore lines starting with # + break; + case 'n': + if (read_types & osmium::osm_entity_bits::node) { + ++data; + opl_parse_node(&data, buffer); + return true; + } + break; + case 'w': + if (read_types & osmium::osm_entity_bits::way) { + ++data; + opl_parse_way(&data, buffer); + return true; + } + break; + case 'r': + if (read_types & osmium::osm_entity_bits::relation) { + ++data; + opl_parse_relation(&data, buffer); + return true; + } + break; + case 'c': + if (read_types & osmium::osm_entity_bits::changeset) { + ++data; + opl_parse_changeset(&data, buffer); + return true; + } + break; + default: + throw opl_error{"unknown type", data}; + } + } catch (opl_error& e) { + e.set_pos(line_count, e.data ? e.data - start_of_line : 0); + throw; + } + + return false; + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + + +#endif // OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP 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 34ca82f2448..0fe49158bb2 100644 --- a/third_party/libosmium/include/osmium/io/detail/output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/output_format.hpp @@ -33,16 +33,16 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include -#include #include #include #include #include -#include +#include #include #include #include @@ -70,9 +70,28 @@ namespace osmium { m_out(std::make_shared()) { } - template - void output_formatted(const char* format, TArgs&&... args) { - append_printf_formatted_string(*m_out, format, std::forward(args)...); + // Simple function to convert integer to string. This is much + // faster than using sprintf, but could be further optimized. + // See https://github.com/miloyip/itoa-benchmark . + void output_int(int64_t value) { + if (value < 0) { + *m_out += '-'; + value = -value; + } + + char temp[20]; + char *t = temp; + do { + *t++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + const auto old_size = m_out->size(); + m_out->resize(old_size + (t - temp)); + char* data = &(*m_out)[old_size]; + do { + *data++ += *--t; + } while (t != temp); } }; // class OutputBlock; @@ -133,11 +152,11 @@ namespace osmium { public: - typedef std::function create_output_type; + using create_output_type = std::function; private: - typedef std::map map_type; + using map_type = std::map; map_type m_callbacks; diff --git a/third_party/libosmium/include/osmium/io/detail/pbf.hpp b/third_party/libosmium/include/osmium/io/detail/pbf.hpp index 88c499326b0..e23f8b9bf13 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf.hpp @@ -78,7 +78,7 @@ namespace osmium { // between representation as double and as int const int64_t lonlat_resolution = 1000 * 1000 * 1000; - const int64_t resolution_convert = lonlat_resolution / osmium::Location::coordinate_precision; + const int64_t resolution_convert = lonlat_resolution / osmium::detail::coordinate_precision; } // namespace detail 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 43de1c18e74..5df191db65e 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp @@ -33,10 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include -#include #include #include #include @@ -44,35 +42,47 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include // IWYU pragma: export #include #include #include +#include +#include +#include +#include #include #include +#include +#include +#include #include -#include -#include +#include #include #include namespace osmium { + namespace builder { + class Builder; + } // namespace builder + namespace io { namespace detail { - using ptr_len_type = std::pair; + using protozero::data_view; using osm_string_len_type = std::pair; class PBFPrimitiveBlockDecoder { - static constexpr size_t initial_buffer_size = 2 * 1024 * 1024; + static constexpr const size_t initial_buffer_size = 2 * 1024 * 1024; - ptr_len_type m_data; + data_view m_data; std::vector m_stringtable; int64_t m_lon_offset = 0; @@ -84,18 +94,18 @@ namespace osmium { osmium::memory::Buffer m_buffer { initial_buffer_size }; - void decode_stringtable(const ptr_len_type& data) { + void decode_stringtable(const data_view& data) { if (!m_stringtable.empty()) { throw osmium::pbf_error("more than one stringtable in pbf file"); } protozero::pbf_message pbf_string_table(data); while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) { - auto str_len = pbf_string_table.get_data(); - if (str_len.second > osmium::max_osm_string_length) { + 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"); } - m_stringtable.emplace_back(str_len.first, osmium::string_size_type(str_len.second)); + m_stringtable.emplace_back(str_view.data(), osmium::string_size_type(str_view.size())); } } @@ -104,7 +114,7 @@ namespace osmium { while (pbf_primitive_block.next()) { switch (pbf_primitive_block.tag()) { case OSMFormat::PrimitiveBlock::required_StringTable_stringtable: - decode_stringtable(pbf_primitive_block.get_data()); + decode_stringtable(pbf_primitive_block.get_view()); break; case OSMFormat::PrimitiveBlock::optional_int32_granularity: m_granularity = pbf_primitive_block.get_int32(); @@ -132,28 +142,28 @@ namespace osmium { switch (pbf_primitive_group.tag()) { case OSMFormat::PrimitiveGroup::repeated_Node_nodes: if (m_read_types & osmium::osm_entity_bits::node) { - decode_node(pbf_primitive_group.get_data()); + decode_node(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense: if (m_read_types & osmium::osm_entity_bits::node) { - decode_dense_nodes(pbf_primitive_group.get_data()); + decode_dense_nodes(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::repeated_Way_ways: if (m_read_types & osmium::osm_entity_bits::way) { - decode_way(pbf_primitive_group.get_data()); + decode_way(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::repeated_Relation_relations: if (m_read_types & osmium::osm_entity_bits::relation) { - decode_relation(pbf_primitive_group.get_data()); + decode_relation(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } @@ -165,7 +175,7 @@ namespace osmium { } } - osm_string_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) { + osm_string_len_type decode_info(const data_view& data, osmium::OSMObject& object) { osm_string_len_type user = std::make_pair("", 0); protozero::pbf_message pbf_info(data); @@ -173,7 +183,7 @@ namespace osmium { switch (pbf_info.tag()) { case OSMFormat::Info::optional_int32_version: { - auto version = pbf_info.get_int32(); + const auto version = pbf_info.get_int32(); if (version < 0) { throw osmium::pbf_error("object version must not be negative"); } @@ -185,7 +195,7 @@ namespace osmium { break; case OSMFormat::Info::optional_int64_changeset: { - auto changeset_id = pbf_info.get_int64(); + const auto changeset_id = pbf_info.get_int64(); if (changeset_id < 0) { throw osmium::pbf_error("object changeset_id must not be negative"); } @@ -209,15 +219,15 @@ namespace osmium { return user; } - using kv_type = std::pair; + using kv_type = protozero::iterator_range; void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) { - if (keys.first != keys.second) { + if (!keys.empty()) { osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); - auto kit = keys.first; - auto vit = vals.first; - while (kit != keys.second) { - if (vit == vals.second) { + auto kit = keys.begin(); + auto vit = vals.begin(); + 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"); } @@ -232,7 +242,7 @@ namespace osmium { return int32_t((c * m_granularity + m_lon_offset) / resolution_convert); } - void decode_node(const ptr_len_type& data) { + void decode_node(const data_view& data) { osmium::builder::NodeBuilder builder(m_buffer); osmium::Node& node = builder.object(); @@ -256,7 +266,7 @@ namespace osmium { vals = pbf_node.get_packed_uint32(); break; case OSMFormat::Node::optional_Info_info: - user = decode_info(pbf_node.get_data(), builder.object()); + user = decode_info(pbf_node.get_view(), builder.object()); break; case OSMFormat::Node::required_sint64_lat: lat = pbf_node.get_sint64(); @@ -287,12 +297,14 @@ namespace osmium { m_buffer.commit(); } - void decode_way(const ptr_len_type& data) { + void decode_way(const data_view& data) { osmium::builder::WayBuilder builder(m_buffer); kv_type keys; kv_type vals; - std::pair refs; + protozero::iterator_range refs; + protozero::iterator_range lats; + protozero::iterator_range lons; osm_string_len_type user = { "", 0 }; @@ -309,11 +321,17 @@ namespace osmium { vals = pbf_way.get_packed_uint32(); break; case OSMFormat::Way::optional_Info_info: - user = decode_info(pbf_way.get_data(), builder.object()); + user = decode_info(pbf_way.get_view(), builder.object()); break; case OSMFormat::Way::packed_sint64_refs: refs = pbf_way.get_packed_sint64(); break; + case OSMFormat::Way::packed_sint64_lat: + lats = pbf_way.get_packed_sint64(); + break; + case OSMFormat::Way::packed_sint64_lon: + lons = pbf_way.get_packed_sint64(); + break; default: pbf_way.skip(); } @@ -321,11 +339,26 @@ namespace osmium { builder.add_user(user.first, user.second); - if (refs.first != refs.second) { + if (!refs.empty()) { osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder); osmium::util::DeltaDecode ref; - while (refs.first != refs.second) { - wnl_builder.add_node_ref(ref.update(*refs.first++)); + if (lats.empty()) { + for (const auto& ref_value : refs) { + wnl_builder.add_node_ref(ref.update(ref_value)); + } + } else { + osmium::util::DeltaDecode lon; + osmium::util::DeltaDecode lat; + while (!refs.empty() && !lons.empty() && !lats.empty()) { + wnl_builder.add_node_ref( + ref.update(refs.front()), + osmium::Location{convert_pbf_coordinate(lon.update(lons.front())), + convert_pbf_coordinate(lat.update(lats.front()))} + ); + refs.drop_front(); + lons.drop_front(); + lats.drop_front(); + } } } @@ -334,14 +367,14 @@ namespace osmium { m_buffer.commit(); } - void decode_relation(const ptr_len_type& data) { + void decode_relation(const data_view& data) { osmium::builder::RelationBuilder builder(m_buffer); kv_type keys; kv_type vals; - std::pair roles; - std::pair refs; - std::pair types; + protozero::iterator_range roles; + protozero::iterator_range refs; + protozero::iterator_range types; osm_string_len_type user = { "", 0 }; @@ -358,7 +391,7 @@ namespace osmium { vals = pbf_relation.get_packed_uint32(); break; case OSMFormat::Relation::optional_Info_info: - user = decode_info(pbf_relation.get_data(), builder.object()); + user = decode_info(pbf_relation.get_view(), builder.object()); break; case OSMFormat::Relation::packed_int32_roles_sid: roles = pbf_relation.get_packed_int32(); @@ -376,21 +409,24 @@ namespace osmium { builder.add_user(user.first, user.second); - if (refs.first != refs.second) { + if (!refs.empty()) { osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder); osmium::util::DeltaDecode ref; - while (roles.first != roles.second && refs.first != refs.second && types.first != types.second) { - const auto& r = m_stringtable.at(*roles.first++); - int type = *types.first++; + while (!roles.empty() && !refs.empty() && !types.empty()) { + 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"); } rml_builder.add_member( osmium::item_type(type + 1), - ref.update(*refs.first++), + ref.update(refs.front()), r.first, r.second ); + roles.drop_front(); + refs.drop_front(); + types.drop_front(); } } @@ -399,22 +435,22 @@ namespace osmium { m_buffer.commit(); } - void decode_dense_nodes(const ptr_len_type& data) { + void decode_dense_nodes(const data_view& data) { bool has_info = false; bool has_visibles = false; - std::pair ids; - std::pair lats; - std::pair lons; + protozero::iterator_range ids; + protozero::iterator_range lats; + protozero::iterator_range lons; - std::pair tags; + protozero::iterator_range tags; - std::pair versions; - std::pair timestamps; - std::pair changesets; - std::pair uids; - std::pair user_sids; - std::pair visibles; + protozero::iterator_range versions; + protozero::iterator_range timestamps; + protozero::iterator_range changesets; + protozero::iterator_range uids; + protozero::iterator_range user_sids; + protozero::iterator_range visibles; protozero::pbf_message pbf_dense_nodes(data); while (pbf_dense_nodes.next()) { @@ -475,11 +511,11 @@ namespace osmium { osmium::util::DeltaDecode dense_changeset; osmium::util::DeltaDecode dense_timestamp; - auto tag_it = tags.first; + auto tag_it = tags.begin(); - while (ids.first != ids.second) { - if (lons.first == lons.second || - lats.first == lats.second) { + while (!ids.empty()) { + if (lons.empty() || + lats.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } @@ -489,43 +525,50 @@ namespace osmium { osmium::builder::NodeBuilder builder(m_buffer); osmium::Node& node = builder.object(); - node.set_id(dense_id.update(*ids.first++)); + node.set_id(dense_id.update(ids.front())); + ids.drop_front(); if (has_info) { - if (versions.first == versions.second || - changesets.first == changesets.second || - timestamps.first == timestamps.second || - uids.first == uids.second || - user_sids.first == user_sids.second) { + if (versions.empty() || + changesets.empty() || + timestamps.empty() || + uids.empty() || + user_sids.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } - auto version = *versions.first++; + const auto version = versions.front(); + versions.drop_front(); if (version < 0) { throw osmium::pbf_error("object version must not be negative"); } node.set_version(static_cast(version)); - auto changeset_id = dense_changeset.update(*changesets.first++); + 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"); } node.set_changeset(static_cast(changeset_id)); - node.set_timestamp(dense_timestamp.update(*timestamps.first++) * m_date_factor / 1000); - node.set_uid_from_signed(static_cast(dense_uid.update(*uids.first++))); + node.set_timestamp(dense_timestamp.update(timestamps.front()) * m_date_factor / 1000); + timestamps.drop_front(); + node.set_uid_from_signed(static_cast(dense_uid.update(uids.front()))); + uids.drop_front(); if (has_visibles) { - if (visibles.first == visibles.second) { + if (visibles.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } - visible = (*visibles.first++) != 0; + visible = (visibles.front() != 0); + visibles.drop_front(); } node.set_visible(visible); - const auto& u = m_stringtable.at(dense_user_sid.update(*user_sids.first++)); + const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front())); + user_sids.drop_front(); builder.add_user(u.first, u.second); } else { builder.add_user(""); @@ -533,8 +576,10 @@ namespace osmium { // even if the node isn't visible, there's still a record // of its lat/lon in the dense arrays. - const auto lon = dense_longitude.update(*lons.first++); - const auto lat = dense_latitude.update(*lats.first++); + const auto lon = dense_longitude.update(lons.front()); + lons.drop_front(); + const auto lat = dense_latitude.update(lats.front()); + lats.drop_front(); if (visible) { builder.object().set_location(osmium::Location( convert_pbf_coordinate(lon), @@ -542,18 +587,18 @@ namespace osmium { )); } - if (tag_it != tags.second) { + if (tag_it != tags.end()) { osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); - while (tag_it != tags.second && *tag_it != 0) { + while (tag_it != tags.end() && *tag_it != 0) { const auto& k = m_stringtable.at(*tag_it++); - if (tag_it == tags.second) { + if (tag_it == tags.end()) { throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs } const auto& v = m_stringtable.at(*tag_it++); tl_builder.add_tag(k.first, k.second, v.first, v.second); } - if (tag_it != tags.second) { + if (tag_it != tags.end()) { ++tag_it; } } @@ -565,7 +610,7 @@ namespace osmium { public: - PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) : + PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types) : m_data(data), m_read_types(read_types) { } @@ -582,7 +627,7 @@ namespace osmium { try { decode_primitive_block_metadata(); decode_primitive_block_data(); - } catch (std::out_of_range&) { + } catch (const std::out_of_range&) { throw osmium::pbf_error("string id out of range"); } @@ -591,17 +636,17 @@ namespace osmium { }; // class PBFPrimitiveBlockDecoder - inline ptr_len_type decode_blob(const std::string& blob_data, std::string& output) { + inline data_view decode_blob(const std::string& blob_data, std::string& output) { int32_t raw_size = 0; - std::pair zlib_data = {nullptr, 0}; + protozero::data_view zlib_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_data(); - if (data_len.second > max_uncompressed_blob_size) { + auto data_len = pbf_blob.get_view(); + if (data_len.size() > max_uncompressed_blob_size) { throw osmium::pbf_error("illegal blob size"); } return data_len; @@ -613,7 +658,7 @@ namespace osmium { } break; case FileFormat::Blob::optional_bytes_zlib_data: - zlib_data = pbf_blob.get_data(); + zlib_data = pbf_blob.get_view(); break; case FileFormat::Blob::optional_bytes_lzma_data: throw osmium::pbf_error("lzma blobs not implemented"); @@ -622,10 +667,10 @@ namespace osmium { } } - if (zlib_data.second != 0 && raw_size != 0) { + if (zlib_data.size() != 0 && raw_size != 0) { return osmium::io::detail::zlib_uncompress_string( - zlib_data.first, - static_cast(zlib_data.second), + zlib_data.data(), + static_cast(zlib_data.size()), static_cast(raw_size), output ); @@ -634,7 +679,7 @@ namespace osmium { throw osmium::pbf_error("blob contains no data"); } - inline osmium::Box decode_header_bbox(const ptr_len_type& data) { + inline osmium::Box decode_header_bbox(const data_view& data) { int64_t left = std::numeric_limits::max(); int64_t right = std::numeric_limits::max(); int64_t top = std::numeric_limits::max(); @@ -674,7 +719,7 @@ namespace osmium { return box; } - inline osmium::io::Header decode_header_block(const ptr_len_type& data) { + inline osmium::io::Header decode_header_block(const data_view& data) { osmium::io::Header header; int i = 0; @@ -682,20 +727,20 @@ namespace osmium { while (pbf_header_block.next()) { switch (pbf_header_block.tag()) { case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox: - header.add_box(decode_header_bbox(pbf_header_block.get_data())); + header.add_box(decode_header_bbox(pbf_header_block.get_view())); break; case OSMFormat::HeaderBlock::repeated_string_required_features: { - auto feature = pbf_header_block.get_data(); - if (!strncmp("OsmSchema-V0.6", feature.first, feature.second)) { + auto feature = pbf_header_block.get_view(); + if (!std::strncmp("OsmSchema-V0.6", feature.data(), feature.size())) { // intentionally left blank - } else if (!strncmp("DenseNodes", feature.first, feature.second)) { + } else if (!std::strncmp("DenseNodes", feature.data(), feature.size())) { header.set("pbf_dense_nodes", true); - } else if (!strncmp("HistoricalInformation", feature.first, feature.second)) { + } else if (!std::strncmp("HistoricalInformation", feature.data(), feature.size())) { header.set_has_multiple_object_versions(true); } else { std::string msg("required feature not supported: "); - msg.append(feature.first, feature.second); + msg.append(feature.data(), feature.size()); throw osmium::pbf_error(msg); } } @@ -708,7 +753,7 @@ namespace osmium { break; case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp: { - 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); } 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 0c6a9ef546d..1253447f3cf 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 @@ -38,25 +38,22 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include -#include #include -#include #include #include +#include #include #include // IWYU pragma: export #include #include -#include -#include +#include #include -#include +#include #include -#include -#include #include #include #include @@ -80,7 +77,7 @@ namespace osmium { */ std::string read_from_input_queue(size_t size) { while (m_input_buffer.size() < size) { - std::string new_data = get_input(); + const std::string new_data = get_input(); if (input_done()) { throw osmium::pbf_error("truncated data (EOF encountered)"); } @@ -106,7 +103,7 @@ namespace osmium { try { const std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order)); size_in_network_byte_order = *reinterpret_cast(input_data.data()); - } catch (osmium::pbf_error&) { + } catch (const osmium::pbf_error&) { return 0; // EOF } @@ -123,13 +120,13 @@ namespace osmium { * type. Return the size of the following Blob. */ size_t decode_blob_header(protozero::pbf_message&& pbf_blob_header, const char* expected_type) { - std::pair blob_header_type; + protozero::data_view blob_header_type; size_t blob_header_datasize = 0; while (pbf_blob_header.next()) { switch (pbf_blob_header.tag()) { case FileFormat::BlobHeader::required_string_type: - blob_header_type = pbf_blob_header.get_data(); + blob_header_type = pbf_blob_header.get_view(); break; case FileFormat::BlobHeader::required_int32_datasize: blob_header_datasize = pbf_blob_header.get_int32(); @@ -143,7 +140,7 @@ namespace osmium { throw osmium::pbf_error("PBF format error: BlobHeader.datasize missing or zero."); } - if (strncmp(expected_type, blob_header_type.first, blob_header_type.second)) { + 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)"); } 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 878d7b4ec12..43aa8cc4d73 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 @@ -41,30 +41,34 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include +#include +#include #include #include #include // IWYU pragma: export #include +#include #include #include #include #include #include #include -#include +#include #include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -101,6 +105,9 @@ namespace osmium { /// Should the visible flag be added to all OSM objects? bool add_visible_flag; + /// Should node locations be added to ways? + bool locations_on_ways; + }; /** @@ -483,6 +490,7 @@ namespace osmium { m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata"); m_options.add_historical_information_flag = file.has_multiple_object_versions(); m_options.add_visible_flag = file.has_multiple_object_versions(); + m_options.locations_on_ways = file.is_true("locations_on_ways"); } PBFOutputFormat(const PBFOutputFormat&) = delete; @@ -514,20 +522,24 @@ namespace osmium { pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation"); } + if (m_options.locations_on_ways) { + pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_optional_features, "LocationsOnWays"); + } + pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator")); - 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()); pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts)); } - std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number"); + 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())); } - 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); } @@ -571,15 +583,30 @@ namespace osmium { pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id()); add_meta(way, pbf_way); - static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type { - return node_ref->ref(); - }; - typedef osmium::util::DeltaEncodeIterator it_type; + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_refs)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(node_ref.ref())); + } + } - const auto& nodes = way.nodes(); - it_type first { nodes.cbegin(), nodes.cend(), map_node_ref }; - it_type last { nodes.cend(), nodes.cend(), map_node_ref }; - pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_refs, first, last); + if (m_options.locations_on_ways) { + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lon)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(lonlat2int(node_ref.location().lon_without_check()))); + } + } + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lat)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(lonlat2int(node_ref.location().lat_without_check()))); + } + } + } } void relation(const osmium::Relation& relation) { @@ -596,14 +623,13 @@ namespace osmium { } } - static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type { - return member->ref(); - }; - typedef osmium::util::DeltaEncodeIterator it_type; - const auto& members = relation.members(); - it_type first { members.cbegin(), members.cend(), map_member_ref }; - it_type last { members.cend(), members.cend(), map_member_ref }; - pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last); + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_sint64_memids)}; + for (const auto& member : relation.members()) { + field.add_element(delta_id.update(member.ref())); + } + } { protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)}; 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 bdaabbaec84..3eb790240b8 100644 --- a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp +++ b/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp @@ -146,7 +146,9 @@ namespace osmium { packed_uint32_keys = 2, packed_uint32_vals = 3, optional_Info_info = 4, - packed_sint64_refs = 8 + packed_sint64_refs = 8, + packed_sint64_lat = 9, + packed_sint64_lon = 10 }; enum class Relation : protozero::pbf_tag_type { diff --git a/third_party/libosmium/include/osmium/io/detail/queue_util.hpp b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp index af845441137..bc4702000e9 100644 --- a/third_party/libosmium/include/osmium/io/detail/queue_util.hpp +++ b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp @@ -47,6 +47,9 @@ namespace osmium { namespace detail { + template + using future_queue_type = osmium::thread::Queue>; + /** * This type of queue contains buffers with OSM data in them. * The "end of file" is marked by an invalid Buffer. @@ -54,7 +57,7 @@ namespace osmium { * transport exceptions. The future also helps with keeping the * data in order. */ - using future_buffer_queue_type = osmium::thread::Queue>; + using future_buffer_queue_type = future_queue_type; /** * This type of queue contains OSM file data in the form it is @@ -71,24 +74,24 @@ namespace osmium { * transport exceptions. The future also helps with keeping the * data in order. */ - using future_string_queue_type = osmium::thread::Queue>; + using future_string_queue_type = future_queue_type; template - inline void add_to_queue(osmium::thread::Queue>& queue, T&& data) { + inline void add_to_queue(future_queue_type& queue, T&& data) { std::promise promise; queue.push(promise.get_future()); promise.set_value(std::forward(data)); } template - inline void add_to_queue(osmium::thread::Queue>& queue, std::exception_ptr&& exception) { + inline void add_to_queue(future_queue_type& queue, std::exception_ptr&& exception) { std::promise promise; queue.push(promise.get_future()); promise.set_exception(std::move(exception)); } template - inline void add_end_of_data_to_queue(osmium::thread::Queue>& queue) { + inline void add_end_of_data_to_queue(future_queue_type& queue) { add_to_queue(queue, T{}); } @@ -103,14 +106,12 @@ namespace osmium { template class queue_wrapper { - using queue_type = osmium::thread::Queue>; - - queue_type& m_queue; + future_queue_type& m_queue; bool m_has_reached_end_of_data; public: - explicit queue_wrapper(queue_type& queue) : + explicit queue_wrapper(future_queue_type& queue) : m_queue(queue), m_has_reached_end_of_data(false) { } 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 769e2b39ab1..a086e5b78b6 100644 --- a/third_party/libosmium/include/osmium/io/detail/read_write.hpp +++ b/third_party/libosmium/include/osmium/io/detail/read_write.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -84,7 +83,7 @@ namespace osmium { #ifdef _WIN32 flags |= O_BINARY; #endif - int fd = ::open(filename.c_str(), flags, 0666); + 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 + "'"); } @@ -108,7 +107,7 @@ namespace osmium { #ifdef _WIN32 flags |= O_BINARY; #endif - int fd = ::open(filename.c_str(), flags); + 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 + "'"); } @@ -133,7 +132,7 @@ namespace osmium { if (write_count > max_write) { write_count = max_write; } - auto length = ::write(fd, output_buffer + offset, static_cast(write_count)); + 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"); } 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 55c622681e8..f1ddc8715c6 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_table.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_table.hpp @@ -34,13 +34,14 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include #include #include -#include #include +#include +#include #include @@ -69,8 +70,8 @@ namespace osmium { std::list m_chunks; void add_chunk() { - m_chunks.push_front(std::string()); - m_chunks.front().reserve(m_chunk_size); + m_chunks.emplace_back(); + m_chunks.back().reserve(m_chunk_size); } public: @@ -82,6 +83,7 @@ namespace osmium { } void clear() noexcept { + assert(!m_chunks.empty()); m_chunks.erase(std::next(m_chunks.begin()), m_chunks.end()); m_chunks.front().clear(); } @@ -93,31 +95,38 @@ namespace osmium { * allocated. */ const char* add(const char* string) { - size_t len = std::strlen(string) + 1; + const size_t len = std::strlen(string) + 1; assert(len <= m_chunk_size); - size_t chunk_len = m_chunks.front().size(); - if (chunk_len + len > m_chunks.front().capacity()) { + size_t chunk_len = m_chunks.back().size(); + if (chunk_len + len > m_chunks.back().capacity()) { add_chunk(); chunk_len = 0; } - m_chunks.front().append(string); - m_chunks.front().append(1, '\0'); + m_chunks.back().append(string); + m_chunks.back().append(1, '\0'); - return m_chunks.front().c_str() + chunk_len; + return m_chunks.back().c_str() + chunk_len; } - class const_iterator : public std::iterator { + class const_iterator { + + using it_type = std::list::const_iterator; - typedef std::list::const_iterator it_type; it_type m_it; const it_type m_last; const char* m_pos; public: + using iterator_category = std::forward_iterator_tag; + using value_type = const char*; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + const_iterator(it_type it, it_type last) : m_it(it), m_last(last), @@ -126,7 +135,7 @@ namespace osmium { const_iterator& operator++() { assert(m_it != m_last); - auto last_pos = m_it->c_str() + m_it->size(); + 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; if (m_pos == last_pos) { @@ -184,18 +193,33 @@ namespace osmium { } size_t get_used_bytes_in_last_chunk() const noexcept { - return m_chunks.front().size(); + return m_chunks.back().size(); } }; // class StringStore - struct StrComp { + struct str_equal { + + bool operator()(const char* lhs, const char* rhs) const noexcept { + return lhs == rhs || std::strcmp(lhs, rhs) == 0; + } + + }; // struct str_equal - bool operator()(const char* lhs, const char* rhs) const { - return strcmp(lhs, rhs) < 0; + struct djb2_hash { + + size_t operator()(const char* str) const noexcept { + size_t hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + return hash; } - }; // struct StrComp + }; // struct djb2_hash class StringTable { @@ -206,14 +230,23 @@ namespace osmium { // Blob. static constexpr const uint32_t max_entries = max_uncompressed_blob_size; + // There is one string table per PBF primitive block. Most of + // them are really small, because most blocks are full of nodes + // with no tags. But string tables can get really large for + // ways with many tags or for large relations. + // The chosen size is enough so that 99% of all string tables + // in typical OSM files will only need a single memory + // allocation. + static constexpr const size_t default_stringtable_chunk_size = 100 * 1024; + StringStore m_strings; - std::map m_index; + std::unordered_map m_index; uint32_t m_size; public: - StringTable() : - m_strings(1024 * 1024), + explicit StringTable(size_t size = default_stringtable_chunk_size) : + m_strings(size), m_index(), m_size(0) { m_strings.add(""); @@ -231,7 +264,7 @@ namespace osmium { } uint32_t add(const char* s) { - auto f = m_index.find(s); + const auto f = m_index.find(s); if (f != m_index.end()) { return uint32_t(f->second); } 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 f80088e6324..0334b0e98ef 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_util.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_util.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -100,36 +101,57 @@ namespace osmium { static const size_t max_size = 0; #endif - size_t old_size = out.size(); + const size_t old_size = out.size(); - int len = string_snprintf(out, - old_size, - max_size, - format, - std::forward(args)...); + const int len = string_snprintf(out, + old_size, + max_size, + format, + std::forward(args)...); assert(len > 0); if (size_t(len) >= max_size) { #ifndef NDEBUG - int len2 = + const int len2 = #endif - string_snprintf(out, - old_size, - size_t(len) + 1, - format, - std::forward(args)...); + string_snprintf(out, + old_size, + size_t(len) + 1, + format, + std::forward(args)...); assert(len2 == len); } out.resize(old_size + size_t(len)); } + // Write out the value with exactly two hex digits. + inline void append_2_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + + // 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]; + + out += hex_digits[(value >> 12) & 0xf]; + out += hex_digits[(value >> 8) & 0xf]; + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + inline void append_utf8_encoded_string(std::string& out, const char* data) { + static const char* lookup_hex = "0123456789abcdef"; const char* end = data + std::strlen(data); while (data != end) { const char* last = data; - uint32_t c = utf8::next(data, end); + const uint32_t c = utf8::next(data, end); // This is a list of Unicode code points that we let // through instead of escaping them. It is incomplete @@ -148,9 +170,9 @@ namespace osmium { } else { out += '%'; if (c <= 0xff) { - append_printf_formatted_string(out, "%02x", c); + append_2_hex_digits(out, c, lookup_hex); } else { - append_printf_formatted_string(out, "%04x", c); + append_min_4_hex_digits(out, c, lookup_hex); } out += '%'; } @@ -174,6 +196,7 @@ namespace osmium { } inline void append_debug_encoded_string(std::string& out, const char* data, const char* prefix, const char* suffix) { + static const char* lookup_hex = "0123456789ABCDEF"; const char* end = data + std::strlen(data); while (data != end) { @@ -194,7 +217,9 @@ namespace osmium { out.append(last, data); } else { out.append(prefix); - append_printf_formatted_string(out, "", c); + out.append(""); out.append(suffix); } } 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 79604863819..85ef811a760 100644 --- a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp +++ b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp @@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include 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 8cb5efe8a17..d8c57d86e74 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 @@ -34,10 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include -#include #include -#include #include #include #include @@ -53,15 +50,19 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include +#include #include #include #include +#include +#include #include +#include +#include #include #include -#include +#include #include #include @@ -192,6 +193,17 @@ namespace osmium { static_cast(data)->characters(text, len); } + // This handler is called when there are any XML entities + // declared in the OSM file. Entities are normally not used, + // but they can be misused. See + // https://en.wikipedia.org/wiki/Billion_laughs + // The handler will just throw an error. + static void entity_declaration_handler(void*, + const XML_Char*, int, const XML_Char*, int, const XML_Char*, + const XML_Char*, const XML_Char*, const XML_Char*) { + throw osmium::xml_error("XML entities are not supported"); + } + public: explicit ExpatXMLParser(T* callback_object) : @@ -202,6 +214,7 @@ namespace osmium { XML_SetUserData(m_parser, callback_object); XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper); XML_SetCharacterDataHandler(m_parser, character_data_wrapper); + XML_SetEntityDeclHandler(m_parser, entity_declaration_handler); } ExpatXMLParser(const ExpatXMLParser&) = delete; @@ -240,11 +253,11 @@ namespace osmium { osmium::Location location; check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "lon")) { - location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number - } else if (!strcmp(name, "lat")) { - location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number - } else if (!strcmp(name, "user")) { + if (!std::strcmp(name, "lon")) { + location.set_lon(value); + } else if (!std::strcmp(name, "lat")) { + location.set_lat(value); + } else if (!std::strcmp(name, "user")) { user = value; } else { object.set_attribute(name, value); @@ -265,15 +278,15 @@ namespace osmium { osmium::Location min; osmium::Location max; check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "min_lon")) { - min.set_lon(atof(value)); - } else if (!strcmp(name, "min_lat")) { - min.set_lat(atof(value)); - } else if (!strcmp(name, "max_lon")) { - max.set_lon(atof(value)); - } else if (!strcmp(name, "max_lat")) { - max.set_lat(atof(value)); - } else if (!strcmp(name, "user")) { + if (!std::strcmp(name, "min_lon")) { + min.set_lon(value); + } else if (!std::strcmp(name, "min_lat")) { + min.set_lat(value); + } else if (!std::strcmp(name, "max_lon")) { + max.set_lon(value); + } else if (!std::strcmp(name, "max_lat")) { + max.set_lat(value); + } else if (!std::strcmp(name, "user")) { user = value; } else { new_changeset.set_attribute(name, value); @@ -309,17 +322,17 @@ namespace osmium { void start_element(const XML_Char* element, const XML_Char** attrs) { switch (m_context) { case context::root: - if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) { - if (!strcmp(element, "osmChange")) { + if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) { + if (!std::strcmp(element, "osmChange")) { m_header.set_has_multiple_object_versions(true); } check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "version")) { + if (!std::strcmp(name, "version")) { m_header.set("version", value); - if (strcmp(value, "0.6")) { + if (std::strcmp(value, "0.6")) { throw osmium::format_version_error(value); } - } else if (!strcmp(name, "generator")) { + } else if (!std::strcmp(name, "generator")) { m_header.set("generator", value); } }); @@ -333,7 +346,7 @@ namespace osmium { break; case context::top: assert(!m_tl_builder); - if (!strcmp(element, "node")) { + if (!std::strcmp(element, "node")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::node) { m_node_builder = std::unique_ptr(new osmium::builder::NodeBuilder(m_buffer)); @@ -342,7 +355,7 @@ namespace osmium { } else { m_context = context::ignored_node; } - } else if (!strcmp(element, "way")) { + } else if (!std::strcmp(element, "way")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::way) { m_way_builder = std::unique_ptr(new osmium::builder::WayBuilder(m_buffer)); @@ -351,7 +364,7 @@ namespace osmium { } else { m_context = context::ignored_way; } - } else if (!strcmp(element, "relation")) { + } else if (!std::strcmp(element, "relation")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::relation) { m_relation_builder = std::unique_ptr(new osmium::builder::RelationBuilder(m_buffer)); @@ -360,7 +373,7 @@ namespace osmium { } else { m_context = context::ignored_relation; } - } else if (!strcmp(element, "changeset")) { + } else if (!std::strcmp(element, "changeset")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::changeset) { m_changeset_builder = std::unique_ptr(new osmium::builder::ChangesetBuilder(m_buffer)); @@ -369,50 +382,56 @@ namespace osmium { } else { m_context = context::ignored_changeset; } - } else if (!strcmp(element, "bounds")) { + } else if (!std::strcmp(element, "bounds")) { osmium::Location min; osmium::Location max; check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "minlon")) { - min.set_lon(atof(value)); - } else if (!strcmp(name, "minlat")) { - min.set_lat(atof(value)); - } else if (!strcmp(name, "maxlon")) { - max.set_lon(atof(value)); - } else if (!strcmp(name, "maxlat")) { - max.set_lat(atof(value)); + if (!std::strcmp(name, "minlon")) { + min.set_lon(value); + } else if (!std::strcmp(name, "minlat")) { + min.set_lat(value); + } else if (!std::strcmp(name, "maxlon")) { + max.set_lon(value); + } else if (!std::strcmp(name, "maxlat")) { + max.set_lat(value); } }); osmium::Box box; box.extend(min).extend(max); m_header.add_box(box); - } else if (!strcmp(element, "delete")) { + } else if (!std::strcmp(element, "delete")) { m_in_delete_section = true; } break; case context::node: m_last_context = context::node; m_context = context::in_object; - if (!strcmp(element, "tag")) { + if (!std::strcmp(element, "tag")) { get_tag(m_node_builder.get(), attrs); } break; case context::way: m_last_context = context::way; m_context = context::in_object; - if (!strcmp(element, "nd")) { + if (!std::strcmp(element, "nd")) { m_tl_builder.reset(); if (!m_wnl_builder) { m_wnl_builder = std::unique_ptr(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get())); } - check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "ref")) { - m_wnl_builder->add_node_ref(osmium::string_to_object_id(value)); + NodeRef nr; + check_attributes(attrs, [this, &nr](const XML_Char* name, const XML_Char* value) { + if (!std::strcmp(name, "ref")) { + nr.set_ref(osmium::string_to_object_id(value)); + } else if (!std::strcmp(name, "lon")) { + nr.location().set_lon(value); + } else if (!std::strcmp(name, "lat")) { + nr.location().set_lat(value); } }); - } else if (!strcmp(element, "tag")) { + m_wnl_builder->add_node_ref(nr); + } else if (!std::strcmp(element, "tag")) { m_wnl_builder.reset(); get_tag(m_way_builder.get(), attrs); } @@ -420,7 +439,7 @@ namespace osmium { case context::relation: m_last_context = context::relation; m_context = context::in_object; - if (!strcmp(element, "member")) { + if (!std::strcmp(element, "member")) { m_tl_builder.reset(); if (!m_rml_builder) { @@ -431,11 +450,11 @@ namespace osmium { object_id_type ref = 0; const char* role = ""; check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "type")) { + if (!std::strcmp(name, "type")) { type = char_to_item_type(value[0]); - } else if (!strcmp(name, "ref")) { + } else if (!std::strcmp(name, "ref")) { ref = osmium::string_to_object_id(value); - } else if (!strcmp(name, "role")) { + } else if (!std::strcmp(name, "role")) { role = static_cast(value); } }); @@ -446,37 +465,37 @@ namespace osmium { throw osmium::xml_error("Missing ref on relation member"); } m_rml_builder->add_member(type, ref, role); - } else if (!strcmp(element, "tag")) { + } else if (!std::strcmp(element, "tag")) { m_rml_builder.reset(); get_tag(m_relation_builder.get(), attrs); } break; case context::changeset: m_last_context = context::changeset; - if (!strcmp(element, "discussion")) { + if (!std::strcmp(element, "discussion")) { m_context = context::discussion; m_tl_builder.reset(); if (!m_changeset_discussion_builder) { m_changeset_discussion_builder = std::unique_ptr(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get())); } - } else if (!strcmp(element, "tag")) { + } else if (!std::strcmp(element, "tag")) { m_context = context::in_object; m_changeset_discussion_builder.reset(); get_tag(m_changeset_builder.get(), attrs); } break; case context::discussion: - if (!strcmp(element, "comment")) { + if (!std::strcmp(element, "comment")) { m_context = context::comment; osmium::Timestamp date; osmium::user_id_type uid = 0; const char* user = ""; check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "date")) { + if (!std::strcmp(name, "date")) { date = osmium::Timestamp(value); - } else if (!strcmp(name, "uid")) { + } else if (!std::strcmp(name, "uid")) { uid = osmium::string_to_user_id(value); - } else if (!strcmp(name, "user")) { + } else if (!std::strcmp(name, "user")) { user = static_cast(value); } }); @@ -484,7 +503,7 @@ namespace osmium { } break; case context::comment: - if (!strcmp(element, "text")) { + if (!std::strcmp(element, "text")) { m_context = context::comment_text; } break; @@ -510,15 +529,15 @@ namespace osmium { assert(false); // should never be here break; case context::top: - if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) { + if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) { mark_header_as_done(); m_context = context::root; - } else if (!strcmp(element, "delete")) { + } else if (!std::strcmp(element, "delete")) { m_in_delete_section = false; } break; case context::node: - assert(!strcmp(element, "node")); + assert(!std::strcmp(element, "node")); m_tl_builder.reset(); m_node_builder.reset(); m_buffer.commit(); @@ -526,7 +545,7 @@ namespace osmium { flush_buffer(); break; case context::way: - assert(!strcmp(element, "way")); + assert(!std::strcmp(element, "way")); m_tl_builder.reset(); m_wnl_builder.reset(); m_way_builder.reset(); @@ -535,7 +554,7 @@ namespace osmium { flush_buffer(); break; case context::relation: - assert(!strcmp(element, "relation")); + assert(!std::strcmp(element, "relation")); m_tl_builder.reset(); m_rml_builder.reset(); m_relation_builder.reset(); @@ -544,7 +563,7 @@ namespace osmium { flush_buffer(); break; case context::changeset: - assert(!strcmp(element, "changeset")); + assert(!std::strcmp(element, "changeset")); m_tl_builder.reset(); m_changeset_discussion_builder.reset(); m_changeset_builder.reset(); @@ -553,15 +572,15 @@ namespace osmium { flush_buffer(); break; case context::discussion: - assert(!strcmp(element, "discussion")); + assert(!std::strcmp(element, "discussion")); m_context = context::changeset; break; case context::comment: - assert(!strcmp(element, "comment")); + assert(!std::strcmp(element, "comment")); m_context = context::discussion; break; case context::comment_text: - assert(!strcmp(element, "text")); + assert(!std::strcmp(element, "text")); m_context = context::comment; m_changeset_discussion_builder->add_comment_text(m_comment_text); break; @@ -569,22 +588,22 @@ namespace osmium { m_context = m_last_context; break; case context::ignored_node: - if (!strcmp(element, "node")) { + if (!std::strcmp(element, "node")) { m_context = context::top; } break; case context::ignored_way: - if (!strcmp(element, "way")) { + if (!std::strcmp(element, "way")) { m_context = context::top; } break; case context::ignored_relation: - if (!strcmp(element, "relation")) { + if (!std::strcmp(element, "relation")) { m_context = context::top; } break; case context::ignored_changeset: - if (!strcmp(element, "changeset")) { + if (!std::strcmp(element, "changeset")) { m_context = context::top; } break; 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 09bd6b3ce8c..3f47b0fb188 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 @@ -34,27 +34,26 @@ 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 @@ -86,8 +85,26 @@ namespace osmium { */ bool use_change_ops; + /// Should node locations be added to ways? + bool locations_on_ways; }; + namespace detail { + + inline void append_lat_lon_attributes(std::string& out, const char* lat, const char* lon, const osmium::Location& location) { + out += ' '; + out += lat; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.y()); + out += "\" "; + out += lon; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.x()); + out += "\""; + } + + } // namespace detail + class XMLOutputBlock : public OutputBlock { // operation (create, modify, delete) for osc files @@ -116,12 +133,21 @@ namespace osmium { write_spaces(prefix_spaces()); } + template + void write_attribute(const char* name, T value) { + *m_out += ' '; + *m_out += name; + *m_out += "=\""; + output_int(value); + *m_out += '"'; + } + void write_meta(const osmium::OSMObject& object) { - output_formatted(" id=\"%" PRId64 "\"", object.id()); + write_attribute("id", object.id()); if (m_options.add_metadata) { if (object.version()) { - output_formatted(" version=\"%d\"", object.version()); + write_attribute("version", object.version()); } if (object.timestamp()) { @@ -131,13 +157,14 @@ namespace osmium { } if (!object.user_is_anonymous()) { - output_formatted(" uid=\"%d\" user=\"", object.uid()); + write_attribute("uid", object.uid()); + *m_out += " user=\""; append_xml_encoded_string(*m_out, object.user()); *m_out += "\""; } if (object.changeset()) { - output_formatted(" changeset=\"%d\"", object.changeset()); + write_attribute("changeset", object.changeset()); } if (m_options.add_visible_flag) { @@ -162,8 +189,11 @@ namespace osmium { } void write_discussion(const osmium::ChangesetDiscussion& comments) { + *m_out += " \n"; for (const auto& comment : comments) { - output_formatted(" \n", node_ref.ref()); + if (m_options.locations_on_ways) { + for (const auto& node_ref : way.nodes()) { + write_prefix(); + *m_out += " \n"; } @@ -332,7 +374,7 @@ namespace osmium { void changeset(const osmium::Changeset& changeset) { *m_out += " 0) { - *m_out += " \n"; + if (!changeset.discussion().empty()) { write_discussion(changeset.discussion()); } @@ -394,9 +434,10 @@ namespace osmium { XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) : OutputFormat(output_queue), m_options() { - m_options.add_metadata = file.is_not_false("add_metadata"); - m_options.use_change_ops = file.is_true("xml_change_format"); - m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops; + m_options.add_metadata = file.is_not_false("add_metadata"); + m_options.use_change_ops = file.is_true("xml_change_format"); + m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops; + m_options.locations_on_ways = file.is_true("locations_on_ways"); } XMLOutputFormat(const XMLOutputFormat&) = delete; @@ -425,10 +466,9 @@ namespace osmium { for (const auto& box : header.boxes()) { out += " \n", box.top_right().lat()); + detail::append_lat_lon_attributes(out, "minlat", "minlon", box.bottom_left()); + detail::append_lat_lon_attributes(out, "maxlat", "maxlon", box.top_right()); + out += "/>\n"; } send_to_output_queue(std::move(out)); diff --git a/third_party/libosmium/include/osmium/io/detail/zlib.hpp b/third_party/libosmium/include/osmium/io/detail/zlib.hpp index 2b6dddb941c..15ece7c5a1f 100644 --- a/third_party/libosmium/include/osmium/io/detail/zlib.hpp +++ b/third_party/libosmium/include/osmium/io/detail/zlib.hpp @@ -33,12 +33,12 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include +#include + #include #include @@ -62,7 +62,7 @@ namespace osmium { std::string output(output_size, '\0'); - auto result = ::compress( + const auto result = ::compress( reinterpret_cast(const_cast(output.data())), &output_size, reinterpret_cast(input.data()), @@ -89,10 +89,10 @@ namespace osmium { * @param output Uncompressed result data. * @returns Pointer and size to incompressed data. */ - inline std::pair zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) { + inline protozero::data_view zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) { output.resize(raw_size); - auto result = ::uncompress( + const auto result = ::uncompress( reinterpret_cast(&*output.begin()), &raw_size, reinterpret_cast(input), @@ -103,7 +103,7 @@ namespace osmium { throw io_error(std::string("failed to uncompress data: ") + zError(result)); } - return std::make_pair(output.data(), output.size()); + return protozero::data_view{output.data(), output.size()}; } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/file.hpp b/third_party/libosmium/include/osmium/io/file.hpp index 56fc8d5133e..812c49429a0 100644 --- a/third_party/libosmium/include/osmium/io/file.hpp +++ b/third_party/libosmium/include/osmium/io/file.hpp @@ -34,7 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include @@ -43,7 +42,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include namespace osmium { @@ -115,7 +113,7 @@ namespace osmium { } // if filename is a URL, default to XML format - std::string protocol = m_filename.substr(0, m_filename.find_first_of(':')); + const std::string protocol = m_filename.substr(0, m_filename.find_first_of(':')); if (protocol == "http" || protocol == "https") { m_file_format = file_format::xml; } @@ -174,7 +172,7 @@ namespace osmium { } for (auto& option : options) { - size_t pos = option.find_first_of('='); + const size_t pos = option.find_first_of('='); if (pos == std::string::npos) { set(option, true); } else { diff --git a/third_party/libosmium/include/osmium/io/gzip_compression.hpp b/third_party/libosmium/include/osmium/io/gzip_compression.hpp index e6ca010de79..5e3e2331aef 100644 --- a/third_party/libosmium/include/osmium/io/gzip_compression.hpp +++ b/third_party/libosmium/include/osmium/io/gzip_compression.hpp @@ -42,15 +42,20 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libz`. */ -#include +#include #include +#ifndef _MSC_VER +# include +#endif + #include #include #include #include #include +#include #include #include #include @@ -102,7 +107,7 @@ namespace osmium { explicit GzipCompressor(int fd, fsync sync) : Compressor(sync), - m_fd(dup(fd)), + m_fd(::dup(fd)), m_gzfile(::gzdopen(fd, "w")) { if (!m_gzfile) { detail::throw_gzip_error(m_gzfile, "write initialization failed"); @@ -171,6 +176,9 @@ namespace osmium { detail::throw_gzip_error(m_gzfile, "read failed"); } buffer.resize(static_cast(nread)); +#if ZLIB_VERNUM >= 0x1240 + set_offset(size_t(::gzoffset(m_gzfile))); +#endif return buffer; } diff --git a/third_party/libosmium/include/osmium/io/input_iterator.hpp b/third_party/libosmium/include/osmium/io/input_iterator.hpp index 8be9759303f..4cde92f5172 100644 --- a/third_party/libosmium/include/osmium/io/input_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/input_iterator.hpp @@ -41,7 +41,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include namespace osmium { @@ -57,7 +56,7 @@ namespace osmium { static_assert(std::is_base_of::value, "TItem must derive from osmium::buffer::Item"); - typedef typename osmium::memory::Buffer::t_iterator item_iterator; + using item_iterator = typename osmium::memory::Buffer::t_iterator; TSource* m_source; std::shared_ptr m_buffer; @@ -69,20 +68,20 @@ namespace osmium { if (!m_buffer || !*m_buffer) { // end of input m_source = nullptr; m_buffer.reset(); - m_iter = item_iterator(); + m_iter = item_iterator{}; return; } - m_iter = m_buffer->begin(); - } while (m_iter == m_buffer->end()); + m_iter = m_buffer->select().begin(); + } while (m_iter == m_buffer->select().end()); } public: - typedef std::input_iterator_tag iterator_category; - typedef TItem value_type; - typedef ptrdiff_t difference_type; - typedef TItem* pointer; - typedef TItem& reference; + using iterator_category = std::input_iterator_tag; + using value_type = TItem; + using difference_type = ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; explicit InputIterator(TSource& source) : m_source(&source) { @@ -99,7 +98,7 @@ namespace osmium { assert(m_buffer); assert(m_iter); ++m_iter; - if (m_iter == m_buffer->end()) { + if (m_iter == m_buffer->select().end()) { update_buffer(); } return *this; diff --git a/third_party/libosmium/include/osmium/io/opl_input.hpp b/third_party/libosmium/include/osmium/io/opl_input.hpp new file mode 100644 index 00000000000..ee9e447d5be --- /dev/null +++ b/third_party/libosmium/include/osmium/io/opl_input.hpp @@ -0,0 +1,46 @@ +#ifndef OSMIUM_IO_OPL_INPUT_HPP +#define OSMIUM_IO_OPL_INPUT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +/** + * @file + * + * Include this file if you want to read OSM OPL files. + * + */ + +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif // OSMIUM_IO_OPL_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/output_iterator.hpp b/third_party/libosmium/include/osmium/io/output_iterator.hpp index ce050ebab61..cf9291df0e8 100644 --- a/third_party/libosmium/include/osmium/io/output_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/output_iterator.hpp @@ -35,10 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include #include #include @@ -51,12 +48,18 @@ namespace osmium { namespace io { template - class OutputIterator : public std::iterator { + class OutputIterator { TDest* m_destination; public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + explicit OutputIterator(TDest& destination) : m_destination(&destination) { } diff --git a/third_party/libosmium/include/osmium/io/overwrite.hpp b/third_party/libosmium/include/osmium/io/overwrite.hpp index 5361698a5f0..bc6a5038ce5 100644 --- a/third_party/libosmium/include/osmium/io/overwrite.hpp +++ b/third_party/libosmium/include/osmium/io/overwrite.hpp @@ -34,6 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #pragma message("Including overwrite.hpp is deprecated, #include instead.") -#include +#include // IWYU pragma: keep #endif // OSMIUM_IO_OVERWRITE_HPP diff --git a/third_party/libosmium/include/osmium/io/reader.hpp b/third_party/libosmium/include/osmium/io/reader.hpp index 7c60511af86..12f97b8e66f 100644 --- a/third_party/libosmium/include/osmium/io/reader.hpp +++ b/third_party/libosmium/include/osmium/io/reader.hpp @@ -62,11 +62,26 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { namespace io { + namespace detail { + + inline size_t get_input_queue_size() noexcept { + const 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); + return n > 2 ? n : 2; + } + + } // namespace detail + /** * This is the user-facing interface for reading OSM files. Instantiate * an object of this class with a file name or osmium::io::File object @@ -75,9 +90,6 @@ namespace osmium { */ class Reader { - static constexpr size_t max_input_queue_size = 20; // XXX - static constexpr size_t max_osmdata_queue_size = 20; // XXX - osmium::io::File m_file; osmium::osm_entity_bits::type m_read_which_entities; @@ -104,6 +116,8 @@ namespace osmium { osmium::thread::thread_handler m_thread; + size_t m_file_size; + // This function will run in a separate thread. static void parser_thread(const osmium::io::File& file, detail::future_string_queue_type& input_queue, @@ -111,8 +125,8 @@ namespace osmium { std::promise&& header_promise, osmium::osm_entity_bits::type read_which_entities) { std::promise promise = std::move(header_promise); - auto creator = detail::ParserFactory::instance().get_creator_function(file); - auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); + const auto creator = detail::ParserFactory::instance().get_creator_function(file); + const auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); parser->parse(); } @@ -133,7 +147,7 @@ namespace osmium { if (pipe(pipefd) < 0) { throw std::system_error(errno, std::system_category(), "opening pipe failed"); } - pid_t pid = fork(); + const pid_t pid = fork(); if (pid < 0) { throw std::system_error(errno, std::system_category(), "fork failed"); } @@ -202,16 +216,17 @@ namespace osmium { m_read_which_entities(read_which_entities), m_status(status::okay), m_childpid(0), - m_input_queue(max_input_queue_size, "raw_input"), + m_input_queue(detail::get_input_queue_size(), "raw_input"), m_decompressor(m_file.buffer() ? osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), m_file.buffer(), m_file.buffer_size()) : osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), open_input_file_or_url(m_file.filename(), &m_childpid))), m_read_thread_manager(*m_decompressor, m_input_queue), - m_osmdata_queue(max_osmdata_queue_size, "parser_results"), + m_osmdata_queue(detail::get_osmdata_queue_size(), "parser_results"), m_osmdata_queue_wrapper(m_osmdata_queue), m_header_future(), m_header(), - m_thread() { + m_thread(), + m_file_size(m_decompressor->file_size()) { std::promise header_promise; m_header_future = header_promise.get_future(); m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), read_which_entities}; @@ -263,7 +278,7 @@ namespace osmium { #ifndef _WIN32 if (m_childpid) { int status; - pid_t pid = ::waitpid(m_childpid, &status, 0); + const pid_t pid = ::waitpid(m_childpid, &status, 0); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { @@ -350,6 +365,32 @@ namespace osmium { return m_status == status::eof || m_status == status::closed; } + /** + * 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 { + return m_file_size; + } + + /** + * Returns the current offset into the input file. Returns 0 if + * the offset is not available (for instance when reading from + * stdin). + * + * The offset can be used together with the result of file_size() + * to estimate how much of the file has been read. Note that due + * to buffering inside Osmium, this value will be larger than + * the amount of data actually available to the application. + * + * Do not call this function too often, certainly not for every + * object you are reading. Depending on the file type it might + * do an expensive system call. + */ + size_t offset() const noexcept { + return m_decompressor->offset(); + } + }; // class Reader /** diff --git a/third_party/libosmium/include/osmium/io/writer.hpp b/third_party/libosmium/include/osmium/io/writer.hpp index b389698fc8b..c12d31776bf 100644 --- a/third_party/libosmium/include/osmium/io/writer.hpp +++ b/third_party/libosmium/include/osmium/io/writer.hpp @@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE. */ #include +#include +#include +#include #include +#include #include -#include #include -#include #include #include @@ -52,11 +54,25 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { + namespace memory { + class Item; + } //namespace memory + namespace io { + namespace detail { + + inline size_t get_output_queue_size() noexcept { + size_t n = osmium::config::get_max_queue_size("OUTPUT", 20); + return n > 2 ? n : 2; + } + + } // namespace detail + /** * This is the user-facing interface for writing OSM files. Instantiate * an object of this class with a file name or osmium::io::File object @@ -194,7 +210,7 @@ namespace osmium { template explicit Writer(const osmium::io::File& file, TArgs&&... args) : m_file(file.check()), - m_output_queue(20, "raw_output"), // XXX + 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_buffer(), m_buffer_size(default_buffer_size), @@ -304,7 +320,7 @@ namespace osmium { } try { m_buffer.push_back(item); - } catch (osmium::buffer_is_full&) { + } catch (const osmium::buffer_is_full&) { do_flush(); m_buffer.push_back(item); } diff --git a/third_party/libosmium/include/osmium/memory/buffer.hpp b/third_party/libosmium/include/osmium/memory/buffer.hpp index 07a31fed90e..8c246db02ca 100644 --- a/third_party/libosmium/include/osmium/memory/buffer.hpp +++ b/third_party/libosmium/include/osmium/memory/buffer.hpp @@ -517,6 +517,16 @@ namespace osmium { */ using const_iterator = t_const_iterator; + template + ItemIteratorRange select() { + return ItemIteratorRange{m_data, m_data + m_committed}; + } + + template + ItemIteratorRange select() const { + return ItemIteratorRange{m_data, m_data + m_committed}; + } + /** * Get iterator for iterating over all items of type T in the * buffer. diff --git a/third_party/libosmium/include/osmium/memory/collection.hpp b/third_party/libosmium/include/osmium/memory/collection.hpp index 17ace7025a3..2a2c0408897 100644 --- a/third_party/libosmium/include/osmium/memory/collection.hpp +++ b/third_party/libosmium/include/osmium/memory/collection.hpp @@ -44,17 +44,23 @@ namespace osmium { namespace memory { template - class CollectionIterator : public std::iterator { + class CollectionIterator { // This data_type is either 'unsigned char*' or 'const unsigned char*' depending // on whether TMember is const. This allows this class to be used as an iterator and // as a const_iterator. - typedef typename std::conditional::value, const unsigned char*, unsigned char*>::type data_type; + using data_type = typename std::conditional::value, const unsigned char*, unsigned char*>::type; data_type m_data; public: + using iterator_category = std::forward_iterator_tag; + using value_type = TMember; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + CollectionIterator() noexcept : m_data(nullptr) { } @@ -112,9 +118,9 @@ namespace osmium { public: - typedef CollectionIterator iterator; - typedef CollectionIterator const_iterator; - typedef TMember value_type; + using iterator = CollectionIterator; + using const_iterator = CollectionIterator; + using value_type = TMember; static constexpr osmium::item_type itemtype = TCollectionItemType; diff --git a/third_party/libosmium/include/osmium/memory/item.hpp b/third_party/libosmium/include/osmium/memory/item.hpp index fd404ce93e6..2df33c7f5da 100644 --- a/third_party/libosmium/include/osmium/memory/item.hpp +++ b/third_party/libosmium/include/osmium/memory/item.hpp @@ -33,8 +33,10 @@ DEALINGS IN THE SOFTWARE. */ +#include #include -#include + +#include namespace osmium { @@ -45,16 +47,21 @@ namespace osmium { class Builder; } // namespace builder + enum class diff_indicator_type { + none = 0, + left = 1, + right = 2, + both = 3 + }; // diff_indicator_type + namespace memory { - typedef uint32_t item_size_type; + using item_size_type = uint32_t; // align datastructures to this many bytes constexpr item_size_type align_bytes = 8; - template - inline T padded_length(T length) noexcept { - static_assert(std::is_integral::value && std::is_unsigned::value, "Template parameter must be unsigned integral type"); + inline std::size_t padded_length(std::size_t length) noexcept { return (length + align_bytes - 1) & ~(align_bytes - 1); } @@ -100,7 +107,8 @@ namespace osmium { item_size_type m_size; item_type m_type; uint16_t m_removed : 1; - uint16_t m_padding : 15; + uint16_t m_diff : 2; + uint16_t m_padding : 13; template friend class CollectionIterator; @@ -121,6 +129,7 @@ namespace osmium { m_size(size), m_type(type), m_removed(false), + m_diff(0), m_padding(0) { } @@ -150,7 +159,7 @@ namespace osmium { } item_size_type padded_size() const { - return padded_length(m_size); + return static_cast_with_assert(padded_length(m_size)); } item_type type() const noexcept { @@ -165,6 +174,19 @@ namespace osmium { m_removed = removed; } + diff_indicator_type diff() const noexcept { + return diff_indicator_type(m_diff); + } + + char diff_as_char() const noexcept { + static constexpr const char* diff_chars = "*-+ "; + return diff_chars[m_diff]; + } + + void set_diff(diff_indicator_type diff) noexcept { + m_diff = uint16_t(diff); + } + }; // class Item static_assert(sizeof(Item) == 8, "Class osmium::Item has wrong size!"); diff --git a/third_party/libosmium/include/osmium/memory/item_iterator.hpp b/third_party/libosmium/include/osmium/memory/item_iterator.hpp index 3886c98705f..27ebc59f054 100644 --- a/third_party/libosmium/include/osmium/memory/item_iterator.hpp +++ b/third_party/libosmium/include/osmium/memory/item_iterator.hpp @@ -34,16 +34,29 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include -#include #include #include namespace osmium { + class Area; + class Changeset; + class InnerRing; + class Node; + class OSMEntity; + class OSMObject; + class OuterRing; + class Relation; + class RelationMemberList; + class TagList; + class Way; + class WayNodeList; + namespace memory { namespace detail { @@ -116,19 +129,19 @@ namespace osmium { } // namespace detail template - class ItemIterator : public std::iterator { + class ItemIterator { static_assert(std::is_base_of::value, "TMember must derive from osmium::memory::Item"); // This data_type is either 'unsigned char*' or 'const unsigned char*' depending // on whether TMember is const. This allows this class to be used as an iterator and // as a const_iterator. - typedef typename std::conditional::value, const unsigned char*, unsigned char*>::type data_type; + using data_type = typename std::conditional::value, const unsigned char*, unsigned char*>::type; data_type m_data; data_type m_end; - void advance_to_next_item_of_right_type() { + void advance_to_next_item_of_right_type() noexcept { while (m_data != m_end && !detail::type_is_compatible::type>(reinterpret_cast(m_data)->type())) { m_data = reinterpret_cast(m_data)->next(); @@ -137,23 +150,29 @@ namespace osmium { public: + using iterator_category = std::forward_iterator_tag; + using value_type = TMember; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + ItemIterator() noexcept : m_data(nullptr), m_end(nullptr) { } - ItemIterator(data_type data, data_type end) : + ItemIterator(data_type data, data_type end) noexcept : m_data(data), m_end(end) { advance_to_next_item_of_right_type(); } template - ItemIterator cast() const { + ItemIterator cast() const noexcept { return ItemIterator(m_data, m_end); } - ItemIterator& operator++() { + ItemIterator& operator++() noexcept { assert(m_data); assert(m_data != m_end); m_data = reinterpret_cast(m_data)->next(); @@ -166,45 +185,50 @@ namespace osmium { * types. Do not use this unless you know what you are * doing. */ - ItemIterator& advance_once() { + ItemIterator& advance_once() noexcept { assert(m_data); assert(m_data != m_end); m_data = reinterpret_cast(m_data)->next(); return *static_cast*>(this); } - ItemIterator operator++(int) { + ItemIterator operator++(int) noexcept { ItemIterator tmp(*this); operator++(); return tmp; } - bool operator==(const ItemIterator& rhs) const { + bool operator==(const ItemIterator& rhs) const noexcept { return m_data == rhs.m_data && m_end == rhs.m_end; } - bool operator!=(const ItemIterator& rhs) const { + bool operator!=(const ItemIterator& rhs) const noexcept { return !(*this == rhs); } - unsigned char* data() const { + data_type data() noexcept { + assert(m_data); + return m_data; + } + + const unsigned char* data() const noexcept { assert(m_data); return m_data; } - TMember& operator*() const { + TMember& operator*() const noexcept { assert(m_data); assert(m_data != m_end); return *reinterpret_cast(m_data); } - TMember* operator->() const { + TMember* operator->() const noexcept { assert(m_data); assert(m_data != m_end); return reinterpret_cast(m_data); } - explicit operator bool() const { + explicit operator bool() const noexcept { return (m_data != nullptr) && (m_data != m_end); } @@ -221,6 +245,77 @@ namespace osmium { return out; } + template + class ItemIteratorRange { + + static_assert(std::is_base_of::value, "Template parameter must derive from osmium::memory::Item"); + + // This data_type is either 'unsigned char*' or + // 'const unsigned char*' depending on whether T is const. + using data_type = typename std::conditional::value, const unsigned char*, unsigned char*>::type; + + data_type m_begin; + data_type m_end; + + public: + + using iterator = ItemIterator; + using const_iterator = ItemIterator; + + ItemIteratorRange(data_type first, data_type last) noexcept : + m_begin(first), + m_end(last) { + } + + iterator begin() noexcept { + return iterator{m_begin, m_end}; + } + + iterator end() noexcept { + return iterator{m_end, m_end}; + } + + const_iterator cbegin() const noexcept { + return const_iterator{m_begin, m_end}; + } + + const_iterator cend() const noexcept { + return const_iterator{m_end, m_end}; + } + + const_iterator begin() const noexcept { + return cbegin(); + } + + const_iterator end() const noexcept { + return cend(); + } + + /** + * Return the number of items in this range. + * + * Note that this methods has worst-case complexity O(n) with n + * being the number of items in the underlying range. + */ + size_t size() const { + if (m_begin == m_end) { + return 0; + } + return std::distance(cbegin(), cend()); + } + + /** + * Is this range empty? + * + * Note that this methods has worst-case complexity O(n) with n + * being the number of items in the underlying range. + */ + bool empty() const { + return size() == 0; + } + + }; // class ItemIteratorRange + } // namespace memory } // namespace osmium diff --git a/third_party/libosmium/include/osmium/object_pointer_collection.hpp b/third_party/libosmium/include/osmium/object_pointer_collection.hpp index 09a52934e04..4db1c084d1a 100644 --- a/third_party/libosmium/include/osmium/object_pointer_collection.hpp +++ b/third_party/libosmium/include/osmium/object_pointer_collection.hpp @@ -40,11 +40,9 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include // IWYU pragma: no_forward_declare osmium::OSMObject -// IWYU pragma: no_forward_declare osmium::memory::Item namespace osmium { @@ -70,8 +68,8 @@ namespace osmium { public: - typedef boost::indirect_iterator::iterator, osmium::OSMObject> iterator; - typedef boost::indirect_iterator::const_iterator, const osmium::OSMObject> const_iterator; + using iterator = boost::indirect_iterator::iterator, osmium::OSMObject>; + using const_iterator = boost::indirect_iterator::const_iterator, const osmium::OSMObject>; ObjectPointerCollection() noexcept : m_objects() { diff --git a/third_party/libosmium/include/osmium/opl.hpp b/third_party/libosmium/include/osmium/opl.hpp new file mode 100644 index 00000000000..5666fa0de94 --- /dev/null +++ b/third_party/libosmium/include/osmium/opl.hpp @@ -0,0 +1,67 @@ +#ifndef OSMIUM_OPL_HPP +#define OSMIUM_OPL_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +namespace osmium { + + /** + * Parses one line in OPL format. The line must not have a newline + * character at the end. Buffer.commit() is called automatically if the + * write succeeded. + * + * @param data Line must be in this zero-delimited string. + * @param buffer Result will be written to this buffer. + * + * @returns true if an entity was parsed, false otherwise (for instance + * when the line is empty). + * @throws osmium::opl_error If the parsing fails. + */ + inline bool opl_parse(const char* data, osmium::memory::Buffer& buffer) { + try { + const bool wrote_something = osmium::io::detail::opl_parse_line(0, data, buffer); + buffer.commit(); + return wrote_something; + } catch (const osmium::opl_error&) { + buffer.rollback(); + throw; + } + } + +} // namespace osmium + + +#endif // OSMIUM_OPL_HPP diff --git a/third_party/libosmium/include/osmium/osm.hpp b/third_party/libosmium/include/osmium/osm.hpp index 594db7572ac..fa8a92da451 100644 --- a/third_party/libosmium/include/osmium/osm.hpp +++ b/third_party/libosmium/include/osmium/osm.hpp @@ -33,11 +33,20 @@ DEALINGS IN THE SOFTWARE. */ -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export /** * @brief Namespace for everything in the Osmium library. diff --git a/third_party/libosmium/include/osmium/osm/area.hpp b/third_party/libosmium/include/osmium/osm/area.hpp index ee232f0bd6f..490fbe95b62 100644 --- a/third_party/libosmium/include/osmium/osm/area.hpp +++ b/third_party/libosmium/include/osmium/osm/area.hpp @@ -35,14 +35,17 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include +#include #include #include #include #include +#include namespace osmium { @@ -144,8 +147,8 @@ namespace osmium { * * @returns Pair (number outer rings, number inner rings) */ - std::pair num_rings() const { - std::pair counter { 0, 0 }; + std::pair num_rings() const { + std::pair counter { 0, 0 }; for (auto it = cbegin(); it != cend(); ++it) { switch (it->type()) { @@ -185,27 +188,51 @@ namespace osmium { } /** + * @deprecated Use inner_rings() instead. + * * Get iterator for iterating over all inner rings in a specified outer * ring. * * @param it Iterator specifying outer ring. * @returns Iterator to first inner ring in specified outer ring. */ - osmium::memory::ItemIterator inner_ring_cbegin(const osmium::memory::ItemIterator& it) const { + OSMIUM_DEPRECATED osmium::memory::ItemIterator inner_ring_cbegin(const osmium::memory::ItemIterator& it) const { return it.cast(); } /** + * @deprecated Use inner_rings() instead. + * * Get iterator for iterating over all inner rings in a specified outer * ring. * * @param it Iterator specifying outer ring. * @returns Iterator one past last inner ring in specified outer ring. */ - osmium::memory::ItemIterator inner_ring_cend(const osmium::memory::ItemIterator& it) const { + OSMIUM_DEPRECATED osmium::memory::ItemIterator inner_ring_cend(const osmium::memory::ItemIterator& it) const { return std::next(it).cast(); } + /** + * Return an iterator range for all outer rings. + * You can use the usual begin() and end() functions to iterate over + * all outer rings. + */ + osmium::memory::ItemIteratorRange outer_rings() const { + return subitems(); + } + + /** + * Return an iterator range for all inner rings in the given outer + * ring. + * You can use the usual begin() and end() functions to iterate over + * all outer rings. + */ + osmium::memory::ItemIteratorRange inner_rings(const osmium::OuterRing& outer) const { + osmium::memory::ItemIteratorRange outer_range{outer.data(), next()}; + return osmium::memory::ItemIteratorRange{outer_range.cbegin().data(), std::next(outer_range.cbegin()).data()}; + } + }; // class Area static_assert(sizeof(Area) % osmium::memory::align_bytes == 0, "Class osmium::Area has wrong size to be aligned properly!"); diff --git a/third_party/libosmium/include/osmium/osm/box.hpp b/third_party/libosmium/include/osmium/osm/box.hpp index 6fcf48d6ca4..52ca93dd171 100644 --- a/third_party/libosmium/include/osmium/osm/box.hpp +++ b/third_party/libosmium/include/osmium/osm/box.hpp @@ -36,7 +36,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include namespace osmium { diff --git a/third_party/libosmium/include/osmium/osm/changeset.hpp b/third_party/libosmium/include/osmium/osm/changeset.hpp index f59db480845..8a503caaca4 100644 --- a/third_party/libosmium/include/osmium/osm/changeset.hpp +++ b/third_party/libosmium/include/osmium/osm/changeset.hpp @@ -33,7 +33,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include @@ -131,7 +133,7 @@ namespace osmium { public: - typedef size_t size_type; + using size_type = size_t; ChangesetDiscussion() : osmium::memory::Collection() { @@ -185,6 +187,11 @@ namespace osmium { public: + // Dummy to avoid warning because of unused private fields. Do not use. + int32_t do_not_use() const noexcept { + return m_padding1 + m_padding2; + } + /// Get ID of this changeset changeset_id_type id() const noexcept { return m_id; @@ -369,23 +376,23 @@ namespace osmium { * @param value Value of the attribute */ void set_attribute(const char* attr, const char* value) { - if (!strcmp(attr, "id")) { + if (!std::strcmp(attr, "id")) { set_id(value); - } else if (!strcmp(attr, "num_changes")) { + } else if (!std::strcmp(attr, "num_changes")) { set_num_changes(value); - } else if (!strcmp(attr, "comments_count")) { + } else if (!std::strcmp(attr, "comments_count")) { set_num_comments(value); - } else if (!strcmp(attr, "created_at")) { + } else if (!std::strcmp(attr, "created_at")) { set_created_at(osmium::Timestamp(value)); - } else if (!strcmp(attr, "closed_at")) { + } else if (!std::strcmp(attr, "closed_at")) { set_closed_at(osmium::Timestamp(value)); - } else if (!strcmp(attr, "uid")) { + } else if (!std::strcmp(attr, "uid")) { set_uid(value); } } - typedef osmium::memory::CollectionIterator iterator; - typedef osmium::memory::CollectionIterator const_iterator; + using iterator = osmium::memory::CollectionIterator; + using const_iterator = osmium::memory::CollectionIterator; iterator begin() { return iterator(subitems_position()); diff --git a/third_party/libosmium/include/osmium/osm/crc.hpp b/third_party/libosmium/include/osmium/osm/crc.hpp index ff39996f5e2..2abeac4a1d8 100644 --- a/third_party/libosmium/include/osmium/osm/crc.hpp +++ b/third_party/libosmium/include/osmium/osm/crc.hpp @@ -36,11 +36,17 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include +#include #include +#include #include +#include +#include #include #include @@ -71,8 +77,8 @@ namespace osmium { # if defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(value); # else - uint64_t val1 = byte_swap_32(value & 0xFFFFFFFF); - uint64_t val2 = byte_swap_32(value >> 32); + const uint64_t val1 = byte_swap_32(value & 0xFFFFFFFF); + const uint64_t val2 = byte_swap_32(value >> 32); return (val1 << 32) | val2; # endif } @@ -86,11 +92,11 @@ namespace osmium { public: - TCRC& operator()() { + TCRC& operator()() noexcept { return m_crc; } - const TCRC& operator()() const { + const TCRC& operator()() const noexcept { return m_crc; } @@ -106,7 +112,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint16_t)); #else - uint16_t v = osmium::util::byte_swap_16(value); + const uint16_t v = osmium::util::byte_swap_16(value); m_crc.process_bytes(&v, sizeof(uint16_t)); #endif } @@ -115,7 +121,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint32_t)); #else - uint32_t v = osmium::util::byte_swap_32(value); + const uint32_t v = osmium::util::byte_swap_32(value); m_crc.process_bytes(&v, sizeof(uint32_t)); #endif } @@ -124,7 +130,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint64_t)); #else - uint64_t v = osmium::util::byte_swap_64(value); + const uint64_t v = osmium::util::byte_swap_64(value); m_crc.process_bytes(&v, sizeof(uint64_t)); #endif } @@ -151,6 +157,7 @@ namespace osmium { void update(const NodeRef& node_ref) { update_int64(node_ref.ref()); + update(node_ref.location()); } void update(const NodeRefList& node_refs) { @@ -205,10 +212,10 @@ namespace osmium { void update(const osmium::Area& area) { update(static_cast(area)); - for (auto it = area.cbegin(); it != area.cend(); ++it) { - if (it->type() == osmium::item_type::outer_ring || - it->type() == osmium::item_type::inner_ring) { - update(static_cast(*it)); + for (const auto& subitem : area) { + if (subitem.type() == osmium::item_type::outer_ring || + subitem.type() == osmium::item_type::inner_ring) { + update(static_cast(subitem)); } } } diff --git a/third_party/libosmium/include/osmium/osm/diff_object.hpp b/third_party/libosmium/include/osmium/osm/diff_object.hpp index 609ab74531c..21cf139a84f 100644 --- a/third_party/libosmium/include/osmium/osm/diff_object.hpp +++ b/third_party/libosmium/include/osmium/osm/diff_object.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include -#include #include #include #include @@ -43,6 +42,10 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Node; + class Way; + class Relation; + /** * A DiffObject holds pointers to three OSMObjects, the current object, * the previous, and the next. They always have the same type (Node, Way, diff --git a/third_party/libosmium/include/osmium/osm/entity_bits.hpp b/third_party/libosmium/include/osmium/osm/entity_bits.hpp index 50b3e4cb082..b8e9ddba830 100644 --- a/third_party/libosmium/include/osmium/osm/entity_bits.hpp +++ b/third_party/libosmium/include/osmium/osm/entity_bits.hpp @@ -33,12 +33,15 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include + #include namespace osmium { /** - * @brief Bitfield for OSM entity types. + * @brief Bit field for OSM entity types. */ namespace osm_entity_bits { @@ -94,8 +97,19 @@ namespace osmium { return lhs; } + /** + * Get entity_bits from item_type. + * + * @pre item_type must be undefined, node, way, relation, area, or + * changeset. + */ inline type from_item_type(osmium::item_type item_type) noexcept { - return static_cast(0x1 << (static_cast(item_type) - 1)); + auto ut = static_cast::type>(item_type); + assert(ut <= 0x05); + if (ut == 0) { + return nothing; + } + return static_cast(0x1 << (ut - 1)); } } // namespace osm_entity_bits diff --git a/third_party/libosmium/include/osmium/osm/location.hpp b/third_party/libosmium/include/osmium/osm/location.hpp index 85f4b162aaf..c5da620cb39 100644 --- a/third_party/libosmium/include/osmium/osm/location.hpp +++ b/third_party/libosmium/include/osmium/osm/location.hpp @@ -35,15 +35,14 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include +#include +#include #include #include -#include - -#include -#include - namespace osmium { /** @@ -62,6 +61,184 @@ namespace osmium { }; // struct invalid_location + namespace detail { + + constexpr const int coordinate_precision = 10000000; + + // Convert string with a floating point number into integer suitable + // for use as coordinate in a Location. + inline int32_t string_to_location_coordinate(const char** data) { + const char* str = *data; + const char* full = str; + + int64_t result = 0; + int sign = 1; + + // one more than significant digits to allow rounding + int64_t scale = 8; + + // paranoia check for maximum number of digits + int max_digits = 10; + + // optional minus sign + if (*str == '-') { + sign = -1; + ++str; + } + + // there has to be at least one digit + if (*str >= '0' && *str <= '9') { + result = *str - '0'; + ++str; + } else { + goto error; + } + + // optional additional digits before decimal point + while (*str >= '0' && *str <= '9' && max_digits > 0) { + result = result * 10 + (*str - '0'); + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + + // optional decimal point + if (*str == '.') { + ++str; + + // read significant digits + for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) { + result = result * 10 + (*str - '0'); + } + + // ignore non-significant digits + max_digits = 20; + while (*str >= '0' && *str <= '9' && max_digits > 0) { + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + } + + // optional exponent in scientific notation + if (*str == 'e' || *str == 'E') { + ++str; + + int esign = 1; + // optional minus sign + if (*str == '-') { + esign = -1; + ++str; + } + + int64_t eresult = 0; + + // there has to be at least one digit in exponent + if (*str >= '0' && *str <= '9') { + eresult = *str - '0'; + ++str; + } else { + goto error; + } + + // optional additional digits in exponent + max_digits = 5; + while (*str >= '0' && *str <= '9' && max_digits > 0) { + eresult = eresult * 10 + (*str - '0'); + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + + scale += eresult * esign; + } + + if (scale < 0) { + result = 0; + } else { + for (; scale > 0; --scale) { + result *= 10; + } + + result = (result + 5) / 10 * sign; + + if (result > std::numeric_limits::max() || + result < std::numeric_limits::min()) { + goto error; + } + } + + *data = str; + return static_cast(result); + + error: + + throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"}; + } + + // Convert integer as used by location for coordinates into a string. + template + inline T append_location_coordinate_to_string(T iterator, int32_t value) { + // handle negative values + if (value < 0) { + *iterator++ = '-'; + value = -value; + } + + // write digits into temporary buffer + int32_t v = value; + char temp[10]; + char* t = temp; + do { + *t++ = char(v % 10) + '0'; + v /= 10; + } while (v != 0); + + while (t-temp < 7) { + *t++ = '0'; + } + + // write out digits before decimal point + if (value >= coordinate_precision) { + if (value >= 10 * coordinate_precision) { + if (value >= 100 * coordinate_precision) { + *iterator++ = *--t; + } + *iterator++ = *--t; + } + *iterator++ = *--t; + } else { + *iterator++ = '0'; + } + + // remove trailing zeros + const char* tn = temp; + while (tn < t && *tn == '0') { + ++tn; + } + + // decimal point + if (t != tn) { + *iterator++ = '.'; + while (t != tn) { + *iterator++ = *--t; + } + } + + return iterator; + } + + } // namespace detail + /** * Locations define a place on earth. * @@ -89,14 +266,12 @@ namespace osmium { // static constexpr int32_t undefined_coordinate = std::numeric_limits::max(); static constexpr int32_t undefined_coordinate = 2147483647; - static constexpr int coordinate_precision = 10000000; - static int32_t double_to_fix(const double c) noexcept { - return static_cast(std::round(c * coordinate_precision)); + return static_cast(std::round(c * detail::coordinate_precision)); } static constexpr double fix_to_double(const int32_t c) noexcept { - return static_cast(c) / coordinate_precision; + return static_cast(c) / detail::coordinate_precision; } /** @@ -154,10 +329,10 @@ namespace osmium { * usual bounds (-180<=lon<=180, -90<=lat<=90). */ constexpr bool valid() const noexcept { - return m_x >= -180 * coordinate_precision - && m_x <= 180 * coordinate_precision - && m_y >= -90 * coordinate_precision - && m_y <= 90 * coordinate_precision; + return m_x >= -180 * detail::coordinate_precision + && m_x <= 180 * detail::coordinate_precision + && m_y >= -90 * detail::coordinate_precision + && m_y <= 90 * detail::coordinate_precision; } constexpr int32_t x() const noexcept { @@ -226,11 +401,47 @@ namespace osmium { return *this; } + Location& set_lon(const char* str) { + const char** data = &str; + m_x = detail::string_to_location_coordinate(data); + if (**data != '\0') { + throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"}; + } + return *this; + } + + Location& set_lat(const char* str) { + const char** data = &str; + m_y = detail::string_to_location_coordinate(data); + if (**data != '\0') { + throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"}; + } + return *this; + } + + Location& set_lon_partial(const char** str) { + m_x = detail::string_to_location_coordinate(str); + return *this; + } + + Location& set_lat_partial(const char** str) { + m_y = detail::string_to_location_coordinate(str); + return *this; + } + template - T as_string(T iterator, const char separator) const { - iterator = osmium::util::double2string(iterator, lon(), 7); + T as_string_without_check(T iterator, const char separator = ',') const { + iterator = detail::append_location_coordinate_to_string(iterator, x()); *iterator++ = separator; - return osmium::util::double2string(iterator, lat(), 7); + return detail::append_location_coordinate_to_string(iterator, y()); + } + + template + T as_string(T iterator, const char separator = ',') const { + if (!valid()) { + throw osmium::invalid_location("invalid location"); + } + return as_string_without_check(iterator, separator); } }; // class Location @@ -273,13 +484,52 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& out, const osmium::Location& location) { if (location) { - out << '(' << location.lon() << ',' << location.lat() << ')'; + out << '('; + location.as_string(std::ostream_iterator(out), ','); + out << ')'; } else { out << "(undefined,undefined)"; } return out; } + namespace detail { + + template + inline size_t hash(const osmium::Location& location) noexcept { + return location.x() ^ location.y(); + } + + template <> + inline size_t hash<8>(const osmium::Location& location) noexcept { + size_t h = location.x(); + h <<= 32; + return h ^ location.y(); + } + + } // namespace detail + } // namespace osmium +namespace std { + +// This pragma is a workaround for a bug in an old libc implementation +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif + template <> + struct hash { + using argument_type = osmium::Location; + using result_type = size_t; + size_t operator()(const osmium::Location& location) const noexcept { + return osmium::detail::hash(location); + } + }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +} // namespace std + #endif // OSMIUM_OSM_LOCATION_HPP diff --git a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp index 84edc076090..6cfdf229587 100644 --- a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp +++ b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp @@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include namespace osmium { @@ -66,7 +67,7 @@ namespace osmium { * Returns the number of NodeRefs in the collection. */ size_t size() const noexcept { - auto size_node_refs = byte_size() - sizeof(NodeRefList); + const auto size_node_refs = byte_size() - sizeof(NodeRefList); assert(size_node_refs % sizeof(NodeRef) == 0); return size_node_refs / sizeof(NodeRef); } diff --git a/third_party/libosmium/include/osmium/osm/object.hpp b/third_party/libosmium/include/osmium/osm/object.hpp index 6d1de6f6799..caa6fbcdd70 100644 --- a/third_party/libosmium/include/osmium/osm/object.hpp +++ b/third_party/libosmium/include/osmium/osm/object.hpp @@ -33,11 +33,10 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include #include +#include #include #include @@ -49,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -172,9 +172,9 @@ namespace osmium { * @returns Reference to object to make calls chainable. */ OSMObject& set_visible(const char* visible) { - if (!strcmp("true", visible)) { + if (!std::strcmp("true", visible)) { set_visible(true); - } else if (!strcmp("false", visible)) { + } else if (!std::strcmp("false", visible)) { set_visible(false); } else { throw std::invalid_argument("Unknown value for visible attribute (allowed is 'true' or 'false')"); @@ -286,6 +286,20 @@ namespace osmium { return *this; } + /** + * Set the timestamp when this object last changed. + * + * @param timestamp Timestamp in ISO format. + * @returns Reference to object to make calls chainable. + */ + OSMObject& set_timestamp(const char* timestamp) { + m_timestamp = detail::parse_timestamp(timestamp); + if (timestamp[20] != '\0') { + throw std::invalid_argument{"can not parse timestamp"}; + } + return *this; + } + /// Get user name for this object. const char* user() const noexcept { return reinterpret_cast(data() + sizeof_object()); @@ -311,25 +325,28 @@ namespace osmium { * * @param attr Name of the attribute (must be one of "id", "version", "changeset", "timestamp", "uid", "visible") * @param value Value of the attribute + * @returns Reference to object to make calls chainable. */ - void set_attribute(const char* attr, const char* value) { - if (!strcmp(attr, "id")) { + OSMObject& set_attribute(const char* attr, const char* value) { + if (!std::strcmp(attr, "id")) { set_id(value); - } else if (!strcmp(attr, "version")) { + } else if (!std::strcmp(attr, "version")) { set_version(value); - } else if (!strcmp(attr, "changeset")) { + } else if (!std::strcmp(attr, "changeset")) { set_changeset(value); - } else if (!strcmp(attr, "timestamp")) { - set_timestamp(osmium::Timestamp(value)); - } else if (!strcmp(attr, "uid")) { + } else if (!std::strcmp(attr, "timestamp")) { + set_timestamp(value); + } else if (!std::strcmp(attr, "uid")) { set_uid(value); - } else if (!strcmp(attr, "visible")) { + } else if (!std::strcmp(attr, "visible")) { set_visible(value); } + + return *this; } - typedef osmium::memory::CollectionIterator iterator; - typedef osmium::memory::CollectionIterator const_iterator; + using iterator = osmium::memory::CollectionIterator; + using const_iterator = osmium::memory::CollectionIterator; iterator begin() { return iterator(subitems_position()); @@ -355,6 +372,26 @@ namespace osmium { return cend(); } + /** + * Get a range of subitems of a specific type. + * + * @tparam The type (must be derived from osmium::memory::Item. + */ + template + osmium::memory::ItemIteratorRange subitems() { + return osmium::memory::ItemIteratorRange{subitems_position(), next()}; + } + + /** + * Get a range of subitems of a specific type. + * + * @tparam The type (must be derived from osmium::memory::Item. + */ + template + osmium::memory::ItemIteratorRange subitems() const { + return osmium::memory::ItemIteratorRange{subitems_position(), next()}; + } + template using t_iterator = osmium::memory::ItemIterator; @@ -399,8 +436,8 @@ namespace osmium { * OSMObjects are equal if their type, id, and version are equal. */ inline bool operator==(const OSMObject& lhs, const OSMObject& rhs) noexcept { - return lhs.type() == rhs.type() && - lhs.id() == rhs.id() && + return lhs.type() == rhs.type() && + lhs.id() == rhs.id() && lhs.version() == rhs.version(); } @@ -409,16 +446,22 @@ namespace osmium { } /** - * OSMObjects can be ordered by type, id and version. - * Note that we use the absolute value of the id for a - * better ordering of objects with negative id. + * OSMObjects can be ordered by type, id, version, and timestamp. Usually + * 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. + * + * 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. + * + * See object_order_type_id_reverse_version if you need a different + * ordering. */ inline bool operator<(const OSMObject& lhs, const OSMObject& rhs) noexcept { - if (lhs.type() != rhs.type()) { - return lhs.type() < rhs.type(); - } - return (lhs.id() == rhs.id() && lhs.version() < rhs.version()) || - lhs.positive_id() < rhs.positive_id(); + 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()); } 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 fe3529b4ace..aa0241d2625 100644 --- a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp +++ b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp @@ -33,12 +33,17 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include +#include +#include namespace osmium { /** - * Function object class for comparing OSM objects for equality by type, id, and version. + * Function object class for comparing OSM objects for equality by type, + * id, and version. */ struct object_equal_type_id_version { @@ -53,8 +58,8 @@ namespace osmium { }; // struct object_equal_type_id_version /** - * Function object class for comparing OSM objects for equality by type and id, - * ignoring the version. + * Function object class for comparing OSM objects for equality by type + * and id, ignoring the version. */ struct object_equal_type_id { @@ -70,7 +75,8 @@ namespace osmium { }; // struct object_equal_type_id /** - * Function object class for ordering OSM objects by type, id, and version. + * Function object class for ordering OSM objects by type, id, version, + * and timestamp. */ struct object_order_type_id_version { @@ -85,18 +91,17 @@ namespace osmium { }; // struct object_order_type_id_version /** - * Function object class for ordering OSM objects by type, id, and reverse version, - * ie objects are ordered by type and id, but later versions of an object are - * ordered before earlier versions of the same object. + * 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. */ struct object_order_type_id_reverse_version { bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept { - if (lhs.type() != rhs.type()) { - return lhs.type() < rhs.type(); - } - return (lhs.id() == rhs.id() && lhs.version() > rhs.version()) || - lhs.positive_id() < rhs.positive_id(); + 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()); } bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept { diff --git a/third_party/libosmium/include/osmium/osm/relation.hpp b/third_party/libosmium/include/osmium/osm/relation.hpp index 9c4e69cc734..2aa9caaf152 100644 --- a/third_party/libosmium/include/osmium/osm/relation.hpp +++ b/third_party/libosmium/include/osmium/osm/relation.hpp @@ -33,13 +33,13 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include // IWYU pragma: keep #include +#include #include #include #include @@ -149,7 +149,7 @@ namespace osmium { public: - typedef size_t size_type; + using size_type = size_t; RelationMemberList() : osmium::memory::Collection() { diff --git a/third_party/libosmium/include/osmium/osm/segment.hpp b/third_party/libosmium/include/osmium/osm/segment.hpp index d35f9706609..c36533e0cf7 100644 --- a/third_party/libosmium/include/osmium/osm/segment.hpp +++ b/third_party/libosmium/include/osmium/osm/segment.hpp @@ -37,7 +37,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/osm/tag.hpp b/third_party/libosmium/include/osmium/osm/tag.hpp index 3f1a29826d3..cd2a913b7a3 100644 --- a/third_party/libosmium/include/osmium/osm/tag.hpp +++ b/third_party/libosmium/include/osmium/osm/tag.hpp @@ -34,7 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include #include #include #include @@ -87,7 +87,7 @@ namespace osmium { }; // class Tag inline bool operator==(const Tag& a, const Tag& b) { - return !std::strcmp(a.key(), b.key()) && !strcmp(a.value(), b.value()); + return !std::strcmp(a.key(), b.key()) && !std::strcmp(a.value(), b.value()); } inline bool operator<(const Tag& a, const Tag& b) { @@ -104,32 +104,72 @@ namespace osmium { class TagList : public osmium::memory::Collection { + const_iterator find_key(const char* key) const noexcept { + return std::find_if(cbegin(), cend(), [key](const Tag& tag) { + return !std::strcmp(tag.key(), key); + }); + } + public: - typedef size_t size_type; + using size_type = size_t; TagList() : osmium::memory::Collection() { } + /** + * Returns the number of tags in this tag list. + */ size_type size() const noexcept { return static_cast(std::distance(begin(), end())); } + /** + * Get tag value for the given tag key. If the key is not set, returns + * the default_value. + * + * @pre @code key != nullptr @endcode + */ const char* get_value_by_key(const char* key, const char* default_value = nullptr) const noexcept { - auto result = std::find_if(cbegin(), cend(), [key](const Tag& tag) { - return !strcmp(tag.key(), key); - }); - if (result == cend()) { - return default_value; - } - return result->value(); + assert(key); + const auto result = find_key(key); + return result == cend() ? default_value : result->value(); } + /** + * Get tag value for the given tag key. If the key is not set, returns + * nullptr. + * + * @pre @code key != nullptr @endcode + */ const char* operator[](const char* key) const noexcept { return get_value_by_key(key); } + /** + * Returns true if the tag with the given key is in the tag list. + * + * @pre @code key != nullptr @endcode + */ + bool has_key(const char* key) const noexcept { + assert(key); + return find_key(key) != cend(); + } + + /** + * Returns true if the tag with the given key and value is in the + * tag list. + * + * @pre @code key != nullptr && value != nullptr @endcode + */ + bool has_tag(const char* key, const char* value) const noexcept { + assert(key); + assert(value); + const auto result = find_key(key); + return result != cend() && !std::strcmp(result->value(), value); + } + }; // class TagList static_assert(sizeof(TagList) % osmium::memory::align_bytes == 0, "Class osmium::TagList has wrong size to be aligned properly!"); diff --git a/third_party/libosmium/include/osmium/osm/timestamp.hpp b/third_party/libosmium/include/osmium/osm/timestamp.hpp index 613752e68dc..5f5243028bf 100644 --- a/third_party/libosmium/include/osmium/osm/timestamp.hpp +++ b/third_party/libosmium/include/osmium/osm/timestamp.hpp @@ -40,12 +40,71 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include // IWYU pragma: keep namespace osmium { + namespace detail { + + inline time_t parse_timestamp(const char* str) { + static const int mon_lengths[] = { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + if (str[ 0] >= '0' && str[ 0] <= '9' && + str[ 1] >= '0' && str[ 1] <= '9' && + str[ 2] >= '0' && str[ 2] <= '9' && + str[ 3] >= '0' && str[ 3] <= '9' && + str[ 4] == '-' && + str[ 5] >= '0' && str[ 5] <= '9' && + str[ 6] >= '0' && str[ 6] <= '9' && + str[ 7] == '-' && + str[ 8] >= '0' && str[ 8] <= '9' && + str[ 9] >= '0' && str[ 9] <= '9' && + str[10] == 'T' && + str[11] >= '0' && str[11] <= '9' && + str[12] >= '0' && str[12] <= '9' && + str[13] == ':' && + str[14] >= '0' && str[14] <= '9' && + str[15] >= '0' && str[15] <= '9' && + str[16] == ':' && + str[17] >= '0' && str[17] <= '9' && + str[18] >= '0' && str[18] <= '9' && + str[19] == 'Z') { + struct tm tm; + tm.tm_year = (str[ 0] - '0') * 1000 + + (str[ 1] - '0') * 100 + + (str[ 2] - '0') * 10 + + (str[ 3] - '0') - 1900; + tm.tm_mon = (str[ 5] - '0') * 10 + (str[ 6] - '0') - 1; + tm.tm_mday = (str[ 8] - '0') * 10 + (str[ 9] - '0'); + tm.tm_hour = (str[11] - '0') * 10 + (str[12] - '0'); + tm.tm_min = (str[14] - '0') * 10 + (str[15] - '0'); + tm.tm_sec = (str[17] - '0') * 10 + (str[18] - '0'); + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + if (tm.tm_year >= 0 && + tm.tm_mon >= 0 && tm.tm_mon <= 11 && + tm.tm_mday >= 1 && tm.tm_mday <= mon_lengths[tm.tm_mon] && + tm.tm_hour >= 0 && tm.tm_hour <= 23 && + tm.tm_min >= 0 && tm.tm_min <= 59 && + tm.tm_sec >= 0 && tm.tm_sec <= 60) { +#ifndef _WIN32 + return timegm(&tm); +#else + return _mkgmtime(&tm); +#endif + } + } + throw std::invalid_argument{"can not parse timestamp"}; + } + + } // namespace detail + /** * A timestamp. Internal representation is an unsigned 32bit integer * holding seconds since epoch (1970-01-01T00:00:00Z), so this will @@ -56,7 +115,7 @@ namespace osmium { class Timestamp { // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0 - static constexpr int timestamp_length = 20 + 1; + static constexpr const int timestamp_length = 20 + 1; // The timestamp format for OSM timestamps in strftime(3) format. // This is the ISO-Format "yyyy-mm-ddThh:mm:ssZ". @@ -97,27 +156,7 @@ namespace osmium { * @throws std::invalid_argument if the timestamp can not be parsed. */ explicit Timestamp(const char* timestamp) { -#ifndef _WIN32 - struct tm tm { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - if (strptime(timestamp, timestamp_format(), &tm) == nullptr) { - throw std::invalid_argument("can't parse timestamp"); - } - m_timestamp = static_cast(timegm(&tm)); -#else - struct tm tm; - int n = sscanf(timestamp, "%4d-%2d-%2dT%2d:%2d:%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); - if (n != 6) { - throw std::invalid_argument("can't parse timestamp"); - } - tm.tm_year -= 1900; - tm.tm_mon--; - tm.tm_wday = 0; - tm.tm_yday = 0; - tm.tm_isdst = 0; - m_timestamp = static_cast(_mkgmtime(&tm)); -#endif + m_timestamp = static_cast(detail::parse_timestamp(timestamp)); } /** diff --git a/third_party/libosmium/include/osmium/osm/types.hpp b/third_party/libosmium/include/osmium/osm/types.hpp index 984dd135c1a..ec46bb2cd20 100644 --- a/third_party/libosmium/include/osmium/osm/types.hpp +++ b/third_party/libosmium/include/osmium/osm/types.hpp @@ -38,25 +38,25 @@ DEALINGS IN THE SOFTWARE. namespace osmium { /* - * The following typedefs are chosen so that they can represent all needed + * The following types are chosen so that they can represent all needed * numbers and still be reasonably space efficient. As the OSM database * needs 64 bit IDs for nodes, this size is used for all object IDs. */ - typedef int64_t object_id_type; ///< Type for OSM object (node, way, or relation) IDs. - typedef uint64_t unsigned_object_id_type; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs. - typedef uint32_t object_version_type; ///< Type for OSM object version number. - typedef uint32_t changeset_id_type; ///< Type for OSM changeset IDs. - typedef uint32_t user_id_type; ///< Type for OSM user IDs. - typedef int32_t signed_user_id_type; ///< Type for signed OSM user IDs. - typedef uint32_t num_changes_type; ///< Type for changeset num_changes. - typedef uint32_t num_comments_type; ///< Type for changeset num_comments. + using object_id_type = int64_t; ///< Type for OSM object (node, way, or relation) IDs. + using unsigned_object_id_type = uint64_t; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs. + using object_version_type = uint32_t; ///< Type for OSM object version number. + using changeset_id_type = uint32_t; ///< Type for OSM changeset IDs. + using user_id_type = uint32_t; ///< Type for OSM user IDs. + using signed_user_id_type = int32_t; ///< Type for signed OSM user IDs. + using num_changes_type = uint32_t; ///< Type for changeset num_changes. + using num_comments_type = uint32_t; ///< Type for changeset num_comments. /** * Size for strings in OSM data such as user names, tag keys, roles, etc. * In Osmium they can be up to 2^16 bytes long, but OSM usually has lower * defined limits. */ - typedef uint16_t string_size_type; + using string_size_type = uint16_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 aed0648984b..190dd2983bc 100644 --- a/third_party/libosmium/include/osmium/osm/types_from_string.hpp +++ b/third_party/libosmium/include/osmium/osm/types_from_string.hpp @@ -35,13 +35,14 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include +#include #include #include #include +#include #include #include @@ -50,7 +51,7 @@ namespace osmium { /** * Convert string with object id to object_id_type. * - * @pre input must not be nullptr. + * @pre @code input != nullptr @endcode * * @param input Input string. * @@ -70,23 +71,29 @@ namespace osmium { /** * Parse string with object type identifier followed by object id. This - * reads strings like "n1234" and "w10". + * reads strings like "n1234" and "w10". If there is no type prefix, + * the default_type is returned. * - * @pre input must not be nullptr. + * @pre @code input != nullptr @endcode + * @pre @code types != osmium::osm_entity_bits::nothing @endcode * * @param input Input string. * @param types Allowed types. Must not be osmium::osm_entity_bits::nothing. + * @param default_type Type used when there is no type prefix. * * @returns std::pair of type and id. * * @throws std::range_error if the value is out of range. */ - inline std::pair string_to_object_id(const char* input, osmium::osm_entity_bits::type types) { + inline std::pair + string_to_object_id(const char* input, + osmium::osm_entity_bits::type types, + osmium::item_type default_type = osmium::item_type::undefined) { assert(input); assert(types != osmium::osm_entity_bits::nothing); if (*input != '\0') { if (std::isdigit(*input)) { - return std::make_pair(osmium::item_type::undefined, string_to_object_id(input)); + return std::make_pair(default_type, string_to_object_id(input)); } osmium::item_type t = osmium::char_to_item_type(*input); if (osmium::osm_entity_bits::from_item_type(t) & types) { @@ -126,7 +133,7 @@ namespace osmium { } /** - * Convert string with object version to object_version_type. + * Convert string with changeset id to changeset_id_type. * * @pre input must not be nullptr. * diff --git a/third_party/libosmium/include/osmium/osm/way.hpp b/third_party/libosmium/include/osmium/osm/way.hpp index 3bc30b0c6fa..f6713fef46f 100644 --- a/third_party/libosmium/include/osmium/osm/way.hpp +++ b/third_party/libosmium/include/osmium/osm/way.hpp @@ -33,12 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include -#include -#include #include #include +#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/relations/collector.hpp b/third_party/libosmium/include/osmium/relations/collector.hpp index 7d7d14d0bad..b8455b4ebe9 100644 --- a/third_party/libosmium/include/osmium/relations/collector.hpp +++ b/third_party/libosmium/include/osmium/relations/collector.hpp @@ -39,13 +39,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -//#include +#include #include -#include #include #include -#include // IWYU pragma: keep +#include #include #include #include @@ -57,6 +56,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Node; + class Way; + /** * @brief Code related to the assembly of OSM relations */ @@ -64,13 +66,6 @@ namespace osmium { namespace detail { - template - inline typename std::iterator_traits::difference_type count_not_removed(const R& range) { - return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) { - return !mm.removed(); - }); - } - } // namespace detail /** @@ -113,7 +108,7 @@ namespace osmium { public: - HandlerPass1(TCollector& collector) noexcept : + explicit HandlerPass1(TCollector& collector) noexcept : m_collector(collector) { } @@ -136,7 +131,7 @@ namespace osmium { public: - HandlerPass2(TCollector& collector) noexcept : + explicit HandlerPass2(TCollector& collector) noexcept : m_collector(collector) { } @@ -193,14 +188,14 @@ namespace osmium { int m_count_complete = 0; - typedef std::function callback_func_type; + using callback_func_type = std::function; callback_func_type m_callback; static constexpr size_t initial_buffer_size = 1024 * 1024; iterator_range find_member_meta(osmium::item_type type, osmium::object_id_type id) { auto& mmv = member_meta(type); - return iterator_range{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))}; + return make_range(std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))); } public: @@ -313,6 +308,7 @@ namespace osmium { } const osmium::Relation& get_relation(size_t offset) const { + assert(m_relations_buffer.committed() > offset); return m_relations_buffer.get(offset); } @@ -323,7 +319,15 @@ namespace osmium { return get_relation(relation_meta.relation_offset()); } + /** + * Get the relation from a member_meta. + */ + const osmium::Relation& get_relation(const MemberMeta& member_meta) const { + return get_relation(m_relations[member_meta.relation_pos()]); + } + osmium::OSMObject& get_member(size_t offset) const { + assert(m_members_buffer.committed() > offset); return m_members_buffer.get(offset); } @@ -360,7 +364,6 @@ namespace osmium { } else { m_relations_buffer.commit(); m_relations.push_back(std::move(relation_meta)); -// std::cerr << "added relation id=" << relation.id() << "\n"; } } @@ -369,15 +372,17 @@ namespace osmium { * search on them. */ void sort_member_meta() { -/* std::cerr << "relations: " << m_relations.size() << "\n"; - std::cerr << "node members: " << m_member_meta[0].size() << "\n"; - std::cerr << "way members: " << m_member_meta[1].size() << "\n"; - std::cerr << "relation members: " << m_member_meta[2].size() << "\n";*/ std::sort(m_member_meta[0].begin(), m_member_meta[0].end()); std::sort(m_member_meta[1].begin(), m_member_meta[1].end()); std::sort(m_member_meta[2].begin(), m_member_meta[2].end()); } + static typename iterator_range::iterator::difference_type count_not_removed(const iterator_range& range) { + return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) { + return !mm.removed(); + }); + } + /** * Find this object in the member vectors and add it to all * relations that need it. @@ -388,7 +393,7 @@ namespace osmium { bool find_and_add_object(const osmium::OSMObject& object) { auto range = find_member_meta(object.type(), object.id()); - if (detail::count_not_removed(range) == 0) { + if (count_not_removed(range) == 0) { // nothing found return false; } @@ -409,9 +414,7 @@ namespace osmium { assert(member_meta.member_id() == object.id()); assert(member_meta.relation_pos() < m_relations.size()); RelationMeta& relation_meta = m_relations[member_meta.relation_pos()]; -// std::cerr << " => " << member_meta.member_pos() << " < " << get_relation(relation_meta).members().size() << " (id=" << get_relation(relation_meta).id() << ")\n"; assert(member_meta.member_pos() < get_relation(relation_meta).members().size()); -// std::cerr << " add way " << member_meta.member_id() << " to rel " << get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; relation_meta.got_one_member(); if (relation_meta.has_all_members()) { const size_t relation_offset = member_meta.relation_pos(); @@ -429,17 +432,17 @@ namespace osmium { const osmium::Relation& relation = get_relation(relation_meta); for (const auto& member : relation.members()) { if (member.ref() != 0) { - auto range = find_member_meta(member.type(), member.ref()); + const auto range = find_member_meta(member.type(), member.ref()); assert(!range.empty()); // if this is the last time this object was needed // then mark it as removed - if (detail::count_not_removed(range) == 1) { + if (count_not_removed(range) == 1) { get_member(range.begin()->buffer_offset()).set_removed(true); } for (auto& member_meta : range) { - if (!member_meta.removed() && relation.id() == get_relation(member_meta.relation_pos()).id()) { + if (!member_meta.removed() && relation.id() == get_relation(member_meta).id()) { member_meta.remove(); break; } diff --git a/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp index f0e9c362516..b28dca1d01c 100644 --- a/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp +++ b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp @@ -33,10 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include -#include #include @@ -132,7 +130,7 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& out, const MemberMeta& mm) { - out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << ")"; + out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << " removed=" << (mm.removed() ? "yes" : "no") << ")"; return out; } diff --git a/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp index 93aa41cf213..b71c5a5f72e 100644 --- a/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp +++ b/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp @@ -117,8 +117,8 @@ namespace osmium { */ struct has_all_members { - typedef RelationMeta& argument_type; - typedef bool result_type; + using argument_type = RelationMeta&; + using result_type = bool; /** * @returns true if this relation is complete, false otherwise. diff --git a/third_party/libosmium/include/osmium/tags/filter.hpp b/third_party/libosmium/include/osmium/tags/filter.hpp index 407992e6d70..27a836061f7 100644 --- a/third_party/libosmium/include/osmium/tags/filter.hpp +++ b/third_party/libosmium/include/osmium/tags/filter.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -76,8 +77,8 @@ namespace osmium { template , typename TValueComp=match_value> class Filter { - typedef TKey key_type; - typedef typename std::conditional::value, bool, TValue>::type value_type; + using key_type = TKey; + using value_type = typename std::conditional::value, bool, TValue>::type; struct Rule { key_type key; @@ -106,10 +107,10 @@ namespace osmium { public: - typedef Filter filter_type; - typedef const osmium::Tag& argument_type; - typedef bool result_type; - typedef boost::filter_iterator iterator; + using filter_type = Filter; + using argument_type = const osmium::Tag&; + using result_type = bool; + using iterator = boost::filter_iterator; explicit Filter(bool default_result = false) : m_default_result(default_result) { @@ -151,9 +152,9 @@ namespace osmium { }; // class Filter - typedef Filter KeyValueFilter; - typedef Filter KeyFilter; - typedef Filter KeyPrefixFilter; + using KeyValueFilter = Filter; + using KeyFilter = Filter; + using KeyPrefixFilter = Filter; } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/regex_filter.hpp b/third_party/libosmium/include/osmium/tags/regex_filter.hpp index 8ea6d60c370..3df94dd0eac 100644 --- a/third_party/libosmium/include/osmium/tags/regex_filter.hpp +++ b/third_party/libosmium/include/osmium/tags/regex_filter.hpp @@ -49,7 +49,7 @@ namespace osmium { } }; // struct match_value - typedef Filter RegexFilter; + 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 b1f346fe142..d7862798cc0 100644 --- a/third_party/libosmium/include/osmium/tags/taglist.hpp +++ b/third_party/libosmium/include/osmium/tags/taglist.hpp @@ -34,7 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include // IWYU pragma: keep #include diff --git a/third_party/libosmium/include/osmium/thread/pool.hpp b/third_party/libosmium/include/osmium/thread/pool.hpp index 207f5551427..613f2272cf4 100644 --- a/third_party/libosmium/include/osmium/thread/pool.hpp +++ b/third_party/libosmium/include/osmium/thread/pool.hpp @@ -34,9 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include -#include #include #include #include @@ -66,7 +64,7 @@ namespace osmium { } if (num_threads < 0) { - num_threads += hardware_concurrency; + num_threads += int(hardware_concurrency); } if (num_threads < 1) { @@ -78,6 +76,11 @@ 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); + return n > 2 ? n : 2; + } + } // namespace detail /** @@ -118,13 +121,11 @@ namespace osmium { osmium::thread::set_thread_name("_osmium_worker"); while (true) { function_wrapper task; - m_work_queue.wait_and_pop_with_timeout(task); - if (task) { - if (task()) { - // The called tasks returns true only when the - // worker thread should shut down. - return; - } + m_work_queue.wait_and_pop(task); + if (task && task()) { + // The called tasks returns true only when the + // worker thread should shut down. + return; } } } @@ -160,10 +161,9 @@ namespace osmium { public: static constexpr int default_num_threads = 0; - static constexpr size_t max_work_queue_size = 10; static Pool& instance() { - static Pool pool(default_num_threads, max_work_queue_size); + static Pool pool(default_num_threads, detail::get_work_queue_size()); return pool; } @@ -176,7 +176,6 @@ namespace osmium { ~Pool() { shutdown_all_workers(); - m_work_queue.shutdown(); } size_t queue_size() const { @@ -190,7 +189,7 @@ namespace osmium { template std::future::type> submit(TFunction&& func) { - typedef typename std::result_of::type result_type; + using result_type = typename std::result_of::type; std::packaged_task task(std::forward(func)); std::future future_result(task.get_future()); diff --git a/third_party/libosmium/include/osmium/thread/queue.hpp b/third_party/libosmium/include/osmium/thread/queue.hpp index 771735883a0..6f4f7b1d95d 100644 --- a/third_party/libosmium/include/osmium/thread/queue.hpp +++ b/third_party/libosmium/include/osmium/thread/queue.hpp @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include @@ -41,7 +40,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include // IWYU pragma: keep (for std::move) +#include // IWYU pragma: keep + +#ifdef OSMIUM_DEBUG_QUEUE_SIZE +# include +# include +#endif namespace osmium { @@ -69,8 +73,6 @@ namespace osmium { /// Used to signal readers when data is available in the queue. std::condition_variable m_data_available; - std::atomic m_done; - #ifdef OSMIUM_DEBUG_QUEUE_SIZE /// The largest size the queue has been so far. size_t m_largest_size; @@ -81,6 +83,16 @@ namespace osmium { /// The number of times the queue was full and a thread pushing /// to the queue was blocked. std::atomic m_full_counter; + + /** + * The number of times wait_and_pop(with_timeout)() was called + * on the queue. + */ + std::atomic m_pop_counter; + + /// The number of times the queue was full and a thread pushing + /// to the queue was blocked. + std::atomic m_empty_counter; #endif public: @@ -97,21 +109,21 @@ namespace osmium { m_name(name), m_mutex(), m_queue(), - m_data_available(), - m_done(false) + m_data_available() #ifdef OSMIUM_DEBUG_QUEUE_SIZE , m_largest_size(0), m_push_counter(0), - m_full_counter(0) + m_full_counter(0), + m_pop_counter(0), + m_empty_counter(0) #endif { } ~Queue() { - shutdown(); #ifdef OSMIUM_DEBUG_QUEUE_SIZE - std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls\n"; + std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls and was empty " << m_empty_counter << " times in " << m_pop_counter << " pop() calls\n"; #endif } @@ -141,15 +153,18 @@ namespace osmium { m_data_available.notify_one(); } - void shutdown() { - m_done = true; - m_data_available.notify_all(); - } - void wait_and_pop(T& value) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_pop_counter; +#endif std::unique_lock lock(m_mutex); +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + if (m_queue.empty()) { + ++m_empty_counter; + } +#endif m_data_available.wait(lock, [this] { - return !m_queue.empty() || m_done; + return !m_queue.empty(); }); if (!m_queue.empty()) { value = std::move(m_queue.front()); @@ -157,22 +172,15 @@ namespace osmium { } } - void wait_and_pop_with_timeout(T& value) { - std::unique_lock lock(m_mutex); - if (!m_data_available.wait_for(lock, std::chrono::seconds(1), [this] { - return !m_queue.empty() || m_done; - })) { - return; - } - if (!m_queue.empty()) { - value = std::move(m_queue.front()); - m_queue.pop(); - } - } - bool try_pop(T& value) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_pop_counter; +#endif std::lock_guard lock(m_mutex); if (m_queue.empty()) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_empty_counter; +#endif return false; } value = std::move(m_queue.front()); diff --git a/third_party/libosmium/include/osmium/thread/sorted_queue.hpp b/third_party/libosmium/include/osmium/thread/sorted_queue.hpp deleted file mode 100644 index 5478643c9c8..00000000000 --- a/third_party/libosmium/include/osmium/thread/sorted_queue.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef OSMIUM_THREAD_SORTED_QUEUE_HPP -#define OSMIUM_THREAD_SORTED_QUEUE_HPP - -/* - -This file is part of Osmium (http://osmcode.org/libosmium). - -Copyright 2013-2016 Jochen Topf and others (see README). - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -*/ - -#include -#include -#include -#include - -namespace osmium { - - namespace thread { - - /** - * This implements a sorted queue. It is a bit like a priority - * queue. We have n worker threads pushing items into the queue - * and one thread pulling them out again "in order". The order - * is defined by the monotonically increasing "num" parameter - * to the push() method. The wait_and_pop() and try_pop() methods - * will only give out the next numbered item. This way several - * workers can work in their own time on different pieces of - * some incoming data, but it all gets serialized properly again - * after the workers have done their work. - */ - template - class SortedQueue { - - typedef typename std::deque::size_type size_type; - - mutable std::mutex m_mutex; - std::deque m_queue; - std::condition_variable m_data_available; - - size_type m_offset; - - // this method expects that we already have the lock - bool empty_intern() const { - return m_queue.front() == T(); - } - - public: - - SortedQueue() : - m_mutex(), - m_queue(1), - m_data_available(), - m_offset(0) { - } - - /** - * Push an item into the queue. - * - * @param value The item to push into the queue. - * @param num Number to describe ordering for the items. - * It must increase monotonically. - */ - void push(T value, size_type num) { - std::lock_guard lock(m_mutex); - - num -= m_offset; - if (m_queue.size() <= num + 1) { - m_queue.resize(num + 2); - } - m_queue[num] = std::move(value); - - m_data_available.notify_one(); - } - - /** - * Wait until the next item becomes available and make it - * available through value. - */ - void wait_and_pop(T& value) { - std::unique_lock lock(m_mutex); - - m_data_available.wait(lock, [this] { - return !empty_intern(); - }); - value = std::move(m_queue.front()); - m_queue.pop_front(); - ++m_offset; - } - - /** - * Get next item if it is available and return true. Or - * return false otherwise. - */ - bool try_pop(T& value) { - std::lock_guard lock(m_mutex); - - if (empty_intern()) { - return false; - } - value = std::move(m_queue.front()); - m_queue.pop_front(); - ++m_offset; - return true; - } - - /** - * The queue is empty. This means try_pop() would fail if called. - * It does not mean that there is nothing on the queue. Because - * the queue is sorted, it could mean that the next item in the - * queue is not available, but other items are. - */ - bool empty() const { - std::lock_guard lock(m_mutex); - - return empty_intern(); - } - - /** - * Returns the number of items in the queue, regardless of whether - * they can be accessed. If this is =0 it - * implies empty()==true, but not the other way around. - */ - size_t size() const { - std::lock_guard lock(m_mutex); - return m_queue.size(); - } - - }; // class SortedQueue - - } // namespace thread - -} // namespace osmium - -#endif // OSMIUM_THREAD_SORTED_QUEUE_HPP diff --git a/third_party/libosmium/include/osmium/thread/util.hpp b/third_party/libosmium/include/osmium/thread/util.hpp index 2ef331a2c8f..2eeb9997ece 100644 --- a/third_party/libosmium/include/osmium/thread/util.hpp +++ b/third_party/libosmium/include/osmium/thread/util.hpp @@ -35,6 +35,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #ifdef __linux__ # include diff --git a/third_party/libosmium/include/osmium/util/config.hpp b/third_party/libosmium/include/osmium/util/config.hpp index c40512322d2..e0412350cda 100644 --- a/third_party/libosmium/include/osmium/util/config.hpp +++ b/third_party/libosmium/include/osmium/util/config.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #ifdef _MSC_VER # define strcasecmp _stricmp @@ -44,7 +45,7 @@ namespace osmium { namespace config { - inline int get_pool_threads() { + inline int get_pool_threads() noexcept { const char* env = getenv("OSMIUM_POOL_THREADS"); if (env) { return std::atoi(env); @@ -52,7 +53,7 @@ namespace osmium { return 0; } - inline bool use_pool_threads_for_pbf_parsing() { + inline bool use_pool_threads_for_pbf_parsing() noexcept { const char* env = getenv("OSMIUM_USE_POOL_THREADS_FOR_PBF_PARSING"); if (env) { if (!strcasecmp(env, "off") || @@ -65,6 +66,18 @@ namespace osmium { return true; } + inline size_t get_max_queue_size(const char* queue_name, size_t default_value) noexcept { + std::string name {"OSMIUM_MAX_"}; + name += queue_name; + name += "_QUEUE_SIZE"; + const char* env = getenv(name.c_str()); + if (env) { + auto value = std::atoi(env); + return value == 0 ? default_value : value; + } + return default_value; + } + } // namespace config } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/delta.hpp b/third_party/libosmium/include/osmium/util/delta.hpp index 34c4eb22821..8894dd73323 100644 --- a/third_party/libosmium/include/osmium/util/delta.hpp +++ b/third_party/libosmium/include/osmium/util/delta.hpp @@ -33,7 +33,7 @@ DEALINGS IN THE SOFTWARE. */ -#include +#include #include #include @@ -118,55 +118,6 @@ namespace osmium { }; // class DeltaDecode - template - class DeltaEncodeIterator : public std::iterator { - - TBaseIterator m_it; - TBaseIterator m_end; - TTransform m_trans; - DeltaEncode m_value; - TDelta m_delta; - - public: - - using value_type = TValue; - using delta_type = TDelta; - - DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) : - m_it(first), - m_end(last), - m_trans(trans), - m_value(m_it != m_end ? m_trans(m_it) : 0), - m_delta(static_cast_with_assert(m_value.value())) { - } - - DeltaEncodeIterator& operator++() { - if (++m_it != m_end) { - m_delta = m_value.update(m_trans(m_it)); - } - return *this; - } - - DeltaEncodeIterator operator++(int) { - DeltaEncodeIterator tmp(*this); - operator++(); - return tmp; - } - - TDelta operator*() { - return m_delta; - } - - bool operator==(const DeltaEncodeIterator& rhs) const { - return m_it == rhs.m_it && m_end == rhs.m_end; - } - - bool operator!=(const DeltaEncodeIterator& rhs) const { - return !(*this == rhs); - } - - }; // class DeltaEncodeIterator - } // namespace util } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/double.hpp b/third_party/libosmium/include/osmium/util/double.hpp index 1352c5fb1b5..9714bf63420 100644 --- a/third_party/libosmium/include/osmium/util/double.hpp +++ b/third_party/libosmium/include/osmium/util/double.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -44,7 +43,7 @@ namespace osmium { namespace util { - constexpr int max_double_length = 20; // should fit any double + constexpr const int max_double_length = 20; // should fit any double /** * Write double to iterator, removing superfluous '0' characters at diff --git a/third_party/libosmium/include/osmium/util/file.hpp b/third_party/libosmium/include/osmium/util/file.hpp index 86b93ff722d..4c951e7e658 100644 --- a/third_party/libosmium/include/osmium/util/file.hpp +++ b/third_party/libosmium/include/osmium/util/file.hpp @@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -47,9 +48,6 @@ DEALINGS IN THE SOFTWARE. #ifndef _MSC_VER # include -#else -// https://msdn.microsoft.com/en-us/library/whx354w1.aspx -# define ftruncate _chsize_s #endif #include @@ -70,7 +68,7 @@ namespace osmium { #ifdef _MSC_VER // Windows implementation // https://msdn.microsoft.com/en-us/library/dfbc2kec.aspx - auto size = ::_filelengthi64(fd); + const auto size = ::_filelengthi64(fd); if (size == -1L) { throw std::system_error(errno, std::system_category(), "_filelengthi64 failed"); } @@ -85,6 +83,44 @@ namespace osmium { #endif } + /** + * Get file size. + * This is a small wrapper around a system call. + * + * @param name File name + * @returns file size + * @throws std::system_error If system call failed + */ + inline 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"); + } +#else + // Unix implementation + struct stat s; + if (::stat(name, &s) != 0) { + throw std::system_error(errno, std::system_category(), "stat failed"); + } +#endif + return size_t(s.st_size); + } + + /** + * Get file size. + * This is a small wrapper around a system call. + * + * @param name File name + * @returns file size + * @throws std::system_error If system call failed + */ + inline size_t file_size(const std::string& name) { + return file_size(name.c_str()); + } + /** * Resize file. * Small wrapper around ftruncate(2) system call. @@ -94,8 +130,13 @@ namespace osmium { * @throws std::system_error If ftruncate(2) call failed */ inline void resize_file(int fd, size_t new_size) { +#ifdef _WIN32 + // 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) { - throw std::system_error(errno, std::system_category(), "ftruncate failed"); +#endif + throw std::system_error(errno, std::system_category(), "resizing file failed"); } } @@ -114,6 +155,37 @@ namespace osmium { #endif } + /** + * Get current offset into file. + * + * @param fd Open file descriptor. + * @returns File offset or 0 if it is not available. + */ + inline size_t file_offset(int fd) { +#ifdef _MSC_VER + // https://msdn.microsoft.com/en-us/library/1yee101t.aspx + auto offset = _lseeki64(fd, 0, SEEK_CUR); +#else + auto offset = ::lseek(fd, 0, SEEK_CUR); +#endif + if (offset == -1) { + return 0; + } + return size_t(offset); + } + + /** + * Check whether the file descriptor refers to a TTY. + */ + inline bool isatty(int fd) { +#ifdef _MSC_VER + // https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx + return _isatty(fd) != 0; +#else + return ::isatty(fd) != 0; +#endif + } + } // namespace util } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/iterator.hpp b/third_party/libosmium/include/osmium/util/iterator.hpp index 4cef51950ce..42d23e830fb 100644 --- a/third_party/libosmium/include/osmium/util/iterator.hpp +++ b/third_party/libosmium/include/osmium/util/iterator.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include namespace osmium { @@ -43,10 +44,10 @@ namespace osmium { using iterator = It; - iterator_range(P&& p) : + explicit iterator_range(P&& p) : P(std::forward

(p)) { } - +/* It begin() { return this->first; } @@ -54,7 +55,7 @@ namespace osmium { It end() { return this->second; } - +*/ It begin() const { return this->first; } @@ -67,7 +68,16 @@ namespace osmium { return begin() == end(); } - }; + }; // struct iterator_range + + /** + * Helper function to create iterator_range from std::pair. + */ + template + inline iterator_range make_range(P&& p) { + static_assert(std::is_same>::value, "make_range needs pair of iterators as argument"); + 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 67e944e7061..4fc8f019ebb 100644 --- a/third_party/libosmium/include/osmium/util/memory_mapping.hpp +++ b/third_party/libosmium/include/osmium/util/memory_mapping.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include @@ -126,22 +127,18 @@ namespace osmium { void make_invalid() noexcept; #ifdef _WIN32 - typedef DWORD flag_type; + using flag_type = DWORD; #else - typedef int flag_type; + using flag_type = int; #endif flag_type get_protection() const noexcept; flag_type get_flags() const noexcept; - // A zero-sized mapping is not allowed by the operating system. - // So if the user asks for a mapping of size 0, we map a full - // page instead. This way we don't have a special case in the rest - // of the code. - static size_t initial_size(size_t size) { + static size_t check_size(size_t size) { if (size == 0) { - return osmium::util::get_pagesize(); + throw std::runtime_error("Zero-sized mapping is not allowed."); } return size; } @@ -218,7 +215,7 @@ namespace osmium { ~MemoryMapping() noexcept { try { unmap(); - } catch (std::system_error&) { + } catch (const std::system_error&) { // Ignore any exceptions because destructor must not throw. } } @@ -306,7 +303,7 @@ namespace osmium { public: - AnonymousMemoryMapping(size_t size) : + explicit AnonymousMemoryMapping(size_t size) : MemoryMapping(size, mapping_mode::write_private) { } @@ -342,7 +339,7 @@ namespace osmium { * @param size Number of objects of type T to be mapped * @throws std::system_error if the mapping fails */ - TypedMemoryMapping(size_t size) : + explicit TypedMemoryMapping(size_t size) : m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) { } @@ -491,7 +488,7 @@ namespace osmium { public: - AnonymousTypedMemoryMapping(size_t size) : + explicit AnonymousTypedMemoryMapping(size_t size) : TypedMemoryMapping(size) { } @@ -550,7 +547,7 @@ inline int osmium::util::MemoryMapping::get_flags() const noexcept { } inline osmium::util::MemoryMapping::MemoryMapping(size_t size, mapping_mode mode, int fd, off_t offset) : - m_size(initial_size(size)), + m_size(check_size(size)), m_offset(offset), m_fd(resize_fd(fd)), m_mapping_mode(mode), @@ -689,7 +686,7 @@ inline void osmium::util::MemoryMapping::make_invalid() noexcept { } inline osmium::util::MemoryMapping::MemoryMapping(size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) : - m_size(initial_size(size)), + m_size(check_size(size)), m_offset(offset), m_fd(resize_fd(fd)), m_mapping_mode(mode), diff --git a/third_party/libosmium/include/osmium/util/misc.hpp b/third_party/libosmium/include/osmium/util/misc.hpp new file mode 100644 index 00000000000..8acecb560e1 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/misc.hpp @@ -0,0 +1,52 @@ +#ifndef OSMIUM_UTIL_MISC_HPP +#define OSMIUM_UTIL_MISC_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +namespace osmium { + + /** + * Like std::tie(), but takes its arguments as const references. Used + * as a helper function when sorting. + */ + template + inline std::tuple + const_tie(const Ts&... args) noexcept { + return std::tuple(args...); + } + +} // namespace osmium + +#endif // OSMIUM_UTIL_MISC_HPP diff --git a/third_party/libosmium/include/osmium/util/options.hpp b/third_party/libosmium/include/osmium/util/options.hpp index 9b00b481123..79818a96ab4 100644 --- a/third_party/libosmium/include/osmium/util/options.hpp +++ b/third_party/libosmium/include/osmium/util/options.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -107,7 +108,7 @@ namespace osmium { * be set to "true". */ void set(std::string data) { - size_t pos = data.find_first_of('='); + const size_t pos = data.find_first_of('='); if (pos == std::string::npos) { m_options[data] = "true"; } else { @@ -122,7 +123,7 @@ namespace osmium { * empty string) is returned. */ std::string get(const std::string& key, const std::string& default_value="") const noexcept { - auto it = m_options.find(key); + const auto it = m_options.find(key); if (it == m_options.end()) { return default_value; } @@ -134,7 +135,7 @@ namespace osmium { * Will return false if the value is unset. */ bool is_true(const std::string& key) const noexcept { - std::string value = get(key); + const std::string value = get(key); return (value == "true" || value == "yes"); } @@ -143,7 +144,7 @@ namespace osmium { * Will return true if the value is unset. */ bool is_not_false(const std::string& key) const noexcept { - std::string value = get(key); + const std::string value = get(key); return !(value == "false" || value == "no"); } diff --git a/third_party/libosmium/include/osmium/util/progress_bar.hpp b/third_party/libosmium/include/osmium/util/progress_bar.hpp new file mode 100644 index 00000000000..814aa2c0e2b --- /dev/null +++ b/third_party/libosmium/include/osmium/util/progress_bar.hpp @@ -0,0 +1,179 @@ +#ifndef OSMIUM_UTIL_PROGRESS_BAR_HPP +#define OSMIUM_UTIL_PROGRESS_BAR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +namespace osmium { + + /** + * Displays a progress bar on STDERR. Can be used together with the + * osmium::io::Reader class for instance. + */ + class ProgressBar { + + static const char* bar() noexcept { + return "======================================================================"; + } + + static const char* spc() noexcept { + return " "; + } + + static constexpr const 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; + + // The sum of the file sizes already done. + size_t m_done_size = 0; + + // The currently read size in the current file. + 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; + + // Is the progress bar enabled at all? + bool m_enable; + + // Used to make sure we do cleanup in the destructor if it was not + // already done. + bool m_do_cleanup = true; + + void display() { + const 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)); + std::cerr << '['; + if (num >= length) { + std::cerr << bar(); + } else { + std::cerr << (bar() + length - num) << '>' << (spc() + num); + } + std::cerr << "] "; + if (percent < 10) { + std::cerr << ' '; + } + if (percent < 100) { + std::cerr << ' '; + } + std::cerr << percent << "% \r"; + } + + public: + + /** + * Initializes the progress bar. No output yet. + * + * @param max_size Max size equivalent to 100%. + * @param enable Set to false to disable (for instance if stderr is + * not a TTY). + */ + ProgressBar(size_t max_size, bool enable) noexcept : + m_max_size(max_size), + m_enable(max_size > 0 && enable) { + } + + ~ProgressBar() { + if (m_do_cleanup) { + try { + done(); + } catch (...) { + // Swallow any exceptions, because a destructor should + // not throw. + } + } + } + + /** + * Call this function to update the progress bar. Actual update will + * only happen if the percentage changed from the last time this + * function was called. + * + * @param current_size Current size. Used together with the max_size + * from constructor to calculate the percentage. + */ + void update(size_t current_size) { + if (!m_enable) { + return; + } + + m_current_size = current_size; + + display(); + } + + /** + * If you are reading multiple files, call this function after each + * file is finished. + * + * @param file_size The size of the file just finished. + */ + void file_done(size_t file_size) { + if (m_enable) { + m_done_size += file_size; + m_current_size = 0; + display(); + } + } + + /** + * Call this at the end. Will update the progress bar to 100% and + * print a final line feed. If this is not called explicitly the + * destructor will also call this. + */ + void done() { + m_do_cleanup = false; + if (m_enable) { + m_done_size = m_max_size; + m_current_size = 0; + display(); + std::cerr << '\n'; + } + } + + }; // class ProgressBar + +} // namespace osmium + +#endif // OSMIUM_UTIL_PROGRESS_BAR_HPP diff --git a/third_party/libosmium/include/osmium/util/string.hpp b/third_party/libosmium/include/osmium/util/string.hpp index 1198288c0fc..2cdb9830e6b 100644 --- a/third_party/libosmium/include/osmium/util/string.hpp +++ b/third_party/libosmium/include/osmium/util/string.hpp @@ -33,9 +33,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include -#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/util/timer.hpp b/third_party/libosmium/include/osmium/util/timer.hpp new file mode 100644 index 00000000000..8cae80addd2 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/timer.hpp @@ -0,0 +1,98 @@ +#ifndef OSMIUM_UTIL_TIMER_HPP +#define OSMIUM_UTIL_TIMER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +#ifdef OSMIUM_WITH_TIMER + +#include + +namespace osmium { + + class Timer { + + using clock = std::chrono::high_resolution_clock; + std::chrono::time_point m_start; + std::chrono::time_point m_stop; + + public: + + Timer() : + m_start(clock::now()) { + } + + void start() { + m_start = clock::now(); + } + + void stop() { + m_stop = clock::now(); + } + + int64_t elapsed_microseconds() const { + return std::chrono::duration_cast(m_stop - m_start).count(); + } + + }; + +} // namespace osmium + +#else + +namespace osmium { + + class Timer { + + public: + + Timer() = default; + + void start() { + } + + void stop() { + } + + int64_t elapsed_microseconds() const { + return 0; + } + + }; + +} // namespace osmium + +#endif + +#endif // OSMIUM_UTIL_TIMER_HPP diff --git a/third_party/libosmium/include/osmium/util/verbose_output.hpp b/third_party/libosmium/include/osmium/util/verbose_output.hpp index c7677a4367a..f85d2654ef7 100644 --- a/third_party/libosmium/include/osmium/util/verbose_output.hpp +++ b/third_party/libosmium/include/osmium/util/verbose_output.hpp @@ -33,11 +33,11 @@ DEALINGS IN THE SOFTWARE. */ -#include - +#include #include #include #include +#include namespace osmium { @@ -75,9 +75,9 @@ namespace osmium { */ void start_line() { if (m_newline) { - time_t elapsed = runtime(); + const time_t elapsed = runtime(); - char old_fill = std::cerr.fill(); + const char old_fill = std::cerr.fill(); std::cerr << '[' << std::setw(2) << (elapsed / 60) << ':' << std::setw(2) << std::setfill('0') << (elapsed % 60) << "] "; std::cerr.fill(old_fill); diff --git a/third_party/libosmium/include/osmium/version.hpp b/third_party/libosmium/include/osmium/version.hpp new file mode 100644 index 00000000000..6f3b0a368dc --- /dev/null +++ b/third_party/libosmium/include/osmium/version.hpp @@ -0,0 +1,42 @@ +#ifndef OSMIUM_VERSION_HPP +#define OSMIUM_VERSION_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#define LIBOSMIUM_VERSION_MAJOR 2 +#define LIBOSMIUM_VERSION_MINOR 9 +#define LIBOSMIUM_VERSION_PATCH 0 + +#define LIBOSMIUM_VERSION_STRING "2.9.0" + +#endif // OSMIUM_VERSION_HPP diff --git a/third_party/libosmium/include/protozero/byteswap.hpp b/third_party/libosmium/include/protozero/byteswap.hpp index a018c1c1795..06ba6ded9a3 100644 --- a/third_party/libosmium/include/protozero/byteswap.hpp +++ b/third_party/libosmium/include/protozero/byteswap.hpp @@ -28,7 +28,7 @@ namespace protozero { * be specialized to actually work. */ template -inline void byteswap(const char* /*data*/, char* /*result*/) { +inline void byteswap(const char* /*data*/, char* /*result*/) noexcept { static_assert(N == 1, "Can only swap 4 or 8 byte values"); } @@ -36,7 +36,7 @@ inline void byteswap(const char* /*data*/, char* /*result*/) { * Swap 4 byte value (int32_t, uint32_t, float) between endianness formats. */ template <> -inline void byteswap<4>(const char* data, char* result) { +inline void byteswap<4>(const char* data, char* result) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP *reinterpret_cast(result) = __builtin_bswap32(*reinterpret_cast(data)); #else @@ -51,7 +51,7 @@ inline void byteswap<4>(const char* data, char* result) { * Swap 8 byte value (int64_t, uint64_t, double) between endianness formats. */ template <> -inline void byteswap<8>(const char* data, char* result) { +inline void byteswap<8>(const char* data, char* result) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP *reinterpret_cast(result) = __builtin_bswap64(*reinterpret_cast(data)); #else diff --git a/third_party/libosmium/include/protozero/exception.hpp b/third_party/libosmium/include/protozero/exception.hpp index 5c7ab547815..ca4340e90d6 100644 --- a/third_party/libosmium/include/protozero/exception.hpp +++ b/third_party/libosmium/include/protozero/exception.hpp @@ -29,7 +29,7 @@ namespace protozero { */ struct exception : std::exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "pbf exception"; } + const char* what() const noexcept override { return "pbf exception"; } }; /** @@ -38,7 +38,7 @@ struct exception : std::exception { */ struct varint_too_long_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "varint too long exception"; } + const char* what() const noexcept override { return "varint too long exception"; } }; /** @@ -47,7 +47,7 @@ struct varint_too_long_exception : exception { */ struct unknown_pbf_wire_type_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "unknown pbf field type exception"; } + const char* what() const noexcept override { return "unknown pbf field type exception"; } }; /** @@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception { */ struct end_of_buffer_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "end of buffer exception"; } + const char* what() const noexcept override { return "end of buffer exception"; } }; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/iterators.hpp b/third_party/libosmium/include/protozero/iterators.hpp new file mode 100644 index 00000000000..00ba9193537 --- /dev/null +++ b/third_party/libosmium/include/protozero/iterators.hpp @@ -0,0 +1,373 @@ +#ifndef PROTOZERO_ITERATORS_HPP +#define PROTOZERO_ITERATORS_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file iterators.hpp + * + * @brief Contains the iterators for access to packed repeated fields. + */ + +#include +#include +#include + +#include +#include + +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN +# include +#endif + +namespace protozero { + +namespace detail { + + // Copy N bytes from src to dest on little endian machines, on big + // endian swap the bytes in the process. + template + inline void copy_or_byteswap(const char* src, void* dest) noexcept { +#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN + std::memcpy(dest, src, N); +#else + byteswap(src, reinterpret_cast(dest)); +#endif + } + +} // end namespace detail + +/** + * A range of iterators based on std::pair. Created from beginning and + * end iterators. Used as a return type from some pbf_reader methods + * that is easy to use with range-based for loops. + */ +template > +class iterator_range : +#ifdef PROTOZERO_STRICT_API + protected +#else + public +#endif + P { + +public: + + /// The type of the iterators in this range. + using iterator = T; + + /// The value type of the underlying iterator. + using value_type = typename std::iterator_traits::value_type; + + /** + * Default constructor. Create empty iterator_range. + */ + constexpr iterator_range() : + P(iterator{}, iterator{}) { + } + + /** + * Create iterator range from two iterators. + * + * @param first_iterator Iterator to beginning or range. + * @param last_iterator Iterator to end or range. + */ + constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) : + P(std::forward(first_iterator), + std::forward(last_iterator)) { + } + + /// Return iterator to beginning of range. + constexpr iterator begin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator end() const noexcept { + return this->second; + } + + /// Return iterator to beginning of range. + constexpr iterator cbegin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator cend() const noexcept { + return this->second; + } + + /// Return true if this range is empty. + constexpr std::size_t empty() const noexcept { + return begin() == end(); + } + + /** + * Get element at the beginning of the range. + * + * @pre Range must not be empty. + */ + value_type front() const { + protozero_assert(!empty()); + return *(this->first); + } + + /** + * Advance beginning of range by one. + * + * @pre Range must not be empty. + */ + void drop_front() { + protozero_assert(!empty()); + ++this->first; + } + + /** + * Swap the contents of this range with the other. + * + * @param other Other range to swap data with. + */ + void swap(iterator_range& other) noexcept { + using std::swap; + swap(this->first, other.first); + swap(this->second, other.second); + } + +}; // struct iterator_range + +/** + * Swap two iterator_ranges. + * + * @param lhs First range. + * @param rhs Second range. + */ +template +inline void swap(iterator_range& lhs, iterator_range& rhs) noexcept { + lhs.swap(rhs); +} + +#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED + +template +using const_fixed_iterator = const T*; + +/** + * Create iterator_range from char pointers to beginning and end of range. + * + * @param first Beginning of range. + * @param last End of range. + */ +template +inline iterator_range> create_fixed_iterator_range(const char* first, const char* last) { + return iterator_range>{reinterpret_cast(first), + reinterpret_cast(last)}; +} + +#else + +/** + * A forward iterator used for accessing packed repeated fields of fixed + * length (fixed32, sfixed32, float, double). + */ +template +class const_fixed_iterator { + + /// Pointer to current iterator position + const char* m_data; + + /// Pointer to end iterator position + const char* m_end; + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_fixed_iterator() noexcept : + m_data(nullptr), + m_end(nullptr) { + } + + const_fixed_iterator(const char* data, const char* end) noexcept : + m_data(data), + m_end(end) { + } + + const_fixed_iterator(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator(const_fixed_iterator&&) noexcept = default; + + const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; + + ~const_fixed_iterator() noexcept = default; + + value_type operator*() const { + value_type result; + detail::copy_or_byteswap(m_data , &result); + return result; + } + + const_fixed_iterator& operator++() { + m_data += sizeof(value_type); + return *this; + } + + const_fixed_iterator operator++(int) { + const const_fixed_iterator tmp(*this); + ++(*this); + return tmp; + } + + bool operator==(const const_fixed_iterator& rhs) const noexcept { + return m_data == rhs.m_data && m_end == rhs.m_end; + } + + bool operator!=(const const_fixed_iterator& rhs) const noexcept { + return !(*this == rhs); + } + +}; // class const_fixed_iterator + +/** + * Create iterator_range from char pointers to beginning and end of range. + * + * @param first Beginning of range. + * @param last End of range. + */ +template +inline iterator_range> create_fixed_iterator_range(const char* first, const char* last) { + return iterator_range>{const_fixed_iterator(first, last), + const_fixed_iterator(last, last)}; +} + +#endif + +/** + * A forward iterator used for accessing packed repeated varint fields + * (int32, uint32, int64, uint64, bool, enum). + */ +template +class const_varint_iterator { + +protected: + + /// Pointer to current iterator position + const char* m_data; + + /// Pointer to end iterator position + const char* m_end; + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_varint_iterator() noexcept : + m_data(nullptr), + m_end(nullptr) { + } + + const_varint_iterator(const char* data, const char* end) noexcept : + m_data(data), + m_end(end) { + } + + const_varint_iterator(const const_varint_iterator&) noexcept = default; + const_varint_iterator(const_varint_iterator&&) noexcept = default; + + const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; + const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; + + ~const_varint_iterator() noexcept = default; + + value_type operator*() const { + const char* d = m_data; // will be thrown away + return static_cast(decode_varint(&d, m_end)); + } + + const_varint_iterator& operator++() { + skip_varint(&m_data, m_end); + return *this; + } + + const_varint_iterator operator++(int) { + const const_varint_iterator tmp(*this); + ++(*this); + return tmp; + } + + bool operator==(const const_varint_iterator& rhs) const noexcept { + return m_data == rhs.m_data && m_end == rhs.m_end; + } + + bool operator!=(const const_varint_iterator& rhs) const noexcept { + return !(*this == rhs); + } + +}; // class const_varint_iterator + +/** + * A forward iterator used for accessing packed repeated svarint fields + * (sint32, sint64). + */ +template +class const_svarint_iterator : public const_varint_iterator { + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_svarint_iterator() noexcept : + const_varint_iterator() { + } + + const_svarint_iterator(const char* data, const char* end) noexcept : + const_varint_iterator(data, end) { + } + + const_svarint_iterator(const const_svarint_iterator&) = default; + const_svarint_iterator(const_svarint_iterator&&) = default; + + const_svarint_iterator& operator=(const const_svarint_iterator&) = default; + const_svarint_iterator& operator=(const_svarint_iterator&&) = default; + + ~const_svarint_iterator() = default; + + value_type operator*() const { + const char* d = this->m_data; // will be thrown away + return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); + } + + const_svarint_iterator& operator++() { + skip_varint(&this->m_data, this->m_end); + return *this; + } + + const_svarint_iterator operator++(int) { + const const_svarint_iterator tmp(*this); + ++(*this); + return tmp; + } + +}; // class const_svarint_iterator + +} // end namespace protozero + +#endif // PROTOZERO_ITERATORS_HPP diff --git a/third_party/libosmium/include/protozero/pbf_builder.hpp b/third_party/libosmium/include/protozero/pbf_builder.hpp index 548f4cecb89..39af53f3f26 100644 --- a/third_party/libosmium/include/protozero/pbf_builder.hpp +++ b/third_party/libosmium/include/protozero/pbf_builder.hpp @@ -57,7 +57,7 @@ class pbf_builder : public pbf_writer { /// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ - inline void add_##name(T tag, type value) { \ + void add_##name(T tag, type value) { \ pbf_writer::add_##name(pbf_tag_type(tag), value); \ } @@ -79,38 +79,38 @@ class pbf_builder : public pbf_writer { #undef PROTOZERO_WRITER_WRAP_ADD_SCALAR /// @endcond - inline void add_bytes(T tag, const char* value, std::size_t size) { + void add_bytes(T tag, const char* value, std::size_t size) { pbf_writer::add_bytes(pbf_tag_type(tag), value, size); } - inline void add_bytes(T tag, const std::string& value) { + void add_bytes(T tag, const std::string& value) { pbf_writer::add_bytes(pbf_tag_type(tag), value); } - inline void add_string(T tag, const char* value, std::size_t size) { + void add_string(T tag, const char* value, std::size_t size) { pbf_writer::add_string(pbf_tag_type(tag), value, size); } - inline void add_string(T tag, const std::string& value) { + void add_string(T tag, const std::string& value) { pbf_writer::add_string(pbf_tag_type(tag), value); } - inline void add_string(T tag, const char* value) { + void add_string(T tag, const char* value) { pbf_writer::add_string(pbf_tag_type(tag), value); } - inline void add_message(T tag, const char* value, std::size_t size) { + void add_message(T tag, const char* value, std::size_t size) { pbf_writer::add_message(pbf_tag_type(tag), value, size); } - inline void add_message(T tag, const std::string& value) { + void add_message(T tag, const std::string& value) { pbf_writer::add_message(pbf_tag_type(tag), value); } /// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ template \ - inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ + void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ } @@ -132,7 +132,7 @@ class pbf_builder : public pbf_writer { #undef PROTOZERO_WRITER_WRAP_ADD_PACKED /// @endcond -}; +}; // class pbf_builder } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_message.hpp b/third_party/libosmium/include/protozero/pbf_message.hpp index 45f01c149a3..05577340812 100644 --- a/third_party/libosmium/include/protozero/pbf_message.hpp +++ b/third_party/libosmium/include/protozero/pbf_message.hpp @@ -13,7 +13,7 @@ documentation. /** * @file pbf_message.hpp * - * @brief Contains the pbf_message class. + * @brief Contains the pbf_message template class. */ #include @@ -75,19 +75,19 @@ class pbf_message : public pbf_reader { pbf_reader(std::forward(args)...) { } - inline bool next() { + bool next() { return pbf_reader::next(); } - inline bool next(T tag) { + bool next(T tag) { return pbf_reader::next(pbf_tag_type(tag)); } - inline T tag() const noexcept { + T tag() const noexcept { return T(pbf_reader::tag()); } -}; +}; // class pbf_message } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_reader.hpp b/third_party/libosmium/include/protozero/pbf_reader.hpp index 58b3884550d..2f4054d2d25 100644 --- a/third_party/libosmium/include/protozero/pbf_reader.hpp +++ b/third_party/libosmium/include/protozero/pbf_reader.hpp @@ -18,13 +18,12 @@ documentation. #include #include -#include -#include #include #include #include #include +#include #include #include @@ -55,16 +54,16 @@ namespace protozero { * * All methods of the pbf_reader class except get_bytes() and get_string() * provide the strong exception guarantee, ie they either succeed or do not - * change the pbf_reader object they are called on. Use the get_data() method + * change the pbf_reader object they are called on. Use the get_view() method * instead of get_bytes() or get_string(), if you need this guarantee. */ class pbf_reader { // A pointer to the next unread data. - const char *m_data = nullptr; + const char* m_data = nullptr; // A pointer to one past the end of data. - const char *m_end = nullptr; + const char* m_end = nullptr; // The wire type of the current field. pbf_wire_type m_wire_type = pbf_wire_type::unknown; @@ -72,118 +71,83 @@ class pbf_reader { // The tag of the current field. pbf_tag_type m_tag = 0; - // Copy N bytes from src to dest on little endian machines, on big endian - // swap the bytes in the process. - template - static void copy_or_byteswap(const char* src, void* dest) noexcept { -#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN - memcpy(dest, src, N); -#else - byteswap(src, reinterpret_cast(dest)); -#endif - } - template - inline T get_fixed() { + T get_fixed() { T result; skip_bytes(sizeof(T)); - copy_or_byteswap(m_data - sizeof(T), &result); + detail::copy_or_byteswap(m_data - sizeof(T), &result); return result; } -#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED - template - using const_fixed_iterator = const T*; + iterator_range> packed_fixed() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + const auto len = get_len_and_skip(); + protozero_assert(len % sizeof(T) == 0); + return create_fixed_iterator_range(m_data - len, m_data); + } template - inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { - return std::make_pair(reinterpret_cast(first), - reinterpret_cast(last)); + T get_varint() { + return static_cast(decode_varint(&m_data, m_end)); } -#else - template - class const_fixed_iterator : public std::iterator { - - const char* m_data; - const char* m_end; - - public: - - const_fixed_iterator() noexcept : - m_data(nullptr), - m_end(nullptr) { - } - - const_fixed_iterator(const char *data, const char* end) noexcept : - m_data(data), - m_end(end) { - } - - const_fixed_iterator(const const_fixed_iterator&) noexcept = default; - const_fixed_iterator(const_fixed_iterator&&) noexcept = default; - - const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; - const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; - - ~const_fixed_iterator() noexcept = default; - - T operator*() { - T result; - copy_or_byteswap(m_data , &result); - return result; - } - - const_fixed_iterator& operator++() { - m_data += sizeof(T); - return *this; - } - - const_fixed_iterator operator++(int) { - const const_fixed_iterator tmp(*this); - ++(*this); - return tmp; - } + T get_svarint() { + protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint"); + return static_cast(decode_zigzag64(decode_varint(&m_data, m_end))); + } - bool operator==(const const_fixed_iterator& rhs) const noexcept { - return m_data == rhs.m_data && m_end == rhs.m_end; - } + pbf_length_type get_length() { + return get_varint(); + } - bool operator!=(const const_fixed_iterator& rhs) const noexcept { - return !(*this == rhs); + void skip_bytes(pbf_length_type len) { + if (m_data + len > m_end) { + throw end_of_buffer_exception(); } + m_data += len; - }; // class const_fixed_iterator - - template - inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { - return std::make_pair(const_fixed_iterator(first, last), - const_fixed_iterator(last, last)); + // In debug builds reset the tag to zero so that we can detect (some) + // wrong code. +#ifndef NDEBUG + m_tag = 0; +#endif } -#endif + pbf_length_type get_len_and_skip() { + const auto len = get_length(); + skip_bytes(len); + return len; + } template - inline std::pair, const_fixed_iterator> packed_fixed() { + iterator_range get_packed() { protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - protozero_assert(len % sizeof(T) == 0); - return create_fixed_iterator_pair(m_data-len, m_data); + const auto len = get_len_and_skip(); + return iterator_range{T{m_data - len, m_data}, + T{m_data, m_data}}; } - template inline T get_varint(); - template inline T get_svarint(); - - inline pbf_length_type get_length() { return get_varint(); } - - inline void skip_bytes(pbf_length_type len); - - inline pbf_length_type get_len_and_skip(); - public: + /** + * Construct a pbf_reader message from a data_view. The pointer from the + * data_view will be stored inside the pbf_reader object, no data is + * copied. So you must* make sure the view stays valid as long as the + * pbf_reader object is used. + * + * The buffer must contain a complete protobuf message. + * + * @post There is no current field. + */ + explicit pbf_reader(const data_view& view) noexcept + : m_data(view.data()), + m_end(view.data() + view.size()), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } + /** * Construct a pbf_reader message from a data pointer and a length. The pointer * will be stored inside the pbf_reader object, no data is copied. So you must @@ -193,7 +157,12 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(const char *data, std::size_t length) noexcept; + pbf_reader(const char* data, std::size_t length) noexcept + : m_data(data), + m_end(data + length), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * Construct a pbf_reader message from a data pointer and a length. The pointer @@ -204,7 +173,12 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(std::pair data) noexcept; + pbf_reader(std::pair data) noexcept + : m_data(data.first), + m_end(data.first + data.second), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * Construct a pbf_reader message from a std::string. A pointer to the string @@ -216,33 +190,53 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(const std::string& data) noexcept; + pbf_reader(const std::string& data) noexcept + : m_data(data.data()), + m_end(data.data() + data.size()), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * pbf_reader can be default constructed and behaves like it has an empty * buffer. */ - inline pbf_reader() noexcept = default; + pbf_reader() noexcept = default; /// pbf_reader messages can be copied trivially. - inline pbf_reader(const pbf_reader&) noexcept = default; + pbf_reader(const pbf_reader&) noexcept = default; /// pbf_reader messages can be moved trivially. - inline pbf_reader(pbf_reader&&) noexcept = default; + pbf_reader(pbf_reader&&) noexcept = default; /// pbf_reader messages can be copied trivially. - inline pbf_reader& operator=(const pbf_reader& other) noexcept = default; + pbf_reader& operator=(const pbf_reader& other) noexcept = default; /// pbf_reader messages can be moved trivially. - inline pbf_reader& operator=(pbf_reader&& other) noexcept = default; + pbf_reader& operator=(pbf_reader&& other) noexcept = default; + + ~pbf_reader() = default; - inline ~pbf_reader() = default; + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(pbf_reader& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_end, other.m_end); + swap(m_wire_type, other.m_wire_type); + swap(m_tag, other.m_tag); + } /** * In a boolean context the pbf_reader class evaluates to `true` if there are * still fields available and to `false` if the last field has been read. */ - inline operator bool() const noexcept; + operator bool() const noexcept { + return m_data < m_end; + } /** * Return the length in bytes of the current message. If you have @@ -272,7 +266,31 @@ class pbf_reader { * @pre There must be no current field. * @post If it returns `true` there is a current field now. */ - inline bool next(); + bool next() { + if (m_data == m_end) { + return false; + } + + const auto value = get_varint(); + m_tag = pbf_tag_type(value >> 3); + + // tags 0 and 19000 to 19999 are not allowed as per + // https://developers.google.com/protocol-buffers/docs/proto + protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range"); + + m_wire_type = pbf_wire_type(value & 0x07); + switch (m_wire_type) { + case pbf_wire_type::varint: + case pbf_wire_type::fixed64: + case pbf_wire_type::length_delimited: + case pbf_wire_type::fixed32: + break; + default: + throw unknown_pbf_wire_type_exception(); + } + + return true; + } /** * Set next field with given tag in the message as the current field. @@ -299,7 +317,16 @@ class pbf_reader { * @pre There must be no current field. * @post If it returns `true` there is a current field now with the given tag. */ - inline bool next(pbf_tag_type tag); + bool next(pbf_tag_type tag) { + while (next()) { + if (m_tag == tag) { + return true; + } else { + skip(); + } + } + return false; + } /** * The tag of the current field. The tag is the field number from the @@ -310,7 +337,9 @@ class pbf_reader { * @returns tag of the current field. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline pbf_tag_type tag() const noexcept; + pbf_tag_type tag() const noexcept { + return m_tag; + } /** * Get the wire type of the current field. The wire types are: @@ -327,7 +356,9 @@ class pbf_reader { * @returns wire type of the current field. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline pbf_wire_type wire_type() const noexcept; + pbf_wire_type wire_type() const noexcept { + return m_wire_type; + } /** * Check the wire type of the current field. @@ -335,7 +366,9 @@ class pbf_reader { * @returns `true` if the current field has the given wire type. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline bool has_wire_type(pbf_wire_type type) const noexcept; + bool has_wire_type(pbf_wire_type type) const noexcept { + return wire_type() == type; + } /** * Consume the current field. @@ -343,7 +376,25 @@ class pbf_reader { * @pre There must be a current field (ie. next() must have returned `true`). * @post The current field was consumed and there is no current field now. */ - inline void skip(); + void skip() { + protozero_assert(tag() != 0 && "call next() before calling skip()"); + switch (wire_type()) { + case pbf_wire_type::varint: + skip_varint(&m_data, m_end); + break; + case pbf_wire_type::fixed64: + skip_bytes(8); + break; + case pbf_wire_type::length_delimited: + skip_bytes(get_length()); + break; + case pbf_wire_type::fixed32: + skip_bytes(4); + break; + default: + protozero_assert(false && "can not be here because next() should have thrown already"); + } + } ///@{ /** @@ -357,7 +408,13 @@ class pbf_reader { * @pre The current field must be of type "bool". * @post The current field was consumed and there is no current field now. */ - inline bool get_bool(); + bool get_bool() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint"); + skip_bytes(1); + return m_data[-1] != 0; // -1 okay because we incremented m_data the line before + } /** * Consume and return value of current "enum" field. @@ -366,7 +423,7 @@ class pbf_reader { * @pre The current field must be of type "enum". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_enum() { + int32_t get_enum() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -378,7 +435,7 @@ class pbf_reader { * @pre The current field must be of type "int32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_int32() { + int32_t get_int32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -390,7 +447,7 @@ class pbf_reader { * @pre The current field must be of type "sint32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_sint32() { + int32_t get_sint32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_svarint(); } @@ -402,7 +459,7 @@ class pbf_reader { * @pre The current field must be of type "uint32". * @post The current field was consumed and there is no current field now. */ - inline uint32_t get_uint32() { + uint32_t get_uint32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -414,7 +471,7 @@ class pbf_reader { * @pre The current field must be of type "int64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_int64() { + int64_t get_int64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -426,7 +483,7 @@ class pbf_reader { * @pre The current field must be of type "sint64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_sint64() { + int64_t get_sint64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_svarint(); } @@ -438,7 +495,7 @@ class pbf_reader { * @pre The current field must be of type "uint64". * @post The current field was consumed and there is no current field now. */ - inline uint64_t get_uint64() { + uint64_t get_uint64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -450,7 +507,11 @@ class pbf_reader { * @pre The current field must be of type "fixed32". * @post The current field was consumed and there is no current field now. */ - inline uint32_t get_fixed32(); + uint32_t get_fixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "sfixed32" field. @@ -459,7 +520,11 @@ class pbf_reader { * @pre The current field must be of type "sfixed32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_sfixed32(); + int32_t get_sfixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "fixed64" field. @@ -468,7 +533,11 @@ class pbf_reader { * @pre The current field must be of type "fixed64". * @post The current field was consumed and there is no current field now. */ - inline uint64_t get_fixed64(); + uint64_t get_fixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "sfixed64" field. @@ -477,7 +546,11 @@ class pbf_reader { * @pre The current field must be of type "sfixed64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_sfixed64(); + int64_t get_sfixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "float" field. @@ -486,7 +559,11 @@ class pbf_reader { * @pre The current field must be of type "float". * @post The current field was consumed and there is no current field now. */ - inline float get_float(); + float get_float() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "double" field. @@ -495,8 +572,29 @@ class pbf_reader { * @pre The current field must be of type "double". * @post The current field was consumed and there is no current field now. */ - inline double get_double(); + double get_double() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "bytes", "string", or "message" + * field. + * + * @returns A data_view object. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bytes", "string", or "message". + * @post The current field was consumed and there is no current field now. + */ + data_view get_view() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return data_view{m_data-len, len}; + } +#ifndef PROTOZERO_STRICT_API /** * Consume and return value of current "bytes" or "string" field. * @@ -505,7 +603,13 @@ class pbf_reader { * @pre The current field must be of type "bytes" or "string". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_data(); + std::pair get_data() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return std::make_pair(m_data-len, len); + } +#endif /** * Consume and return value of current "bytes" field. @@ -514,7 +618,9 @@ class pbf_reader { * @pre The current field must be of type "bytes". * @post The current field was consumed and there is no current field now. */ - inline std::string get_bytes(); + std::string get_bytes() { + return std::string(get_view()); + } /** * Consume and return value of current "string" field. @@ -523,7 +629,9 @@ class pbf_reader { * @pre The current field must be of type "string". * @post The current field was consumed and there is no current field now. */ - inline std::string get_string(); + std::string get_string() { + return std::string(get_view()); + } /** * Consume and return value of current "message" field. @@ -532,136 +640,35 @@ class pbf_reader { * @pre The current field must be of type "message". * @post The current field was consumed and there is no current field now. */ - inline pbf_reader get_message() { - return pbf_reader(get_data()); + pbf_reader get_message() { + return pbf_reader(get_view()); } ///@} -private: - - template - class const_varint_iterator : public std::iterator { - - protected: - - const char* m_data; - const char* m_end; - - public: - - const_varint_iterator() noexcept : - m_data(nullptr), - m_end(nullptr) { - } - - const_varint_iterator(const char *data, const char* end) noexcept : - m_data(data), - m_end(end) { - } - - const_varint_iterator(const const_varint_iterator&) noexcept = default; - const_varint_iterator(const_varint_iterator&&) noexcept = default; - - const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; - const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; - - ~const_varint_iterator() noexcept = default; - - T operator*() { - const char* d = m_data; // will be thrown away - return static_cast(decode_varint(&d, m_end)); - } - - const_varint_iterator& operator++() { - // Ignore the result, we call decode_varint() just for the - // side-effect of updating m_data. - decode_varint(&m_data, m_end); - return *this; - } - - const_varint_iterator operator++(int) { - const const_varint_iterator tmp(*this); - ++(*this); - return tmp; - } - - bool operator==(const const_varint_iterator& rhs) const noexcept { - return m_data == rhs.m_data && m_end == rhs.m_end; - } - - bool operator!=(const const_varint_iterator& rhs) const noexcept { - return !(*this == rhs); - } - - }; // class const_varint_iterator - - template - class const_svarint_iterator : public const_varint_iterator { - - public: - - const_svarint_iterator() noexcept : - const_varint_iterator() { - } - - const_svarint_iterator(const char *data, const char* end) noexcept : - const_varint_iterator(data, end) { - } - - const_svarint_iterator(const const_svarint_iterator&) = default; - const_svarint_iterator(const_svarint_iterator&&) = default; - - const_svarint_iterator& operator=(const const_svarint_iterator&) = default; - const_svarint_iterator& operator=(const_svarint_iterator&&) = default; - - ~const_svarint_iterator() = default; - - T operator*() { - const char* d = this->m_data; // will be thrown away - return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); - } - - const_svarint_iterator& operator++() { - // Ignore the result, we call decode_varint() just for the - // side-effect of updating m_data. - decode_varint(&this->m_data, this->m_end); - return *this; - } - - const_svarint_iterator operator++(int) { - const const_svarint_iterator tmp(*this); - ++(*this); - return tmp; - } - - }; // class const_svarint_iterator - -public: - /// Forward iterator for iterating over bool (int32 varint) values. - typedef const_varint_iterator< int32_t> const_bool_iterator; + using const_bool_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over enum (int32 varint) values. - typedef const_varint_iterator< int32_t> const_enum_iterator; + using const_enum_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over int32 (varint) values. - typedef const_varint_iterator< int32_t> const_int32_iterator; + using const_int32_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over sint32 (varint) values. - typedef const_svarint_iterator const_sint32_iterator; + using const_sint32_iterator = const_svarint_iterator; /// Forward iterator for iterating over uint32 (varint) values. - typedef const_varint_iterator const_uint32_iterator; + using const_uint32_iterator = const_varint_iterator; /// Forward iterator for iterating over int64 (varint) values. - typedef const_varint_iterator< int64_t> const_int64_iterator; + using const_int64_iterator = const_varint_iterator< int64_t>; /// Forward iterator for iterating over sint64 (varint) values. - typedef const_svarint_iterator const_sint64_iterator; + using const_sint64_iterator = const_svarint_iterator; /// Forward iterator for iterating over uint64 (varint) values. - typedef const_varint_iterator const_uint64_iterator; + using const_uint64_iterator = const_varint_iterator; ///@{ /** @@ -677,7 +684,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed bool". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_bool(); + iterator_range get_packed_bool() { + return get_packed(); + } /** * Consume current "repeated packed enum" field. @@ -688,7 +697,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed enum". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_enum(); + iterator_range get_packed_enum() { + return get_packed(); + } /** * Consume current "repeated packed int32" field. @@ -699,7 +710,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed int32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_int32(); + iterator_range get_packed_int32() { + return get_packed(); + } /** * Consume current "repeated packed sint32" field. @@ -710,7 +723,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sint32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_sint32(); + iterator_range get_packed_sint32() { + return get_packed(); + } /** * Consume current "repeated packed uint32" field. @@ -721,7 +736,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed uint32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_uint32(); + iterator_range get_packed_uint32() { + return get_packed(); + } /** * Consume current "repeated packed int64" field. @@ -732,7 +749,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed int64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_int64(); + iterator_range get_packed_int64() { + return get_packed(); + } /** * Consume current "repeated packed sint64" field. @@ -743,7 +762,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sint64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_sint64(); + iterator_range get_packed_sint64() { + return get_packed(); + } /** * Consume current "repeated packed uint64" field. @@ -754,7 +775,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed uint64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_uint64(); + iterator_range get_packed_uint64() { + return get_packed(); + } /** * Consume current "repeated packed fixed32" field. @@ -765,7 +788,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed fixed32". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_fixed32() -> decltype(packed_fixed()) { + auto get_packed_fixed32() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -778,7 +801,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sfixed32". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_sfixed32() -> decltype(packed_fixed()) { + auto get_packed_sfixed32() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -791,7 +814,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed fixed64". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_fixed64() -> decltype(packed_fixed()) { + auto get_packed_fixed64() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -804,7 +827,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sfixed64". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_sfixed64() -> decltype(packed_fixed()) { + auto get_packed_sfixed64() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -817,7 +840,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed float". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_float() -> decltype(packed_fixed()) { + auto get_packed_float() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -830,7 +853,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed double". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_double() -> decltype(packed_fixed()) { + auto get_packed_double() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -838,238 +861,14 @@ class pbf_reader { }; // class pbf_reader -pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept - : m_data(data), - m_end(data + length), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::pbf_reader(std::pair data) noexcept - : m_data(data.first), - m_end(data.first + data.second), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::pbf_reader(const std::string& data) noexcept - : m_data(data.data()), - m_end(data.data() + data.size()), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::operator bool() const noexcept { - return m_data < m_end; -} - -bool pbf_reader::next() { - if (m_data == m_end) { - return false; - } - - auto value = get_varint(); - m_tag = value >> 3; - - // tags 0 and 19000 to 19999 are not allowed as per - // https://developers.google.com/protocol-buffers/docs/proto - protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range"); - - m_wire_type = pbf_wire_type(value & 0x07); - switch (m_wire_type) { - case pbf_wire_type::varint: - case pbf_wire_type::fixed64: - case pbf_wire_type::length_delimited: - case pbf_wire_type::fixed32: - break; - default: - throw unknown_pbf_wire_type_exception(); - } - - return true; -} - -bool pbf_reader::next(pbf_tag_type requested_tag) { - while (next()) { - if (m_tag == requested_tag) { - return true; - } else { - skip(); - } - } - return false; -} - -pbf_tag_type pbf_reader::tag() const noexcept { - return m_tag; -} - -pbf_wire_type pbf_reader::wire_type() const noexcept { - return m_wire_type; -} - -bool pbf_reader::has_wire_type(pbf_wire_type type) const noexcept { - return wire_type() == type; -} - -void pbf_reader::skip_bytes(pbf_length_type len) { - if (m_data + len > m_end) { - throw end_of_buffer_exception(); - } - m_data += len; - -// In debug builds reset the tag to zero so that we can detect (some) -// wrong code. -#ifndef NDEBUG - m_tag = 0; -#endif -} - -void pbf_reader::skip() { - protozero_assert(tag() != 0 && "call next() before calling skip()"); - switch (wire_type()) { - case pbf_wire_type::varint: - (void)get_uint32(); // called for the side-effect of skipping value - break; - case pbf_wire_type::fixed64: - skip_bytes(8); - break; - case pbf_wire_type::length_delimited: - skip_bytes(get_length()); - break; - case pbf_wire_type::fixed32: - skip_bytes(4); - break; - default: - protozero_assert(false && "can not be here because next() should have thrown already"); - } -} - -pbf_length_type pbf_reader::get_len_and_skip() { - auto len = get_length(); - skip_bytes(len); - return len; -} - -template -T pbf_reader::get_varint() { - return static_cast(decode_varint(&m_data, m_end)); -} - -template -T pbf_reader::get_svarint() { - protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint"); - return static_cast(decode_zigzag64(decode_varint(&m_data, m_end))); -} - -uint32_t pbf_reader::get_fixed32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -int32_t pbf_reader::get_sfixed32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -uint64_t pbf_reader::get_fixed64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -int64_t pbf_reader::get_sfixed64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -float pbf_reader::get_float() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -double pbf_reader::get_double() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -bool pbf_reader::get_bool() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); - protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint"); - skip_bytes(1); - return m_data[-1] != 0; // -1 okay because we incremented m_data the line before -} - -std::pair pbf_reader::get_data() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); - auto len = get_len_and_skip(); - return std::make_pair(m_data-len, len); -} - -std::string pbf_reader::get_bytes() { - auto d = get_data(); - return std::string(d.first, d.second); -} - -std::string pbf_reader::get_string() { - return get_bytes(); -} - -std::pair pbf_reader::get_packed_bool() { - return get_packed_int32(); -} - -std::pair pbf_reader::get_packed_enum() { - return get_packed_int32(); -} - -std::pair pbf_reader::get_packed_int32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_int32_iterator(m_data-len, m_data), - pbf_reader::const_int32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_uint32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_uint32_iterator(m_data-len, m_data), - pbf_reader::const_uint32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_sint32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_sint32_iterator(m_data-len, m_data), - pbf_reader::const_sint32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_int64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_int64_iterator(m_data-len, m_data), - pbf_reader::const_int64_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_uint64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_uint64_iterator(m_data-len, m_data), - pbf_reader::const_uint64_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_sint64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_sint64_iterator(m_data-len, m_data), - pbf_reader::const_sint64_iterator(m_data, m_data)); +/** + * Swap two pbf_reader objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept { + lhs.swap(rhs); } } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_writer.hpp b/third_party/libosmium/include/protozero/pbf_writer.hpp index 422e1477232..bce1c3ffcbf 100644 --- a/third_party/libosmium/include/protozero/pbf_writer.hpp +++ b/third_party/libosmium/include/protozero/pbf_writer.hpp @@ -22,6 +22,7 @@ documentation. #include #include #include +#include #include #include @@ -68,38 +69,38 @@ class pbf_writer { // parent to the position where the data of the submessage is written to. std::size_t m_pos = 0; - inline void add_varint(uint64_t value) { + void add_varint(uint64_t value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); write_varint(std::back_inserter(*m_data), value); } - inline void add_field(pbf_tag_type tag, pbf_wire_type type) { + void add_field(pbf_tag_type tag, pbf_wire_type type) { protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range"); - uint32_t b = (tag << 3) | uint32_t(type); + const uint32_t b = (tag << 3) | uint32_t(type); add_varint(b); } - inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) { + void add_tagged_varint(pbf_tag_type tag, uint64_t value) { add_field(tag, pbf_wire_type::varint); add_varint(value); } template - inline void add_fixed(T value) { + void add_fixed(T value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); #if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN m_data->append(reinterpret_cast(&value), sizeof(T)); #else - auto size = m_data->size(); + const auto size = m_data->size(); m_data->resize(size + sizeof(T)); byteswap(reinterpret_cast(&value), const_cast(m_data->data() + size)); #endif } template - inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) { + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) { if (first == last) { return; } @@ -112,12 +113,12 @@ class pbf_writer { } template - inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) { + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) { if (first == last) { return; } - auto length = std::distance(first, last); + const auto length = std::distance(first, last); add_length_varint(tag, sizeof(T) * pbf_length_type(length)); reserve(sizeof(T) * std::size_t(length)); @@ -127,7 +128,7 @@ class pbf_writer { } template - inline void add_packed_varint(pbf_tag_type tag, It first, It last) { + void add_packed_varint(pbf_tag_type tag, It first, It last) { if (first == last) { return; } @@ -140,7 +141,7 @@ class pbf_writer { } template - inline void add_packed_svarint(pbf_tag_type tag, It first, It last) { + void add_packed_svarint(pbf_tag_type tag, It first, It last) { if (first == last) { return; } @@ -155,14 +156,18 @@ class pbf_writer { // The number of bytes to reserve for the varint holding the length of // a length-delimited field. The length has to fit into pbf_length_type, // and a varint needs 8 bit for every 7 bit. - static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1; + enum constant_reserve_bytes : int { + reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1 + }; // If m_rollpack_pos is set to this special value, it means that when // the submessage is closed, nothing needs to be done, because the length // of the submessage has already been written correctly. - static const std::size_t size_is_known = std::numeric_limits::max(); + enum constant_size_is_known : std::size_t { + size_is_known = std::numeric_limits::max() + }; - inline void open_submessage(pbf_tag_type tag, std::size_t size) { + void open_submessage(pbf_tag_type tag, std::size_t size) { protozero_assert(m_pos == 0); protozero_assert(m_data); if (size == 0) { @@ -177,7 +182,7 @@ class pbf_writer { m_pos = m_data->size(); } - inline void rollback_submessage() { + void rollback_submessage() { protozero_assert(m_pos != 0); protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_data); @@ -185,20 +190,20 @@ class pbf_writer { m_pos = 0; } - inline void commit_submessage() { + void commit_submessage() { protozero_assert(m_pos != 0); protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_data); - auto length = pbf_length_type(m_data->size() - m_pos); + const auto length = pbf_length_type(m_data->size() - m_pos); protozero_assert(m_data->size() >= m_pos - reserve_bytes); - auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length); + const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length); m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos)); m_pos = 0; } - inline void close_submessage() { + void close_submessage() { protozero_assert(m_data); if (m_pos == 0 || m_rollback_pos == size_is_known) { return; @@ -210,7 +215,7 @@ class pbf_writer { } } - inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) { + void add_length_varint(pbf_tag_type tag, pbf_length_type length) { add_field(tag, pbf_wire_type::length_delimited); add_varint(length); } @@ -222,7 +227,7 @@ class pbf_writer { * stores a reference to that string and adds all data to it. The string * doesn't have to be empty. The pbf_writer will just append data. */ - inline explicit pbf_writer(std::string& data) noexcept : + explicit pbf_writer(std::string& data) noexcept : m_data(&data), m_parent_writer(nullptr), m_pos(0) { @@ -232,7 +237,7 @@ class pbf_writer { * Create a writer without a data store. In this form the writer can not * be used! */ - inline pbf_writer() noexcept : + pbf_writer() noexcept : m_data(nullptr), m_parent_writer(nullptr), m_pos(0) { @@ -248,7 +253,7 @@ class pbf_writer { * Setting this allows some optimizations but is only possible in * a few very specific cases. */ - inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : + pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : m_data(parent_writer.m_data), m_parent_writer(&parent_writer), m_pos(0) { @@ -262,17 +267,30 @@ class pbf_writer { pbf_writer& operator=(const pbf_writer&) noexcept = default; /// A pbf_writer object can be moved - inline pbf_writer(pbf_writer&&) noexcept = default; + pbf_writer(pbf_writer&&) noexcept = default; /// A pbf_writer object can be moved - inline pbf_writer& operator=(pbf_writer&&) noexcept = default; + pbf_writer& operator=(pbf_writer&&) noexcept = default; - inline ~pbf_writer() { + ~pbf_writer() { if (m_parent_writer) { m_parent_writer->close_submessage(); } } + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(pbf_writer& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_parent_writer, other.m_parent_writer); + swap(m_rollback_pos, other.m_rollback_pos); + swap(m_pos, other.m_pos); + } + /** * Reserve size bytes in the underlying message store in addition to * whatever the message store already holds. So unlike @@ -286,7 +304,14 @@ class pbf_writer { m_data->reserve(m_data->size() + size); } - inline void rollback() { + /** + * Cancel writing of this submessage. The complete submessage will be + * removed as if it was never created and no fields were added. + * + * @pre Must be a pbf_writer of a submessage, ie one opened with the + * pbf_writer constructor taking a parent message. + */ + void rollback() { protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent"); protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage"); m_parent_writer->rollback_submessage(); @@ -304,7 +329,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_bool(pbf_tag_type tag, bool value) { + void add_bool(pbf_tag_type tag, bool value) { add_field(tag, pbf_wire_type::varint); protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); @@ -317,7 +342,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_enum(pbf_tag_type tag, int32_t value) { + void add_enum(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -327,7 +352,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_int32(pbf_tag_type tag, int32_t value) { + void add_int32(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -337,7 +362,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sint32(pbf_tag_type tag, int32_t value) { + void add_sint32(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, encode_zigzag32(value)); } @@ -347,7 +372,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_uint32(pbf_tag_type tag, uint32_t value) { + void add_uint32(pbf_tag_type tag, uint32_t value) { add_tagged_varint(tag, value); } @@ -357,7 +382,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_int64(pbf_tag_type tag, int64_t value) { + void add_int64(pbf_tag_type tag, int64_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -367,7 +392,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sint64(pbf_tag_type tag, int64_t value) { + void add_sint64(pbf_tag_type tag, int64_t value) { add_tagged_varint(tag, encode_zigzag64(value)); } @@ -377,7 +402,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_uint64(pbf_tag_type tag, uint64_t value) { + void add_uint64(pbf_tag_type tag, uint64_t value) { add_tagged_varint(tag, value); } @@ -387,7 +412,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_fixed32(pbf_tag_type tag, uint32_t value) { + void add_fixed32(pbf_tag_type tag, uint32_t value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -398,7 +423,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sfixed32(pbf_tag_type tag, int32_t value) { + void add_sfixed32(pbf_tag_type tag, int32_t value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -409,7 +434,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_fixed64(pbf_tag_type tag, uint64_t value) { + void add_fixed64(pbf_tag_type tag, uint64_t value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -420,7 +445,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sfixed64(pbf_tag_type tag, int64_t value) { + void add_sfixed64(pbf_tag_type tag, int64_t value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -431,7 +456,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_float(pbf_tag_type tag, float value) { + void add_float(pbf_tag_type tag, float value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -442,7 +467,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_double(pbf_tag_type tag, double value) { + void add_double(pbf_tag_type tag, double value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -454,7 +479,7 @@ class pbf_writer { * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { + void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); protozero_assert(size <= std::numeric_limits::max()); @@ -468,7 +493,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_bytes(pbf_tag_type tag, const std::string& value) { + void add_bytes(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_bytes(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -479,7 +514,7 @@ class pbf_writer { * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) { + void add_string(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -489,7 +524,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_string(pbf_tag_type tag, const std::string& value) { + void add_string(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_string(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -500,7 +545,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Pointer to value to be written */ - inline void add_string(pbf_tag_type tag, const char* value) { + void add_string(pbf_tag_type tag, const char* value) { add_bytes(tag, value, std::strlen(value)); } @@ -511,7 +556,7 @@ class pbf_writer { * @param value Pointer to message to be written * @param size Length of the message */ - inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) { + void add_message(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -521,7 +566,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written. The value must be a complete message. */ - inline void add_message(pbf_tag_type tag, const std::string& value) { + void add_message(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -535,126 +590,126 @@ class pbf_writer { /** * Add "repeated packed bool" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to bool. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed enum" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed int32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed sint32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_svarint(tag, first, last); } /** * Add "repeated packed uint32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed int64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed sint64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_svarint(tag, first, last); } /** * Add "repeated packed uint64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed fixed32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -662,14 +717,14 @@ class pbf_writer { /** * Add "repeated packed sfixed32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -677,14 +732,14 @@ class pbf_writer { /** * Add "repeated packed fixed64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -692,14 +747,14 @@ class pbf_writer { /** * Add "repeated packed sfixed64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -707,14 +762,14 @@ class pbf_writer { /** * Add "repeated packed float" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to float. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -722,14 +777,14 @@ class pbf_writer { /** * Add "repeated packed double" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to double. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -742,6 +797,16 @@ class pbf_writer { }; // class pbf_writer +/** + * Swap two pbf_writer objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept { + lhs.swap(rhs); +} + namespace detail { class packed_field { @@ -817,19 +882,46 @@ namespace detail { } // end namespace detail +/// Class for generating packed repeated bool fields. using packed_field_bool = detail::packed_field_varint; + +/// Class for generating packed repeated enum fields. using packed_field_enum = detail::packed_field_varint; + +/// Class for generating packed repeated int32 fields. using packed_field_int32 = detail::packed_field_varint; + +/// Class for generating packed repeated sint32 fields. using packed_field_sint32 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint32 fields. using packed_field_uint32 = detail::packed_field_varint; + +/// Class for generating packed repeated int64 fields. using packed_field_int64 = detail::packed_field_varint; + +/// Class for generating packed repeated sint64 fields. using packed_field_sint64 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint64 fields. using packed_field_uint64 = detail::packed_field_varint; + +/// Class for generating packed repeated fixed32 fields. using packed_field_fixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed32 fields. using packed_field_sfixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated fixed64 fields. using packed_field_fixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed64 fields. using packed_field_sfixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated float fields. using packed_field_float = detail::packed_field_fixed; + +/// Class for generating packed repeated double fields. using packed_field_double = detail::packed_field_fixed; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/types.hpp b/third_party/libosmium/include/protozero/types.hpp index 6856b3dde79..8b046387e78 100644 --- a/third_party/libosmium/include/protozero/types.hpp +++ b/third_party/libosmium/include/protozero/types.hpp @@ -16,33 +16,173 @@ documentation. * @brief Contains the declaration of low-level types used in the pbf format. */ +#include #include +#include +#include +#include + +#include namespace protozero { +/** + * The type used for field tags (field numbers). + */ +using pbf_tag_type = uint32_t; + +/** + * The type used to encode type information. + * See the table on + * https://developers.google.com/protocol-buffers/docs/encoding + */ +enum class pbf_wire_type : uint32_t { + varint = 0, // int32/64, uint32/64, sint32/64, bool, enum + fixed64 = 1, // fixed64, sfixed64, double + length_delimited = 2, // string, bytes, embedded messages, + // packed repeated fields + fixed32 = 5, // fixed32, sfixed32, float + unknown = 99 // used for default setting in this library +}; + +/** + * The type used for length values, such as the length of a field. + */ +using pbf_length_type = uint32_t; + +#ifdef PROTOZERO_USE_VIEW +using data_view = PROTOZERO_USE_VIEW; +#else + +/** + * Holds a pointer to some data and a length. + * + * This class is supposed to be compatible with the std::string_view + * that will be available in C++17. + */ +class data_view { + + const char* m_data; + std::size_t m_size; + +public: + + /** + * Default constructor. Construct an empty data_view. + */ + constexpr data_view() noexcept + : m_data(nullptr), + m_size(0) { + } + + /** + * Create data_view from pointer and size. + * + * @param data Pointer to the data. + * @param size Length of the data. + */ + constexpr data_view(const char* data, std::size_t size) noexcept + : m_data(data), + m_size(size) { + } + /** - * The type used for field tags (field numbers). + * Create data_view from string. + * + * @param str String with the data. */ - typedef uint32_t pbf_tag_type; + data_view(const std::string& str) noexcept + : m_data(str.data()), + m_size(str.size()) { + } /** - * The type used to encode type information. - * See the table on - * https://developers.google.com/protocol-buffers/docs/encoding + * Create data_view from zero-terminated string. + * + * @param data Pointer to the data. */ - enum class pbf_wire_type : uint32_t { - varint = 0, // int32/64, uint32/64, sint32/64, bool, enum - fixed64 = 1, // fixed64, sfixed64, double - length_delimited = 2, // string, bytes, embedded messages, - // packed repeated fields - fixed32 = 5, // fixed32, sfixed32, float - unknown = 99 // used for default setting in this library - }; + data_view(const char* data) noexcept + : m_data(data), + m_size(std::strlen(data)) { + } /** - * The type used for length values, such as the length of a field. + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. */ - typedef uint32_t pbf_length_type; + void swap(data_view& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_size, other.m_size); + } + + /// Return pointer to data. + constexpr const char* data() const noexcept { + return m_data; + } + + /// Return length of data in bytes. + constexpr std::size_t size() const noexcept { + return m_size; + } + + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + */ + std::string to_string() const { + protozero_assert(m_data); + return std::string{m_data, m_size}; + } + + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + */ + explicit operator std::string() const { + protozero_assert(m_data); + return std::string{m_data, m_size}; + } + +}; // class data_view + +/** + * Swap two data_view objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(data_view& lhs, data_view& rhs) noexcept { + lhs.swap(rhs); +} + +/** + * Two data_view instances are equal if they have the same size and the + * same content. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator==(data_view& lhs, data_view& rhs) noexcept { + return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data()); +} + +/** + * Two data_view instances are not equal if they have different sizes or the + * content differs. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator!=(data_view& lhs, data_view& rhs) noexcept { + return !(lhs == rhs); +} + +#endif + } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/varint.hpp b/third_party/libosmium/include/protozero/varint.hpp index 4242df95142..f6142d1c17f 100644 --- a/third_party/libosmium/include/protozero/varint.hpp +++ b/third_party/libosmium/include/protozero/varint.hpp @@ -23,13 +23,54 @@ documentation. namespace protozero { /** - * The maximum length of a 64bit varint. + * The maximum length of a 64 bit varint. */ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; -// from https://github.com/facebook/folly/blob/master/folly/Varint.h +namespace detail { + + // from https://github.com/facebook/folly/blob/master/folly/Varint.h + inline uint64_t decode_varint_impl(const char** data, const char* end) { + const int8_t* begin = reinterpret_cast(*data); + const int8_t* iend = reinterpret_cast(end); + const int8_t* p = begin; + uint64_t val = 0; + + if (iend - begin >= max_varint_length) { // fast path + do { + int64_t b; + b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break; + throw varint_too_long_exception(); + } while (false); + } else { + int shift = 0; + while (p != iend && *p < 0) { + val |= uint64_t(*p++ & 0x7f) << shift; + shift += 7; + } + if (p == iend) { + throw end_of_buffer_exception(); + } + val |= uint64_t(*p++) << shift; + } + + *data = reinterpret_cast(p); + return val; + } + +} // end namespace detail + /** - * Decode a 64bit varint. + * Decode a 64 bit varint. * * Strong exception guarantee: if there is an exception the data pointer will * not be changed. @@ -39,54 +80,68 @@ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; * @param[in] end Pointer one past the end of the input data. * @returns The decoded integer * @throws varint_too_long_exception if the varint is longer then the maximum - * length that would fit in a 64bit int. Usually this means your data + * length that would fit in a 64 bit int. Usually this means your data * is corrupted or you are trying to read something as a varint that * isn't. * @throws end_of_buffer_exception if the *end* of the buffer was reached * before the end of the varint. */ inline uint64_t decode_varint(const char** data, const char* end) { + // If this is a one-byte varint, decode it here. + if (end != *data && ((**data & 0x80) == 0)) { + uint64_t val = uint64_t(**data); + ++(*data); + return val; + } + // If this varint is more than one byte, defer to complete implementation. + return detail::decode_varint_impl(data, end); +} + +/** + * Skip over a varint. + * + * Strong exception guarantee: if there is an exception the data pointer will + * not be changed. + * + * @param[in,out] data Pointer to pointer to the input data. After the function + * returns this will point to the next data to be read. + * @param[in] end Pointer one past the end of the input data. + * @throws end_of_buffer_exception if the *end* of the buffer was reached + * before the end of the varint. + */ +inline void skip_varint(const char** data, const char* end) { const int8_t* begin = reinterpret_cast(*data); const int8_t* iend = reinterpret_cast(end); const int8_t* p = begin; - uint64_t val = 0; - - if (iend - begin >= max_varint_length) { // fast path - do { - int64_t b; - b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break; - throw varint_too_long_exception(); - } while (false); - } else { - int shift = 0; - while (p != iend && *p < 0) { - val |= uint64_t(*p++ & 0x7f) << shift; - shift += 7; - } - if (p == iend) { - throw end_of_buffer_exception(); - } - val |= uint64_t(*p++) << shift; + + while (p != iend && *p < 0) { + ++p; + } + + if (p >= begin + max_varint_length) { + throw varint_too_long_exception(); } + if (p == iend) { + throw end_of_buffer_exception(); + } + + ++p; + *data = reinterpret_cast(p); - return val; } /** - * Varint-encode a 64bit integer. + * Varint encode a 64 bit integer. + * + * @tparam T An output iterator type. + * @param data Output iterator the varint encoded value will be written to + * byte by byte. + * @param value The integer that will be encoded. + * @throws Any exception thrown by increment or dereference operator on data. */ -template -inline int write_varint(OutputIterator data, uint64_t value) { +template +inline int write_varint(T data, uint64_t value) { int n=1; while (value >= 0x80) { diff --git a/third_party/libosmium/include/protozero/version.hpp b/third_party/libosmium/include/protozero/version.hpp index 7b60e2eda8d..127e6497255 100644 --- a/third_party/libosmium/include/protozero/version.hpp +++ b/third_party/libosmium/include/protozero/version.hpp @@ -10,13 +10,26 @@ documentation. *****************************************************************************/ +/** + * @file version.hpp + * + * @brief Contains macros defining the protozero version. + */ + +/// The major version number #define PROTOZERO_VERSION_MAJOR 1 -#define PROTOZERO_VERSION_MINOR 3 -#define PROTOZERO_VERSION_PATCH 0 +/// The minor version number +#define PROTOZERO_VERSION_MINOR 4 + +/// The patch number +#define PROTOZERO_VERSION_PATCH 2 + +/// The complete version number #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) -#define PROTOZERO_VERSION_STRING "1.3.0" +/// Version number as string +#define PROTOZERO_VERSION_STRING "1.4.2" #endif // PROTOZERO_VERSION_HPP diff --git a/third_party/libosmium/osmium.imp b/third_party/libosmium/osmium.imp index c45794d08eb..77c5c37aaaf 100644 --- a/third_party/libosmium/osmium.imp +++ b/third_party/libosmium/osmium.imp @@ -2,10 +2,15 @@ # # Configuration for Include-What-You-Use tool # -# https://code.google.com/p/include-what-you-use/ +# http://include-what-you-use.org/ # #----------------------------------------------------------------------------- [ { "include": ["", "private", "", "public"] }, - { "include": ["", "public", "", "public"] } + { "include": ["", "private", "", "public"] }, + { "include": ["", "public", "", "public"] }, + { "include": ['"utf8/checked.h"', "private", "", "public"] }, + { "include": ['"utf8/unchecked.h"', "private", "", "public"] }, + { "include": ["", "public", "", "public"] }, + { "include": ["", "public", "", "public"] } ] diff --git a/third_party/libosmium/test/CMakeLists.txt b/third_party/libosmium/test/CMakeLists.txt index cea67ecd893..6230cde23da 100644 --- a/third_party/libosmium/test/CMakeLists.txt +++ b/third_party/libosmium/test/CMakeLists.txt @@ -85,6 +85,40 @@ function(add_unit_test _tgroup _tname) endif() endfunction() + +#----------------------------------------------------------------------------- +# +# Prepare some variables so querying it for tests work properly. +# +#----------------------------------------------------------------------------- + +if(NOT GEOS_FOUND) + set(GEOS_FOUND FALSE) +endif() + +if(NOT PROJ_FOUND) + set(PROJ_FOUND FALSE) +endif() + +if(NOT SPARSEHASH_FOUND) + set(SPARSEHASH_FOUND FALSE) +endif() + +if(NOT BZIP2_FOUND) + set(BZIP2_FOUND FALSE) +endif() + +if(NOT Threads_FOUND) + set(Threads_FOUND FALSE) +endif() + +if(GEOS_FOUND AND PROJ_FOUND) + set(GEOS_AND_PROJ_FOUND TRUE) +else() + set(GEOS_AND_PROJ_FOUND FALSE) +endif() + + #----------------------------------------------------------------------------- # # Add all tests. @@ -93,6 +127,7 @@ endfunction() add_unit_test(area test_area_id) add_unit_test(area test_node_ref_segment) +add_unit_test(basic test_area) add_unit_test(basic test_box) add_unit_test(basic test_changeset) add_unit_test(basic test_crc) @@ -112,11 +147,6 @@ add_unit_test(buffer test_buffer_purge) add_unit_test(builder test_attr) -if(GEOS_FOUND AND PROJ_FOUND) - set(GEOS_AND_PROJ_FOUND TRUE) -else() - set(GEOS_AND_PROJ_FOUND FALSE) -endif() add_unit_test(geom test_factory_with_projection ENABLE_IF ${GEOS_AND_PROJ_FOUND} LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY}) @@ -129,17 +159,19 @@ add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY}) add_unit_test(geom test_mercator) add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY}) add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) -add_unit_test(geom test_tile) +add_unit_test(geom test_tile ENABLE_IF ${GEOS_FOUND}) add_unit_test(geom test_wkb) add_unit_test(geom test_wkt) add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND}) +add_unit_test(index test_file_based_index) add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES}) add_unit_test(io test_file_formats) add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES};${OSMIUM_PBF_LIBRARIES}") add_unit_test(io test_reader_with_mock_decompression ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) add_unit_test(io test_reader_with_mock_parser ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) +add_unit_test(io test_opl_parser) add_unit_test(io test_output_utils) add_unit_test(io test_output_iterator ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(io test_string_table) diff --git a/third_party/libosmium/test/README b/third_party/libosmium/test/README index 8195824b48e..868b533f29e 100644 --- a/third_party/libosmium/test/README +++ b/third_party/libosmium/test/README @@ -3,11 +3,3 @@ Osmium uses Catch (https://github.com/philsquared/Catch/) for its unit tests. Only one header file is needed (catch.hpp) which can be downloaded from http://builds.catch-lib.net/ and put into the include directory. -Osmium needs a few changes to catch.hpp, they were patched in. To be able to -compare with the original version, it is stored in include/catch_orig.hpp. - -Changes are: -* Disable more warnings in GCC -* CATCH_CONFIG_CPP11_NULLPTR must be set for MSVC -* Problem with test running in loop: https://github.com/philsquared/Catch/issues/271 - diff --git a/third_party/libosmium/test/data-tests/include/common.hpp b/third_party/libosmium/test/data-tests/include/common.hpp index a6fd3df6162..4dad07d32ef 100644 --- a/third_party/libosmium/test/data-tests/include/common.hpp +++ b/third_party/libosmium/test/data-tests/include/common.hpp @@ -10,9 +10,9 @@ #include #include -typedef osmium::index::map::Dummy index_neg_type; -typedef osmium::index::map::SparseMemArray index_pos_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_neg_type = osmium::index::map::Dummy; +using index_pos_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; #include "check_basics_handler.hpp" #include "check_wkt_handler.hpp" diff --git a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp index cf4fc521a82..6d0328c8d59 100644 --- a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp +++ b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp @@ -1,7 +1,9 @@ +#include #include #include #include +#include #include @@ -17,21 +19,20 @@ #include #include -typedef osmium::index::map::SparseMemArray index_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; struct less_charptr { - bool operator()(const char* a, const char* b) const { + bool operator()(const char* a, const char* b) const noexcept { return std::strcmp(a, b) < 0; } }; // less_charptr -typedef std::map tagmap_type; +using tagmap_type = std::map; -inline tagmap_type create_map(const osmium::TagList& taglist) { +tagmap_type create_map(const osmium::TagList& taglist) { tagmap_type map; for (auto& tag : taglist) { @@ -52,7 +53,7 @@ class TestHandler : public osmium::handler::Handler { std::ofstream m_out; - bool m_first_out {true}; + bool m_first_out{true}; public: @@ -77,7 +78,7 @@ class TestHandler : public osmium::handler::Handler { } void node(const osmium::Node& node) { - gdalcpp::Feature feature(m_layer_point, m_ogr_factory.create_point(node)); + gdalcpp::Feature feature{m_layer_point, m_ogr_factory.create_point(node)}; feature.set_field("id", static_cast(node.id())); feature.set_field("type", node.tags().get_value_by_key("type")); feature.add_to_layer(); @@ -85,11 +86,11 @@ class TestHandler : public osmium::handler::Handler { void way(const osmium::Way& way) { try { - gdalcpp::Feature feature(m_layer_lines, m_ogr_factory.create_linestring(way)); + gdalcpp::Feature feature{m_layer_lines, m_ogr_factory.create_linestring(way)}; feature.set_field("id", static_cast(way.id())); feature.set_field("type", way.tags().get_value_by_key("type")); feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } } @@ -103,10 +104,10 @@ class TestHandler : public osmium::handler::Handler { } m_out << "{\n \"test_id\": " << (area.orig_id() / 1000) << ",\n \"area_id\": " << area.id() << ",\n \"from_id\": " << area.orig_id() << ",\n \"from_type\": \"" << (area.from_way() ? "way" : "relation") << "\",\n \"wkt\": \""; try { - std::string wkt = m_wkt_factory.create_multipolygon(area); + const std::string wkt = m_wkt_factory.create_multipolygon(area); m_out << wkt << "\",\n \"tags\": {"; - auto tagmap = create_map(area.tags()); + const auto tagmap = create_map(area.tags()); bool first = true; for (auto& tag : tagmap) { if (first) { @@ -117,11 +118,11 @@ class TestHandler : public osmium::handler::Handler { m_out << '"' << tag.first << "\": \"" << tag.second << '"'; } m_out << "}\n}"; - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { m_out << "INVALID\"\n}"; } try { - gdalcpp::Feature feature(m_layer_mpoly, m_ogr_factory.create_multipolygon(area)); + gdalcpp::Feature feature{m_layer_mpoly, m_ogr_factory.create_multipolygon(area)}; feature.set_field("id", static_cast(area.orig_id())); std::string from_type; @@ -132,7 +133,7 @@ class TestHandler : public osmium::handler::Handler { } feature.set_field("from_type", from_type.c_str()); feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n"; } } @@ -144,35 +145,38 @@ class TestHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " INFILE\n"; - exit(1); + std::exit(1); } - std::string output_format("SQLite"); - std::string input_filename(argv[1]); - std::string output_filename("multipolygon.db"); + const std::string output_format{"SQLite"}; + const std::string input_filename{argv[1]}; + const std::string output_filename{"multipolygon.db"}; CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }}; + gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}}; - osmium::area::ProblemReporterOGR problem_reporter(dataset); - osmium::area::Assembler::config_type assembler_config(&problem_reporter); - assembler_config.enable_debug_output(); - osmium::area::MultipolygonCollector collector(assembler_config); + osmium::area::ProblemReporterOGR problem_reporter{dataset}; + osmium::area::Assembler::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}; std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1(input_filename); + osmium::io::Reader reader1{input_filename}; collector.read_relations(reader1); reader1.close(); std::cerr << "Pass 1 done\n"; index_type index; - location_handler_type location_handler(index); + location_handler_type location_handler{index}; location_handler.ignore_errors(); - TestHandler test_handler(dataset); + TestHandler test_handler{dataset}; std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2(input_filename); + osmium::io::Reader reader2{input_filename}; osmium::apply(reader2, location_handler, test_handler, collector.handler([&test_handler](const osmium::memory::Buffer& area_buffer) { osmium::apply(area_buffer, test_handler); })); diff --git a/third_party/libosmium/test/data-tests/testdata-overview.cpp b/third_party/libosmium/test/data-tests/testdata-overview.cpp index 43d672dd1c3..4994f23efa1 100644 --- a/third_party/libosmium/test/data-tests/testdata-overview.cpp +++ b/third_party/libosmium/test/data-tests/testdata-overview.cpp @@ -1,6 +1,7 @@ /* The code in this file is released into the Public Domain. */ #include +#include #include @@ -12,8 +13,8 @@ #include #include -typedef osmium::index::map::SparseMemArray index_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; class TestOverviewHandler : public osmium::handler::Handler { @@ -64,7 +65,7 @@ class TestOverviewHandler : public osmium::handler::Handler { } feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } } @@ -76,24 +77,24 @@ class TestOverviewHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " INFILE\n"; - exit(1); + std::exit(1); } - std::string output_format("SQLite"); - std::string input_filename(argv[1]); - std::string output_filename("testdata-overview.db"); + const std::string output_format{"SQLite"}; + const std::string input_filename{argv[1]}; + const std::string output_filename{"testdata-overview.db"}; ::unlink(output_filename.c_str()); CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - gdalcpp::Dataset dataset(output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }); + gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; index_type index; - location_handler_type location_handler(index); + location_handler_type location_handler{index}; location_handler.ignore_errors(); - TestOverviewHandler handler(dataset); + TestOverviewHandler handler{dataset}; osmium::apply(reader, location_handler, handler); reader.close(); diff --git a/third_party/libosmium/test/data-tests/testdata-testcases.cpp b/third_party/libosmium/test/data-tests/testdata-testcases.cpp index 0ea7fc859f3..d05c28327a4 100644 --- a/third_party/libosmium/test/data-tests/testdata-testcases.cpp +++ b/third_party/libosmium/test/data-tests/testdata-testcases.cpp @@ -15,11 +15,9 @@ int main(int argc, char* argv[]) { std::cerr << "Running tests from '" << dirname << "' (from TESTCASES_DIR environment variable)\n"; } else { std::cerr << "Please set TESTCASES_DIR environment variable.\n"; - exit(1); + std::exit(1); } - int result = Catch::Session().run(argc, argv); - - return result; + return Catch::Session().run(argc, argv); } diff --git a/third_party/libosmium/test/data-tests/testdata-xml.cpp b/third_party/libosmium/test/data-tests/testdata-xml.cpp index b5a0e9028ed..0d2739a3a81 100644 --- a/third_party/libosmium/test/data-tests/testdata-xml.cpp +++ b/third_party/libosmium/test/data-tests/testdata-xml.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include @@ -14,14 +16,14 @@ #include std::string S_(const char* s) { - return std::string(s); + return std::string{s}; } 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"; - exit(2); + std::exit(2); } std::string f; @@ -47,11 +49,11 @@ struct header_buffer_type { // file contents fit into small buffers. std::string read_file(const char* test_id) { - int fd = osmium::io::detail::open_for_reading(filename(test_id)); + const int fd = osmium::io::detail::open_for_reading(filename(test_id)); assert(fd >= 0); std::string input(10000, '\0'); - auto n = ::read(fd, reinterpret_cast(const_cast(input.data())), 10000); + const auto n = ::read(fd, reinterpret_cast(const_cast(input.data())), 10000); assert(n >= 0); input.resize(static_cast(n)); @@ -61,10 +63,10 @@ std::string read_file(const char* test_id) { } std::string read_gz_file(const char* test_id, const char* suffix) { - int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix)); + const int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix)); assert(fd >= 0); - osmium::io::GzipDecompressor gzip_decompressor(fd); + osmium::io::GzipDecompressor gzip_decompressor{fd}; std::string input = gzip_decompressor.read(); gzip_decompressor.close(); @@ -81,7 +83,7 @@ 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::osm_entity_bits::all); + osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::osm_entity_bits::all}; parser.parse(); header_buffer_type result; @@ -117,9 +119,9 @@ TEST_CASE("Reading OSM XML 100") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("100-correct_but_no_data")); + osmium::io::Reader reader{filename("100-correct_but_no_data")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -129,9 +131,9 @@ TEST_CASE("Reading OSM XML 100") { } SECTION("Using Reader asking for header only") { - osmium::io::Reader reader(filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing); + osmium::io::Reader reader{filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); reader.close(); } @@ -146,15 +148,15 @@ TEST_CASE("Reading OSM XML 101") { REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error); try { read_xml("101-missing_version"); - } catch (osmium::format_version_error& e) { + } 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")); - osmium::io::Header header = reader.header(); + 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); @@ -170,16 +172,16 @@ TEST_CASE("Reading OSM XML 102") { REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error); try { read_xml("102-wrong_version"); - } catch (osmium::format_version_error& e) { + } 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")); + osmium::io::Reader reader{filename("102-wrong_version")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::format_version_error); @@ -195,15 +197,15 @@ TEST_CASE("Reading OSM XML 103") { REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error); try { read_xml("103-old_version"); - } catch (osmium::format_version_error& e) { + } 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")); - osmium::io::Header header = reader.header(); + 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); @@ -219,7 +221,7 @@ TEST_CASE("Reading OSM XML 104") { REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error); try { read_xml("104-empty_file"); - } catch (osmium::xml_error& e) { + } catch (const osmium::xml_error& e) { REQUIRE(e.line == 1); REQUIRE(e.column == 0); } @@ -227,8 +229,8 @@ TEST_CASE("Reading OSM XML 104") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("104-empty_file")); - osmium::io::Header header = reader.header(); + 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); @@ -245,8 +247,8 @@ TEST_CASE("Reading OSM XML 105") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("105-incomplete_xml_file")); - osmium::io::Header header = reader.header(); + 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); @@ -270,9 +272,9 @@ TEST_CASE("Reading OSM XML 120") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("120-correct_gzip_file_without_data", "osm.gz")); + osmium::io::Reader reader{filename("120-correct_gzip_file_without_data", "osm.gz")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -296,8 +298,8 @@ TEST_CASE("Reading OSM XML 121") { 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")); - osmium::io::Header header = reader.header(); + 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(); }); @@ -317,8 +319,8 @@ TEST_CASE("Reading OSM XML 122") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("122-no_osm_element")); - osmium::io::Header header = reader.header(); + 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); @@ -331,19 +333,19 @@ TEST_CASE("Reading OSM XML 122") { TEST_CASE("Reading OSM XML 140") { SECTION("Using Reader") { - osmium::io::Reader reader(filename("140-unicode")); + osmium::io::Reader reader{filename("140-unicode")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); int count = 0; - for (auto it = buffer.begin(); it != buffer.end(); ++it) { + for (const auto& node : buffer.select()) { ++count; - REQUIRE(it->id() == count); - const osmium::TagList& t = it->tags(); + REQUIRE(node.id() == count); + const osmium::TagList& t = node.tags(); const char* uc = t["unicode_char"]; - auto len = atoi(t["unicode_utf8_length"]); + const auto len = atoi(t["unicode_utf8_length"]); REQUIRE(len == strlen(uc)); REQUIRE(S_(uc) == t["unicode_xml"]); @@ -382,7 +384,7 @@ TEST_CASE("Reading OSM XML 140") { TEST_CASE("Reading OSM XML 141") { SECTION("Using Reader") { - osmium::io::Reader reader(filename("141-entities")); + osmium::io::Reader reader{filename("141-entities")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); REQUIRE(buffer.committed() > 0); @@ -406,40 +408,40 @@ TEST_CASE("Reading OSM XML 141") { TEST_CASE("Reading OSM XML 142") { SECTION("Using Reader to read nodes") { - osmium::io::Reader reader(filename("142-whitespace")); + osmium::io::Reader reader{filename("142-whitespace")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); int count = 0; - for (auto it = buffer.begin(); it != buffer.end(); ++it) { + for (const auto& node : buffer.select()) { ++count; - REQUIRE(it->id() == count); - REQUIRE(it->tags().size() == 1); - const osmium::Tag& tag = *(it->tags().begin()); + REQUIRE(node.id() == count); + REQUIRE(node.tags().size() == 1); + const osmium::Tag& tag = *(node.tags().begin()); switch (count) { case 1: - REQUIRE(S_(it->user()) == "user name"); + 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_(it->user()) == "line\nfeed"); + 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_(it->user()) == "carriage\rreturn"); + 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_(it->user()) == "tab\tulator"); + 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_(it->user()) == "unencoded linefeed"); + REQUIRE(S_(node.user()) == "unencoded linefeed"); REQUIRE(S_(tag.key()) == "key with unencoded linefeed"); REQUIRE(S_(tag.value()) == "value with unencoded linefeed"); break; @@ -451,12 +453,12 @@ TEST_CASE("Reading OSM XML 142") { } SECTION("Using Reader to read relation") { - osmium::io::Reader reader(filename("142-whitespace")); + osmium::io::Reader reader{filename("142-whitespace")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); - auto it = buffer.begin(); - REQUIRE(it != buffer.end()); + auto it = buffer.select().begin(); + REQUIRE(it != buffer.select().end()); REQUIRE(it->id() == 21); const auto& members = it->members(); REQUIRE(members.size() == 5); @@ -505,9 +507,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("200-nodes")); + osmium::io::Reader reader{filename("200-nodes")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -519,9 +521,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for nodes") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::node); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::node}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -533,9 +535,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for header only") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::nothing); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::nothing}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); REQUIRE_THROWS({ @@ -546,9 +548,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for ways") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::way); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::way}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); diff --git a/third_party/libosmium/test/include/catch.hpp b/third_party/libosmium/test/include/catch.hpp index f04214990ce..879fc5b1d11 100644 --- a/third_party/libosmium/test/include/catch.hpp +++ b/third_party/libosmium/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.3.3 - * Generated: 2016-01-22 07:51:36.661106 + * Catch v1.5.6 + * Generated: 2016-06-09 19:20:41.460328 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -62,7 +62,11 @@ #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#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 ) @@ -89,7 +93,7 @@ // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? - +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -102,6 +106,18 @@ // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 +#ifdef __cplusplus + +# if __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +# endif + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + +#endif + #ifdef __clang__ # if __has_feature(cxx_nullptr) @@ -112,6 +128,10 @@ # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif +# if defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# endif + #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// @@ -136,9 +156,13 @@ // GCC #ifdef __GNUC__ -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#endif +# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -173,13 +197,20 @@ #endif +// Use __COUNTER__ if the compiler supports it +#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ + ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __clang__ && __clang_major__ >= 3 ) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -# define CATCH_CPP11_OR_GREATER +#if defined(CATCH_CPP11_OR_GREATER) # if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR @@ -246,6 +277,13 @@ #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif // noexcept support: #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) @@ -672,24 +710,28 @@ void registerTestCaseFunction #ifdef CATCH_CONFIG_VARIADIC_MACROS /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + 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, ... ) \ @@ -697,24 +739,28 @@ void registerTestCaseFunction #else /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ namespace{ \ - struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ } \ - void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + 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 ) \ @@ -1287,37 +1333,37 @@ namespace Internal { template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { - return opCast( lhs ) == opCast( rhs ); + return bool( opCast( lhs ) == opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) != opCast( rhs ); + return bool( opCast( lhs ) != opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) < opCast( rhs ); + return bool( opCast( lhs ) < opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) > opCast( rhs ); + return bool( opCast( lhs ) > opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) >= opCast( rhs ); + return bool( opCast( lhs ) >= opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) <= opCast( rhs ); + return bool( opCast( lhs ) <= opCast( rhs ) ); } }; @@ -2020,13 +2066,14 @@ namespace Catch { do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ } \ catch( ... ) { \ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ @@ -2563,10 +2610,12 @@ namespace Catch { } /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ - static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED @@ -3331,6 +3380,11 @@ namespace Catch { InLexicographicalOrder, InRandomOrder }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; class TestSpec; @@ -3350,7 +3404,7 @@ namespace Catch { virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; - virtual bool forceColour() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; }; } @@ -3404,7 +3458,7 @@ namespace Catch { }; class DebugOutStream : public IStream { - std::auto_ptr m_streamBuf; + CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); @@ -3439,14 +3493,14 @@ namespace Catch { noThrow( false ), showHelp( false ), showInvisibles( false ), - forceColour( false ), filenamesAsTags( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ) + runOrder( RunTests::InDeclarationOrder ), + useColour( UseColour::Auto ) {} bool listTests; @@ -3459,7 +3513,6 @@ namespace Catch { bool noThrow; bool showHelp; bool showInvisibles; - bool forceColour; bool filenamesAsTags; int abortAfter; @@ -3469,6 +3522,7 @@ namespace Catch { WarnAbout::What warnings; ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; std::string outputFilename; std::string name; @@ -3534,7 +3588,7 @@ namespace Catch { virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual bool forceColour() const { return m_data.forceColour; } + virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } private: @@ -3552,7 +3606,7 @@ namespace Catch { } ConfigData m_data; - std::auto_ptr m_stream; + CATCH_AUTO_PTR( IStream const ) m_stream; TestSpec m_testSpec; }; @@ -3572,6 +3626,8 @@ namespace Catch { #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h +// Version 0.0.2.4 + // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) @@ -3596,6 +3652,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3730,15 +3787,165 @@ namespace Tbc { #endif // TBC_TEXT_FORMAT_H_INCLUDED // ----------- end of #include from tbc_text_format.h ----------- -// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h +// ........... back in clara.h #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +# define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +# define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +# define CLARA_AUTO_PTR( T ) std::unique_ptr +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + #include -#include #include #include +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif + // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE @@ -3797,23 +4004,15 @@ namespace Clara { else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } - inline void convertInto( bool _source, bool& _dest ) { - _dest = _source; - } - template - inline void convertInto( bool, T& ) { - throw std::runtime_error( "Invalid conversion" ); - } template struct IArgFunction { virtual ~IArgFunction() {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS IArgFunction() = default; IArgFunction( IArgFunction const& ) = default; -# endif +#endif virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual void setFlag( ConfigT& config ) const = 0; virtual bool takesArg() const = 0; virtual IArgFunction* clone() const = 0; }; @@ -3821,11 +4020,11 @@ namespace Clara { template class BoundArgFunction { public: - BoundArgFunction() : functionObj( CATCH_NULL ) {} + BoundArgFunction() : functionObj( CLARA_NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; delete functionObj; functionObj = newFunctionObj; return *this; @@ -3835,13 +4034,10 @@ namespace Clara { void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } - void setFlag( ConfigT& config ) const { - functionObj->setFlag( config ); - } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { - return functionObj != CATCH_NULL; + return functionObj != CLARA_NULL; } private: IArgFunction* functionObj; @@ -3850,7 +4046,6 @@ namespace Clara { template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} - virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; @@ -3861,9 +4056,6 @@ namespace Clara { virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } - virtual void setFlag( C& p ) const { - convertInto( true, p.*member ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; @@ -3876,11 +4068,6 @@ namespace Clara { convertInto( stringValue, value ); (p.*member)( value ); } - virtual void setFlag( C& p ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - (p.*member)( value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); @@ -3894,9 +4081,6 @@ namespace Clara { if( value ) (p.*member)(); } - virtual void setFlag( C& p ) const { - (p.*member)(); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); @@ -3911,9 +4095,6 @@ namespace Clara { if( value ) function( obj ); } - virtual void setFlag( C& p ) const { - function( p ); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); @@ -3927,11 +4108,6 @@ namespace Clara { convertInto( stringValue, value ); function( obj, value ); } - virtual void setFlag( C& obj ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - function( obj, value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); @@ -3939,8 +4115,20 @@ namespace Clara { } // namespace Detail - struct Parser { - Parser() : separators( " \t=:" ) {} + inline std::vector argsToVector( int argc, char const* const* const argv ) { + std::vector args( static_cast( argc ) ); + for( std::size_t i = 0; i < static_cast( argc ); ++i ) + args[i] = argv[i]; + + return args; + } + + class Parser { + enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + public: struct Token { enum Type { Positional, ShortOpt, LongOpt }; @@ -3949,38 +4137,75 @@ namespace Clara { std::string data; }; - void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { + Parser() : mode( None ), from( 0 ), inQuotes( false ){} + + void parseIntoTokens( std::vector const& args, std::vector& tokens ) { const std::string doubleDash = "--"; - for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) - parseIntoTokens( argv[i] , tokens); - } - void parseIntoTokens( std::string arg, std::vector& tokens ) const { - while( !arg.empty() ) { - Parser::Token token( Parser::Token::Positional, arg ); - arg = ""; - if( token.data[0] == '-' ) { - if( token.data.size() > 1 && token.data[1] == '-' ) { - token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); - } - else { - token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); - if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { - arg = "-" + token.data.substr( 1 ); - token.data = token.data.substr( 0, 1 ); - } - } - } - if( token.type != Parser::Token::Positional ) { - std::size_t pos = token.data.find_first_of( separators ); - if( pos != std::string::npos ) { - arg = token.data.substr( pos+1 ); - token.data = token.data.substr( 0, pos ); - } - } - tokens.push_back( token ); + for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) + parseIntoTokens( args[i], tokens); + } + + void parseIntoTokens( std::string const& arg, std::vector& tokens ) { + for( std::size_t i = 0; i <= arg.size(); ++i ) { + char c = arg[i]; + if( c == '"' ) + inQuotes = !inQuotes; + mode = handleMode( i, c, arg, tokens ); + } + } + Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + switch( mode ) { + case None: return handleNone( i, c ); + case MaybeShortOpt: return handleMaybeShortOpt( i, c ); + case ShortOpt: + case LongOpt: + case SlashOpt: return handleOpt( i, c, arg, tokens ); + case Positional: return handlePositional( i, c, arg, tokens ); + default: throw std::logic_error( "Unknown mode" ); + } + } + + Mode handleNone( std::size_t i, char c ) { + if( inQuotes ) { + from = i; + return Positional; + } + switch( c ) { + case '-': return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': from = i+1; return SlashOpt; +#endif + default: from = i; return Positional; + } + } + Mode handleMaybeShortOpt( std::size_t i, char c ) { + switch( c ) { + case '-': from = i+1; return LongOpt; + default: from = i; return ShortOpt; } } - std::string separators; + 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; + + std::string optName = arg.substr( from, i-from ); + if( mode == ShortOpt ) + for( std::size_t j = 0; j < optName.size(); ++j ) + tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); + else if( mode == SlashOpt && optName.size() == 1 ) + tokens.push_back( Token( Token::ShortOpt, optName ) ); + else + tokens.push_back( Token( Token::LongOpt, optName ) ); + return None; + } + Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) + return mode; + + std::string data = arg.substr( from, i-from ); + tokens.push_back( Token( Token::Positional, data ) ); + return None; + } }; template @@ -4059,7 +4284,7 @@ namespace Clara { } }; - typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr; + typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; friend void addOptName( Arg& arg, std::string const& optName ) { @@ -4135,8 +4360,8 @@ namespace Clara { m_arg->description = description; return *this; } - ArgBuilder& detail( std::string const& _detail ) { - m_arg->detail = _detail; + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; return *this; } @@ -4219,14 +4444,14 @@ namespace Clara { maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usageText( it->commands(), Detail::TextAttributes() + Detail::Text usage( it->commands(), Detail::TextAttributes() .setWidth( maxWidth+indent ) .setIndent( indent ) ); Detail::Text desc( it->description, Detail::TextAttributes() .setWidth( width - maxWidth - 3 ) ); - for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) { - std::string usageCol = i < usageText.size() ? usageText[i] : ""; + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; os << usageCol; if( i < desc.size() && !desc[i].empty() ) @@ -4283,21 +4508,21 @@ namespace Clara { return oss.str(); } - ConfigT parse( int argc, char const * const * argv ) const { + ConfigT parse( std::vector const& args ) const { ConfigT config; - parseInto( argc, argv, config ); + parseInto( args, config ); return config; } - std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { - std::string processName = argv[0]; + std::vector parseInto( std::vector const& args, ConfigT& config ) const { + std::string processName = args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; - parser.parseIntoTokens( argc, argv, tokens ); + parser.parseIntoTokens( args, tokens ); return populate( tokens, config ); } @@ -4328,7 +4553,7 @@ namespace Clara { arg.boundField.set( config, tokens[++i].data ); } else { - arg.boundField.setFlag( config ); + arg.boundField.set( config, "true" ); } break; } @@ -4471,6 +4696,21 @@ namespace Catch { ? ShowDurations::Always : ShowDurations::Never; } + inline void setUseColour( ConfigData& config, std::string const& value ) { + std::string mode = toLower( value ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); + } + inline void forceColour( ConfigData& config ) { + config.useColour = UseColour::Yes; + } inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { std::ifstream f( _filename.c_str() ); if( !f.is_open() ) @@ -4557,7 +4797,7 @@ namespace Catch { cli["-d"]["--durations"] .describe( "show test durations" ) - .bind( &setShowDurations, "yes/no" ); + .bind( &setShowDurations, "yes|no" ); cli["-f"]["--input-file"] .describe( "load test names to run from a file" ) @@ -4585,8 +4825,12 @@ namespace Catch { .bind( &setRngSeed, "'time'|number" ); cli["--force-colour"] - .describe( "force colourised output" ) - .bind( &ConfigData::forceColour ); + .describe( "force colourised output (deprecated)" ) + .bind( &forceColour ); + + cli["--use-colour"] + .describe( "should output be colourised" ) + .bind( &setUseColour, "yes|no" ); return cli; } @@ -5017,6 +5261,8 @@ namespace Catch bool aborting; }; + class MultipleReporters; + struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); @@ -5044,6 +5290,8 @@ namespace Catch virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } }; struct IReporterFactory : IShared { @@ -5261,6 +5509,10 @@ namespace TestCaseTracking { virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( std::string const& name ) = 0; virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; }; class TrackerContext { @@ -5385,6 +5637,10 @@ namespace TestCaseTracking { m_parent->openChild(); } } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + void open() { m_runState = Executing; moveToThis(); @@ -5448,13 +5704,16 @@ namespace TestCaseTracking { {} virtual ~SectionTracker(); + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - section = dynamic_cast( childTracker ); - assert( section ); + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); } else { section = new SectionTracker( name, ctx, ¤tTracker ); @@ -5479,13 +5738,16 @@ namespace TestCaseTracking { {} virtual ~IndexTracker(); + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - tracker = dynamic_cast( childTracker ); - assert( tracker ); + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = static_cast( childTracker ); } else { tracker = new IndexTracker( name, ctx, ¤tTracker, size ); @@ -5692,6 +5954,11 @@ namespace Catch { while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); + if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, @@ -6083,10 +6350,10 @@ namespace Catch { Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } - int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); @@ -6110,7 +6377,7 @@ namespace Catch { m_config.reset(); } - int run( int argc, char const* const argv[] ) { + int run( int argc, char const* const* const argv ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) @@ -6180,13 +6447,31 @@ namespace Catch { #include #include +#ifdef CATCH_CPP14_OR_GREATER +#include +#endif + namespace Catch { - struct LexSort { - bool operator() (TestCase i,TestCase j) const { return (i + static void shuffle( V& vector ) { + RandomNumberGenerator rng; +#ifdef CATCH_CPP14_OR_GREATER + std::shuffle( vector.begin(), vector.end(), rng ); +#else + std::random_shuffle( vector.begin(), vector.end(), rng ); +#endif + } }; inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { @@ -6195,14 +6480,12 @@ namespace Catch { switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end(), LexSort() ); + std::sort( sorted.begin(), sorted.end() ); break; case RunTests::InRandomOrder: { seedRng( config ); - - RandomNumberGenerator rng; - std::random_shuffle( sorted.begin(), sorted.end(), rng ); + RandomNumberGenerator::shuffle( sorted ); } break; case RunTests::InDeclarationOrder: @@ -6221,13 +6504,15 @@ namespace Catch { it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ){ - Catch::cerr() - << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - exit(1); + if( !prev.second ) { + std::ostringstream ss; + + ss << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); } } } @@ -6815,7 +7100,18 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - return &s_instance; + + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = !isDebuggerActive() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); } } // end anon namespace @@ -6866,7 +7162,14 @@ namespace { IColourImpl* platformColourInstance() { Ptr config = getCurrentContext().getConfig(); - return (config && config->forceColour()) || isatty(STDOUT_FILENO) + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes ? PosixColourImpl::instance() : NoColourImpl::instance(); } @@ -6891,9 +7194,7 @@ namespace Catch { Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { - static IColourImpl* impl = isDebuggerActive() - ? NoColourImpl::instance() - : platformColourInstance(); + static IColourImpl* impl = platformColourInstance(); impl->use( _colourCode ); } @@ -7270,7 +7571,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 3, 3, "", 0 ); + Version libraryVersion( 1, 5, 6, "", 0 ); } @@ -8249,13 +8550,18 @@ class MultipleReporters : public SharedImpl { ++it ) (*it)->skipTest( testInfo ); } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } + }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { - MultipleReporters* multi = dynamic_cast( existingReporter.get() ); + MultipleReporters* multi = existingReporter->tryAsMulti(); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); @@ -8435,7 +8741,7 @@ namespace Catch { virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& assertionStats ) { + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); @@ -9566,7 +9872,7 @@ namespace Catch { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } - else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { + else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " 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 3261c24e143..5e33c2baef3 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 @@ -20,91 +20,109 @@ TEST_CASE("NodeRefSegmentClass") { osmium::NodeRef nr3(3, { 1.2, 3.6 }); osmium::NodeRef nr4(4, { 1.2, 3.7 }); - NodeRefSegment s1(nr1, nr2, nullptr, nullptr); + NodeRefSegment s1(nr1, nr2); REQUIRE(s1.first().ref() == 1); REQUIRE(s1.second().ref() == 2); - NodeRefSegment s2(nr2, nr3, nullptr, nullptr); + NodeRefSegment s2(nr2, nr3); REQUIRE(s2.first().ref() == 3); REQUIRE(s2.second().ref() == 2); - NodeRefSegment s3(nr3, nr4, nullptr, nullptr); + NodeRefSegment s3(nr3, nr4); REQUIRE(s3.first().ref() == 3); REQUIRE(s3.second().ref() == 4); } SECTION("intersection") { - NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, nullptr, nullptr); - NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, nullptr, nullptr); - NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, nullptr, nullptr); - NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, nullptr, nullptr); - NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, nullptr, nullptr); - NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}}, nullptr, nullptr); - NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}}, nullptr, nullptr); + 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}}); REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0)); + REQUIRE(calculate_intersection(s2, s1) == osmium::Location(1.0, 1.0)); + REQUIRE(calculate_intersection(s1, s3) == osmium::Location()); + REQUIRE(calculate_intersection(s3, s1) == osmium::Location()); + REQUIRE(calculate_intersection(s2, s3) == osmium::Location()); + REQUIRE(calculate_intersection(s3, s2) == osmium::Location()); + REQUIRE(calculate_intersection(s1, s4) == osmium::Location()); + REQUIRE(calculate_intersection(s4, s1) == osmium::Location()); + REQUIRE(calculate_intersection(s1, s5) == osmium::Location(2.0, 2.0)); - REQUIRE(calculate_intersection(s1, s1) == osmium::Location()); - REQUIRE(calculate_intersection(s1, s6) == osmium::Location()); - REQUIRE(calculate_intersection(s1, s7) == osmium::Location()); - } + REQUIRE(calculate_intersection(s5, s1) == osmium::Location(2.0, 2.0)); - SECTION("intersection of very long segments") { - NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}}, nullptr, nullptr); - NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); - REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0)); + REQUIRE(calculate_intersection(s1, s6) == osmium::Location(1.0, 1.0)); + REQUIRE(calculate_intersection(s6, s1) == osmium::Location(1.0, 1.0)); - NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr); - NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); - REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0)); + REQUIRE(calculate_intersection(s1, s7) == osmium::Location(1.0, 1.0)); + REQUIRE(calculate_intersection(s7, s1) == osmium::Location(1.0, 1.0)); - NodeRefSegment s5({ 1, {-90.0000001, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr); - NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr); - REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0)); + REQUIRE(calculate_intersection(s6, s7) == osmium::Location()); + REQUIRE(calculate_intersection(s7, s6) == osmium::Location()); } - SECTION("to_left_of") { - osmium::Location loc { 2.0, 2.0 }; + SECTION("intersection of collinear segments") { + 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}}); // *-* + + REQUIRE(calculate_intersection(s1, s1) == osmium::Location()); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {4.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false); - REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); + REQUIRE(calculate_intersection(s1, s2) == osmium::Location()); + REQUIRE(calculate_intersection(s2, s1) == osmium::Location()); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 3.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s3) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s3, s1) == osmium::Location(1.0, 0.0)); - REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 1.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s4) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s4, s1) == osmium::Location(1.0, 0.0)); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s5) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s5, s1) == osmium::Location(1.0, 0.0)); - REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false); - REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s6) == osmium::Location(2.0, 0.0)); + REQUIRE(calculate_intersection(s6, s1) == osmium::Location(2.0, 0.0)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false); - REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s7) == osmium::Location(2.0, 0.0)); + REQUIRE(calculate_intersection(s7, s1) == osmium::Location(2.0, 0.0)); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 3.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s8) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s8, s1) == osmium::Location(1.0, 0.0)); - REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s1, s9) == osmium::Location()); + REQUIRE(calculate_intersection(s9, s1) == osmium::Location()); - REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 2.0}}, nullptr, nullptr).to_left_of(loc)); - REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s5, s6) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s6, s5) == osmium::Location(1.0, 0.0)); - REQUIRE(NodeRefSegment({0, {0.0, 1.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false); - REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {4.0, 0.0}}, nullptr, nullptr).to_left_of(loc) == false); + REQUIRE(calculate_intersection(s7, s8) == osmium::Location(1.0, 0.0)); + REQUIRE(calculate_intersection(s8, s7) == osmium::Location(1.0, 0.0)); + } + + SECTION("intersection of very long segments") { + NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}}); + NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}); + 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}}); + 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}}); + REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0)); } SECTION("ordering") { @@ -127,3 +145,64 @@ TEST_CASE("NodeRefSegmentClass") { } +TEST_CASE("Ordering of NodeRefSegments") { + osmium::NodeRef nr0(0, { 0.0, 0.0 }); + osmium::NodeRef nr1(1, { 1.0, 0.0 }); + osmium::NodeRef nr2(2, { 0.0, 1.0 }); + osmium::NodeRef nr3(3, { 2.0, 0.0 }); + osmium::NodeRef nr4(4, { 0.0, 2.0 }); + osmium::NodeRef nr5(5, { 1.0, 1.0 }); + 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); + + // s1 + REQUIRE_FALSE(s1 < s1); + REQUIRE(s2 < s1); + REQUIRE(s1 < s3); + REQUIRE(s4 < s1); + REQUIRE(s5 < s1); + REQUIRE(s6 < s1); + REQUIRE(s7 < s1); + + // s2 + REQUIRE_FALSE(s2 < s2); + REQUIRE(s2 < s3); + REQUIRE(s2 < s4); + REQUIRE(s2 < s5); + REQUIRE(s2 < s6); + REQUIRE(s2 < s7); + + // s3 + REQUIRE_FALSE(s3 < s3); + REQUIRE(s4 < s3); + REQUIRE(s5 < s3); + REQUIRE(s6 < s3); + REQUIRE(s7 < s3); + + // s4 + REQUIRE_FALSE(s4 < s4); + REQUIRE(s4 < s5); + REQUIRE(s4 < s6); + REQUIRE(s4 < s7); + + // s5 + REQUIRE_FALSE(s5 < s5); + REQUIRE(s5 < s6); + REQUIRE(s7 < s5); + + // s6 + REQUIRE_FALSE(s6 < s6); + REQUIRE(s7 < s6); + + // s7 + REQUIRE_FALSE(s7 < s7); +} + diff --git a/third_party/libosmium/test/t/basic/test_area.cpp b/third_party/libosmium/test/t/basic/test_area.cpp new file mode 100644 index 00000000000..f2847dcc166 --- /dev/null +++ b/third_party/libosmium/test/t/basic/test_area.cpp @@ -0,0 +1,78 @@ +#include "catch.hpp" + +#include + +#include +#include +#include + +using namespace osmium::builder::attr; + +TEST_CASE("Build area") { + osmium::memory::Buffer buffer(10000); + + osmium::builder::add_area(buffer, + _id(17), + _version(3), + _visible(), + _cid(333), + _uid(21), + _timestamp(time_t(123)), + _user("foo"), + _tag("landuse", "forest"), + _tag("name", "Sherwood Forest"), + _outer_ring({ + {1, {3.2, 4.2}}, + {2, {3.5, 4.7}}, + {3, {3.6, 4.9}}, + {1, {3.2, 4.2}} + }), + _inner_ring({ + {5, {1.0, 1.0}}, + {6, {8.0, 1.0}}, + {7, {8.0, 8.0}}, + {8, {1.0, 8.0}}, + {5, {1.0, 1.0}} + }) + ); + + const osmium::Area& area = buffer.get(0); + + REQUIRE(17 == area.id()); + REQUIRE(3 == area.version()); + REQUIRE(true == area.visible()); + REQUIRE(333 == area.changeset()); + REQUIRE(21 == area.uid()); + REQUIRE(std::string("foo") == area.user()); + REQUIRE(123 == uint32_t(area.timestamp())); + REQUIRE(2 == area.tags().size()); + + int inner = 0; + int outer = 0; + for (const auto& subitem : area) { + switch (subitem.type()) { + case osmium::item_type::outer_ring: { + const auto& ring = static_cast(subitem); + REQUIRE(ring.size() == 4); + ++outer; + } + break; + case osmium::item_type::inner_ring: { + const auto& ring = static_cast(subitem); + REQUIRE(ring.size() == 5); + ++inner; + } + break; + default: + break; + } + } + + REQUIRE(outer == 1); + REQUIRE(inner == 1); + + osmium::CRC crc32; + crc32.update(area); + REQUIRE(crc32().checksum() == 0x2b2b7fa0); +} + diff --git a/third_party/libosmium/test/t/basic/test_entity_bits.cpp b/third_party/libosmium/test/t/basic/test_entity_bits.cpp index f15068b1c27..13de94b5fae 100644 --- a/third_party/libosmium/test/t/basic/test_entity_bits.cpp +++ b/third_party/libosmium/test/t/basic/test_entity_bits.cpp @@ -21,6 +21,7 @@ TEST_CASE("entity_bits") { REQUIRE(! (entities & osmium::osm_entity_bits::way)); REQUIRE(entities == osmium::osm_entity_bits::node); + REQUIRE(osmium::osm_entity_bits::nothing == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined)); REQUIRE(osmium::osm_entity_bits::node == osmium::osm_entity_bits::from_item_type(osmium::item_type::node)); REQUIRE(osmium::osm_entity_bits::way == osmium::osm_entity_bits::from_item_type(osmium::item_type::way)); REQUIRE(osmium::osm_entity_bits::relation == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation)); diff --git a/third_party/libosmium/test/t/basic/test_location.cpp b/third_party/libosmium/test/t/basic/test_location.cpp index 3fd8d155a51..dc5b37872e8 100644 --- a/third_party/libosmium/test/t/basic/test_location.cpp +++ b/third_party/libosmium/test/t/basic/test_location.cpp @@ -137,7 +137,7 @@ TEST_CASE("Location") { } SECTION("output_defined") { - osmium::Location p(-3.2, 47.3); + osmium::Location p(-3.20, 47.30); std::stringstream out; out << p; REQUIRE(out.str() == "(-3.2,47.3)"); @@ -152,3 +152,221 @@ TEST_CASE("Location") { } +TEST_CASE("Location hash") { + if (sizeof(size_t) == 8) { + REQUIRE(std::hash{}({0, 0}) == 0); + REQUIRE(std::hash{}({0, 1}) == 1); + REQUIRE(std::hash{}({1, 0}) == 0x100000000); + REQUIRE(std::hash{}({1, 1}) == 0x100000001); + } else { + REQUIRE(std::hash{}({0, 0}) == 0); + REQUIRE(std::hash{}({0, 1}) == 1); + REQUIRE(std::hash{}({1, 0}) == 1); + REQUIRE(std::hash{}({1, 1}) == 0); + } +} + +#define CR(s, v, r) { \ + const char* strm = "-" s; \ + const char* strp = strm + 1; \ + REQUIRE(std::atof(strp) == Approx( v / 10000000.0)); \ + REQUIRE(std::atof(strm) == Approx(-v / 10000000.0)); \ + const char** data = &strp; \ + REQUIRE(osmium::detail::string_to_location_coordinate(data) == v); \ + REQUIRE(std::string{*data} == r); \ + data = &strm; \ + REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v); \ + REQUIRE(std::string{*data} == r); \ + } + +#define C(s, v) CR(s, v, "") + +#define F(s) { \ + const char* strm = "-" s; \ + const char* strp = strm + 1; \ + const char** data = &strp; \ + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ + data = &strm; \ + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ + } + +TEST_CASE("Parsing coordinates from strings") { + F("x"); + F("."); + F("--"); + F(""); + F(" "); + F(" 123"); + + CR("123 ", 1230000000, " "); + CR("123x", 1230000000, "x"); + CR("1.2x", 12000000, "x"); + + C("0", 0); + + C("1", 10000000); + C("2", 20000000); + + C("9", 90000000); + C("10", 100000000); + C("11", 110000000); + + C("90", 900000000); + C("100", 1000000000); + C("101", 1010000000); + + C("00", 0); + C("01", 10000000); + C("001", 10000000); + C("0001", 10000000); + + F("1234"); + F("1234."); + F("12345678901234567890"); + + C("0.", 0); + C("0.0", 0); + C("1.", 10000000); + C("1.0", 10000000); + C("1.2", 12000000); + C("0.1", 1000000); + C("0.01", 100000); + C("0.001", 10000); + C("0.0001", 1000); + C("0.00001", 100); + C("0.000001", 10); + C("0.0000001", 1); + + C("1.1234567", 11234567); + C("1.12345670", 11234567); + C("1.12345674", 11234567); + C("1.123456751", 11234568); + C("1.12345679", 11234568); + C("1.12345680", 11234568); + C("1.12345681", 11234568); + + C("180.0000000", 1800000000); + C("180.0000001", 1800000001); + C("179.9999999", 1799999999); + C("179.99999999", 1800000000); + C("200.123", 2001230000); + + C("1e2", 1000000000); + C("1e1", 100000000); + C("1e0", 10000000); + C("1e-1", 1000000); + C("1e-2", 100000); + C("1e-3", 10000); + C("1e-4", 1000); + C("1e-5", 100); + C("1e-6", 10); + C("1e-7", 1); + + C("1.0e2", 1000000000); + C("1.1e1", 110000000); + C("0.1e1", 10000000); + C("1.2e0", 12000000); + C("1.9e-1", 1900000); + C("2.0e-2", 200000); + C("2.1e-3", 21000); + C("9.0e-4", 9000); + C("9.1e-5", 910); + C("1.0e-6", 10); + C("1.0e-7", 1); + C("1.4e-7", 1); + C("1.5e-7", 2); + C("1.9e-7", 2); + C("0.5e-7", 1); + C("0.1e-7", 0); + C("0.0e-7", 0); + C("1.9e-8", 0); + C("1.9e-9", 0); + C("1.9e-10", 0); + + F("e"); + F(" e"); + F(" 1.1e2"); + F("1.0e3"); + F("5e4"); + F("5.0e2"); + F("3e2"); + F("1e"); + F("0.5e"); + F("1e10"); + + CR("1e2 ", 1000000000, " "); + CR("1.1e2 ", 1100000000, " "); + CR("1.1e2x", 1100000000, "x"); + CR("1.1e2:", 1100000000, ":"); +} + +#undef C +#undef CR +#undef F + +#define CW(v, s) buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); \ + CHECK(buffer == s); \ + buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); \ + CHECK(buffer == "-" s); + +TEST_CASE("Writing coordinates into string") { + std::string buffer; + + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); + CHECK(buffer == "0"); + + CW( 10000000, "1"); + CW( 90000000, "9"); + CW( 100000000, "10"); + CW(1000000000, "100"); + CW(2000000000, "200"); + + CW( 1000000, "0.1"); + CW( 100000, "0.01"); + CW( 10000, "0.001"); + CW( 1000, "0.0001"); + CW( 100, "0.00001"); + CW( 10, "0.000001"); + CW( 1, "0.0000001"); + + CW( 1230000, "0.123"); + CW( 9999999, "0.9999999"); + CW( 40101010, "4.010101"); + CW( 494561234, "49.4561234"); + CW(1799999999, "179.9999999"); +} + +#undef CW + +TEST_CASE("set lon/lat from string") { + osmium::Location loc; + loc.set_lon("1.2"); + loc.set_lat("3.4"); + 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); +} + +TEST_CASE("set lon/lat from string with trailing characters using partial") { + osmium::Location loc; + const char* x = "1.2x"; + const char* y = "3.4 "; + loc.set_lon_partial(&x); + loc.set_lat_partial(&y); + REQUIRE(loc.lon() == Approx(1.2)); + REQUIRE(loc.lat() == Approx(3.4)); + REQUIRE(*x == 'x'); + REQUIRE(*y == ' '); +} + diff --git a/third_party/libosmium/test/t/basic/test_node.cpp b/third_party/libosmium/test/t/basic/test_node.cpp index b7f4c6cce4f..e5dbe8a7ca2 100644 --- a/third_party/libosmium/test/t/basic/test_node.cpp +++ b/third_party/libosmium/test/t/basic/test_node.cpp @@ -9,7 +9,7 @@ using namespace osmium::builder::attr; TEST_CASE("Build node") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(17), @@ -36,7 +36,7 @@ TEST_CASE("Build node") { REQUIRE(false == node.deleted()); REQUIRE(333 == node.changeset()); REQUIRE(21 == node.uid()); - REQUIRE(std::string("foo") == node.user()); + REQUIRE(std::string{"foo"} == node.user()); REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); REQUIRE(2 == node.tags().size()); @@ -51,7 +51,7 @@ TEST_CASE("Build node") { } TEST_CASE("default values for node attributes") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(0)); @@ -62,22 +62,23 @@ TEST_CASE("default values for node attributes") { REQUIRE(true == node.visible()); REQUIRE(0 == node.changeset()); REQUIRE(0 == node.uid()); - REQUIRE(std::string("") == node.user()); + REQUIRE(std::string{} == node.user()); REQUIRE(0 == uint32_t(node.timestamp())); REQUIRE(osmium::Location() == node.location()); REQUIRE(0 == node.tags().size()); } TEST_CASE("set node attributes from strings") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(0)); osmium::Node& node = buffer.get(0); node.set_id("-17") .set_version("3") - .set_visible(true) + .set_visible("true") .set_changeset("333") + .set_timestamp("2014-03-17T16:23:08Z") .set_uid("21"); REQUIRE(-17l == node.id()); @@ -85,11 +86,52 @@ TEST_CASE("set node attributes from strings") { REQUIRE(3 == node.version()); REQUIRE(true == node.visible()); REQUIRE(333 == node.changeset()); + REQUIRE(std::string{"2014-03-17T16:23:08Z"} == node.timestamp().to_iso()); REQUIRE(21 == node.uid()); } +TEST_CASE("set node attributes from strings using set_attribute()") { + osmium::memory::Buffer buffer{10000}; + + osmium::builder::add_node(buffer, _id(0)); + + osmium::Node& node = buffer.get(0); + node.set_attribute("id", "-17") + .set_attribute("version", "3") + .set_attribute("visible", "true") + .set_attribute("changeset", "333") + .set_attribute("timestamp", "2014-03-17T16:23:08Z") + .set_attribute("uid", "21"); + + REQUIRE(-17l == node.id()); + REQUIRE(17ul == node.positive_id()); + REQUIRE(3 == node.version()); + REQUIRE(true == node.visible()); + REQUIRE(333 == node.changeset()); + REQUIRE(std::string{"2014-03-17T16:23:08Z"} == node.timestamp().to_iso()); + REQUIRE(21 == node.uid()); +} + +TEST_CASE("Setting attributes from bad data on strings should fail") { + osmium::memory::Buffer buffer{10000}; + + osmium::builder::add_node(buffer, _id(0)); + + osmium::Node& node = buffer.get(0); + REQUIRE_THROWS(node.set_id("bar")); + REQUIRE_THROWS(node.set_id("123x")); + REQUIRE_THROWS(node.set_version("123x")); + REQUIRE_THROWS(node.set_visible("foo")); + REQUIRE_THROWS(node.set_changeset("123x")); + REQUIRE_THROWS(node.set_changeset("NULL")); + REQUIRE_THROWS(node.set_timestamp("2014-03-17T16:23:08Zx")); + REQUIRE_THROWS(node.set_timestamp("2014-03-17T16:23:99Z")); + REQUIRE_THROWS(node.set_uid("123x")); + REQUIRE_THROWS(node.set_uid("anonymous")); +} + TEST_CASE("set large id") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; int64_t id = 3000000000l; osmium::builder::add_node(buffer, _id(id)); @@ -104,7 +146,7 @@ TEST_CASE("set large id") { } TEST_CASE("set tags on node") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _user("foo"), @@ -114,11 +156,11 @@ TEST_CASE("set tags on node") { const osmium::Node& node = buffer.get(0); REQUIRE(nullptr == node.tags().get_value_by_key("fail")); - REQUIRE(std::string("pub") == node.tags().get_value_by_key("amenity")); - REQUIRE(std::string("pub") == node.get_value_by_key("amenity")); + REQUIRE(std::string{"pub"} == node.tags().get_value_by_key("amenity")); + REQUIRE(std::string{"pub"} == node.get_value_by_key("amenity")); - REQUIRE(std::string("default") == node.tags().get_value_by_key("fail", "default")); - REQUIRE(std::string("pub") == node.tags().get_value_by_key("amenity", "default")); - REQUIRE(std::string("pub") == node.get_value_by_key("amenity", "default")); + REQUIRE(std::string{"default"} == node.tags().get_value_by_key("fail", "default")); + REQUIRE(std::string{"pub"} == node.tags().get_value_by_key("amenity", "default")); + REQUIRE(std::string{"pub"} == node.get_value_by_key("amenity", "default")); } diff --git a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp index 461969fe917..42bcbff6364 100644 --- a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp +++ b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp @@ -1,76 +1,80 @@ #include "catch.hpp" +#include +#include +#include + #include #include #include #include -TEST_CASE("Object_Comparisons") { - - using namespace osmium::builder::attr; - - SECTION("order") { - osmium::memory::Buffer buffer(10 * 1000); - - osmium::builder::add_node(buffer, _id(10), _version(1)); - osmium::builder::add_node(buffer, _id(15), _version(2)); - - auto it = buffer.begin(); - osmium::Node& node1 = static_cast(*it); - osmium::Node& node2 = static_cast(*(++it)); - - REQUIRE(node1 < node2); - REQUIRE_FALSE(node1 > node2); - node1.set_id(20); - node1.set_version(1); - node2.set_id(20); - node2.set_version(2); - REQUIRE(node1 < node2); - REQUIRE_FALSE(node1 > node2); - node1.set_id(-10); - node1.set_version(2); - node2.set_id(-15); - node2.set_version(1); - REQUIRE(node1 < node2); - REQUIRE_FALSE(node1 > node2); +using namespace osmium::builder::attr; + +TEST_CASE("Node comparisons") { + + osmium::memory::Buffer buffer(10 * 1000); + std::vector> nodes; + + SECTION("nodes are ordered by id, version, and timestamp") { + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + + REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend())); } - SECTION("order_types") { - osmium::memory::Buffer buffer(10 * 1000); - - osmium::builder::add_node(buffer, _id(3), _version(3)); - osmium::builder::add_node(buffer, _id(3), _version(4)); - osmium::builder::add_node(buffer, _id(3), _version(4)); - osmium::builder::add_way(buffer, _id(2), _version(2)); - osmium::builder::add_relation(buffer, _id(1), _version(1)); - - auto it = buffer.begin(); - const osmium::Node& node1 = static_cast(*it); - const osmium::Node& node2 = static_cast(*(++it)); - const osmium::Node& node3 = static_cast(*(++it)); - const osmium::Way& way = static_cast(*(++it)); - const osmium::Relation& relation = static_cast(*(++it)); - - REQUIRE(node1 < node2); - REQUIRE(node2 < way); - REQUIRE_FALSE(node2 > way); - REQUIRE(way < relation); - REQUIRE(node1 < relation); - - REQUIRE(osmium::object_order_type_id_version()(node1, node2)); - REQUIRE(osmium::object_order_type_id_reverse_version()(node2, node1)); - REQUIRE(osmium::object_order_type_id_version()(node1, way)); - REQUIRE(osmium::object_order_type_id_reverse_version()(node1, way)); - - REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, node2)); - REQUIRE(osmium::object_equal_type_id_version()(node2, node3)); - - REQUIRE(osmium::object_equal_type_id()(node1, node2)); - REQUIRE(osmium::object_equal_type_id()(node2, node3)); - - REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, way)); - REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, relation)); - REQUIRE_FALSE(osmium::object_equal_type_id()(node1, relation)); + SECTION("equal nodes are not different") { + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + + REQUIRE(nodes[0] == nodes[1]); + REQUIRE_FALSE(nodes[0] < nodes[1]); + REQUIRE_FALSE(nodes[0] > nodes[1]); + } + + SECTION("IDs are ordered by 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)))); + + REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend())); + } + + SECTION("reverse version ordering") { + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + + REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend(), osmium::object_order_type_id_reverse_version{})); } } + +TEST_CASE("Object comparisons") { + + osmium::memory::Buffer buffer(10 * 1000); + std::vector> objects; + + SECTION("types are ordered nodes, then ways, then relations") { + objects.emplace_back(buffer.get( osmium::builder::add_node( buffer, _id(3)))); + objects.emplace_back(buffer.get( osmium::builder::add_way( buffer, _id(2)))); + objects.emplace_back(buffer.get(osmium::builder::add_relation(buffer, _id(1)))); + + REQUIRE(std::is_sorted(objects.cbegin(), objects.cend())); + } + +} + diff --git a/third_party/libosmium/test/t/basic/test_timestamp.cpp b/third_party/libosmium/test/t/basic/test_timestamp.cpp index f80ffcf5f29..561a705b55e 100644 --- a/third_party/libosmium/test/t/basic/test_timestamp.cpp +++ b/third_party/libosmium/test/t/basic/test_timestamp.cpp @@ -75,3 +75,47 @@ TEST_CASE("Timestamp") { } } + +TEST_CASE("Valid timestamps") { + + std::vector test_cases = { + "1970-01-01T00:00:01Z", + "2000-01-01T00:00:00Z", + "2006-12-31T23:59:59Z", + "2030-12-31T23:59:59Z", + "2016-02-28T23:59:59Z", + "2016-03-31T23:59:59Z" + }; + + for (const auto& tc : test_cases) { + 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{"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{"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{"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{"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); +} + diff --git a/third_party/libosmium/test/t/basic/test_types_from_string.cpp b/third_party/libosmium/test/t/basic/test_types_from_string.cpp index 2481ae87649..2866b8f74d5 100644 --- a/third_party/libosmium/test/t/basic/test_types_from_string.cpp +++ b/third_party/libosmium/test/t/basic/test_types_from_string.cpp @@ -31,9 +31,17 @@ TEST_CASE("set type and ID from string") { REQUIRE(r_2.first == osmium::item_type::relation); REQUIRE(r_2.second == -2); - auto x3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr); - REQUIRE(x3.first == osmium::item_type::undefined); - REQUIRE(x3.second == 3); + 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); + 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); + 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); diff --git a/third_party/libosmium/test/t/basic/test_way.cpp b/third_party/libosmium/test/t/basic/test_way.cpp index c4fa9d54257..005ef300061 100644 --- a/third_party/libosmium/test/t/basic/test_way.cpp +++ b/third_party/libosmium/test/t/basic/test_way.cpp @@ -46,7 +46,7 @@ TEST_CASE("Build way") { osmium::CRC crc32; crc32.update(way); - REQUIRE(crc32().checksum() == 0x7676d0c2); + REQUIRE(crc32().checksum() == 0x65f6ba91); } TEST_CASE("build closed way") { diff --git a/third_party/libosmium/test/t/builder/test_attr.cpp b/third_party/libosmium/test/t/builder/test_attr.cpp index 258ae7a911b..dedde77a7d3 100644 --- a/third_party/libosmium/test/t/builder/test_attr.cpp +++ b/third_party/libosmium/test/t/builder/test_attr.cpp @@ -67,14 +67,14 @@ TEST_CASE("create node using builders") { osmium::builder::add_node(buffer, _id(5), _visible(true)); osmium::builder::add_node(buffer, _id(6), _visible(false)); - auto it = buffer.cbegin(); + auto it = buffer.select().cbegin(); REQUIRE_FALSE(it++->visible()); REQUIRE_FALSE(it++->visible()); REQUIRE(it++->visible()); REQUIRE(it++->visible()); REQUIRE(it++->visible()); REQUIRE_FALSE(it++->visible()); - REQUIRE(it == buffer.cend()); + REQUIRE(it == buffer.select().cend()); } SECTION("order of attributes doesn't matter") { diff --git a/third_party/libosmium/test/t/geom/test_geojson.cpp b/third_party/libosmium/test/t/geom/test_geojson.cpp index a9e839822c8..725be7fb940 100644 --- a/third_party/libosmium/test/t/geom/test_geojson.cpp +++ b/third_party/libosmium/test/t/geom/test_geojson.cpp @@ -96,7 +96,7 @@ SECTION("area_1outer_0inner") { REQUIRE(!area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 2); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().first); { std::string json {factory.create_multipolygon(area)}; @@ -112,8 +112,8 @@ SECTION("area_1outer_1inner") { REQUIRE(!area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 3); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().first); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().second); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().second); { std::string json {factory.create_multipolygon(area)}; @@ -129,24 +129,24 @@ SECTION("area_2outer_2inner") { REQUIRE(area.is_multipolygon()); REQUIRE(std::distance(area.cbegin(), area.cend()) == 5); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().first); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().second); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().second); int outer_ring=0; int inner_ring=0; - for (auto it_outer = area.cbegin(); it_outer != area.cend(); ++it_outer) { + for (const auto& outer : area.outer_rings()) { if (outer_ring == 0) { - REQUIRE(it_outer->front().ref() == 1); + REQUIRE(outer.front().ref() == 1); } else if (outer_ring == 1) { - REQUIRE(it_outer->front().ref() == 100); + REQUIRE(outer.front().ref() == 100); } else { REQUIRE(false); } - for (auto it_inner = area.inner_ring_cbegin(it_outer); it_inner != area.inner_ring_cend(it_outer); ++it_inner) { + for (const auto& inner : area.inner_rings(outer)) { if (outer_ring == 0 && inner_ring == 0) { - REQUIRE(it_inner->front().ref() == 5); + REQUIRE(inner.front().ref() == 5); } else if (outer_ring == 0 && inner_ring == 1) { - REQUIRE(it_inner->front().ref() == 10); + REQUIRE(inner.front().ref() == 10); } else { REQUIRE(false); } diff --git a/third_party/libosmium/test/t/geom/test_geos.cpp b/third_party/libosmium/test/t/geom/test_geos.cpp index 8bf11c93e92..f74027c24b0 100644 --- a/third_party/libosmium/test/t/geom/test_geos.cpp +++ b/third_party/libosmium/test/t/geom/test_geos.cpp @@ -1,6 +1,7 @@ #include "catch.hpp" #include +#include #include "area_helper.hpp" #include "wnl_helper.hpp" @@ -11,16 +12,16 @@ TEST_CASE("GEOS geometry factory - create point") { std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; REQUIRE(3.2 == point->getX()); REQUIRE(4.2 == point->getY()); - REQUIRE(-1 == point->getSRID()); + REQUIRE(4326 == point->getSRID()); } -TEST_CASE("GEOS geometry factory - create point with non-default srid") { - osmium::geom::GEOSFactory<> factory(4326); +TEST_CASE("GEOS geometry factory - create point in web mercator") { + osmium::geom::GEOSFactory factory; std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(3.2 == point->getX()); - REQUIRE(4.2 == point->getY()); - REQUIRE(4326 == point->getSRID()); + REQUIRE(Approx(356222.3705384755l) == point->getX()); + REQUIRE(Approx(467961.143605213l) == point->getY()); + REQUIRE(3857 == point->getSRID()); } TEST_CASE("GEOS geometry factory - create point with externally created GEOS factory") { @@ -89,6 +90,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings REQUIRE(1 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); + REQUIRE(p0); REQUIRE(0 == p0->getNumInteriorRing()); const geos::geom::LineString* l0e = p0->getExteriorRing(); @@ -108,6 +110,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring REQUIRE(1 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); + REQUIRE(p0); REQUIRE(1 == p0->getNumInteriorRing()); const geos::geom::LineString* l0e = p0->getExteriorRing(); @@ -127,12 +130,14 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring REQUIRE(2 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); + REQUIRE(p0); REQUIRE(2 == p0->getNumInteriorRing()); const geos::geom::LineString* l0e = p0->getExteriorRing(); REQUIRE(5 == l0e->getNumPoints()); const geos::geom::Polygon* p1 = dynamic_cast(mp->getGeometryN(1)); + REQUIRE(p1); REQUIRE(0 == p1->getNumInteriorRing()); const geos::geom::LineString* l1e = p1->getExteriorRing(); diff --git a/third_party/libosmium/test/t/geom/test_ogr.cpp b/third_party/libosmium/test/t/geom/test_ogr.cpp index 26d34c13abc..3490c5759d9 100644 --- a/third_party/libosmium/test/t/geom/test_ogr.cpp +++ b/third_party/libosmium/test/t/geom/test_ogr.cpp @@ -68,6 +68,7 @@ SECTION("area_1outer_0inner") { REQUIRE(1 == mp->getNumGeometries()); const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); REQUIRE(0 == p0->getNumInteriorRings()); const OGRLineString* l0e = p0->getExteriorRing(); @@ -86,6 +87,7 @@ SECTION("area_1outer_1inner") { REQUIRE(1 == mp->getNumGeometries()); const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); REQUIRE(1 == p0->getNumInteriorRings()); const OGRLineString* l0e = p0->getExteriorRing(); @@ -105,12 +107,14 @@ SECTION("area_2outer_2inner") { REQUIRE(2 == mp->getNumGeometries()); const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); REQUIRE(2 == p0->getNumInteriorRings()); const OGRLineString* l0e = p0->getExteriorRing(); REQUIRE(5 == l0e->getNumPoints()); const OGRPolygon* p1 = dynamic_cast(mp->getGeometryRef(1)); + REQUIRE(p1); REQUIRE(0 == p1->getNumInteriorRings()); const OGRLineString* l1e = p1->getExteriorRing(); diff --git a/third_party/libosmium/test/t/geom/test_tile.cpp b/third_party/libosmium/test/t/geom/test_tile.cpp index e80cb960410..5454fed7909 100644 --- a/third_party/libosmium/test/t/geom/test_tile.cpp +++ b/third_party/libosmium/test/t/geom/test_tile.cpp @@ -8,86 +8,96 @@ #include "test_tile_data.hpp" -TEST_CASE("Tile") { +TEST_CASE("Tile from x0.0 y0.0 at zoom 0") { + osmium::Location l{0.0, 0.0}; - SECTION("x0.0 y0.0 zoom 0") { - osmium::Location l(0.0, 0.0); + osmium::geom::Tile t{0, l}; - osmium::geom::Tile t(0, l); + REQUIRE(t.x == 0); + REQUIRE(t.y == 0); + REQUIRE(t.z == 0); + REQUIRE(t.valid()); +} - REQUIRE(t.x == 0); - REQUIRE(t.y == 0); - REQUIRE(t.z == 0); - } +TEST_CASE("Tile from x180.0 y90.0 at zoom 0") { + osmium::Location l{180.0, 90.0}; - SECTION("x180.0 y90.0 zoom 0") { - osmium::Location l(180.0, 90.0); + osmium::geom::Tile t{0, l}; - osmium::geom::Tile t(0, l); + REQUIRE(t.x == 0); + REQUIRE(t.y == 0); + REQUIRE(t.z == 0); + REQUIRE(t.valid()); +} - REQUIRE(t.x == 0); - REQUIRE(t.y == 0); - REQUIRE(t.z == 0); - } +TEST_CASE("Tile from x180.0 y90.0 at zoom 4") { + osmium::Location l{180.0, 90.0}; - SECTION("x180.0 y90.0 zoom 4") { - osmium::Location l(180.0, 90.0); + osmium::geom::Tile t{4, l}; - osmium::geom::Tile t(4, l); + REQUIRE(t.x == (1 << 4) - 1); + REQUIRE(t.y == 0); + REQUIRE(t.z == 4); + REQUIRE(t.valid()); +} - REQUIRE(t.x == (1 << 4) - 1); - REQUIRE(t.y == 0); - REQUIRE(t.z == 4); - } +TEST_CASE("Tile from x0.0 y0.0 at zoom 4") { + osmium::Location l{0.0, 0.0}; - SECTION("x0.0 y0.0 zoom 4") { - osmium::Location l(0.0, 0.0); + osmium::geom::Tile t{4, l}; - osmium::geom::Tile t(4, l); + auto n = 1 << (4-1); + REQUIRE(t.x == n); + REQUIRE(t.y == n); + REQUIRE(t.z == 4); + REQUIRE(t.valid()); +} - auto n = 1 << (4-1); - REQUIRE(t.x == n); - REQUIRE(t.y == n); - REQUIRE(t.z == 4); - } +TEST_CASE("Tile from max values at zoom 4") { + osmium::geom::Tile t{4u, 15u, 15u}; + REQUIRE(t.valid()); +} - SECTION("equality") { - osmium::geom::Tile a(4, 3, 4); - osmium::geom::Tile b(4, 3, 4); - osmium::geom::Tile c(4, 4, 3); - REQUIRE(a == b); - REQUIRE(a != c); - REQUIRE(b != c); - } +TEST_CASE("Tile from max values at zoom 30") { + osmium::geom::Tile t{30u, (1u<<30) - 1, (1u<<30) - 1}; + REQUIRE(t.valid()); +} - SECTION("order") { - osmium::geom::Tile a(2, 3, 4); - osmium::geom::Tile b(4, 3, 4); - osmium::geom::Tile c(4, 4, 3); - osmium::geom::Tile d(4, 4, 2); - REQUIRE(a < b); - REQUIRE(a < c); - REQUIRE(b < c); - REQUIRE(d < c); - } +TEST_CASE("Tile equality") { + osmium::geom::Tile a{4, 3, 4}; + osmium::geom::Tile b{4, 3, 4}; + osmium::geom::Tile c{4, 4, 3}; + REQUIRE(a == b); + REQUIRE(a != c); + REQUIRE(b != c); +} - SECTION("tilelist") { - std::istringstream input_data(s); - while (input_data) { - double lon, lat; - uint32_t x, y, zoom; - input_data >> lon; - input_data >> lat; - input_data >> x; - input_data >> y; - input_data >> zoom; - - osmium::Location l(lon, lat); - osmium::geom::Tile t(zoom, l); - REQUIRE(t.x == x); - REQUIRE(t.y == y); - } - } +TEST_CASE("Tile order") { + osmium::geom::Tile a{4, 3, 4}; + osmium::geom::Tile b{6, 3, 4}; + osmium::geom::Tile c{6, 4, 3}; + osmium::geom::Tile d{6, 4, 2}; + REQUIRE(a < b); + REQUIRE(a < c); + REQUIRE(b < c); + REQUIRE(d < c); +} +TEST_CASE("Check a random list of tiles") { + std::istringstream input_data(s); + while (input_data) { + double lon, lat; + uint32_t x, y, zoom; + input_data >> lon; + input_data >> lat; + input_data >> x; + input_data >> y; + input_data >> zoom; + + osmium::Location l{lon, lat}; + osmium::geom::Tile t{zoom, l}; + REQUIRE(t.x == x); + REQUIRE(t.y == y); + } } diff --git a/third_party/libosmium/test/t/geom/test_wkb.cpp b/third_party/libosmium/test/t/geom/test_wkb.cpp index 49710cbc2f5..d4d9228689f 100644 --- a/third_party/libosmium/test/t/geom/test_wkb.cpp +++ b/third_party/libosmium/test/t/geom/test_wkb.cpp @@ -1,115 +1,135 @@ #include "catch.hpp" +#include #include #include "wnl_helper.hpp" #if __BYTE_ORDER == __LITTLE_ENDIAN -TEST_CASE("WKB_Geometry_byte_order_dependent") { +TEST_CASE("WKB geometry factory (byte-order-dependant), points") { + const osmium::Location loc{3.2, 4.2}; -SECTION("point") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - - std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"01010000009A99999999990940CDCCCCCCCCCC1040"} == wkb); -} + SECTION("point") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("point_ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "01010000009A99999999990940CDCCCCCCCCCC1040"); + } - std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"0101000020E61000009A99999999990940CDCCCCCCCCCC1040"} == wkb); -} + SECTION("point in web mercator") { + osmium::geom::WKBFactory factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("linestring") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "010100000028706E7BF9BD1541B03E0D93E48F1C41"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); + SECTION("point in ewkb") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); - { - std::string wkb {factory.create_linestring(wnl)}; - REQUIRE(std::string{"0102000000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040"); } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; - REQUIRE(std::string{"010200000003000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"} == wkb); - } + SECTION("point in ewkb in web mercator") { + osmium::geom::WKBFactory factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"0102000000040000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41"); } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"010200000004000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"} == wkb); - } } -SECTION("linestring_ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); +TEST_CASE("WKB geometry factory (byte-order-dependant)") { - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); + osmium::memory::Buffer buffer{10000}; - std::string ewkb {factory.create_linestring(wnl)}; - REQUIRE(std::string{"0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == ewkb); -} + SECTION("linestring") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const auto& wnl = create_test_wnl_okay(buffer); -SECTION("linestring_with_two_same_locations") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + { + const std::string wkb{factory.create_linestring(wnl)}; + REQUIRE(wkb == "0102000000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_same_location(buffer); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + REQUIRE(wkb == "010200000003000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"); + } - 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); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkb == "0102000000040000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); + } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkb == "010200000004000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"); + } } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb); + SECTION("linestring as ewkb") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + + const auto& wnl = create_test_wnl_okay(buffer); + + const std::string ewkb{factory.create_linestring(wnl)}; + REQUIRE(ewkb == "0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); } -} -SECTION("linestring_with_undefined_location") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + SECTION("linestring with two same locations") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_undefined_location(buffer); + const auto& wnl = create_test_wnl_same_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); -} + SECTION("unique forwards (default)") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl), 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); + } -#endif + SECTION("all forwards") { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkb == "0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"); + } + + SECTION("all backwards") { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkb == "0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"); + } + } -TEST_CASE("WKB_Geometry_byte_order_independent") { + SECTION("linestring with undefined location") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("empty_point") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const auto& wnl = create_test_wnl_undefined_location(buffer); + + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("empty_linestring") { +#endif + +TEST_CASE("WKB geometry (byte-order-independent)") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_empty(buffer); + SECTION("empty point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + } - 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); -} + 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); + } } diff --git a/third_party/libosmium/test/t/geom/test_wkt.cpp b/third_party/libosmium/test/t/geom/test_wkt.cpp index 55ccb4ada59..f6913c40b81 100644 --- a/third_party/libosmium/test/t/geom/test_wkt.cpp +++ b/third_party/libosmium/test/t/geom/test_wkt.cpp @@ -1,136 +1,133 @@ #include "catch.hpp" +#include #include #include "area_helper.hpp" #include "wnl_helper.hpp" -TEST_CASE("WKT_Geometry") { +TEST_CASE("WKT geometry for point") { -SECTION("point") { osmium::geom::WKTFactory<> factory; - std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"POINT(3.2 4.2)"} == wkt); -} + 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") { - osmium::geom::WKTFactory<> factory; + SECTION("empty point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("linestring") { - osmium::geom::WKTFactory<> factory; - - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); - - { - std::string wkt {factory.create_linestring(wnl)}; - REQUIRE(std::string{"LINESTRING(3.2 4.2,3.5 4.7,3.6 4.9)"} == wkt); - } +TEST_CASE("WKT geometry for point in ekwt") { + osmium::geom::WKTFactory<> factory(7, osmium::geom::wkt_type::ewkt); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.6 4.9,3.5 4.7,3.2 4.2)"} == wkt); - } + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "SRID=4326;POINT(3.2 4.2)"); +} - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"LINESTRING(3.2 4.2,3.5 4.7,3.5 4.7,3.6 4.9)"} == wkt); - } +TEST_CASE("WKT geometry for point in ekwt in web mercator") { + osmium::geom::WKTFactory factory(2, osmium::geom::wkt_type::ewkt); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.6 4.9,3.5 4.7,3.5 4.7,3.2 4.2)"} == wkt); - } + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "SRID=3857;POINT(356222.37 467961.14)"); } -SECTION("empty_linestring") { +TEST_CASE("WKT geometry factory") { osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_empty(buffer); + osmium::memory::Buffer buffer{10000}; - 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); -} + SECTION("linestring") { + const auto& wnl = create_test_wnl_okay(buffer); -SECTION("linestring_with_two_same_locations") { - osmium::geom::WKTFactory<> factory; + SECTION("unique forwards (default)") { + const std::string wkt{factory.create_linestring(wnl)}; + REQUIRE(wkt == "LINESTRING(3.2 4.2,3.5 4.7,3.6 4.9)"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_same_location(buffer); + SECTION("unique backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.6 4.9,3.5 4.7,3.2 4.2)"); + } - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + SECTION("all forwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkt == "LINESTRING(3.2 4.2,3.5 4.7,3.5 4.7,3.6 4.9)"); + } - try { - factory.create_linestring(wnl); - } catch (osmium::geometry_error& e) { - REQUIRE(e.id() == 0); - REQUIRE(std::string(e.what()) == "need at least two points for linestring"); + SECTION("all backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.6 4.9,3.5 4.7,3.5 4.7,3.2 4.2)"); + } } - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + SECTION("empty linestring") { + const auto& wnl = create_test_wnl_empty(buffer); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"LINESTRING(3.5 4.7,3.5 4.7)"} == wkt); + 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); } - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.5 4.7,3.5 4.7)"} == wkt); + 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); + + try { + factory.create_linestring(wnl); + } catch (const osmium::geometry_error& e) { + REQUIRE(e.id() == 0); + REQUIRE(std::string(e.what()) == "need at least two points for linestring"); + } + } + + SECTION("unique backwards") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + } + + SECTION("all forwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkt == "LINESTRING(3.5 4.7,3.5 4.7)"); + } + + SECTION("all backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.5 4.7,3.5 4.7)"); + } } -} - -SECTION("linestring_with_undefined_location") { - osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_undefined_location(buffer); + SECTION("linestring with undefined location") { + const auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); -} - -SECTION("area_1outer_0inner") { - osmium::geom::WKTFactory<> factory; + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + } - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); + SECTION("area with one outer and no inner rings") { + const osmium::Area& area = create_test_area_1outer_0inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((3.2 4.2,3.5 4.7,3.6 4.9,3.2 4.2)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((3.2 4.2,3.5 4.7,3.6 4.9,3.2 4.2)))"); } -} - -SECTION("area_1outer_1inner") { - osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + SECTION("area with one outer and one inner ring") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,8 1,8 8,1 8,1 1)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,8 1,8 8,1 8,1 1)))"); } -} -SECTION("area_2outer_2inner") { - osmium::geom::WKTFactory<> factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); + SECTION("area with two outer and two inner rings") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,4 1,4 4,1 4,1 1),(5 5,5 7,7 7,5 5)),((10 10,11 10,11 11,10 11,10 10)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,4 1,4 4,1 4,1 1),(5 5,5 7,7 7,5 5)),((10 10,11 10,11 11,10 11,10 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 new file mode 100644 index 00000000000..42cf5745680 --- /dev/null +++ b/third_party/libosmium/test/t/index/test_file_based_index.cpp @@ -0,0 +1,155 @@ + +#include "catch.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +TEST_CASE("File based index") { + + int fd = osmium::detail::create_tmp_file(); + + REQUIRE(osmium::util::file_size(fd) == 0); + + const osmium::unsigned_object_id_type id1 = 6; + const osmium::unsigned_object_id_type id2 = 3; + const osmium::Location loc1(1.2, 4.5); + const osmium::Location loc2(3.5, -7.2); + + SECTION("dense index") { + using index_type = osmium::index::map::DenseFileArray; + constexpr const size_t S = sizeof(index_type::element_type); + + { + 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); + + index.set(id1, loc1); + REQUIRE(index.size() == 7); + + index.set(id2, loc2); + REQUIRE(index.size() == 7); + + index.sort(); + + 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(index.size() == 7); + REQUIRE(std::distance(index.cbegin(), index.cend()) == 7); + + REQUIRE(osmium::util::file_size(fd) >= (6 * S)); + } + + { + index_type index(fd); + REQUIRE(osmium::util::file_size(fd) >= (6 * S)); + + REQUIRE(index.size() == 7); + + 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(index.size() == 7); + REQUIRE(std::distance(index.cbegin(), index.cend()) == 7); + + auto it = index.cbegin(); + REQUIRE(*it++ == osmium::Location{}); + REQUIRE(*it++ == osmium::Location{}); + REQUIRE(*it++ == osmium::Location{}); + REQUIRE(*it++ == loc2); + REQUIRE(*it++ == osmium::Location{}); + REQUIRE(*it++ == osmium::Location{}); + REQUIRE(*it++ == loc1); + REQUIRE(it++ == index.cend()); + } + } + + SECTION("sparse index") { + using index_type = osmium::index::map::SparseFileArray; + constexpr const size_t S = sizeof(index_type::element_type); + + { + 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); + + index.set(id1, loc1); + REQUIRE(index.size() == 1); + + index.set(id2, loc2); + REQUIRE(index.size() == 2); + + index.sort(); + + 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(index.size() == 2); + REQUIRE(std::distance(index.cbegin(), index.cend()) == 2); + + REQUIRE(osmium::util::file_size(fd) >= (2 * S)); + } + + { + index_type index(fd); + REQUIRE(osmium::util::file_size(fd) >= (2 * S)); + + REQUIRE(index.size() == 2); + + 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(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 4aca238b690..810ef3be97e 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 @@ -19,8 +19,8 @@ template void test_func_all(TIndex& index) { osmium::unsigned_object_id_type id1 = 12; osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1(1.2, 4.5); - osmium::Location loc2(3.5, -7.2); + osmium::Location loc1{1.2, 4.5}; + osmium::Location loc2{3.5, -7.2}; REQUIRE_THROWS_AS(index.get(id1), osmium::not_found); @@ -29,6 +29,8 @@ void test_func_all(TIndex& index) { 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); } @@ -37,8 +39,8 @@ template void test_func_real(TIndex& index) { osmium::unsigned_object_id_type id1 = 12; osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1(1.2, 4.5); - osmium::Location loc2(3.5, -7.2); + osmium::Location loc1{1.2, 4.5}; + osmium::Location loc2{3.5, -7.2}; index.set(id1, loc1); index.set(id2, loc2); @@ -48,18 +50,26 @@ void test_func_real(TIndex& 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(100), osmium::not_found); 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(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); } TEST_CASE("IdToLocation") { SECTION("Dummy") { - typedef osmium::index::map::Dummy index_type; + using index_type = osmium::index::map::Dummy; index_type index1; @@ -73,7 +83,7 @@ TEST_CASE("IdToLocation") { } SECTION("DenseMemArray") { - typedef osmium::index::map::DenseMemArray index_type; + using index_type = osmium::index::map::DenseMemArray; index_type index1; index1.reserve(1000); @@ -86,7 +96,7 @@ TEST_CASE("IdToLocation") { #ifdef __linux__ SECTION("DenseMmapArray") { - typedef osmium::index::map::DenseMmapArray index_type; + using index_type = osmium::index::map::DenseMmapArray; index_type index1; test_func_all(index1); @@ -99,7 +109,7 @@ TEST_CASE("IdToLocation") { #endif SECTION("DenseFileArray") { - typedef osmium::index::map::DenseFileArray index_type; + using index_type = osmium::index::map::DenseFileArray; index_type index1; test_func_all(index1); @@ -111,7 +121,7 @@ TEST_CASE("IdToLocation") { #ifdef OSMIUM_WITH_SPARSEHASH SECTION("SparseMemTable") { - typedef osmium::index::map::SparseMemTable index_type; + using index_type = osmium::index::map::SparseMemTable; index_type index1; test_func_all(index1); @@ -123,7 +133,7 @@ TEST_CASE("IdToLocation") { #endif SECTION("SparseMemMap") { - typedef osmium::index::map::SparseMemMap index_type; + using index_type = osmium::index::map::SparseMemMap; index_type index1; test_func_all(index1); @@ -133,7 +143,7 @@ TEST_CASE("IdToLocation") { } SECTION("SparseMemArray") { - typedef osmium::index::map::SparseMemArray index_type; + using index_type = osmium::index::map::SparseMemArray; index_type index1; @@ -149,10 +159,10 @@ TEST_CASE("IdToLocation") { } SECTION("Dynamic map choice") { - typedef osmium::index::map::Map map_type; + using map_type = osmium::index::map::Map; const auto& map_factory = osmium::index::MapFactory::instance(); - std::vector map_type_names = map_factory.map_types(); + const std::vector map_type_names = map_factory.map_types(); REQUIRE(map_type_names.size() >= 5); for (const auto& map_type_name : map_type_names) { diff --git a/third_party/libosmium/test/t/io/test_opl_parser.cpp b/third_party/libosmium/test/t/io/test_opl_parser.cpp new file mode 100644 index 00000000000..9ad6eeb754e --- /dev/null +++ b/third_party/libosmium/test/t/io/test_opl_parser.cpp @@ -0,0 +1,1075 @@ + +#include +#include + +#include "catch.hpp" + +#include +#include + +namespace oid = osmium::io::detail; + +TEST_CASE("Parse OPL: base exception") { + osmium::opl_error e{"foo"}; + REQUIRE(e.data == nullptr); + REQUIRE(e.line == 0); + REQUIRE(e.column == 0); + REQUIRE(e.msg == "OPL error: foo"); + REQUIRE(std::string{e.what()} == "OPL error: foo"); +} + +TEST_CASE("Parse OPL: exception with line and column") { + const char* d = "data"; + osmium::opl_error e{"bar", d}; + e.set_pos(17, 23); + REQUIRE(e.data == d); + REQUIRE(e.line == 17); + REQUIRE(e.column == 23); + REQUIRE(e.msg == "OPL error: bar on line 17 column 23"); + REQUIRE(std::string{e.what()} == "OPL error: bar on line 17 column 23"); +} + +TEST_CASE("Parse OPL: space") { + std::string d{"a b \t c"}; + + const char* s = d.data(); + REQUIRE_THROWS_AS({ + oid::opl_parse_space(&s); + }, 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); + + ++s; + oid::opl_parse_space(&s); + REQUIRE(*s == 'c'); +} + +TEST_CASE("Parse OPL: check for space") { + REQUIRE(oid::opl_non_empty("aaa")); + REQUIRE(oid::opl_non_empty("a b")); + REQUIRE_FALSE(oid::opl_non_empty(" ")); + REQUIRE_FALSE(oid::opl_non_empty(" x")); + REQUIRE_FALSE(oid::opl_non_empty("\tx")); + REQUIRE_FALSE(oid::opl_non_empty("")); +} + +TEST_CASE("Parse OPL: skip section") { + std::string d{"abcd efgh"}; + const char* skip1 = d.data() + 4; + const char* skip2 = d.data() + 9; + const char* s = d.data(); + REQUIRE(oid::opl_skip_section(&s) == skip1); + REQUIRE(s == skip1); + ++s; + REQUIRE(oid::opl_skip_section(&s) == skip2); + REQUIRE(s == skip2); +} + +TEST_CASE("Parse OPL: parse escaped") { + std::string result; + + SECTION("Empty string") { + const char* s = ""; + 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"); + } + + 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"); + } + + SECTION("Too long") { + const char* s = "123456780"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_escaped(&s, result); + }, "OPL error: hex escape too long"); + } + + SECTION("No data") { + const char* s = "%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result[0] == '\0'); + REQUIRE(s == e); + } + + SECTION("One hex char") { + const char* s = "9%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "\t"); + REQUIRE(s == e); + } + + SECTION("Two hex chars (lowercase)") { + const char* s = "3c%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "<"); + REQUIRE(s == e); + } + + SECTION("Two hex char (uppercase)") { + const char* s = "3C%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "<"); + REQUIRE(s == e); + } + + SECTION("Longer unicode characters") { + const char* s1 = "30dc%"; + oid::opl_parse_escaped(&s1, result); + result.append("_"); + const char* s2 = "1d11e%"; + oid::opl_parse_escaped(&s2, result); + result.append("_"); + const char* s3 = "1f6eb%"; + oid::opl_parse_escaped(&s3, result); + REQUIRE(result == u8"\u30dc_\U0001d11e_\U0001f6eb"); + } + + SECTION("Data after %") { + const char* s = "5a%abc"; + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "Z"); + REQUIRE(std::string{s} == "abc"); + } + +} + +TEST_CASE("Parse OPL: parse string") { + std::string result; + + SECTION("empty string") { + const char* s = ""; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 0); + REQUIRE(result == ""); + REQUIRE(s == e); + } + + SECTION("normal string") { + const char* s = "foo"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with space") { + const char* s = "foo bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with tab") { + const char* s = "foo\tbar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with comma") { + const char* s = "foo,bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with equal sign") { + const char* s = "foo=bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with escaped characters") { + const char* s = "foo%3d%bar"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 7); + REQUIRE(result == "foo=bar"); + REQUIRE(s == e); + } + + SECTION("string with escaped characters at end") { + const char* s = "foo%3d%"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 4); + REQUIRE(result == "foo="); + REQUIRE(s == e); + } + + SECTION("string with invalid escaping") { + const char* s = "foo%"; + 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"); + } + +} + +template +T test_parse_int(const char* s) { + auto r = oid::opl_parse_int(&s); + REQUIRE(*s == 'x'); + return r; +} + +TEST_CASE("Parse OPL: integer") { + REQUIRE(test_parse_int("0x") == 0); + REQUIRE(test_parse_int("-0x") == 0); + REQUIRE(test_parse_int("1x") == 1); + REQUIRE(test_parse_int("17x") == 17); + REQUIRE(test_parse_int("-1x") == -1); + 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("-x"); + }, "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("99999999999999999999999x"); + }, "OPL error: integer too long"); +} + +TEST_CASE("Parse OPL: int32_t") { + REQUIRE(test_parse_int("0x") == 0); + 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"); +} + +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("12345678901x"); + }, "OPL error: integer too long"); + + REQUIRE_THROWS_WITH({ + test_parse_int("-12345678901x"); + }, "OPL error: integer too long"); +} + +TEST_CASE("Parse OPL: visible flag") { + const char* data = "V"; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_visible(&data)); + REQUIRE(e == data); + +} + +TEST_CASE("Parse OPL: deleted flag") { + const char* data = "D"; + const char* e = data + std::strlen(data); + REQUIRE_FALSE(oid::opl_parse_visible(&data)); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: invalid visible flag") { + const char* data = "x"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_visible(&data); + }, "OPL error: invalid visible flag"); +} + +TEST_CASE("Parse OPL: timestamp (empty)") { + const char* data = ""; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (space)") { + const char* data = " "; + const char* e = data; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (tab)") { + const char* data = "\t"; + const char* e = data; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (invalid)") { + const char* data = "abc"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_timestamp(&data); + }, "OPL error: can not parse timestamp"); +} + +TEST_CASE("Parse OPL: timestamp (valid)") { + const char* data = "2016-03-04T17:28:03Z"; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{"2016-03-04T17:28:03Z"}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: valid timestamp with trailing data") { + const char* data = "2016-03-04T17:28:03Zxxx"; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{"2016-03-04T17:28:03Z"}); + REQUIRE(std::string{data} == "xxx"); +} + +TEST_CASE("Parse OPL: tags") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* data = ""; + REQUIRE_THROWS_WITH({ + oid::opl_parse_tags(data, buffer); + }, "OPL error: expected '='"); + } + + SECTION("One tag") { + const char* data = "foo=bar"; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 1); + REQUIRE(std::string{taglist.begin()->key()} == "foo"); + REQUIRE(std::string{taglist.begin()->value()} == "bar"); + } + + SECTION("Empty key and value are allowed") { + const char* data = "="; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 1); + REQUIRE(std::string{taglist.begin()->key()} == ""); + REQUIRE(std::string{taglist.begin()->value()} == ""); + } + + SECTION("Multiple tags") { + const char* data = "highway=residential,oneway=yes,maxspeed=30"; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 3); + auto it = taglist.cbegin(); + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "residential"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "yes"); + ++it; + REQUIRE(std::string{it->key()} == "maxspeed"); + REQUIRE(std::string{it->value()} == "30"); + ++it; + REQUIRE(it == taglist.cend()); + } + + SECTION("No equal signs") { + const char* data = "a"; + 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 ','"); + } + +} + +TEST_CASE("Parse OPL: nodes") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* const s = ""; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() == 0); + } + + 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'"); + } + + 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"); + } + + SECTION("Valid format: one node") { + const char* const s = "n123"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 1); + REQUIRE(wnl.begin()->ref() == 123); + } + + SECTION("Valid format: two nodes") { + const char* const s = "n123,n456"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + ++it; + REQUIRE(it->ref() == 456); + ++it; + REQUIRE(it == wnl.end()); + } + + SECTION("Trailing comma") { + const char* const s = "n123,n456,"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + ++it; + REQUIRE(it->ref() == 456); + ++it; + REQUIRE(it == wnl.end()); + } + + SECTION("Way nodes with coordinates") { + const char* const s = "n123x1.2y3.4,n456x33y0.1"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + const osmium::Location loc1{1.2, 3.4}; + REQUIRE(it->location() == loc1); + ++it; + REQUIRE(it->ref() == 456); + const osmium::Location loc2{33.0, 0.1}; + REQUIRE(it->location() == loc2); + ++it; + REQUIRE(it == wnl.end()); + } + +} + +TEST_CASE("Parse OPL: members") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* const s = ""; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() == 0); + } + + 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"); + } + + 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"); + } + + 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 '@'"); + } + + SECTION("Valid format: one member") { + const char* const s = "n123@foo"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 1); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == "foo"); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Valid format: one member without role") { + const char* const s = "n123@"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 1); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Valid format: three members") { + const char* const s = "n123@,w456@abc,r78@type"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 3); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 456); + REQUIRE(std::string{it->role()} == "abc"); + ++it; + REQUIRE(it->type() == osmium::item_type::relation); + REQUIRE(it->ref() == 78); + REQUIRE(std::string{it->role()} == "type"); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Trailing comma") { + const char* const s = "n123@,w456@abc,r78@type,"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 3); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 456); + REQUIRE(std::string{it->role()} == "abc"); + ++it; + REQUIRE(it->type() == osmium::item_type::relation); + REQUIRE(it->ref() == 78); + REQUIRE(std::string{it->role()} == "type"); + ++it; + REQUIRE(it == rml.end()); + } + + +} + +TEST_CASE("Parse node") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Node with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + } + + SECTION("Node with trailing space") { + const char* s = "17 "; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + } + + SECTION("Node with id and version") { + const char* s = "17 v23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Node with multiple spaces") { + const char* s = "17 v23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Node with tab instead of space") { + const char* s = "17\tv23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Full node (no tags)") { + const char* s = "125799 v6 dV c7711393 t2011-03-29T21:43:10Z i45445 uUScha T x8.7868047 y53.0749415"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 125799); + REQUIRE(node.version() == 6); + REQUIRE(node.visible()); + REQUIRE(node.changeset() == 7711393); + REQUIRE(node.timestamp() == osmium::Timestamp{"2011-03-29T21:43:10Z"}); + REQUIRE(node.uid() == 45445); + REQUIRE(std::string{node.user()} == "UScha"); + osmium::Location loc{8.7868047, 53.0749415}; + REQUIRE(node.location() == loc); + REQUIRE(node.tags().empty()); + } + + SECTION("Node with tags)") { + const char* s = "123 v1 c456 Thighway=residential,oneway=true,name=High%20%Street"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 123); + REQUIRE(node.version() == 1); + REQUIRE(node.changeset() == 456); + REQUIRE(node.tags().size() == 3); + + auto it = node.tags().cbegin(); + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "residential"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "true"); + ++it; + REQUIRE(std::string{it->key()} == "name"); + REQUIRE(std::string{it->value()} == "High Street"); + ++it; + REQUIRE(it == node.tags().cend()); + } + + SECTION("Order does not matter") { + const char* s = "125799 c7711393 dV v6 i45445 uUScha T t2011-03-29T21:43:10Z y53.0749415 x8.7868047"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 125799); + REQUIRE(node.version() == 6); + REQUIRE(node.visible()); + REQUIRE(node.changeset() == 7711393); + REQUIRE(node.timestamp() == osmium::Timestamp{"2011-03-29T21:43:10Z"}); + REQUIRE(node.uid() == 45445); + REQUIRE(std::string{node.user()} == "UScha"); + osmium::Location loc{8.7868047, 53.0749415}; + REQUIRE(node.location() == loc); + REQUIRE(node.tags().empty()); + } + +} + +TEST_CASE("Parse way") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Way with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_way(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Way& way = buffer.get(0); + REQUIRE(way.id() == 17); + } + + SECTION("Complete way") { + const char* s = "78216 v12 dV c35895909 t2015-12-11T22:01:57Z i7412 umjulius Tdestination=Interlaken;%20%Kandersteg;%20%Zweisimmen,highway=motorway_link,name=Thun%20%Süd,oneway=yes,ref=17,surface=asphalt Nn1011242,n2569390773,n2569390769,n255308687,n2569390761,n255308689,n255308691,n1407526499,n255308692,n3888362655,n255308693,n255308694,n255308695,n255308686"; + const char* e = s + std::strlen(s); + oid::opl_parse_way(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Way& way = buffer.get(0); + REQUIRE(way.id() == 78216); + REQUIRE(way.version() == 12); + REQUIRE(way.visible()); + REQUIRE(way.changeset() == 35895909); + REQUIRE(way.timestamp() == osmium::Timestamp{"2015-12-11T22:01:57Z"}); + REQUIRE(way.uid() == 7412); + REQUIRE(std::string{way.user()} == "mjulius"); + REQUIRE(way.tags().size() == 6); + + auto it = way.tags().cbegin(); + REQUIRE(std::string{it->key()} == "destination"); + REQUIRE(std::string{it->value()} == "Interlaken; Kandersteg; Zweisimmen"); + ++it; + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "motorway_link"); + ++it; + REQUIRE(std::string{it->key()} == "name"); + REQUIRE(std::string{it->value()} == "Thun Süd"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "yes"); + ++it; + REQUIRE(std::string{it->key()} == "ref"); + REQUIRE(std::string{it->value()} == "17"); + ++it; + REQUIRE(std::string{it->key()} == "surface"); + REQUIRE(std::string{it->value()} == "asphalt"); + ++it; + REQUIRE(it == way.tags().cend()); + + REQUIRE(way.nodes().size() == 14); + std::vector ids = { + 1011242, 2569390773, 2569390769, 255308687, 2569390761, 255308689, + 255308691, 1407526499, 255308692, 3888362655, 255308693, 255308694, + 255308695, 255308686 + }; + REQUIRE(std::equal(way.nodes().cbegin(), way.nodes().cend(), ids.cbegin())); + } + +} + +TEST_CASE("Parse relation") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Relation with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_relation(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Relation& relation = buffer.get(0); + REQUIRE(relation.id() == 17); + } + + SECTION("Complete relation") { + const char* s = "1074 v45 dV c20048094 t2014-01-17T10:27:04Z i86566 uwisieb Ttype=multipolygon,landuse=forest Mw255722275@inner,w256126142@outer,w24402792@inner,w256950103@outer,w255722279@outer"; + const char* e = s + std::strlen(s); + oid::opl_parse_relation(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Relation& relation = buffer.get(0); + REQUIRE(relation.id() == 1074); + REQUIRE(relation.version() == 45); + REQUIRE(relation.visible()); + REQUIRE(relation.changeset() == 20048094); + REQUIRE(relation.timestamp() == osmium::Timestamp{"2014-01-17T10:27:04Z"}); + REQUIRE(relation.uid() == 86566); + REQUIRE(std::string{relation.user()} == "wisieb"); + REQUIRE(relation.tags().size() == 2); + + auto it = relation.tags().cbegin(); + REQUIRE(std::string{it->key()} == "type"); + REQUIRE(std::string{it->value()} == "multipolygon"); + ++it; + REQUIRE(std::string{it->key()} == "landuse"); + REQUIRE(std::string{it->value()} == "forest"); + ++it; + REQUIRE(it == relation.tags().cend()); + + REQUIRE(relation.members().size() == 5); + auto mit = relation.members().cbegin(); + REQUIRE(mit->type() == osmium::item_type::way); + REQUIRE(mit->ref() == 255722275); + REQUIRE(std::string{mit->role()} == "inner"); + ++mit; + REQUIRE(mit->type() == osmium::item_type::way); + REQUIRE(mit->ref() == 256126142); + REQUIRE(std::string{mit->role()} == "outer"); + ++mit; + ++mit; + ++mit; + ++mit; + REQUIRE(mit == relation.members().cend()); + } + +} + +TEST_CASE("Parse changeset") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Changeset with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_changeset(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Changeset& changeset = buffer.get(0); + REQUIRE(changeset.id() == 17); + } + + SECTION("Complete changeset") { + const char* s = "873494 k1 s2009-04-21T08:52:49Z e2009-04-21T09:52:49Z d0 i13093 uTiberiusNero x13.923302 y50.957069 X14.0337519 Y50.9824084 Tcreated_by=Potlatch%20%0.11"; + const char* e = s + std::strlen(s); + oid::opl_parse_changeset(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Changeset& changeset = buffer.get(0); + REQUIRE(changeset.id() == 873494); + REQUIRE(changeset.created_at() == osmium::Timestamp{"2009-04-21T08:52:49Z"}); + REQUIRE(changeset.closed_at() == osmium::Timestamp{"2009-04-21T09:52:49Z"}); + REQUIRE(changeset.num_changes() == 1); + REQUIRE(changeset.num_comments() == 0); + REQUIRE(changeset.uid() == 13093); + REQUIRE(std::string{changeset.user()} == "TiberiusNero"); + REQUIRE(changeset.tags().size() == 1); + + auto it = changeset.tags().cbegin(); + REQUIRE(std::string{it->key()} == "created_by"); + REQUIRE(std::string{it->value()} == "Potlatch 0.11"); + ++it; + REQUIRE(it == changeset.tags().cend()); + + osmium::Box box{13.923302, 50.957069, 14.0337519, 50.9824084}; + REQUIRE(box == changeset.bounds()); + } + +} + +TEST_CASE("Parse line") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty line") { + const char* s = ""; + REQUIRE_FALSE(oid::opl_parse_line(0, "", buffer)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Comment line") { + REQUIRE_FALSE(oid::opl_parse_line(0, "# abc", buffer)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Fail") { + 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"); + } + + SECTION("Node, but not asking for nodes") { + REQUIRE_FALSE(oid::opl_parse_line(0, "n12 v1", buffer, osmium::osm_entity_bits::way)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Node") { + REQUIRE(oid::opl_parse_line(0, "n12 v3", buffer)); + REQUIRE(buffer.written() > 0); + 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); + 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); + 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); + REQUIRE(item.type() == osmium::item_type::changeset); + } + +} + +TEST_CASE("Get context for errors") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Unknown object type") { + bool error = false; + try { + oid::opl_parse_line(0, "~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 0); + REQUIRE(e.column == 0); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node id") { + bool error = false; + try { + oid::opl_parse_line(0, "n~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 0); + REQUIRE(e.column == 1); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node expect space") { + bool error = false; + try { + oid::opl_parse_line(1, "n123~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 1); + REQUIRE(e.column == 4); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node unknown attribute") { + bool error = false; + try { + oid::opl_parse_line(2, "n123 ~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 2); + REQUIRE(e.column == 5); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node version not an int") { + bool error = false; + try { + oid::opl_parse_line(3, "n123 v~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 3); + REQUIRE(e.column == 6); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + +} + +TEST_CASE("Parse line with external interface") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Node") { + REQUIRE(osmium::opl_parse("n12 v3", buffer)); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.written() == buffer.committed()); + const auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::node); + REQUIRE(static_cast(item).id() == 12); + } + + SECTION("Empty line") { + REQUIRE_FALSE(osmium::opl_parse("", buffer)); + REQUIRE(buffer.written() == 0); + REQUIRE(buffer.committed() == 0); + } + + SECTION("Failure") { + 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); + } + +} + diff --git a/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp index 566295aff24..63b8bd2978f 100644 --- a/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp +++ b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp @@ -19,7 +19,7 @@ class MockDecompressor : public osmium::io::Decompressor { public: - MockDecompressor(const std::string& fail_in) : + explicit MockDecompressor(const std::string& fail_in) : Decompressor(), m_fail_in(fail_in) { if (m_fail_in == "constructor") { @@ -87,7 +87,7 @@ TEST_CASE("Test Reader using MockDecompressor") { try { osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error constructor"); } } @@ -99,7 +99,7 @@ TEST_CASE("Test Reader using MockDecompressor") { osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); reader.read(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error first read"); } } @@ -112,7 +112,7 @@ TEST_CASE("Test Reader using MockDecompressor") { reader.read(); reader.read(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error second read"); } } @@ -127,7 +127,7 @@ TEST_CASE("Test Reader using MockDecompressor") { reader.read(); reader.close(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error close"); } } 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 c71847ce513..c5c99759831 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 @@ -78,7 +78,7 @@ TEST_CASE("Test Reader using MockParser") { try { osmium::io::Reader reader(with_data_dir("t/io/data.osm")); reader.header(); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in header"); } } @@ -89,7 +89,7 @@ TEST_CASE("Test Reader using MockParser") { reader.header(); try { reader.read(); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in read"); } reader.close(); @@ -101,7 +101,7 @@ TEST_CASE("Test Reader using MockParser") { reader.header(); try { throw std::runtime_error("error in user code"); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in user code"); } REQUIRE(reader.read()); 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 ab977e8bc47..1e762451301 100644 --- a/third_party/libosmium/test/t/io/test_string_table.cpp +++ b/third_party/libosmium/test/t/io/test_string_table.cpp @@ -7,6 +7,8 @@ TEST_CASE("String store") { SECTION("empty") { REQUIRE(ss.begin() == ss.end()); + REQUIRE(ss.get_chunk_size() == 100); + REQUIRE(ss.get_chunk_count() == 1); } SECTION("add zero-length string") { @@ -17,6 +19,8 @@ TEST_CASE("String store") { REQUIRE(s1 == *it); REQUIRE(std::string(*it) == ""); REQUIRE(++it == ss.end()); + + REQUIRE(ss.get_chunk_count() == 1); } SECTION("add strings") { @@ -30,6 +34,9 @@ TEST_CASE("String store") { 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") { @@ -45,9 +52,9 @@ TEST_CASE("String store") { } SECTION("add many strings") { - for (const char* teststring : {"a", "abc", "abcd", "abcde"}) { + for (const char* teststring : {"", "a", "abc", "abcd", "abcde"}) { int i = 0; - for (; i < 100; ++i) { + for (; i < 200; ++i) { ss.add(teststring); } @@ -57,7 +64,9 @@ TEST_CASE("String store") { } REQUIRE(i == 0); + REQUIRE(ss.get_chunk_count() > 1); ss.clear(); + REQUIRE(ss.get_chunk_count() == 1); } } @@ -90,5 +99,32 @@ TEST_CASE("String table") { REQUIRE(st.size() == 1); } + SECTION("add empty string") { + 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); + st.add(s.c_str()); + } + + REQUIRE(st.size() == n + 1); + + auto it = st.begin(); + REQUIRE(std::string{} == *it++); + for (int i = 0; i < n; ++i) { + REQUIRE(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 b56dfeb1368..d3c28368b53 100644 --- a/third_party/libosmium/test/t/io/test_writer.cpp +++ b/third_party/libosmium/test/t/io/test_writer.cpp @@ -18,9 +18,9 @@ TEST_CASE("Writer") { osmium::memory::Buffer buffer = reader.read(); REQUIRE(buffer); REQUIRE(buffer.committed() > 0); - auto num = std::distance(buffer.cbegin(), buffer.cend()); + auto num = std::distance(buffer.select().cbegin(), buffer.select().cend()); REQUIRE(num > 0); - REQUIRE(buffer.cbegin()->id() == 1); + REQUIRE(buffer.select().cbegin()->id() == 1); std::string filename; @@ -81,9 +81,8 @@ TEST_CASE("Writer") { osmium::memory::Buffer buffer_check = reader_check.read(); REQUIRE(buffer_check); REQUIRE(buffer_check.committed() > 0); - REQUIRE(std::distance(buffer_check.cbegin(), buffer_check.cend()) == num); - REQUIRE(buffer_check.cbegin()->id() == 1); - + REQUIRE(buffer_check.select().size() == num); + REQUIRE(buffer_check.select().cbegin()->id() == 1); } SECTION("Interrupted writer after open") { 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 c2d3bbd492d..a28d537fee8 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 @@ -15,7 +15,7 @@ class MockCompressor : public osmium::io::Compressor { public: - MockCompressor(const std::string& fail_in) : + explicit MockCompressor(const std::string& fail_in) : Compressor(osmium::io::fsync::no), m_fail_in(fail_in) { if (m_fail_in == "constructor") { @@ -56,8 +56,7 @@ TEST_CASE("Write with mock compressor") { osmium::memory::Buffer buffer = reader.read(); REQUIRE(buffer); REQUIRE(buffer.committed() > 0); - auto num = std::distance(buffer.cbegin(), buffer.cend()); - REQUIRE(num > 0); + REQUIRE(buffer.select().size() > 0); SECTION("fail on construction") { 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 a43d59183f1..d059f6b8703 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 @@ -62,8 +62,7 @@ TEST_CASE("Test Writer with MockOutputFormat") { osmium::memory::Buffer buffer = reader.read(); REQUIRE(buffer); REQUIRE(buffer.committed() > 0); - auto num = std::distance(buffer.cbegin(), buffer.cend()); - REQUIRE(num > 0); + REQUIRE(buffer.select().size() > 0); SECTION("error in header") { diff --git a/third_party/libosmium/test/t/tags/test_filter.cpp b/third_party/libosmium/test/t/tags/test_filter.cpp index fa21de1c5b9..260a4badb24 100644 --- a/third_party/libosmium/test/t/tags/test_filter.cpp +++ b/third_party/libosmium/test/t/tags/test_filter.cpp @@ -24,7 +24,7 @@ void check_filter(const osmium::TagList& tag_list, const TFilter filter, const s } const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer, std::initializer_list> tags) { - auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); + const auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); return buffer.get(pos); } @@ -42,7 +42,7 @@ TEST_CASE("Filter") { { "source", "GPS" } // no match }); - std::vector results = { true, false, false }; + const std::vector results = { true, false, false }; check_filter(tag_list, filter, results); } @@ -84,7 +84,7 @@ TEST_CASE("Filter") { { "source", "GPS" } }); - std::vector results = {true, true, false}; + const std::vector results = {true, true, false}; check_filter(tag_list, filter, results); } 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 470a2000211..3bdf5e96764 100644 --- a/third_party/libosmium/test/t/tags/test_tag_list.cpp +++ b/third_party/libosmium/test/t/tags/test_tag_list.cpp @@ -167,10 +167,16 @@ TEST_CASE("create tag list") { }); } - const osmium::TagList& tl = *buffer.begin(); + const osmium::TagList& tl = *buffer.select().cbegin(); REQUIRE(osmium::item_type::tag_list == tl.type()); REQUIRE(2 == tl.size()); + REQUIRE(tl.has_key("highway")); + REQUIRE_FALSE(tl.has_key("unknown")); + REQUIRE(tl.has_tag("highway", "primary")); + REQUIRE_FALSE(tl.has_tag("highway", "false")); + REQUIRE_FALSE(tl.has_tag("foo", "bar")); + auto it = tl.begin(); REQUIRE(std::string("highway") == it->key()); REQUIRE(std::string("primary") == it->value()); 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 0231f30e14e..044176eef9d 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 @@ -3,7 +3,7 @@ // Define assert() to throw this error. This enables the tests to check that // the assert() fails. struct assert_error : public std::runtime_error { - assert_error(const char* what_arg) : std::runtime_error(what_arg) { + explicit assert_error(const char* what_arg) : std::runtime_error(what_arg) { } }; #define assert(x) if (!(x)) { throw(assert_error(#x)); } diff --git a/third_party/libosmium/test/t/util/test_delta.cpp b/third_party/libosmium/test/t/util/test_delta.cpp index 667c9b4433f..27bd8be72d7 100644 --- a/third_party/libosmium/test/t/util/test_delta.cpp +++ b/third_party/libosmium/test/t/util/test_delta.cpp @@ -70,25 +70,3 @@ TEST_CASE("delta encode and decode") { } -TEST_CASE("delta encode iterator") { - std::vector data = { 4, 5, 13, 22, 12 }; - - auto l = [](std::vector::const_iterator it) -> int { - return *it; - }; - - typedef osmium::util::DeltaEncodeIterator::const_iterator, decltype(l), int> it_type; - it_type it(data.begin(), data.end(), l); - it_type end(data.end(), data.end(), l); - - REQUIRE(*it == 4); - ++it; - REQUIRE(*it++ == 1); - REQUIRE(*it == 8); - ++it; - REQUIRE(*it++ == 9); - REQUIRE(*it == -10); - ++it; - REQUIRE(it == end); -} - 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 29893f7c70b..647d2a05d98 100644 --- a/third_party/libosmium/test/t/util/test_memory_mapping.cpp +++ b/third_party/libosmium/test/t/util/test_memory_mapping.cpp @@ -33,15 +33,10 @@ TEST_CASE("anonymous mapping") { mapping.unmap(); // second unmap is okay } - SECTION("memory mapping of zero length should work") { - osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private); - REQUIRE(mapping.get_addr() != nullptr); - - REQUIRE(mapping.size() == osmium::util::get_pagesize()); - - REQUIRE(!!mapping); - mapping.unmap(); - REQUIRE(!mapping); + SECTION("memory mapping of zero length should fail") { + REQUIRE_THROWS({ + osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private); + }); } SECTION("moving a memory mapping should work") {