[libosmium] 01/02: Imported Upstream version 0.0~20150303-f074d94
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Fri Mar 6 17:08:47 UTC 2015
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository libosmium.
commit 1e500844e4c90ed3b0fd829059e560fe237850eb
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Fri Mar 6 13:50:06 2015 +0100
Imported Upstream version 0.0~20150303-f074d94
---
.gitignore | 4 +
.travis.yml | 51 +
.ycm_extra_conf.py | 44 +
CMakeLists.txt | 331 +
CONTRIBUTING.md | 132 +
LICENSE.txt | 23 +
Makefile | 25 +
README.md | 104 +
appveyor.yml | 76 +
benchmarks/CMakeLists.txt | 48 +
benchmarks/README.md | 41 +
benchmarks/download_data.sh | 12 +
benchmarks/osmium_benchmark_count.cpp | 54 +
benchmarks/osmium_benchmark_count_tag.cpp | 55 +
benchmarks/osmium_benchmark_index_map.cpp | 41 +
.../osmium_benchmark_static_vs_dynamic_index.cpp | 136 +
benchmarks/run_benchmark_count.sh | 22 +
benchmarks/run_benchmark_count_tag.sh | 22 +
benchmarks/run_benchmark_index_map.sh | 27 +
.../run_benchmark_static_vs_dynamic_index.sh | 21 +
benchmarks/run_benchmarks.sh | 15 +
benchmarks/setup.sh | 34 +
cmake/FindGem.cmake | 153 +
cmake/FindOSMPBF.cmake | 50 +
cmake/FindOsmium.cmake | 336 +
cmake/README | 3 +
cmake/build.bat | 15 +
cmake/iwyu.sh | 43 +
doc/CMakeLists.txt | 35 +
doc/Doxyfile.in | 2313 +++++
doc/README.md | 8 +
doc/doc.txt | 26 +
doc/header.html | 56 +
doc/osmium.css | 22 +
examples/CMakeLists.txt | 115 +
examples/osmium_area_test.cpp | 138 +
examples/osmium_convert.cpp | 112 +
examples/osmium_count.cpp | 57 +
examples/osmium_create_node_cache.cpp | 58 +
examples/osmium_debug.cpp | 52 +
examples/osmium_index.cpp | 237 +
examples/osmium_read.cpp | 32 +
examples/osmium_serdump.cpp | 209 +
examples/osmium_toogr.cpp | 246 +
examples/osmium_toogr2.cpp | 333 +
examples/osmium_toogr2_exp.cpp | 307 +
examples/osmium_use_node_cache.cpp | 71 +
include/boost_unicode_iterator.hpp | 776 ++
include/mmap_for_windows.hpp | 103 +
include/osmium/area/assembler.hpp | 783 ++
include/osmium/area/detail/node_ref_segment.hpp | 262 +
include/osmium/area/detail/proto_ring.hpp | 274 +
include/osmium/area/detail/segment_list.hpp | 216 +
include/osmium/area/multipolygon_collector.hpp | 212 +
include/osmium/area/problem_reporter.hpp | 149 +
include/osmium/area/problem_reporter_exception.hpp | 96 +
include/osmium/area/problem_reporter_ogr.hpp | 223 +
include/osmium/area/problem_reporter_stream.hpp | 96 +
include/osmium/builder/builder.hpp | 224 +
include/osmium/builder/builder_helper.hpp | 103 +
include/osmium/builder/osm_object_builder.hpp | 284 +
include/osmium/diff_handler.hpp | 67 +
include/osmium/diff_iterator.hpp | 129 +
include/osmium/diff_visitor.hpp | 104 +
include/osmium/dynamic_handler.hpp | 195 +
include/osmium/experimental/flex_reader.hpp | 129 +
include/osmium/geom/coordinates.hpp | 97 +
include/osmium/geom/factory.hpp | 328 +
include/osmium/geom/geojson.hpp | 154 +
include/osmium/geom/geos.hpp | 230 +
include/osmium/geom/haversine.hpp | 94 +
include/osmium/geom/mercator_projection.hpp | 109 +
include/osmium/geom/ogr.hpp | 206 +
include/osmium/geom/projection.hpp | 167 +
include/osmium/geom/relations.hpp | 57 +
include/osmium/geom/util.hpp | 75 +
include/osmium/geom/wkb.hpp | 277 +
include/osmium/geom/wkt.hpp | 150 +
include/osmium/handler.hpp | 101 +
include/osmium/handler/chain.hpp | 128 +
include/osmium/handler/disk_store.hpp | 111 +
include/osmium/handler/dump.hpp | 294 +
include/osmium/handler/node_locations_for_ways.hpp | 182 +
include/osmium/handler/object_relations.hpp | 106 +
include/osmium/index/detail/create_map_with_fd.hpp | 73 +
include/osmium/index/detail/mmap_vector_anon.hpp | 78 +
include/osmium/index/detail/mmap_vector_base.hpp | 184 +
include/osmium/index/detail/mmap_vector_file.hpp | 85 +
include/osmium/index/detail/tmpfile.hpp | 62 +
include/osmium/index/detail/typed_mmap.hpp | 229 +
include/osmium/index/detail/vector_map.hpp | 238 +
include/osmium/index/detail/vector_multimap.hpp | 152 +
include/osmium/index/index.hpp | 100 +
include/osmium/index/map.hpp | 255 +
include/osmium/index/map/all.hpp | 46 +
include/osmium/index/map/dense_file_array.hpp | 67 +
include/osmium/index/map/dense_mem_array.hpp | 57 +
include/osmium/index/map/dense_mmap_array.hpp | 60 +
include/osmium/index/map/dummy.hpp | 88 +
include/osmium/index/map/sparse_file_array.hpp | 67 +
include/osmium/index/map/sparse_mem_array.hpp | 60 +
include/osmium/index/map/sparse_mem_map.hpp | 115 +
include/osmium/index/map/sparse_mem_table.hpp | 146 +
include/osmium/index/map/sparse_mmap_array.hpp | 60 +
include/osmium/index/multimap.hpp | 128 +
include/osmium/index/multimap/all.hpp | 41 +
include/osmium/index/multimap/hybrid.hpp | 200 +
.../osmium/index/multimap/sparse_file_array.hpp | 54 +
include/osmium/index/multimap/sparse_mem_array.hpp | 58 +
.../osmium/index/multimap/sparse_mem_multimap.hpp | 151 +
.../osmium/index/multimap/sparse_mmap_array.hpp | 58 +
include/osmium/index/node_locations_map.hpp | 70 +
include/osmium/io/any_compression.hpp | 48 +
include/osmium/io/any_input.hpp | 51 +
include/osmium/io/any_output.hpp | 52 +
include/osmium/io/bzip2_compression.hpp | 289 +
include/osmium/io/compression.hpp | 281 +
include/osmium/io/detail/input_format.hpp | 158 +
include/osmium/io/detail/opl_output_format.hpp | 317 +
include/osmium/io/detail/output_format.hpp | 156 +
include/osmium/io/detail/pbf.hpp | 98 +
include/osmium/io/detail/pbf_input_format.hpp | 245 +
include/osmium/io/detail/pbf_output_format.hpp | 944 ++
include/osmium/io/detail/pbf_parser.hpp | 455 +
include/osmium/io/detail/pbf_stringtable.hpp | 204 +
include/osmium/io/detail/read_thread.hpp | 106 +
include/osmium/io/detail/read_write.hpp | 160 +
include/osmium/io/detail/write_thread.hpp | 86 +
include/osmium/io/detail/xml_input_format.hpp | 724 ++
include/osmium/io/detail/xml_output_format.hpp | 484 ++
include/osmium/io/detail/zlib.hpp | 113 +
include/osmium/io/error.hpp | 58 +
include/osmium/io/file.hpp | 344 +
include/osmium/io/file_compression.hpp | 72 +
include/osmium/io/file_format.hpp | 78 +
include/osmium/io/gzip_compression.hpp | 246 +
include/osmium/io/header.hpp | 122 +
include/osmium/io/input_iterator.hpp | 140 +
include/osmium/io/opl_output.hpp | 39 +
include/osmium/io/output_iterator.hpp | 124 +
include/osmium/io/overwrite.hpp | 52 +
include/osmium/io/pbf_input.hpp | 49 +
include/osmium/io/pbf_output.hpp | 49 +
include/osmium/io/reader.hpp | 312 +
include/osmium/io/reader_iterator.hpp | 51 +
include/osmium/io/writer.hpp | 145 +
include/osmium/io/xml_input.hpp | 48 +
include/osmium/io/xml_output.hpp | 47 +
include/osmium/memory/buffer.hpp | 535 ++
include/osmium/memory/collection.hpp | 154 +
include/osmium/memory/item.hpp | 179 +
include/osmium/memory/item_iterator.hpp | 234 +
include/osmium/object_pointer_collection.hpp | 112 +
include/osmium/osm.hpp | 48 +
include/osmium/osm/area.hpp | 193 +
include/osmium/osm/box.hpp | 207 +
include/osmium/osm/changeset.hpp | 336 +
include/osmium/osm/diff_object.hpp | 156 +
include/osmium/osm/entity.hpp | 79 +
include/osmium/osm/entity_bits.hpp | 105 +
include/osmium/osm/item_type.hpp | 173 +
include/osmium/osm/location.hpp | 285 +
include/osmium/osm/node.hpp | 76 +
include/osmium/osm/node_ref.hpp | 173 +
include/osmium/osm/node_ref_list.hpp | 135 +
include/osmium/osm/object.hpp | 437 +
include/osmium/osm/object_comparisons.hpp | 110 +
include/osmium/osm/relation.hpp | 189 +
include/osmium/osm/segment.hpp | 105 +
include/osmium/osm/tag.hpp | 140 +
include/osmium/osm/timestamp.hpp | 171 +
include/osmium/osm/types.hpp | 83 +
include/osmium/osm/undirected_segment.hpp | 100 +
include/osmium/osm/way.hpp | 115 +
include/osmium/relations/collector.hpp | 544 ++
include/osmium/relations/detail/member_meta.hpp | 158 +
include/osmium/relations/detail/relation_meta.hpp | 136 +
include/osmium/tags/filter.hpp | 162 +
include/osmium/tags/regex_filter.hpp | 58 +
include/osmium/tags/taglist.hpp | 67 +
include/osmium/thread/function_wrapper.hpp | 111 +
include/osmium/thread/pool.hpp | 180 +
include/osmium/thread/queue.hpp | 181 +
include/osmium/thread/sorted_queue.hpp | 159 +
include/osmium/thread/util.hpp | 87 +
include/osmium/util/cast.hpp | 102 +
include/osmium/util/compatibility.hpp | 47 +
include/osmium/util/config.hpp | 72 +
include/osmium/util/double.hpp | 93 +
include/osmium/util/options.hpp | 155 +
include/osmium/util/string.hpp | 68 +
include/osmium/util/verbose_output.hpp | 139 +
include/osmium/visitor.hpp | 255 +
osmium.imp | 11 +
test/CMakeLists.txt | 141 +
test/README | 13 +
test/data-tests/.gitignore | 1 +
test/data-tests/CMakeLists.txt | 118 +
test/data-tests/README.md | 10 +
test/data-tests/include/check_basics_handler.hpp | 92 +
test/data-tests/include/check_wkt_handler.hpp | 86 +
test/data-tests/include/common.hpp | 22 +
test/data-tests/include/testdata-testcases.hpp | 10 +
test/data-tests/multipolygon.qgs | 880 ++
test/data-tests/run-testdata-multipolygon.cmake | 46 +
test/data-tests/testcases/test-100.cpp | 41 +
test/data-tests/testcases/test-101.cpp | 43 +
test/data-tests/testcases/test-110.cpp | 58 +
test/data-tests/testdata-multipolygon.cpp | 291 +
test/data-tests/testdata-overview.cpp | 197 +
test/data-tests/testdata-testcases.cpp | 27 +
test/data-tests/testdata-xml.cpp | 368 +
test/include/catch.hpp | 9003 ++++++++++++++++++++
test/include/catch_orig.hpp | 8997 +++++++++++++++++++
test/include/utils.hpp | 18 +
test/include/win_mkstemp.hpp | 42 +
test/t/area/test_area_id.cpp | 25 +
test/t/area/test_node_ref_segment.cpp | 115 +
test/t/basic/helper.hpp | 97 +
test/t/basic/test_box.cpp | 81 +
test/t/basic/test_changeset.cpp | 57 +
test/t/basic/test_entity_bits.cpp | 31 +
test/t/basic/test_location.cpp | 154 +
test/t/basic/test_node.cpp | 117 +
test/t/basic/test_node_ref.cpp | 57 +
test/t/basic/test_object_comparisons.cpp | 147 +
test/t/basic/test_relation.cpp | 60 +
test/t/basic/test_timestamp.cpp | 45 +
test/t/basic/test_way.cpp | 82 +
test/t/buffer/test_buffer_node.cpp | 135 +
test/t/buffer/test_buffer_purge.cpp | 186 +
test/t/geom/helper.hpp | 15 +
test/t/geom/test_factory_with_projection.cpp | 41 +
test/t/geom/test_geojson.cpp | 236 +
test/t/geom/test_geos.cpp | 198 +
test/t/geom/test_geos_wkb.cpp | 156 +
test/t/geom/test_mercator.cpp | 37 +
test/t/geom/test_ogr.cpp | 185 +
test/t/geom/test_projection.cpp | 131 +
test/t/geom/test_wkb.cpp | 133 +
test/t/geom/test_wkt.cpp | 198 +
test/t/index/test_id_to_location.cpp | 170 +
test/t/index/test_typed_mmap.cpp | 99 +
test/t/io/data.osm | 4 +
test/t/io/data.osm.bz2 | Bin 0 -> 200 bytes
test/t/io/data.osm.gz | Bin 0 -> 187 bytes
test/t/io/data_bzip2.txt | 1 +
test/t/io/data_bzip2.txt.bz2 | Bin 0 -> 45 bytes
test/t/io/test_bzip2.cpp | 33 +
test/t/io/test_file_formats.cpp | 251 +
test/t/io/test_output_iterator.cpp | 37 +
test/t/io/test_reader.cpp | 117 +
test/t/tags/test_filter.cpp | 216 +
test/t/tags/test_operators.cpp | 61 +
test/t/tags/test_tag_list.cpp | 76 +
test/t/thread/test_pool.cpp | 69 +
test/t/util/test_cast_with_assert.cpp | 89 +
test/t/util/test_double.cpp | 33 +
test/t/util/test_options.cpp | 48 +
test/t/util/test_string.cpp | 57 +
test/test_main.cpp | 2 +
test/valgrind.supp | 47 +
262 files changed, 55613 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..36118e1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+core
+*.swp
+build*
+.ycm_extra_conf.pyc
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..73dff72
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,51 @@
+#-----------------------------------------------------------------------------
+#
+# Configuration for continuous integration service at travis-ci.org
+#
+#-----------------------------------------------------------------------------
+
+language: cpp
+
+compiler:
+ - gcc
+ - clang
+
+env:
+ - CONFIGURATION=Dev
+ - CONFIGURATION=Release
+
+before_install:
+ # we need at least g++-4.8 for c++11 features
+ - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
+ - sudo apt-get update --yes --quiet
+
+install:
+ - cd ..
+ # upgrade compilers
+ - sudo apt-get install --yes gcc-4.8 g++-4.8
+ # make sure 'cpp' is the just installed current one
+ - sudo rm /usr/bin/cpp
+ - sudo ln -s /usr/bin/cpp-4.8 /usr/bin/cpp
+ # upgrade libosmium dependencies
+ - sudo apt-get install --yes make libboost-dev libboost-program-options-dev libsparsehash-dev libprotobuf-dev protobuf-compiler libgeos++-dev libproj-dev libgdal1h libgdal-dev
+ - git clone https://github.com/osmcode/osm-testdata.git
+ # OSMPBF is too old, install from git
+ #- sudo apt-get install --yes libosmpbf-dev
+ - git clone https://github.com/scrosby/OSM-binary.git
+ - cd OSM-binary/src
+ - make
+ - sudo make install
+ - cd ../..
+ - cd libosmium
+
+before_script:
+ - true
+
+script:
+ - if [ "${CXX}" = 'g++' ]; then export CXX=g++-4.8; fi;
+ - mkdir build
+ - cd build
+ - cmake -LA -DCMAKE_BUILD_TYPE=${CONFIGURATION} ..
+ - make VERBOSE=1
+ - ctest --output-on-failure
+
diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
new file mode 100644
index 0000000..b0f9d3e
--- /dev/null
+++ b/.ycm_extra_conf.py
@@ -0,0 +1,44 @@
+#-----------------------------------------------------------------------------
+#
+# Configuration for YouCompleteMe Vim plugin
+#
+# http://valloric.github.io/YouCompleteMe/
+#
+#-----------------------------------------------------------------------------
+
+# some default flags
+# for more information install clang-3.2-doc package and
+# check UsersManual.html
+flags = [
+'-Werror',
+'-Wall',
+'-Wextra',
+'-pedantic',
+'-Wno-return-type',
+'-Wno-unused-parameter',
+'-Wno-unused-variable',
+
+'-std=c++11',
+
+# '-x' and 'c++' also required
+# use 'c' for C projects
+'-x',
+'c++',
+
+# libosmium include dirs
+'-Iinclude',
+'-Itest/include',
+'-Itest/data-test/include',
+
+# include third party libraries
+'-I/usr/include/gdal',
+]
+
+# youcompleteme is calling this function to get flags
+# You can also set database for flags. Check: JSONCompilationDatabase.html in
+# clang-3.2-doc package
+def FlagsForFile( filename ):
+ return {
+ 'flags': flags,
+ 'do_cache': True
+ }
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..b48f271
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,331 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium
+#
+#-----------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+
+
+#-----------------------------------------------------------------------------
+#
+# Project version
+#
+#-----------------------------------------------------------------------------
+
+set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev"
+ CACHE STRING
+ "List of available configuration types"
+ FORCE)
+
+project(libosmium)
+
+set(LIBOSMIUM_VERSION_MAJOR 0)
+set(LIBOSMIUM_VERSION_MINOR 0)
+set(LIBOSMIUM_VERSION_PATCH 1)
+
+set(LIBOSMIUM_VERSION
+ ${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH})
+
+
+#-----------------------------------------------------------------------------
+#
+# Build options
+#
+# (Change with -DOPTION=VALUE on cmake command line.)
+#
+#-----------------------------------------------------------------------------
+
+if(CMAKE_BUILD_TYPE STREQUAL "Dev")
+ set(dev_build ON)
+else()
+ set(dev_build OFF)
+endif()
+
+option(BUILD_EXAMPLES "compile example programs" ON)
+option(BUILD_TESTING "compile unit tests, please run them with ctest" ON)
+
+option(BUILD_HEADERS "compile every header file on its own" ${dev_build})
+option(BUILD_BENCHMARKS "compile benchmark programs" ${dev_build})
+option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${dev_build})
+
+
+#-----------------------------------------------------------------------------
+#
+# Find external dependencies
+#
+#-----------------------------------------------------------------------------
+
+find_package(Boost 1.38)
+mark_as_advanced(CLEAR BOOST_ROOT)
+
+if(Boost_FOUND)
+ include_directories(${Boost_INCLUDE_DIRS})
+else()
+ set(BOOST_ROOT "NOT FOUND: please choose" CACHE PATH "")
+ message(FATAL_ERROR "PLEASE, specify the directory where the Boost library is installed in BOOST_ROOT")
+endif()
+
+set(OSMIUM_INCLUDE_DIR include)
+find_package(Osmium COMPONENTS io gdal geos proj sparsehash)
+include_directories(${OSMIUM_INCLUDE_DIRS})
+
+if(MSVC)
+ find_path(GETOPT_INCLUDE_DIR getopt.h)
+ find_library(GETOPT_LIBRARY NAMES wingetopt)
+ if(GETOPT_INCLUDE_DIR AND GETOPT_LIBRARY)
+ include_directories(${GETOPT_INCLUDE_DIR})
+ list(APPEND OSMIUM_LIBRARIES ${GETOPT_LIBRARY})
+ else()
+ set(GETOPT_MISSING 1)
+ endif()
+endif()
+
+include_directories(include)
+
+
+#-----------------------------------------------------------------------------
+#
+# Decide which C++ version to use (Minimum/default: C++11).
+#
+#-----------------------------------------------------------------------------
+if(NOT MSVC)
+ if(NOT USE_CPP_VERSION)
+ set(USE_CPP_VERSION c++11)
+ endif()
+ message(STATUS "Use C++ version: ${USE_CPP_VERSION}")
+ # following only available from cmake 2.8.12:
+ # add_compile_options(-std=${USE_CPP_VERSION})
+ # so using this instead:
+ add_definitions(-std=${USE_CPP_VERSION})
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
+# Compiler and Linker flags
+#
+#-----------------------------------------------------------------------------
+if(MSVC)
+ set(USUAL_COMPILE_OPTIONS "/Ox")
+else()
+ set(USUAL_COMPILE_OPTIONS "-O3 -g")
+endif()
+
+if(WIN32)
+ add_definitions(-DWIN32 -D_WIN32 -DMSWIN32 -DBGDWIN32
+ -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0600)
+endif()
+
+set(CMAKE_CXX_FLAGS_DEV "${USUAL_COMPILE_OPTIONS}"
+ CACHE STRING "Flags used by the compiler during developer builds."
+ FORCE)
+
+set(CMAKE_EXE_LINKER_FLAGS_DEV ""
+ CACHE STRING "Flags used by the linker during developer builds."
+ FORCE)
+mark_as_advanced(
+ CMAKE_CXX_FLAGS_DEV
+ CMAKE_EXE_LINKER_FLAGS_DEV
+)
+
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}"
+ CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds."
+ FORCE)
+
+
+#-----------------------------------------------------------------------------
+#
+# Build Type
+#
+#-----------------------------------------------------------------------------
+# In 'Dev' mode: compile with very strict warnings and turn them into errors.
+if(CMAKE_BUILD_TYPE STREQUAL "Dev")
+ if(NOT MSVC)
+ add_definitions(-Werror)
+ endif()
+ add_definitions(${OSMIUM_WARNING_OPTIONS})
+endif()
+
+# Force RelWithDebInfo build type if none was given
+if(CMAKE_BUILD_TYPE)
+ set(build_type ${CMAKE_BUILD_TYPE})
+else()
+ set(build_type "RelWithDebInfo")
+endif()
+
+set(CMAKE_BUILD_TYPE ${build_type}
+ CACHE STRING
+ "Choose the type of build, options are: ${CMAKE_CONFIGURATION_TYPES}."
+ FORCE)
+
+
+#-----------------------------------------------------------------------------
+#
+# Unit and data tests
+#
+#-----------------------------------------------------------------------------
+enable_testing()
+
+if(BUILD_TESTING OR BUILD_DATA_TESTS)
+ find_program(MEMORYCHECK_COMMAND valgrind)
+
+ set(MEMORYCHECK_COMMAND_OPTIONS
+ "--trace-children=yes --leak-check=full --show-reachable=yes --error-exitcode=1")
+
+ set(MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/test/valgrind.supp")
+endif()
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
+
+if(BUILD_DATA_TESTS)
+ add_subdirectory(test/data-tests)
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
+# Optional "cppcheck" target that checks C++ code
+#
+#-----------------------------------------------------------------------------
+message(STATUS "Looking for cppcheck")
+find_program(CPPCHECK cppcheck)
+
+if(CPPCHECK)
+ message(STATUS "Looking for cppcheck - found")
+ set(CPPCHECK_OPTIONS
+ --enable=warning,style,performance,portability,information,missingInclude)
+
+ # cpp doesn't find system includes for some reason, suppress that report
+ set(CPPCHECK_OPTIONS ${CPPCHECK_OPTIONS} --suppress=missingIncludeSystem)
+
+ file(GLOB_RECURSE ALL_INCLUDES include/osmium/*.hpp)
+ file(GLOB ALL_EXAMPLES examples/*.cpp)
+ file(GLOB ALL_UNIT_TESTS test/t/*/test_*.cpp)
+ file(GLOB ALL_DATA_TESTS test/data-tests/*.cpp)
+
+ if(Osmium_DEBUG)
+ message(STATUS "Checking includes : ${ALL_INCLUDES}")
+ message(STATUS "Checking example code : ${ALL_EXAMPLES}")
+ message(STATUS "Checking unit test code: ${ALL_UNIT_TESTS}")
+ message(STATUS "Checking data test code: ${ALL_DATA_TESTS}")
+ endif()
+
+ set(CPPCHECK_FILES
+ ${ALL_INCLUDES}
+ ${ALL_EXAMPLES}
+ ${ALL_UNIT_TESTS}
+ ${ALL_DATA_TESTS})
+
+ add_custom_target(cppcheck
+ ${CPPCHECK}
+ --std=c++11 ${CPPCHECK_OPTIONS}
+ -I ${CMAKE_SOURCE_DIR}/include
+ ${CPPCHECK_FILES}
+ )
+else()
+ message(STATUS "Looking for cppcheck - not found")
+ message(STATUS " Build target 'cppcheck' will not be available.")
+endif(CPPCHECK)
+
+
+#-----------------------------------------------------------------------------
+#
+# Examples, benchmarks and documentation
+#
+#-----------------------------------------------------------------------------
+
+if(BUILD_EXAMPLES)
+ add_subdirectory(examples)
+endif()
+
+if(BUILD_BENCHMARKS)
+ add_subdirectory(benchmarks)
+endif()
+
+add_subdirectory(doc)
+
+
+#-----------------------------------------------------------------------------
+#
+# Headers
+#
+# This will try to compile include files on their own to detect missing
+# include directives and other dependency-related problems. Note that if this
+# work, it is not enough to be sure it will compile in production code.
+# But if it reports an error we know we are missing something.
+#
+#-----------------------------------------------------------------------------
+if(BUILD_HEADERS)
+ file(GLOB_RECURSE
+ ALL_HPPS
+ RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/include"
+ include/osmium/*.hpp)
+
+ file(MAKE_DIRECTORY header_check)
+
+ foreach(hpp ${ALL_HPPS})
+ string(REPLACE ".hpp" "" tmp ${hpp})
+ string(REPLACE "/" "__" libname ${tmp})
+
+ # Create a dummy .cpp file that includes the header file we want to
+ # check.
+ set(DUMMYCPP ${CMAKE_BINARY_DIR}/header_check/${libname}.cpp)
+ file(WRITE ${DUMMYCPP} "#include <${hpp}>\n")
+
+ # There is no way in CMake to just compile but not link a C++ file,
+ # so we pretend to build a library here.
+ add_library(${libname} STATIC ${DUMMYCPP} include/${hpp})
+
+ #### this is better but only supported from cmake 3.0:
+ ###add_library(${libname} OBJECT ${DUMMYCPP} include/${hpp})
+
+ endforeach()
+endif()
+
+install(DIRECTORY include/osmium DESTINATION include)
+
+# We only have a copy of this file so we can use older boost versions which
+# don't have it. We probably don't want to install it.
+#install(FILES include/boost_unicode_iterator.hpp DESTINATION include)
+
+
+#-----------------------------------------------------------------------------
+#
+# Packaging
+#
+#-----------------------------------------------------------------------------
+
+set(CPACK_PACKAGE_VERSION_MAJOR ${LIBOSMIUM_VERSION_MAJOR})
+set(CPACK_PACKAGE_VERSION_MINOR ${LIBOSMIUM_VERSION_MINOR})
+set(CPACK_PACKAGE_VERSION_PATCH ${LIBOSMIUM_VERSION_PATCH})
+
+if(WIN32)
+ set(CPACK_GENERATOR ZIP)
+else()
+ set(CPACK_GENERATOR TGZ)
+endif()
+
+include(CPack)
+
+
+#-----------------------------------------------------------------------------
+#
+# Print warnings at the end
+#
+#-----------------------------------------------------------------------------
+if(BUILD_DATA_TESTS AND OSM_TESTDATA STREQUAL "OSM_TESTDATA-NOTFOUND")
+ message("\n========================== WARNING ==========================")
+ message("osm-testdata directory not found, data tests were disabled!\n")
+ message("You can get it from https://github.com/osmcode/osm-testdata")
+ message("Clone it into the same directory libosmium is in")
+ message("or set the OSM_TESTDATA cmake variable to its path.")
+ message("=============================================================\n")
+endif()
+
+#-----------------------------------------------------------------------------
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..323c847
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,132 @@
+
+# Notes for Developers
+
+Read this if you want to contribute to Libosmium.
+
+
+## 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 constexpr is better.
+* Variables, attributes, and function names are lowercase with
+ `underscores_between_words`.
+* Class attribute names start with `m_` (member).
+* Template parameters are single uppercase letters or start with uppercase `T`
+ and use CamelCase.
+* Typedefs have `names_like_this_type` which end in `_type`.
+* Macros should only be used for controlling which parts of the code should be
+ included when compiling.
+* Use `descriptive_variable_names`, exceptions are well-established conventions
+ like `i` for a loop variable. Iterators are usually called `it`.
+* Declare variables where they are first used (C++ style), not at the beginning
+ of a function (old C style).
+* Names from external namespaces (even `std`) are always mentioned explicitly.
+ Do not use `using` (except for `std::swap`). This way we can't even by
+ accident pollute the namespace of the code including Osmium.
+* `#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 should be declared "explicit"
+ unless there is a reason for them not to be. Document that reason.
+
+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/stable)
+GCC 4.7.3 - works
+GCC 4.8 - works
+clang 3.0 - too old, not supported (Debian wheezy/stable, Ubuntu 12.04 LTS)
+clang 3.2 - works
+
+C++11 features you should not use:
+* Inherited Constructors (works only in GCC 4.8+ and clang 3.3+, not in Visual
+ Studio)
+
+
+## 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
+<http://code.google.com/p/include-what-you-use/>.
+
+
+## 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/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7b2b83d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+
+all:
+ mkdir -p build && cd build && cmake .. && $(MAKE)
+
+doc:
+ mkdir -p build && cd build && cmake .. && $(MAKE) doc
+
+clean:
+ if test -d build; then cd build && $(MAKE) clean; fi
+
+distclean:
+ rm -fr build
+
+#deb:
+# debuild -I -us -uc
+#
+#deb-clean:
+# debuild clean
+
+indent:
+ astyle --style=java --indent-namespaces --indent-switches --pad-header --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp test/\*.cpp
+# astyle --style=java --indent-namespaces --indent-switches --pad-header --unpad-paren --align-pointer=type --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp test/\*.cpp
+
+.PHONY: clean distclean deb deb-clean doc indent
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..503440e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,104 @@
+# Libosmium
+
+http://osmcode.org/libosmium
+
+A fast and flexible C++ library for working with OpenStreetMap data.
+
+[](http://travis-ci.org/osmcode/libosmium)
+[](https://ci.appveyor.com/project/Mapbox/libosmium)
+
+Libosmium is developed on Linux, but also works on OSX and Windows (with some
+limitations).
+
+There are a few applications that use the Osmium library in the examples
+directory. See the [osmium-contrib](http://github.com/osmcode/osmium-contrib)
+repository for more example code.
+
+## Prerequisites
+
+Because Libosmium uses many C++11 features you need a modern compiler and
+standard C++ library. Osmium needs at least GCC 4.8 or clang (LLVM) 3.4.
+(Some parts may work with older versions.)
+
+Different parts of Libosmium (and the applications built on top of it) need
+different libraries. You DO NOT NEED to install all of them, just install those
+you need for your programs.
+
+For details see the
+[list of dependencies](https://github.com/osmcode/libosmium/wiki/Libosmium-dependencies).
+
+
+## Directories
+
+* benchmarks: Some benchmarks checking different parts of Libosmium.
+
+* cmake: CMake configuration scripts.
+
+* doc: Config for documentation.
+
+* examples: Osmium example applications.
+
+* include: C/C++ include files. All of Libosmium is in those header files
+ which are needed for building Osmium applications.
+
+* test: Tests (see below).
+
+
+## Building
+
+Osmium is a header-only library, so there is nothing to build for the
+library itself.
+
+But there are some tests and examples that can be build. Libosmium uses
+cmake:
+
+ mkdir build
+ cd build
+ cmake ..
+ make
+
+This will build the examples and tests. Call `ctest` to run the tests.
+
+For more see the
+[Libosmium Wiki](https://github.com/osmcode/libosmium/wiki/Building-Libosmium).
+
+
+## Testing
+
+See the
+[Libosmium Wiki](https://github.com/osmcode/libosmium/wiki/Testing-Libosmium)
+for instructions.
+
+
+## Osmium on 32bit Machines
+
+Osmium works well on 64 bit machines, but on 32 bit machines there are some
+problems. Be aware that not everything will work on 32 bit architectures.
+This is mostly due to the 64 bit needed for node IDs. Also Osmium hasn't been
+tested well on 32 bit systems. Here are some issues you might run into:
+
+* Google Sparsehash does not work on 32 bit machines in our use case.
+* The `mmap` system call is called with a `size_t` argument, so it can't
+ give you more than 4GByte of memory on 32 bit systems. This might be a
+ problem.
+
+Please report any issues you have and we might be able to solve them.
+
+
+## Switching from the old Osmium
+
+If you have been using the old version of Osmium at
+https://github.com/joto/osmium you might want to read about the
+[changes needed](https://github.com/osmcode/libosmium/wiki/Changes-from-old-versions-of-Osmium).
+
+
+## License
+
+Libosmium is available under the Boost Software License. See LICENSE.txt.
+
+
+## Authors
+
+Libosmium was mainly written and is maintained by Jochen Topf
+(jochen at topf.org). See the git commit log for other authors.
+
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..b3b3f51
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,76 @@
+#-----------------------------------------------------------------------------
+#
+# Configuration for continuous integration service at appveyor.com
+#
+#-----------------------------------------------------------------------------
+
+environment:
+ matrix:
+ - config: Dev
+ - config: RelWithDebInfo
+
+# branches to build
+branches:
+ # whitelist
+ only:
+ - master
+
+# Operating system (build VM template)
+os: Visual Studio 2014 CTP4
+
+# scripts that are called at very beginning, before repo cloning
+init:
+
+# clone directory
+clone_folder: c:\projects\libosmium
+
+platform: x64
+
+install:
+ # show all availble 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%
+ #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://mapnik.s3.amazonaws.com/deps/cmake-3.1.0-win32-x86.7z -FileName cm.7z
+ - ps: Start-FileDownload https://mapnik.s3.amazonaws.com/dist/dev/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 https://github.com/osmcode/osm-testdata.git
+
+build_script:
+ - cd c:\projects\libosmium
+ - mkdir build
+ - cd build
+ - echo %config%
+ - cmake .. -LA -G "Visual Studio 14 Win64" -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 -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobu [...]
+ - 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 -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobuf\incl [...]
+ #- nmake
+
+test_script:
+ - ctest --output-on-failure -C %config%
+
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
new file mode 100644
index 0000000..6a4ca16
--- /dev/null
+++ b/benchmarks/CMakeLists.txt
@@ -0,0 +1,48 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium benchmarks
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring benchmarks")
+
+set(BENCHMARKS
+ count
+ count_tag
+ index_map
+ static_vs_dynamic_index
+ CACHE STRING "Benchmark programs"
+)
+
+
+#-----------------------------------------------------------------------------
+#
+# Configure benchmarks
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring benchmarks - Building these benchmarks:")
+foreach(benchmark ${BENCHMARKS})
+ message(STATUS " - osmium_benchmark_${benchmark}")
+ add_executable(osmium_benchmark_${benchmark}
+ "osmium_benchmark_${benchmark}.cpp")
+ target_link_libraries(osmium_benchmark_${benchmark}
+ ${OSMIUM_IO_LIBRARIES}
+ ${BENCHMARK_LIBS_${benchmark}})
+ configure_file(run_benchmark_${benchmark}.sh
+ ${CMAKE_CURRENT_BINARY_DIR}/run_benchmark_${benchmark}.sh
+ @ONLY)
+endforeach()
+
+foreach(file setup run_benchmarks)
+ configure_file(${file}.sh ${CMAKE_CURRENT_BINARY_DIR}/${file}.sh @ONLY)
+endforeach()
+
+
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring benchmarks - done")
+
+
+#-----------------------------------------------------------------------------
diff --git a/benchmarks/README.md b/benchmarks/README.md
new file mode 100644
index 0000000..f10045c
--- /dev/null
+++ b/benchmarks/README.md
@@ -0,0 +1,41 @@
+
+# Benchmarks
+
+Benchmarks check the performance of different parts of Libosmium.
+
+## Preparations
+
+To run the benchmarks first make a directory for the data files somewhere
+(outside the repository) and set the `DATA_DIR` environment variable:
+
+ export DATA_DIR=benchmark_data
+ mkdir $DATA_DIR
+
+Then copy the OSM files you want to do the benchmarks with into this directory.
+You can use the `download_data.sh` script to download a selection of OSM files
+in different sizes, but you can use a different selection, too. The benchmarks
+will use whatever files you have in the `DATA_DIR` directory.
+
+The download script will start the data files names with a number in order of
+the size of the file from smallest to largest. You can use the same convention
+or use a different one. Benchmarks will be run on the files in alphabetical
+order.
+
+The files don't have to be in that directory, you can add soft links from that
+directory to the real file locations if that suits you.
+
+## Compiling the benchmarks
+
+To build the benchmarks set the `BUILD_BENCHMARKS` option when configuring with
+CMake and run the compilation by calling `make` (or whatever build tool you
+are using).
+
+## Running the benchmarks
+
+Go to the build directory and run `benchmarks/run_benchmarks.sh`. You can also
+run each benchmark on its own by calling the respective script in the
+`benchmarks` directory.
+
+Results of the benchmarks will be printed to stdout, you might want to redirect
+them into a file.
+
diff --git a/benchmarks/download_data.sh b/benchmarks/download_data.sh
new file mode 100755
index 0000000..8a6a8ff
--- /dev/null
+++ b/benchmarks/download_data.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# download_data.sh
+#
+
+cd $DATA_DIR
+curl --location --output 1_liechtenstein.osm.pbf http://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf # about 1 MB
+curl --location --output 2_bremen.osm.pbf http://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf # about 13 MB
+curl --location --output 3_sachsen.osm.pbf http://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 120 MB
+curl --location --output 4_germany.osm.pbf http://download.geofabrik.de/europe/germany-latest.osm.pbf # about 2 GB
+curl --location --output 5_planet.osm.pbf http://planet.osm.org/pbf/planet-latest.osm.pbf # about 26 GB
+
diff --git a/benchmarks/osmium_benchmark_count.cpp b/benchmarks/osmium_benchmark_count.cpp
new file mode 100644
index 0000000..701d6fa
--- /dev/null
+++ b/benchmarks/osmium_benchmark_count.cpp
@@ -0,0 +1,54 @@
+/*
+
+ The code in this file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/visitor.hpp>
+
+struct CountHandler : public osmium::handler::Handler {
+
+ int nodes = 0;
+ int ways = 0;
+ int relations = 0;
+
+ void node(osmium::Node&) {
+ ++nodes;
+ }
+
+ void way(osmium::Way&) {
+ ++ways;
+ }
+
+ void relation(osmium::Relation&) {
+ ++relations;
+ }
+
+};
+
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+ exit(1);
+ }
+
+ std::string input_filename = argv[1];
+
+ osmium::io::Reader reader(input_filename);
+
+ CountHandler handler;
+ osmium::apply(reader, handler);
+ reader.close();
+
+ std::cout << "Nodes: " << handler.nodes << "\n";
+ std::cout << "Ways: " << handler.ways << "\n";
+ std::cout << "Relations: " << handler.relations << "\n";
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/benchmarks/osmium_benchmark_count_tag.cpp b/benchmarks/osmium_benchmark_count_tag.cpp
new file mode 100644
index 0000000..4a77c34
--- /dev/null
+++ b/benchmarks/osmium_benchmark_count_tag.cpp
@@ -0,0 +1,55 @@
+/*
+
+ The code in this file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/visitor.hpp>
+
+struct CountHandler : public osmium::handler::Handler {
+
+ int counter = 0;
+ int all = 0;
+
+ void node(osmium::Node& node) {
+ ++all;
+ const char* amenity = node.tags().get_value_by_key("amenity");
+ if (amenity && !strcmp(amenity, "post_box")) {
+ ++counter;
+ }
+ }
+
+ void way(osmium::Way&) {
+ ++all;
+ }
+
+ void relation(osmium::Relation&) {
+ ++all;
+ }
+
+};
+
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+ exit(1);
+ }
+
+ std::string input_filename = argv[1];
+
+ osmium::io::Reader reader(input_filename);
+
+ CountHandler handler;
+ osmium::apply(reader, handler);
+ reader.close();
+
+ std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << "\n";
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/benchmarks/osmium_benchmark_index_map.cpp b/benchmarks/osmium_benchmark_index_map.cpp
new file mode 100644
index 0000000..fa75fb2
--- /dev/null
+++ b/benchmarks/osmium_benchmark_index_map.cpp
@@ -0,0 +1,41 @@
+/*
+
+ The code in this file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/index/map/all.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+
+typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_type> location_handler_type;
+
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE FORMAT\n";
+ exit(1);
+ }
+
+ std::string input_filename = argv[1];
+ std::string location_store = argv[2];
+
+ osmium::io::Reader reader(input_filename);
+
+ const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+ std::unique_ptr<index_type> index = map_factory.create_map(location_store);
+ location_handler_type location_handler(*index);
+ location_handler.ignore_errors();
+
+ osmium::apply(reader, location_handler);
+ reader.close();
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp b/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp
new file mode 100644
index 0000000..9c47c84
--- /dev/null
+++ b/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp
@@ -0,0 +1,136 @@
+/*
+
+ This benchmarks compares the run time for statically vs. dynamically
+ configured index maps. You can configure index maps at compile-time using
+ typedefs or at run-time using polymorphism.
+
+ This will read the input file into a buffer and then run the
+ NodeLocationForWays handler multiple times over the complete data. The
+ number of runs depends on the size of the input, but is never smaller
+ than 10.
+
+ Do not run this with very large input files! It will need about 10 times
+ as much RAM as the file size of the input file.
+
+ The code in this file is released into the Public Domain.
+
+*/
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <iostream>
+#include <limits>
+
+#include <osmium/index/map/all.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> static_index_type;
+const std::string location_store="sparse_mem_array";
+
+typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> dynamic_index_type;
+
+typedef osmium::handler::NodeLocationsForWays<static_index_type> static_location_handler_type;
+typedef osmium::handler::NodeLocationsForWays<dynamic_index_type> dynamic_location_handler_type;
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+ exit(1);
+ }
+
+ std::string input_filename = argv[1];
+
+ osmium::memory::Buffer buffer = osmium::io::read_file(input_filename);
+ google::protobuf::ShutdownProtobufLibrary();
+
+ const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+
+ const auto buffer_size = buffer.committed() / (1024*1024); // buffer size in MBytes
+ const int runs = std::max(10, static_cast<int>(5000ull / buffer_size));
+
+ std::cout << "input: filename=" << input_filename << " buffer_size=" << buffer_size << "MBytes\n";
+ std::cout << "runs: " << runs << "\n";
+
+ double static_min = std::numeric_limits<double>::max();
+ double static_sum = 0;
+ double static_max = 0;
+
+ double dynamic_min = std::numeric_limits<double>::max();
+ double dynamic_sum = 0;
+ double dynamic_max = 0;
+
+ for (int i = 0; i < runs; ++i) {
+
+ {
+ // static index
+ 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);
+
+ auto start = std::chrono::steady_clock::now();
+ osmium::apply(tmp_buffer, static_location_handler);
+ auto end = std::chrono::steady_clock::now();
+
+ double duration = std::chrono::duration<double, std::milli>(end-start).count();
+
+ if (duration < static_min) static_min = duration;
+ if (duration > static_max) static_max = duration;
+ static_sum += duration;
+ }
+
+ {
+ // dynamic index
+ osmium::memory::Buffer tmp_buffer(buffer.committed());
+ for (const auto& item : buffer) {
+ tmp_buffer.add_item(item);
+ tmp_buffer.commit();
+ }
+
+ std::unique_ptr<dynamic_index_type> index = map_factory.create_map(location_store);
+ dynamic_location_handler_type dynamic_location_handler(*index);
+ dynamic_location_handler.ignore_errors();
+
+ auto start = std::chrono::steady_clock::now();
+ osmium::apply(tmp_buffer, dynamic_location_handler);
+ auto end = std::chrono::steady_clock::now();
+
+ double duration = std::chrono::duration<double, std::milli>(end-start).count();
+
+ if (duration < dynamic_min) dynamic_min = duration;
+ if (duration > dynamic_max) dynamic_max = duration;
+ dynamic_sum += duration;
+ }
+ }
+
+ double static_avg = static_sum/runs;
+ 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;
+
+ 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;
+
+ std::cout << "difference:";
+ std::cout << " min=" << diff_min << "ms (" << percent_min << "%)";
+ std::cout << " avg=" << diff_avg << "ms (" << percent_avg << "%)";
+ std::cout << " max=" << diff_max << "ms (" << percent_max << "%)\n";
+}
+
diff --git a/benchmarks/run_benchmark_count.sh b/benchmarks/run_benchmark_count.sh
new file mode 100755
index 0000000..d71508f
--- /dev/null
+++ b/benchmarks/run_benchmark_count.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# run_benchmark_count.sh
+#
+
+set -e
+
+BENCHMARK_NAME=count
+
+. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
+
+CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
+
+echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
+for data in $OB_DATA_FILES; do
+ filename=`basename $data`
+ filesize=`stat --format="%s" --dereference $data`
+ for n in $OB_SEQ; do
+ $OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
+ done
+done
+
diff --git a/benchmarks/run_benchmark_count_tag.sh b/benchmarks/run_benchmark_count_tag.sh
new file mode 100755
index 0000000..4fa6a10
--- /dev/null
+++ b/benchmarks/run_benchmark_count_tag.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# run_benchmark_count_tag.sh
+#
+
+set -e
+
+BENCHMARK_NAME=count_tag
+
+. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
+
+CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
+
+echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
+for data in $OB_DATA_FILES; do
+ filename=`basename $data`
+ filesize=`stat --format="%s" --dereference $data`
+ for n in $OB_SEQ; do
+ $OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
+ done
+done
+
diff --git a/benchmarks/run_benchmark_index_map.sh b/benchmarks/run_benchmark_index_map.sh
new file mode 100755
index 0000000..30984d4
--- /dev/null
+++ b/benchmarks/run_benchmark_index_map.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# run_benchmark_index_map.sh
+#
+
+set -e
+
+BENCHMARK_NAME=index_map
+
+. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
+
+CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
+
+#MAPS="sparse_mem_map sparse_mem_table sparse_mem_array sparse_mmap_array sparse_file_array dense_mem_array dense_mmap_array dense_file_array"
+MAPS="sparse_mem_map sparse_mem_table sparse_mem_array sparse_mmap_array sparse_file_array"
+
+echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
+for data in $OB_DATA_FILES; do
+ filename=`basename $data`
+ filesize=`stat --format="%s" --dereference $data`
+ for map in $MAPS; do
+ for n in $OB_SEQ; do
+ $OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data $map 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
+ done
+ done
+done
+
diff --git a/benchmarks/run_benchmark_static_vs_dynamic_index.sh b/benchmarks/run_benchmark_static_vs_dynamic_index.sh
new file mode 100755
index 0000000..05e32f1
--- /dev/null
+++ b/benchmarks/run_benchmark_static_vs_dynamic_index.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# run_benchmark_static_vs_dynamic_index.sh
+#
+
+set -e
+
+BENCHMARK_NAME=static_vs_dynamic_index
+
+. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
+
+CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
+
+for data in $OB_DATA_FILES; do
+ filesize=`stat --format="%s" --dereference $data`
+ if [ $filesize -lt 500000000 ]; then
+ echo "========================"
+ $CMD $data
+ fi
+done
+
diff --git a/benchmarks/run_benchmarks.sh b/benchmarks/run_benchmarks.sh
new file mode 100755
index 0000000..6a20c02
--- /dev/null
+++ b/benchmarks/run_benchmarks.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# run_benchmarks.sh
+#
+# Run all benchmarks.
+#
+
+set -e
+
+for benchmark in @CMAKE_BINARY_DIR@/benchmarks/run_benchmark_*.sh; do
+ name=`basename $benchmark`
+ echo "Running $name..."
+ $benchmark
+done
+
diff --git a/benchmarks/setup.sh b/benchmarks/setup.sh
new file mode 100755
index 0000000..9733bfe
--- /dev/null
+++ b/benchmarks/setup.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# setup.sh
+#
+
+if [ -z $DATA_DIR ]; then
+ echo "Please set DATA_DIR environment variable before running benchmark"
+ exit 1
+fi
+
+OB_DIR=@CMAKE_BINARY_DIR@/benchmarks
+
+OB_RUNS=3
+OB_SEQ=`seq -s' ' 1 $OB_RUNS`
+
+OB_TIME_CMD=/usr/bin/time
+OB_TIME_FORMAT="%M %e %S %U %P %C"
+
+OB_DATA_FILES=`find -L $DATA_DIR -mindepth 1 -maxdepth 1 -type f | sort`
+
+echo "BENCHMARK: $BENCHMARK_NAME"
+echo "---------------------"
+echo "CPU:"
+grep '^model name' /proc/cpuinfo | tail -1
+grep '^cpu MHz' /proc/cpuinfo | tail -1
+grep '^cpu cores' /proc/cpuinfo | tail -1
+grep '^siblings' /proc/cpuinfo | tail -1
+
+echo "---------------------"
+echo "MEMORY:"
+free
+echo "---------------------"
+echo "RESULTS:"
+
diff --git a/cmake/FindGem.cmake b/cmake/FindGem.cmake
new file mode 100644
index 0000000..f5389d1
--- /dev/null
+++ b/cmake/FindGem.cmake
@@ -0,0 +1,153 @@
+# Author thomas.roehr at dfki.de
+#
+# Version 0.3 2013-07-02
+# - rely on `gem content` to find library and header
+# - introduce GEM_OS_PKG to allow search via pkgconfig
+# Version 0.2 2010-01-14
+# - add support for searching for multiple gems
+# Version 0.1 2010-12-15
+# - support basic search functionality
+# - tested to find rice
+#
+# OUTPUT:
+#
+# GEM_INCLUDE_DIRS After successful search contains the include directores
+#
+# GEM_LIBRARIES After successful search contains the full path of each found library
+#
+#
+# Usage:
+# set(GEM_DEBUG TRUE)
+# find_package(Gem COMPONENTS rice hoe)
+# include_directories(${GEM_INCLUDE_DIRS})
+# target_link_libraries(${GEM_LIBRARIES}
+#
+# in case pkg-config should be used to search for the os pkg, set GEM_OS_PKG, i.e.
+# set(GEM_OS_PKG TRUE)
+#
+# Check for how 'gem' should be called
+include(FindPackageHandleStandardArgs)
+find_program(GEM_EXECUTABLE
+ NAMES "gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}"
+ "gem${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}"
+ "gem-${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}"
+ "gem-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}"
+ "gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}"
+ "gem${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}"
+ "gem-${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}"
+ "gem-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}"
+ "gem")
+
+# Making backward compatible
+if(Gem_DEBUG)
+ set(GEM_DEBUG TRUE)
+endif()
+
+if(NOT GEM_EXECUTABLE)
+ MESSAGE(FATAL_ERROR "Could not find the gem executable - install 'gem' first")
+endif()
+
+if(NOT Gem_FIND_COMPONENTS)
+ MESSAGE(FATAL_ERROR "If searching for a Gem you have to provide COMPONENTS with the name of the gem")
+endif()
+
+foreach(Gem_NAME ${Gem_FIND_COMPONENTS})
+ set(GEM_${Gem_NAME}_FOUND TRUE)
+ list(APPEND components_found_vars GEM_${Gem_NAME}_FOUND)
+ # If the gem is installed as a gem
+ if(NOT GEM_OS_PKG)
+ set(GEM_HOME ENV{GEM_HOME})
+
+ # Use `gem content <gem-name>` to extract current information about installed gems
+ # Store the information into ${GEM_LOCAL_INFO}
+ EXECUTE_PROCESS(COMMAND ${GEM_EXECUTABLE} content ${Gem_NAME}
+ RESULT_VARIABLE GEM_RUN_RESULT
+ OUTPUT_VARIABLE GEM_LOCAL_INFO)
+
+ if(GEM_RUN_RESULT STREQUAL "0")
+ list(APPEND FOUND_GEMS ${Gem_NAME})
+ set(_library_NAME_PATTERN lib${Gem_NAME}.a
+ lib${Gem_NAME}.so
+ lib${Gem_NAME}.dylib
+ ${Gem_NAME}.a
+ ${Gem_NAME}.so
+ ${Gem_NAME}.dylib
+ .*.a
+ .*.so
+ .*.dylib
+ )
+
+ set(_header_SUFFIX_PATTERN
+ .h
+ .hh
+ .hpp
+ )
+
+ # Create a list from the output results of the gem command
+ string(REPLACE "\n" ";" GEM_CONTENT_LIST "${GEM_LOCAL_INFO}")
+ foreach(_gem_CONTENT_PATH ${GEM_CONTENT_LIST})
+
+ # Convert so that only '/' Unix path separator are being using
+ # needed to do proper regex matching
+ FILE(TO_CMAKE_PATH ${_gem_CONTENT_PATH} gem_CONTENT_PATH)
+
+ # Identify library -- checking for a library in the gems 'lib' (sub)directory
+ # Search for an existing library, but only within the gems folder
+ foreach(_library_NAME ${_library_NAME_PATTERN})
+ STRING(REGEX MATCH ".*${Gem_NAME}.*/lib/.*${_library_NAME}$" GEM_PATH_INFO "${gem_CONTENT_PATH}")
+ if(NOT "${GEM_PATH_INFO}" STREQUAL "")
+ list(APPEND GEM_LIBRARIES ${GEM_PATH_INFO})
+ break()
+ endif()
+ endforeach()
+
+ # Identify headers
+ # Checking for available headers in an include directory
+ foreach(_header_PATTERN ${_header_SUFFIX_PATTERN})
+ STRING(REGEX MATCH ".*${Gem_NAME}.*/include/.*${_header_PATTERN}$" GEM_PATH_INFO "${gem_CONTENT_PATH}")
+ if(NOT "${GEM_PATH_INFO}" STREQUAL "")
+ STRING(REGEX REPLACE "(.*${Gem_NAME}.*/include/).*${_header_PATTERN}$" "\\1" GEM_PATH_INFO "${gem_CONTENT_PATH}")
+ list(APPEND GEM_INCLUDE_DIRS ${GEM_PATH_INFO})
+ break()
+ endif()
+ endforeach()
+ endforeach()
+ else()
+ set(GEM_${Gem_NAME}_FOUND FALSE)
+ endif()
+ else(NOT GEM_OS_PKG)
+ pkg_check_modules(GEM_PKG ${Gem_NAME})
+ set(GEM_${GEM_NAME}_FOUND GEM_PKG_FOUND)
+ set(GEM_INCLUDE_DIRS ${GEM_PKG_INCLUDE_DIRS})
+ set(GEM_LIBRARIES ${GEM_PKG_LIBRARIES} ${GEM_PKG_STATIC_LIBRARIES})
+ list(APPEND GEM_LIBRARIES ${GEM_PKG_LDFLAGS} ${GEM_PKG_STATIC_LDFLAGS})
+ list(APPEND GEM_LIBRARIES ${GEM_PKG_LDFLAGS_OTHER} ${GEM_PKG_STATIC_LDFLAGS_OTHER})
+
+ if(GEM_DEBUG)
+ message(STATUS "GEM_OS_PKG is defined")
+ message(STATUS "GEM_INCLUDE_DIRS ${GEM_INCLUDE_DIRS}")
+ message(STATUS "GEM_STATIC_LIBRARY_DIRS ${GEM_PKG_STATIC_LIBRARY_DIRS}")
+ message(STATUS "GEM_LIBRARY_DIRS ${GEM_PKG_STATIC_LIBRARY_DIRS}")
+ message(STATUS "GEM_STATIC_LIBRARIES ${GEM_PKG_STATIC_LIBRARIES}")
+ message(STATUS "GEM_LIBRARIES ${GEM_LIBRARIES}")
+ endif()
+ endif()
+
+ if(GEM_DEBUG)
+ message(STATUS "${Gem_NAME} library dir: ${GEM_LIBRARIES}")
+ message(STATUS "${Gem_NAME} include dir: ${GEM_INCLUDE_DIRS}")
+ endif()
+endforeach()
+
+# Compact the lists
+if(DEFINED GEM_LIBRARIES)
+ LIST(REMOVE_DUPLICATES GEM_LIBRARIES)
+endif()
+if(DEFINED GEM_INCLUDE_DIRS)
+ LIST(REMOVE_DUPLICATES GEM_INCLUDE_DIRS)
+endif()
+
+find_package_handle_standard_args(GEM
+ REQUIRED_VARS ${components_found_vars}
+ FAIL_MESSAGE "Could not find all required gems")
+
diff --git a/cmake/FindOSMPBF.cmake b/cmake/FindOSMPBF.cmake
new file mode 100644
index 0000000..deeebd8
--- /dev/null
+++ b/cmake/FindOSMPBF.cmake
@@ -0,0 +1,50 @@
+#
+# Locate OSMPBF library
+#
+# This module defines
+# OSMPBF_FOUND - if false, do not try to link to OSMPBF
+# OSMPBF_LIBRARIES - full library path name
+# OSMPBF_INCLUDE_DIRS - where to find OSMPBF.hpp
+#
+# Note that the expected include convention is
+# #include <osmpbf/osmpbf.h>
+# and not
+# #include <osmpbf.h>
+#
+
+find_path(OSMPBF_INCLUDE_DIR osmpbf/osmpbf.h
+ HINTS $ENV{OSMPBF_DIR}
+ PATH_SUFFIXES include
+ PATHS
+ ~/Library/Frameworks
+ /Library/Frameworks
+ /usr/local
+ /usr
+ /opt/local # DarwinPorts
+ /opt
+)
+
+find_library(OSMPBF_LIBRARY
+ NAMES osmpbf
+ HINTS $ENV{OSMPBF_DIR}
+ PATH_SUFFIXES lib64 lib
+ PATHS
+ ~/Library/Frameworks
+ /Library/Frameworks
+ /usr/local
+ /usr
+ /opt/local
+ /opt
+)
+
+# Handle the QUIETLY and REQUIRED arguments and set OSMPBF_FOUND to TRUE if
+# all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(OSMPBF DEFAULT_MSG OSMPBF_LIBRARY OSMPBF_INCLUDE_DIR)
+
+# Copy the results to the output variables.
+if(OSMPBF_FOUND)
+ set(OSMPBF_INCLUDE_DIRS ${OSMPBF_INCLUDE_DIR})
+ set(OSMPBF_LIBRARIES ${OSMPBF_LIBRARY})
+endif()
+
diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake
new file mode 100644
index 0000000..4c33708
--- /dev/null
+++ b/cmake/FindOsmium.cmake
@@ -0,0 +1,336 @@
+#----------------------------------------------------------------------
+#
+# FindOsmium.cmake
+#
+# Find the Libosmium headers and, optionally, several components needed for
+# different Libosmium functions.
+#
+#----------------------------------------------------------------------
+#
+# Usage:
+#
+# Copy this file somewhere into your project directory, where cmake can
+# find it. Usually this will be a directory called "cmake" which you can
+# add to the CMake module search path with the following line in your
+# CMakeLists.txt:
+#
+# list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+#
+# Then add the following in your CMakeLists.txt:
+#
+# find_package(Osmium REQUIRED COMPONENTS <XXX>)
+# include_directories(${OSMIUM_INCLUDE_DIRS})
+#
+# For the <XXX> substitute a space separated list of one or more of the
+# following components:
+#
+# pbf - include libraries needed for PBF input and output
+# xml - include libraries needed for XML input and output
+# io - include libraries needed for any type of input/output
+# geos - include if you want to use any of the GEOS functions
+# gdal - include if you want to use any of the OGR functions
+# proj - include if you want to use any of the Proj.4 functions
+# sparsehash - include if you use the sparsehash index
+#
+# You can check for success with something like this:
+#
+# if(NOT OSMIUM_FOUND)
+# message(WARNING "Libosmium not found!\n")
+# endif()
+#
+#----------------------------------------------------------------------
+#
+# Variables:
+#
+# OSMIUM_FOUND - True if Osmium found.
+# OSMIUM_INCLUDE_DIRS - Where to find include files.
+# OSMIUM_XML_LIBRARIES - Libraries needed for XML I/O.
+# OSMIUM_PBF_LIBRARIES - Libraries needed for PBF I/O.
+# OSMIUM_IO_LIBRARIES - Libraries needed for XML or PBF I/O.
+# OSMIUM_LIBRARIES - All libraries Osmium uses somewhere.
+#
+#----------------------------------------------------------------------
+
+# Look for the header file.
+find_path(OSMIUM_INCLUDE_DIR osmium/osm.hpp
+ PATH_SUFFIXES include
+ PATHS
+ ../libosmium
+ ../../libosmium
+ libosmium
+ ~/Library/Frameworks
+ /Library/Frameworks
+ /usr/local
+ /usr/
+ /opt/local # DarwinPorts
+ /opt
+)
+
+# Handle the QUIETLY and REQUIRED arguments and set OSMIUM_FOUND to TRUE if
+# all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(OSMIUM REQUIRED_VARS OSMIUM_INCLUDE_DIR)
+
+# Copy the results to the output variables.
+if(OSMIUM_FOUND)
+ set(OSMIUM_INCLUDE_DIRS ${OSMIUM_INCLUDE_DIR})
+else()
+ set(OSMIUM_INCLUDE_DIRS "")
+endif()
+
+if(Osmium_FIND_REQUIRED AND NOT OSMIUM_FOUND)
+ message(FATAL_ERROR "Can not find libosmium headers, please install them or configure the paths")
+endif()
+
+#----------------------------------------------------------------------
+#
+# Check for optional components
+#
+#----------------------------------------------------------------------
+if(Osmium_FIND_COMPONENTS)
+ foreach(_component ${Osmium_FIND_COMPONENTS})
+ string(TOUPPER ${_component} _component_uppercase)
+ set(Osmium_USE_${_component_uppercase} TRUE)
+ endforeach()
+endif()
+
+#----------------------------------------------------------------------
+# Component 'io' is an alias for 'pbf' and 'xml'
+if(Osmium_USE_IO)
+ set(Osmium_USE_PBF TRUE)
+ set(Osmium_USE_XML TRUE)
+endif()
+
+#----------------------------------------------------------------------
+# Component 'ogr' is an alias for 'gdal'
+if(Osmium_USE_OGR)
+ set(Osmium_USE_GDAL TRUE)
+endif()
+
+#----------------------------------------------------------------------
+# Component 'pbf'
+if(Osmium_USE_PBF)
+ find_package(OSMPBF)
+ find_package(Protobuf)
+ find_package(ZLIB)
+ find_package(Threads)
+
+ if(OSMPBF_FOUND AND PROTOBUF_FOUND AND ZLIB_FOUND AND Threads_FOUND)
+ list(APPEND OSMIUM_PBF_LIBRARIES
+ ${OSMPBF_LIBRARIES}
+ ${PROTOBUF_LITE_LIBRARY}
+ ${ZLIB_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
+ )
+ if(WIN32)
+ list(APPEND OSMIUM_PBF_LIBRARIES ws2_32)
+ endif()
+ list(APPEND OSMIUM_INCLUDE_DIRS
+ ${OSMPBF_INCLUDE_DIRS}
+ ${PROTOBUF_INCLUDE_DIR}
+ ${ZLIB_INCLUDE_DIR}
+ )
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: Can not find some libraries for PBF input/output, please install them or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+# Component 'xml'
+if(Osmium_USE_XML)
+ find_package(EXPAT)
+ find_package(BZip2)
+ find_package(ZLIB)
+ find_package(Threads)
+
+ if(EXPAT_FOUND AND BZIP2_FOUND AND ZLIB_FOUND AND Threads_FOUND)
+ list(APPEND OSMIUM_XML_LIBRARIES
+ ${EXPAT_LIBRARIES}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
+ )
+ list(APPEND OSMIUM_INCLUDE_DIRS
+ ${EXPAT_INCLUDE_DIR}
+ ${BZIP2_INCLUDE_DIR}
+ ${ZLIB_INCLUDE_DIR}
+ )
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: Can not find some libraries for XML input/output, please install them or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+list(APPEND OSMIUM_IO_LIBRARIES
+ ${OSMIUM_PBF_LIBRARIES}
+ ${OSMIUM_XML_LIBRARIES}
+)
+
+list(APPEND OSMIUM_LIBRARIES
+ ${OSMIUM_IO_LIBRARIES}
+)
+
+#----------------------------------------------------------------------
+# Component 'geos'
+if(Osmium_USE_GEOS)
+ find_path(GEOS_INCLUDE_DIR geos/geom.h)
+ find_library(GEOS_LIBRARY NAMES geos)
+
+ if(GEOS_INCLUDE_DIR AND GEOS_LIBRARY)
+ SET(GEOS_FOUND 1)
+ list(APPEND OSMIUM_LIBRARIES ${GEOS_LIBRARY})
+ list(APPEND OSMIUM_INCLUDE_DIRS ${GEOS_INCLUDE_DIR})
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: GEOS library is required but not found, please install it or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+# Component 'gdal' (alias 'ogr')
+if(Osmium_USE_GDAL)
+ find_package(GDAL)
+
+ if(GDAL_FOUND)
+ list(APPEND OSMIUM_LIBRARIES ${GDAL_LIBRARIES})
+ list(APPEND OSMIUM_INCLUDE_DIRS ${GDAL_INCLUDE_DIRS})
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: GDAL library is required but not found, please install it or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+# Component 'proj'
+if(Osmium_USE_PROJ)
+ find_path(PROJ_INCLUDE_DIR proj_api.h)
+ find_library(PROJ_LIBRARY NAMES proj)
+
+ if(PROJ_INCLUDE_DIR AND PROJ_LIBRARY)
+ set(PROJ_FOUND 1)
+ list(APPEND OSMIUM_LIBRARIES ${PROJ_LIBRARY})
+ list(APPEND OSMIUM_INCLUDE_DIRS ${PROJ_INCLUDE_DIR})
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: PROJ.4 library is required but not found, please install it or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+# Component 'sparsehash'
+if(Osmium_USE_SPARSEHASH)
+ find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable)
+
+ if(SPARSEHASH_INCLUDE_DIR)
+ # Find size of sparsetable::size_type. This does not work on older
+ # CMake versions because they can do this check only in C, not in C++.
+ # Until we find a better way, we'll live with that.
+ include(CheckTypeSize)
+ set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR})
+ set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable")
+ check_type_size("google::sparsetable<int>::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX)
+ set(CMAKE_EXTRA_INCLUDE_FILES)
+ set(CMAKE_REQUIRED_INCLUDES)
+
+ # Sparsetable::size_type must be at least 8 bytes (64bit), otherwise
+ # OSM object IDs will not fit.
+ if(SPARSETABLE_SIZE_TYPE GREATER 7)
+ set(SPARSEHASH_FOUND 1)
+ add_definitions(-DOSMIUM_WITH_SPARSEHASH=${SPARSEHASH_FOUND})
+ list(APPEND OSMIUM_INCLUDE_DIRS ${SPARSEHASH_INCLUDE_DIR})
+ elseif(SPARSETABLE_SIZE_TYPE STREQUAL "")
+ message(WARNING "Osmium: Disabled Google SparseHash library because we can't detect whether we are on a 64bit system.")
+ else()
+ message(WARNING "Osmium: Disabled Google SparseHash library on 32bit system (size_type=${SPARSETABLE_SIZE_TYPE}).")
+ endif()
+ else()
+ set(_missing_libraries 1)
+ message(WARNING "Osmium: Google SparseHash library is required but not found, please install it or configure the paths.")
+ endif()
+endif()
+
+#----------------------------------------------------------------------
+
+list(REMOVE_DUPLICATES OSMIUM_INCLUDE_DIRS)
+
+if(OSMIUM_XML_LIBRARIES)
+ list(REMOVE_DUPLICATES OSMIUM_XML_LIBRARIES)
+endif()
+
+if(OSMIUM_PBF_LIBRARIES)
+ list(REMOVE_DUPLICATES OSMIUM_PBF_LIBRARIES)
+endif()
+
+if(OSMIUM_IO_LIBRARIES)
+ list(REMOVE_DUPLICATES OSMIUM_IO_LIBRARIES)
+endif()
+
+if(OSMIUM_LIBRARIES)
+ list(REMOVE_DUPLICATES OSMIUM_LIBRARIES)
+endif()
+
+#----------------------------------------------------------------------
+#
+# Check that all required libraries are available
+#
+#----------------------------------------------------------------------
+if(Osmium_FIND_REQUIRED AND _missing_libraries)
+ message(FATAL_ERROR "Required library or libraries missing. Aborting.")
+endif()
+
+#----------------------------------------------------------------------
+#
+# Add compiler flags
+#
+#----------------------------------------------------------------------
+add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64)
+
+if(MSVC)
+ add_definitions(-wd4996)
+
+ # Disable warning C4068: "unknown pragma" because we want it to ignore
+ # pragmas for other compilers.
+ add_definitions(-wd4068)
+
+ # Disable warning C4715: "not all control paths return a value" because
+ # it generates too many false positives.
+ add_definitions(-wd4715)
+
+ # Disable warning C4351: new behavior: elements of array '...' will be
+ # default initialized. The new behaviour is correct and we don't support
+ # old compilers anyway.
+ add_definitions(-wd4351)
+
+ add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS)
+endif()
+
+if(APPLE)
+# following only available from cmake 2.8.12:
+# add_compile_options(-stdlib=libc++)
+# so using this instead:
+ add_definitions(-stdlib=libc++)
+ set(LDFLAGS ${LDFLAGS} -stdlib=libc++)
+endif()
+
+#----------------------------------------------------------------------
+
+# This is a set of recommended warning options that can be added when compiling
+# libosmium code.
+if(MSVC)
+ set(OSMIUM_WARNING_OPTIONS "/W3 /wd4514" CACHE STRING "Recommended warning options for libosmium")
+else()
+ set(OSMIUM_WARNING_OPTIONS "-Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast -Wno-return-type" CACHE STRING "Recommended warning options for libosmium")
+endif()
+
+set(OSMIUM_DRACONIC_CLANG_OPTIONS "-Wdocumentation -Wunused-exception-parameter -Wmissing-declarations -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-exit-time-destructors -Wno-global-constructors -Wno-padded -Wno-switch-enum -Wno-missing-prototypes -Wno-weak-vtables -Wno-cast-align -Wno-float-equal")
+
+if(Osmium_DEBUG)
+ message(STATUS "OSMIUM_XML_LIBRARIES=" ${OSMIUM_XML_LIBRARIES})
+ message(STATUS "OSMIUM_PBF_LIBRARIES=" ${OSMIUM_PBF_LIBRARIES})
+ message(STATUS "OSMIUM_IO_LIBRARIES=" ${OSMIUM_IO_LIBRARIES})
+ message(STATUS "OSMIUM_LIBRARIES=" ${OSMIUM_LIBRARIES})
+ message(STATUS "OSMIUM_INCLUDE_DIRS=" ${OSMIUM_INCLUDE_DIRS})
+endif()
+
diff --git a/cmake/README b/cmake/README
new file mode 100644
index 0000000..4a035f7
--- /dev/null
+++ b/cmake/README
@@ -0,0 +1,3 @@
+
+FindGem.cmake from https://github.com/rock-core/base-cmake
+
diff --git a/cmake/build.bat b/cmake/build.bat
new file mode 100644
index 0000000..5ffab12
--- /dev/null
+++ b/cmake/build.bat
@@ -0,0 +1,15 @@
+call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64
+set CMAKE_PREFIX_PATH=C:\PROJ
+set VERSION=Debug
+set TESTS=ON
+set ALLHPPS=ON
+set PREFIX=d:\libs18d
+set BOOST_ROOT=d:\boost
+
+cmake .. -G "Visual Studio 12 Win64" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=%PREFIX% -DBOOST_ROOT=%BOOST_ROOT% -DBoost_USE_STATIC_LIBS=ON -DBUILD_TESTING=%TESTS% -DBUILD_TRY_HPPS=%ALLHPPS$ -T CTP_Nov2013
+msbuild /clp:Verbosity=minimal /nologo libosmium.sln /flp1:logfile=build_errors.txt;errorsonly /flp2:logfile=build_warnings.txt;warningsonly
+set PATH=%PATH%;%PREFIX%/bin
+
+del test\osm-testdata\*.db
+del test\osm-testdata\*.json
+if "%TESTS%"=="ON" ctest -VV >build_tests.log
diff --git a/cmake/iwyu.sh b/cmake/iwyu.sh
new file mode 100755
index 0000000..d203844
--- /dev/null
+++ b/cmake/iwyu.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# This will run IWYU (Include What You Use) on includes files. The iwyu
+# program isn't very reliable and crashes often, but is still useful.
+#
+# TODO: This script should be integrated with cmake in some way...
+#
+
+cmdline="iwyu -Xiwyu --mapping_file=osmium.imp -std=c++11 -I include"
+
+log=build/iwyu.log
+
+echo "INCLUDE WHAT YOU USE REPORT:" >$log
+
+allok=yes
+
+mkdir -p build/check_reports
+
+for file in `find include/osmium -name \*.hpp`; do
+ mkdir -p `dirname build/check_reports/$file`
+ ifile="build/check_reports/${file%.hpp}.iwyu"
+ $cmdline $file >$ifile 2>&1
+ if grep -q 'has correct #includes/fwd-decls' ${ifile}; then
+ echo "\n\033[1m\033[32m========\033[0m \033[1m${file}\033[0m" >>$log
+ echo "[OK] ${file}"
+ elif grep -q 'Assertion failed' ${ifile}; then
+ echo "\n\033[1m======== ${file}\033[0m" >>$log
+ echo "[--] ${file}"
+ allok=no
+ else
+ echo "\n\033[1m\033[31m========\033[0m \033[1m${file}\033[0m" >>$log
+ echo "[ ] ${file}"
+ allok=no
+ fi
+ cat $ifile >>$log
+done
+
+if [ "$allok" = "yes" ]; then
+ echo "All files OK"
+else
+ echo "There were errors"
+fi
+
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644
index 0000000..9d69a16
--- /dev/null
+++ b/doc/CMakeLists.txt
@@ -0,0 +1,35 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium documentation
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring documentation")
+
+message(STATUS "Looking for doxygen")
+find_package(Doxygen)
+
+if(DOXYGEN_FOUND)
+ message(STATUS "Looking for doxygen - found")
+ configure_file(header.html ${CMAKE_CURRENT_BINARY_DIR}/header.html @ONLY)
+ configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
+ add_custom_target(doc
+ ${DOXYGEN_EXECUTABLE}
+ ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Generating API documentation with Doxygen" VERBATIM
+ )
+# install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html"
+# DESTINATION "share/doc/libosmium-dev")
+else()
+ message(STATUS "Looking for doxygen - not found")
+ message(STATUS " Disabled making of documentation.")
+endif()
+
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring documentation - done")
+
+
+#-----------------------------------------------------------------------------
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
new file mode 100644
index 0000000..6535259
--- /dev/null
+++ b/doc/Doxyfile.in
@@ -0,0 +1,2313 @@
+# Doxyfile 1.8.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Libosmium"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @LIBOSMIUM_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Fast and flexible C++ library for working with OpenStreetMap data"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = "@PROJECT_BINARY_DIR@/doc"
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/include
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @PROJECT_SOURCE_DIR@/include/osmium \
+ @PROJECT_SOURCE_DIR@/doc/doc.txt
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.hpp
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = detail
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS = *::detail
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = NO
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER = header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET = doc/osmium.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NONE
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = __linux__
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..7ca8e7c
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,8 @@
+
+The `header.html` is created with:
+
+`doxygen -w html header.html footer.html stylesheet.css`
+
+This might have to be rn again for newer Doxygen versions. After that add
+changes back in.
+
diff --git a/doc/doc.txt b/doc/doc.txt
new file mode 100644
index 0000000..1f06f4f
--- /dev/null
+++ b/doc/doc.txt
@@ -0,0 +1,26 @@
+
+/**
+ * @mainpage
+ *
+ * Osmium is a fast and flexible C++ library for working with OpenStreetMap
+ * data.
+ *
+ * This is the API documentation that was automatically created from the
+ * source code. For more information about the Osmium Library see
+ * http://osmcode.org/libosmium .
+ *
+ * Osmium is free software and available under the Boost Software License.
+ * The source code is available at https://github.com/osmcode/libosmium .
+ *
+ * Osmium is a header-only library. You do not need to compile and link it,
+ * just include the headers you need.
+ *
+ * Everything in namespaces called "detail" is for internal Osmium use only,
+ * do not depend on it in your code. Do not include any include files in
+ * directories named "detail" directly. Include files in directories called
+ * "experimental" and everything in namespaces called "experimental" is
+ * unsupported and may change at any time regardless of the status of the rest
+ * of the library.
+ *
+ */
+
diff --git a/doc/header.html b/doc/header.html
new file mode 100644
index 0000000..495d500
--- /dev/null
+++ b/doc/header.html
@@ -0,0 +1,56 @@
+<!-- HTML header for doxygen 1.8.8-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<meta name="generator" content="Doxygen $doxygenversion"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath^jquery.js"></script>
+<script type="text/javascript" src="$relpath^dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <!--BEGIN PROJECT_LOGO-->
+ <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
+ <!--END PROJECT_LOGO-->
+ <!--BEGIN PROJECT_NAME-->
+ <td style="padding-left: 0.5em;">
+ <div id="projectname">$projectname
+ <!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
+ </div>
+ <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
+ </td>
+ <!--END PROJECT_NAME-->
+ <!--BEGIN !PROJECT_NAME-->
+ <!--BEGIN PROJECT_BRIEF-->
+ <td style="padding-left: 0.5em;">
+ <div id="projectbrief">$projectbrief</div>
+ </td>
+ <!--END PROJECT_BRIEF-->
+ <!--END !PROJECT_NAME-->
+ <!--BEGIN DISABLE_INDEX-->
+ <!--BEGIN SEARCHENGINE-->
+ <td>$searchbox</td>
+ <!--END SEARCHENGINE-->
+ <!--END DISABLE_INDEX-->
+ </tr>
+ </tbody>
+</table>
+</div>
+<!--END TITLEAREA-->
+<!-- end header part -->
diff --git a/doc/osmium.css b/doc/osmium.css
new file mode 100644
index 0000000..6256e84
--- /dev/null
+++ b/doc/osmium.css
@@ -0,0 +1,22 @@
+
+body {
+ font-family: "Droid Sans",Helvetica,Arial,sans-serif;
+ background-color: #ffffff;
+ color: #202060;
+}
+
+.tabs, .tabs2, .tabs3, .navpath ul, .tablist li {
+ background-image: none;
+}
+
+.tabs, .tabs2, .tabs3 {
+ border-top: 1px solid #202060;
+}
+
+div.contents {
+ margin: 0px;
+ padding-top: 10px;
+ padding-left: 12px;
+ padding-right: 8px;
+}
+
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..c9f5960
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,115 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium examples
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring examples")
+
+set(EXAMPLES
+ area_test
+ convert
+ count
+ create_node_cache
+ debug
+ index
+ read
+ serdump
+ toogr
+ toogr2
+ toogr2_exp
+ use_node_cache
+ CACHE STRING "Example programs"
+)
+
+
+#-----------------------------------------------------------------------------
+#
+# Examples depending on wingetopt
+#
+#-----------------------------------------------------------------------------
+set(GETOPT_EXAMPLES area_test convert serdump toogr toogr2 toogr2_exp)
+if(NOT GETOPT_MISSING)
+ foreach(example ${GETOPT_EXAMPLES})
+ list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY})
+ endforeach()
+else()
+ message(STATUS "Configuring examples - Skipping examples because on Visual Studio the wingetopt library is needed and was not found:")
+ foreach(example ${GETOPT_EXAMPLES})
+ message(STATUS " - osmium_${example}")
+ list(REMOVE_ITEM EXAMPLES ${example})
+ endforeach()
+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()
+
+
+#-----------------------------------------------------------------------------
+#
+# Examples depending on GDAL/PROJ.4/SparseHash
+#
+#-----------------------------------------------------------------------------
+set(OGR_EXAMPLES toogr toogr2 toogr2_exp)
+
+if(GDAL_FOUND AND PROJ_FOUND AND SPARSEHASH_FOUND)
+ foreach(example ${OGR_EXAMPLES})
+ list(APPEND EXAMPLE_LIBS_${example} ${GDAL_LIBRARIES})
+ list(APPEND EXAMPLE_LIBS_${example} ${PROJ_LIBRARIES})
+ endforeach()
+else()
+ message(STATUS "Configuring examples - Skipping examples because GDAL and/or Proj.4 and/or SparseHash not found:")
+ foreach(example ${OGR_EXAMPLES})
+ message(STATUS " - osmium_${example}")
+ list(REMOVE_ITEM EXAMPLES ${example})
+ endforeach()
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
+# Configure examples
+#
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring examples - Building these examples:")
+foreach(example ${EXAMPLES})
+ message(STATUS " - osmium_${example}")
+ add_executable(osmium_${example} "osmium_${example}.cpp")
+ target_link_libraries(osmium_${example} ${OSMIUM_IO_LIBRARIES} ${EXAMPLE_LIBS_${example}})
+endforeach()
+
+
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring examples - done")
+
+
+#-----------------------------------------------------------------------------
diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp
new file mode 100644
index 0000000..ee2ba12
--- /dev/null
+++ b/examples/osmium_area_test.cpp
@@ -0,0 +1,138 @@
+/*
+
+ This is an example tool that creates multipolygons from OSM data
+ and dumps them to stdout.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <getopt.h>
+
+#include <osmium/area/assembler.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/dynamic_handler.hpp>
+#include <osmium/geom/wkt.hpp>
+#include <osmium/handler/dump.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> location_handler_type;
+
+class WKTDump : public osmium::handler::Handler {
+
+ osmium::geom::WKTFactory<> m_factory ;
+
+ std::ostream& m_out;
+
+public:
+
+ WKTDump(std::ostream& out) :
+ m_out(out) {
+ }
+
+ 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";
+ }
+ }
+
+}; // class WKTDump
+
+void print_help() {
+ std::cout << "osmium_area_test [OPTIONS] OSMFILE\n\n"
+ << "Read OSMFILE and build multipolygons from it.\n"
+ << "\nOptions:\n"
+ << " -h, --help This help message\n"
+ << " -w, --dump-wkt Dump area geometries as WKT\n"
+ << " -o, --dump-objects Dump area objects\n";
+}
+
+int main(int argc, char* argv[]) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"dump-wkt", no_argument, 0, 'w'},
+ {"dump-objects", no_argument, 0, 'o'},
+ {0, 0, 0, 0}
+ };
+
+ osmium::handler::DynamicHandler handler;
+
+ while (true) {
+ int c = getopt_long(argc, argv, "hwo", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'w':
+ handler.set<WKTDump>(std::cout);
+ break;
+ case 'o':
+ handler.set<osmium::handler::Dump>(std::cout);
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ int remaining_args = argc - optind;
+ if (remaining_args != 1) {
+ std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n";
+ exit(1);
+ }
+
+ osmium::io::File infile(argv[optind]);
+
+ osmium::area::Assembler::config_type assembler_config;
+ osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
+
+ std::cout << "Pass 1...\n";
+ osmium::io::Reader reader1(infile, osmium::osm_entity_bits::relation);
+ collector.read_relations(reader1);
+ reader1.close();
+ std::cout << "Pass 1 done\n";
+
+ std::cout << "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
+
+ std::cout << "Pass 2...\n";
+ osmium::io::Reader reader2(infile);
+ osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) {
+ osmium::apply(buffer, handler);
+ }));
+ reader2.close();
+ std::cout << "Pass 2 done\n";
+
+ std::cout << "Memory:\n";
+ collector.used_memory();
+
+ std::vector<const osmium::Relation*> incomplete_relations = collector.get_incomplete_relations();
+ if (!incomplete_relations.empty()) {
+ std::cerr << "Warning! Some member ways missing for these multipolygon relations:";
+ for (const auto* relation : incomplete_relations) {
+ std::cerr << " " << relation->id();
+ }
+ std::cerr << "\n";
+ }
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_convert.cpp b/examples/osmium_convert.cpp
new file mode 100644
index 0000000..7956e11
--- /dev/null
+++ b/examples/osmium_convert.cpp
@@ -0,0 +1,112 @@
+/*
+
+ Convert OSM files from one format into another.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+#include <getopt.h>
+
+#include <osmium/io/any_input.hpp>
+
+#include <osmium/io/any_output.hpp>
+
+void print_help() {
+ std::cout << "osmium_convert [OPTIONS] [INFILE [OUTFILE]]\n\n" \
+ << "If INFILE or OUTFILE is not given stdin/stdout is assumed.\n" \
+ << "File format is autodetected from file name suffix.\n" \
+ << "Use -f and -t options to force file format.\n" \
+ << "\nFile types:\n" \
+ << " osm normal OSM file\n" \
+ << " osc OSM change file\n" \
+ << " osh OSM file with history information\n" \
+ << "\nFile format:\n" \
+ << " (default) XML encoding\n" \
+ << " pbf binary PBF encoding\n" \
+ << " opl OPL encoding\n" \
+ << "\nFile compression\n" \
+ << " gz compressed with gzip\n" \
+ << " bz2 compressed with bzip2\n" \
+ << "\nOptions:\n" \
+ << " -h, --help This help message\n" \
+ << " -f, --from-format=FORMAT Input format\n" \
+ << " -t, --to-format=FORMAT Output format\n";
+}
+
+int main(int argc, char* argv[]) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"from-format", required_argument, 0, 'f'},
+ {"to-format", required_argument, 0, 't'},
+ {0, 0, 0, 0}
+ };
+
+ std::string input_format;
+ std::string output_format;
+
+ while (true) {
+ int c = getopt_long(argc, argv, "dhf:t:", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'f':
+ input_format = optarg;
+ break;
+ case 't':
+ output_format = optarg;
+ break;
+ default:
+ 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];
+ }
+
+ osmium::io::File infile(input, input_format);
+
+ osmium::io::File outfile(output, output_format);
+
+ if (infile.has_multiple_object_versions() && !outfile.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);
+ osmium::io::Header header = reader.header();
+ header.set("generator", "osmium_convert");
+
+ osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow);
+ while (osmium::memory::Buffer buffer = reader.read()) {
+ writer(std::move(buffer));
+ }
+ writer.close();
+ reader.close();
+ } catch (std::exception& e) {
+ std::cerr << e.what() << "\n";
+ exit_code = 1;
+ }
+
+ google::protobuf::ShutdownProtobufLibrary();
+ return exit_code;
+}
+
diff --git a/examples/osmium_count.cpp b/examples/osmium_count.cpp
new file mode 100644
index 0000000..dca18bf
--- /dev/null
+++ b/examples/osmium_count.cpp
@@ -0,0 +1,57 @@
+/*
+
+ This is a small tool that counts the number of nodes, ways, and relations in
+ the input file.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/visitor.hpp>
+
+struct CountHandler : public osmium::handler::Handler {
+
+ int nodes = 0;
+ int ways = 0;
+ int relations = 0;
+
+ void node(osmium::Node&) {
+ ++nodes;
+ }
+
+ void way(osmium::Way&) {
+ ++ways;
+ }
+
+ void relation(osmium::Relation&) {
+ ++relations;
+ }
+
+};
+
+
+int main(int argc, char* argv[]) {
+
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+ exit(1);
+ }
+
+ osmium::io::File infile(argv[1]);
+ osmium::io::Reader reader(infile);
+
+ CountHandler handler;
+ osmium::apply(reader, handler);
+ reader.close();
+
+ std::cout << "Nodes: " << handler.nodes << "\n";
+ std::cout << "Ways: " << handler.ways << "\n";
+ std::cout << "Relations: " << handler.relations << "\n";
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_create_node_cache.cpp b/examples/osmium_create_node_cache.cpp
new file mode 100644
index 0000000..74f7596
--- /dev/null
+++ b/examples/osmium_create_node_cache.cpp
@@ -0,0 +1,58 @@
+/*
+
+ 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <iostream>
+
+#include <osmium/io/pbf_input.hpp>
+#include <osmium/io/xml_input.hpp>
+
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/dense_mmap_array.hpp>
+#include <osmium/index/map/dense_file_array.hpp>
+
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+//typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+typedef osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> 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();
+
+ google::protobuf::ShutdownProtobufLibrary();
+
+ return 0;
+}
+
diff --git a/examples/osmium_debug.cpp b/examples/osmium_debug.cpp
new file mode 100644
index 0000000..6878ed1
--- /dev/null
+++ b/examples/osmium_debug.cpp
@@ -0,0 +1,52 @@
+/*
+
+ This is a small tool to dump the contents of the input file.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/handler/dump.hpp>
+#include <osmium/io/any_input.hpp>
+
+int main(int argc, char* argv[]) {
+ 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);
+ }
+
+ osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all;
+
+ if (argc == 3) {
+ read_types = osmium::osm_entity_bits::nothing;
+ std::string types = argv[2];
+ if (types.find('n') != std::string::npos) read_types |= osmium::osm_entity_bits::node;
+ if (types.find('w') != std::string::npos) read_types |= osmium::osm_entity_bits::way;
+ if (types.find('r') != std::string::npos) read_types |= osmium::osm_entity_bits::relation;
+ if (types.find('c') != std::string::npos) read_types |= osmium::osm_entity_bits::changeset;
+ }
+
+ osmium::io::Reader reader(argv[1], read_types);
+ osmium::io::Header header = reader.header();
+
+ std::cout << "HEADER:\n generator=" << header.get("generator") << "\n";
+
+ for (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);
+ }
+
+ reader.close();
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_index.cpp b/examples/osmium_index.cpp
new file mode 100644
index 0000000..b612140
--- /dev/null
+++ b/examples/osmium_index.cpp
@@ -0,0 +1,237 @@
+/*
+
+ Example program to look at Osmium indexes on disk.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <fcntl.h>
+#include <iomanip>
+#include <iostream>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <boost/program_options.hpp>
+
+#include <osmium/index/map/dense_file_array.hpp>
+#include <osmium/index/map/sparse_file_array.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+template <typename TKey, typename TValue>
+class IndexSearch {
+
+ typedef typename osmium::index::map::DenseFileArray<TKey, TValue> dense_index_type;
+ typedef typename osmium::index::map::SparseFileArray<TKey, TValue> sparse_index_type;
+
+ int m_fd;
+ bool m_dense_format;
+
+ void dump_dense() {
+ dense_index_type index(m_fd);
+
+ for (size_t i = 0; i < index.size(); ++i) {
+ if (index.get(i) != TValue()) {
+ std::cout << i << " " << index.get(i) << "\n";
+ }
+ }
+ }
+
+ void dump_sparse() {
+ sparse_index_type index(m_fd);
+
+ for (auto& element : index) {
+ std::cout << element.first << " " << element.second << "\n";
+ }
+ }
+
+ bool search_dense(TKey key) {
+ dense_index_type index(m_fd);
+
+ try {
+ TValue value = index.get(key);
+ std::cout << key << " " << value << std::endl;
+ } catch (...) {
+ std::cout << key << " not found" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool search_sparse(TKey key) {
+ typedef typename sparse_index_type::element_type element_type;
+ sparse_index_type index(m_fd);
+
+ element_type elem {key, TValue()};
+ auto positions = std::equal_range(index.begin(), index.end(), elem, [](const element_type& lhs, const element_type& rhs) {
+ return lhs.first < rhs.first;
+ });
+ if (positions.first == positions.second) {
+ std::cout << key << " not found" << std::endl;
+ return false;
+ }
+
+ for (auto& it = positions.first; it != positions.second; ++it) {
+ std::cout << it->first << " " << it->second << "\n";
+ }
+
+ return true;
+ }
+
+public:
+
+ IndexSearch(int fd, bool dense_format) :
+ m_fd(fd),
+ m_dense_format(dense_format) {
+ }
+
+ void dump() {
+ if (m_dense_format) {
+ dump_dense();
+ } else {
+ dump_sparse();
+ }
+ }
+
+ bool search(TKey key) {
+ if (m_dense_format) {
+ return search_dense(key);
+ } else {
+ return search_sparse(key);
+ }
+ }
+
+ bool search(std::vector<TKey> keys) {
+ bool found_all = true;
+
+ for (const auto key : keys) {
+ if (!search(key)) {
+ found_all = false;
+ }
+ }
+
+ return found_all;
+ }
+
+}; // class IndexSearch
+
+enum return_code : int {
+ okay = 0,
+ not_found = 1,
+ error = 2,
+ fatal = 3
+};
+
+namespace po = boost::program_options;
+
+class Options {
+
+ po::variables_map vm;
+
+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<std::string>(), "Read given index file in array format")
+ ("list,l", po::value<std::string>(), "Read given index file in list format")
+ ("dump,d", "Dump contents of index file to STDOUT")
+ ("search,s", po::value<std::vector<osmium::unsigned_object_id_type>>(), "Search for given id (Option can appear multiple times)")
+ ("type,t", po::value<std::string>(), "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);
+ }
+
+ 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);
+ }
+
+ if (!vm.count("type")) {
+ std::cerr << "Need --type argument." << std::endl;
+ exit(return_code::fatal);
+ }
+
+ const std::string& type = vm["type"].as<std::string>();
+ 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);
+ }
+ }
+
+ const std::string& filename() const {
+ if (vm.count("array")) {
+ return vm["array"].as<std::string>();
+ } else {
+ return vm["list"].as<std::string>();
+ }
+ }
+
+ bool dense_format() const {
+ return vm.count("array") != 0;
+ }
+
+ bool do_dump() const {
+ return vm.count("dump") != 0;
+ }
+
+ std::vector<osmium::unsigned_object_id_type> search_keys() const {
+ return vm["search"].as<std::vector<osmium::unsigned_object_id_type>>();
+ }
+
+ bool type_is(const char* type) const {
+ return vm["type"].as<std::string>() == type;
+ }
+
+}; // class Options
+
+int main(int argc, char* argv[]) {
+ std::ios_base::sync_with_stdio(false);
+
+ Options options(argc, argv);
+
+ std::cout << std::fixed << std::setprecision(7);
+ int fd = open(options.filename().c_str(), O_RDWR);
+
+ bool result_okay = true;
+
+ if (options.type_is("location")) {
+ IndexSearch<osmium::unsigned_object_id_type, osmium::Location> is(fd, options.dense_format());
+
+ if (options.do_dump()) {
+ is.dump();
+ } else {
+ result_okay = is.search(options.search_keys());
+ }
+ } else {
+ IndexSearch<osmium::unsigned_object_id_type, size_t> is(fd, options.dense_format());
+
+ if (options.do_dump()) {
+ is.dump();
+ } else {
+ result_okay = is.search(options.search_keys());
+ }
+ }
+
+ exit(result_okay ? return_code::okay : return_code::not_found);
+}
+
diff --git a/examples/osmium_read.cpp b/examples/osmium_read.cpp
new file mode 100644
index 0000000..1bb0299
--- /dev/null
+++ b/examples/osmium_read.cpp
@@ -0,0 +1,32 @@
+/*
+
+ This is a small tool that reads and discards the contents of the input file.
+ (Used for timing.)
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+
+#include <osmium/io/any_input.hpp>
+
+int main(int argc, char* argv[]) {
+
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+ exit(1);
+ }
+
+ osmium::io::File infile(argv[1]);
+ osmium::io::Reader reader(infile);
+
+ while (osmium::memory::Buffer buffer = reader.read()) {
+ // do nothing
+ }
+
+ reader.close();
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_serdump.cpp b/examples/osmium_serdump.cpp
new file mode 100644
index 0000000..a774a8d
--- /dev/null
+++ b/examples/osmium_serdump.cpp
@@ -0,0 +1,209 @@
+/*
+
+ This is a small tool to dump the contents of the input file
+ in serialized format to stdout.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <cerrno>
+#include <cstring>
+#include <getopt.h>
+#include <iostream>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _MSC_VER
+# include <direct.h>
+#endif
+
+#include <osmium/io/pbf_input.hpp>
+#include <osmium/io/xml_input.hpp>
+#include <osmium/handler/disk_store.hpp>
+#include <osmium/handler/object_relations.hpp>
+
+#include <osmium/index/map/sparse_mem_array.hpp>
+#include <osmium/index/multimap/sparse_mem_multimap.hpp>
+#include <osmium/index/multimap/sparse_mem_array.hpp>
+#include <osmium/index/multimap/hybrid.hpp>
+
+// ==============================================================================
+// Choose the following depending on the size of the input OSM files:
+// ==============================================================================
+// for smaller OSM files (extracts)
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, size_t> offset_index_type;
+//typedef osmium::index::map::SparseMapMmap<osmium::unsigned_object_id_type, size_t> offset_index_type;
+//typedef osmium::index::map::SparseMapFile<osmium::unsigned_object_id_type, size_t> offset_index_type;
+
+typedef osmium::index::multimap::SparseMemArray<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type> map_type;
+//typedef osmium::index::multimap::SparseMemMultimap<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type> map_type;
+//typedef osmium::index::multimap::Hybrid<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type> map_type;
+
+// ==============================================================================
+// for very large OSM files (planet)
+//typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, size_t> offset_index_type;
+// ==============================================================================
+
+void print_help() {
+ std::cout << "osmium_serdump OSMFILE DIR\n" \
+ << "Serialize content of OSMFILE into data file in DIR.\n" \
+ << "\nOptions:\n" \
+ << " -h, --help This help message\n";
+}
+
+int main(int argc, char* argv[]) {
+ std::ios_base::sync_with_stdio(false);
+
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while (true) {
+ int c = getopt_long(argc, argv, "h", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ default:
+ exit(2);
+ }
+ }
+
+ int remaining_args = argc - optind;
+
+ if (remaining_args != 2) {
+ std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n";
+ exit(2);
+ }
+
+ std::string dir(argv[optind+1]);
+#ifndef _WIN32
+ int result = ::mkdir(dir.c_str(), 0777);
+#else
+ int result = mkdir(dir.c_str());
+#endif
+ if (result == -1 && errno != EEXIST) {
+ std::cerr << "Problem creating directory '" << dir << "': " << strerror(errno) << "\n";
+ 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);
+ }
+
+ offset_index_type node_index;
+ offset_index_type way_index;
+ offset_index_type relation_index;
+
+ osmium::handler::DiskStore disk_store_handler(data_fd, node_index, way_index, relation_index);
+
+ map_type map_node2way;
+ map_type map_node2relation;
+ map_type map_way2relation;
+ map_type map_relation2relation;
+
+ osmium::handler::ObjectRelations object_relations_handler(map_node2way, map_node2relation, map_way2relation, map_relation2relation);
+
+ osmium::io::Reader reader(argv[1]);
+
+ while (osmium::memory::Buffer buffer = reader.read()) {
+ disk_store_handler(buffer); // XXX
+ osmium::apply(buffer, object_relations_handler);
+ }
+
+ reader.close();
+
+ {
+ std::string index_file(dir + "/nodes.idx");
+ 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);
+ }
+ node_index.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ std::string index_file(dir + "/ways.idx");
+ 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);
+ }
+ way_index.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ std::string index_file(dir + "/relations.idx");
+ 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);
+ }
+ relation_index.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ map_node2way.sort();
+ std::string index_file(dir + "/node2way.map");
+ 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);
+ }
+ map_node2way.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ map_node2relation.sort();
+ std::string index_file(dir + "/node2rel.map");
+ 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);
+ }
+ map_node2relation.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ map_way2relation.sort();
+ std::string index_file(dir + "/way2rel.map");
+ 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);
+ }
+ map_way2relation.dump_as_list(fd);
+ close(fd);
+ }
+
+ {
+ map_relation2relation.sort();
+ std::string index_file(dir + "/rel2rel.map");
+ 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);
+ }
+ map_relation2relation.dump_as_list(fd);
+ close(fd);
+ }
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_toogr.cpp b/examples/osmium_toogr.cpp
new file mode 100644
index 0000000..6d8ab8d
--- /dev/null
+++ b/examples/osmium_toogr.cpp
@@ -0,0 +1,246 @@
+/*
+
+ This is an example tool that converts OSM data to some output format
+ like Spatialite or Shapefiles using the OGR library.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+#include <getopt.h>
+
+#include <osmium/index/map/all.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+
+#include <osmium/geom/ogr.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> location_handler_type;
+
+class MyOGRHandler : public osmium::handler::Handler {
+
+ OGRDataSource* m_data_source;
+ OGRLayer* m_layer_point;
+ OGRLayer* m_layer_linestring;
+
+ osmium::geom::OGRFactory<> m_factory;
+
+public:
+
+ MyOGRHandler(const std::string& driver_name, const std::string& filename) {
+
+ OGRRegisterAll();
+
+ OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str());
+ if (!driver) {
+ std::cerr << driver_name << " driver not available.\n";
+ exit(1);
+ }
+
+ CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
+ const char* options[] = { "SPATIALITE=TRUE", nullptr };
+ m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
+ if (!m_data_source) {
+ std::cerr << "Creation of output file failed.\n";
+ exit(1);
+ }
+
+ OGRSpatialReference sparef;
+ sparef.SetWellKnownGeogCS("WGS84");
+ m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr);
+ if (!m_layer_point) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_id("id", OFTReal);
+ layer_point_field_id.SetWidth(10);
+
+ if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_operator("operator", OFTString);
+ layer_point_field_operator.SetWidth(30);
+
+ if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) {
+ std::cerr << "Creating operator field failed.\n";
+ exit(1);
+ }
+
+ /* Transactions might make things faster, then again they might not.
+ Feel free to experiment and benchmark and report back. */
+ m_layer_point->StartTransaction();
+
+ m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr);
+ if (!m_layer_linestring) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_id("id", OFTReal);
+ layer_linestring_field_id.SetWidth(10);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_type("type", OFTString);
+ layer_linestring_field_type.SetWidth(30);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ m_layer_linestring->StartTransaction();
+ }
+
+ ~MyOGRHandler() {
+ m_layer_linestring->CommitTransaction();
+ m_layer_point->CommitTransaction();
+ OGRDataSource::DestroyDataSource(m_data_source);
+ OGRCleanupAll();
+ }
+
+ void node(const osmium::Node& node) {
+ const char* amenity = node.tags().get_value_by_key("amenity");
+ if (amenity && !strcmp(amenity, "post_box")) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+ feature->SetField("operator", node.tags().get_value_by_key("operator"));
+
+ if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ const char* highway = way.tags().get_value_by_key("highway");
+ if (highway) {
+ try {
+ std::unique_ptr<OGRLineString> ogr_linestring = m_factory.create_linestring(way);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id", static_cast<double>(way.id()));
+ feature->SetField("type", highway);
+
+ if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
+ }
+ }
+ }
+
+};
+
+/* ================================================== */
+
+void print_help() {
+ std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \
+ << "If INFILE is not given stdin is assumed.\n" \
+ << "If OUTFILE is not given 'ogr_out' is used.\n" \
+ << "\nOptions:\n" \
+ << " -h, --help This help message\n" \
+ << " -l, --location_store=TYPE Set location store\n" \
+ << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n" \
+ << " -L See available location stores\n";
+}
+
+int main(int argc, char* argv[]) {
+ const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"format", required_argument, 0, 'f'},
+ {"location_store", required_argument, 0, 'l'},
+ {"list_location_stores", no_argument, 0, 'L'},
+ {0, 0, 0, 0}
+ };
+
+ std::string output_format { "SQLite" };
+ std::string location_store { "sparse_mem_array" };
+
+ while (true) {
+ int c = getopt_long(argc, argv, "hf:l:L", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'f':
+ output_format = optarg;
+ break;
+ case 'l':
+ location_store = optarg;
+ break;
+ case 'L':
+ std::cout << "Available map types:\n";
+ for (const auto& map_type : map_factory.map_types()) {
+ std::cout << " " << map_type << "\n";
+ }
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ std::string input_filename;
+ std::string output_filename("ogr_out");
+ int remaining_args = argc - optind;
+ if (remaining_args > 2) {
+ std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl;
+ exit(1);
+ } else if (remaining_args == 2) {
+ input_filename = argv[optind];
+ output_filename = argv[optind+1];
+ } else if (remaining_args == 1) {
+ input_filename = argv[optind];
+ } else {
+ input_filename = "-";
+ }
+
+ osmium::io::Reader reader(input_filename);
+
+ std::unique_ptr<index_pos_type> index_pos = map_factory.create_map(location_store);
+ index_neg_type index_neg;
+ location_handler_type location_handler(*index_pos, index_neg);
+ location_handler.ignore_errors();
+
+ MyOGRHandler ogr_handler(output_format, output_filename);
+
+ osmium::apply(reader, location_handler, ogr_handler);
+ reader.close();
+
+ google::protobuf::ShutdownProtobufLibrary();
+
+ int locations_fd = open("locations.dump", O_WRONLY | O_CREAT, 0644);
+ if (locations_fd < 0) {
+ throw std::system_error(errno, std::system_category(), "Open failed");
+ }
+ index_pos->dump_as_list(locations_fd);
+ close(locations_fd);
+}
+
diff --git a/examples/osmium_toogr2.cpp b/examples/osmium_toogr2.cpp
new file mode 100644
index 0000000..a966c5e
--- /dev/null
+++ b/examples/osmium_toogr2.cpp
@@ -0,0 +1,333 @@
+/*
+
+ This is an example tool that converts OSM data to some output format
+ like Spatialite or Shapefiles using the OGR library.
+
+ This version does multipolygon handling (in contrast to the osmium_toogr
+ example which doesn't).
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+#include <getopt.h>
+
+// usually you only need one or two of these
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/assembler.hpp>
+
+#include <osmium/geom/mercator_projection.hpp>
+//#include <osmium/geom/projection.hpp>
+#include <osmium/geom/ogr.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> location_handler_type;
+
+class MyOGRHandler : public osmium::handler::Handler {
+
+ OGRDataSource* m_data_source;
+ OGRLayer* m_layer_point;
+ OGRLayer* m_layer_linestring;
+ OGRLayer* m_layer_polygon;
+
+ // Choose one of the following:
+
+ // 1. Use WGS84, do not project coordinates.
+ //osmium::geom::OGRFactory<> m_factory {};
+
+ // 2. Project coordinates into "Web Mercator".
+ osmium::geom::OGRFactory<osmium::geom::MercatorProjection> m_factory;
+
+ // 3. Use any projection that the proj library can handle.
+ // (Initialize projection with EPSG code or proj string).
+ // In addition you need to link with "-lproj" and add
+ // #include <osmium/geom/projection.hpp>.
+ //osmium::geom::OGRFactory<osmium::geom::Projection> m_factory {osmium::geom::Projection(3857)};
+
+public:
+
+ MyOGRHandler(const std::string& driver_name, const std::string& filename) {
+
+ OGRRegisterAll();
+
+ OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str());
+ if (!driver) {
+ std::cerr << driver_name << " driver not available.\n";
+ exit(1);
+ }
+
+ CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
+ const char* options[] = { "SPATIALITE=TRUE", nullptr };
+ m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
+ if (!m_data_source) {
+ std::cerr << "Creation of output file failed.\n";
+ exit(1);
+ }
+
+ OGRSpatialReference sparef;
+ sparef.importFromProj4(m_factory.proj_string().c_str());
+
+ m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr);
+ if (!m_layer_point) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_id("id", OFTReal);
+ layer_point_field_id.SetWidth(10);
+
+ if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_operator("operator", OFTString);
+ layer_point_field_operator.SetWidth(30);
+
+ if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) {
+ std::cerr << "Creating operator field failed.\n";
+ exit(1);
+ }
+
+ /* Transactions might make things faster, then again they might not.
+ Feel free to experiment and benchmark and report back. */
+ m_layer_point->StartTransaction();
+
+ m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr);
+ if (!m_layer_linestring) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_id("id", OFTReal);
+ layer_linestring_field_id.SetWidth(10);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_type("type", OFTString);
+ layer_linestring_field_type.SetWidth(30);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ m_layer_linestring->StartTransaction();
+
+ m_layer_polygon = m_data_source->CreateLayer("buildings", &sparef, wkbMultiPolygon, nullptr);
+ if (!m_layer_polygon) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_id("id", OFTInteger);
+ layer_polygon_field_id.SetWidth(10);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_type("type", OFTString);
+ layer_polygon_field_type.SetWidth(30);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ m_layer_polygon->StartTransaction();
+ }
+
+ ~MyOGRHandler() {
+ m_layer_polygon->CommitTransaction();
+ m_layer_linestring->CommitTransaction();
+ m_layer_point->CommitTransaction();
+ OGRDataSource::DestroyDataSource(m_data_source);
+ OGRCleanupAll();
+ }
+
+ void node(const osmium::Node& node) {
+ const char* amenity = node.tags()["amenity"];
+ if (amenity && !strcmp(amenity, "post_box")) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+ feature->SetField("operator", node.tags()["operator"]);
+
+ if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ const char* highway = way.tags()["highway"];
+ if (highway) {
+ try {
+ std::unique_ptr<OGRLineString> ogr_linestring = m_factory.create_linestring(way);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id", static_cast<double>(way.id()));
+ feature->SetField("type", highway);
+
+ if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
+ }
+ }
+ }
+
+ void area(const osmium::Area& area) {
+ const char* building = area.tags()["building"];
+ if (building) {
+ try {
+ std::unique_ptr<OGRMultiPolygon> ogr_polygon = m_factory.create_multipolygon(area);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn());
+ feature->SetGeometry(ogr_polygon.get());
+ feature->SetField("id", static_cast<int>(area.id()));
+ feature->SetField("type", building);
+
+ std::string type = "";
+ if (area.from_way()) {
+ type += "w";
+ } else {
+ type += "r";
+ }
+ feature->SetField("type", type.c_str());
+
+ if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n";
+ }
+ }
+ }
+
+};
+
+/* ================================================== */
+
+void print_help() {
+ std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \
+ << "If INFILE is not given stdin is assumed.\n" \
+ << "If OUTFILE is not given 'ogr_out' is used.\n" \
+ << "\nOptions:\n" \
+ << " -h, --help This help message\n" \
+ << " -d, --debug Enable debug output\n" \
+ << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n";
+}
+
+int main(int argc, char* argv[]) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"debug", no_argument, 0, 'd'},
+ {"format", required_argument, 0, 'f'},
+ {0, 0, 0, 0}
+ };
+
+ std::string output_format("SQLite");
+ bool debug = false;
+
+ while (true) {
+ int c = getopt_long(argc, argv, "hdf:", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'd':
+ debug = true;
+ break;
+ case 'f':
+ output_format = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ std::string input_filename;
+ std::string output_filename("ogr_out");
+ int remaining_args = argc - optind;
+ if (remaining_args > 2) {
+ std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl;
+ exit(1);
+ } else if (remaining_args == 2) {
+ input_filename = argv[optind];
+ output_filename = argv[optind+1];
+ } else if (remaining_args == 1) {
+ input_filename = argv[optind];
+ } else {
+ input_filename = "-";
+ }
+
+ osmium::area::Assembler::config_type assembler_config;
+ assembler_config.enable_debug_output(debug);
+ osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
+
+ std::cerr << "Pass 1...\n";
+ osmium::io::Reader reader1(input_filename);
+ collector.read_relations(reader1);
+ reader1.close();
+ std::cerr << "Pass 1 done\n";
+
+ index_pos_type index_pos;
+ index_neg_type index_neg;
+ location_handler_type location_handler(index_pos, index_neg);
+ location_handler.ignore_errors();
+
+ MyOGRHandler ogr_handler(output_format, output_filename);
+
+ std::cerr << "Pass 2...\n";
+ osmium::io::Reader reader2(input_filename);
+
+ osmium::apply(reader2, location_handler, ogr_handler, collector.handler([&ogr_handler](const osmium::memory::Buffer& area_buffer) {
+ osmium::apply(area_buffer, ogr_handler);
+ }));
+
+ reader2.close();
+ std::cerr << "Pass 2 done\n";
+
+ std::vector<const osmium::Relation*> incomplete_relations = collector.get_incomplete_relations();
+ if (!incomplete_relations.empty()) {
+ std::cerr << "Warning! Some member ways missing for these multipolygon relations:";
+ for (const auto* relation : incomplete_relations) {
+ std::cerr << " " << relation->id();
+ }
+ std::cerr << "\n";
+ }
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_toogr2_exp.cpp b/examples/osmium_toogr2_exp.cpp
new file mode 100644
index 0000000..474da96
--- /dev/null
+++ b/examples/osmium_toogr2_exp.cpp
@@ -0,0 +1,307 @@
+/*
+
+ This is an example tool that converts OSM data to some output format
+ like Spatialite or Shapefiles using the OGR library.
+
+ This version does multipolygon handling (in contrast to the osmium_toogr
+ example which doesn't).
+
+ This version (..._exp) uses a new experimental unsupported interface.
+
+ The code in this example file is released into the Public Domain.
+
+*/
+
+#include <iostream>
+#include <getopt.h>
+
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+#include <osmium/visitor.hpp>
+
+#include <osmium/geom/mercator_projection.hpp>
+//#include <osmium/geom/projection.hpp>
+#include <osmium/geom/ogr.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/experimental/flex_reader.hpp>
+
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+typedef osmium::handler::NodeLocationsForWays<index_type> location_handler_type;
+
+class MyOGRHandler : public osmium::handler::Handler {
+
+ OGRDataSource* m_data_source;
+ OGRLayer* m_layer_point;
+ OGRLayer* m_layer_linestring;
+ OGRLayer* m_layer_polygon;
+
+ // Choose one of the following:
+
+ // 1. Use WGS84, do not project coordinates.
+ //osmium::geom::OGRFactory<> m_factory {};
+
+ // 2. Project coordinates into "Web Mercator".
+ osmium::geom::OGRFactory<osmium::geom::MercatorProjection> m_factory;
+
+ // 3. Use any projection that the proj library can handle.
+ // (Initialize projection with EPSG code or proj string).
+ // In addition you need to link with "-lproj" and add
+ // #include <osmium/geom/projection.hpp>.
+ //osmium::geom::OGRFactory<osmium::geom::Projection> m_factory {osmium::geom::Projection(3857)};
+
+public:
+
+ MyOGRHandler(const std::string& driver_name, const std::string& filename) {
+
+ OGRRegisterAll();
+
+ OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str());
+ if (!driver) {
+ std::cerr << driver_name << " driver not available.\n";
+ exit(1);
+ }
+
+ CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
+ const char* options[] = { "SPATIALITE=TRUE", nullptr };
+ m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
+ if (!m_data_source) {
+ std::cerr << "Creation of output file failed.\n";
+ exit(1);
+ }
+
+ OGRSpatialReference sparef;
+ sparef.importFromProj4(m_factory.proj_string().c_str());
+
+ m_layer_point = m_data_source->CreateLayer("postboxes", &sparef, wkbPoint, nullptr);
+ if (!m_layer_point) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_id("id", OFTReal);
+ layer_point_field_id.SetWidth(10);
+
+ if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_operator("operator", OFTString);
+ layer_point_field_operator.SetWidth(30);
+
+ if (m_layer_point->CreateField(&layer_point_field_operator) != OGRERR_NONE) {
+ std::cerr << "Creating operator field failed.\n";
+ exit(1);
+ }
+
+ /* Transactions might make things faster, then again they might not.
+ Feel free to experiment and benchmark and report back. */
+ m_layer_point->StartTransaction();
+
+ m_layer_linestring = m_data_source->CreateLayer("roads", &sparef, wkbLineString, nullptr);
+ if (!m_layer_linestring) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_id("id", OFTReal);
+ layer_linestring_field_id.SetWidth(10);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_type("type", OFTString);
+ layer_linestring_field_type.SetWidth(30);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ m_layer_linestring->StartTransaction();
+
+ m_layer_polygon = m_data_source->CreateLayer("buildings", &sparef, wkbMultiPolygon, nullptr);
+ if (!m_layer_polygon) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_id("id", OFTInteger);
+ layer_polygon_field_id.SetWidth(10);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_type("type", OFTString);
+ layer_polygon_field_type.SetWidth(30);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ m_layer_polygon->StartTransaction();
+ }
+
+ ~MyOGRHandler() {
+ m_layer_polygon->CommitTransaction();
+ m_layer_linestring->CommitTransaction();
+ m_layer_point->CommitTransaction();
+ OGRDataSource::DestroyDataSource(m_data_source);
+ OGRCleanupAll();
+ }
+
+ void node(const osmium::Node& node) {
+ const char* amenity = node.tags()["amenity"];
+ if (amenity && !strcmp(amenity, "post_box")) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+ feature->SetField("operator", node.tags()["operator"]);
+
+ if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ const char* highway = way.tags()["highway"];
+ if (highway) {
+ try {
+ std::unique_ptr<OGRLineString> ogr_linestring = m_factory.create_linestring(way);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id", static_cast<double>(way.id()));
+ feature->SetField("type", highway);
+
+ if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
+ }
+ }
+ }
+
+ void area(const osmium::Area& area) {
+ const char* building = area.tags()["building"];
+ if (building) {
+ try {
+ std::unique_ptr<OGRMultiPolygon> ogr_polygon = m_factory.create_multipolygon(area);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn());
+ feature->SetGeometry(ogr_polygon.get());
+ feature->SetField("id", static_cast<int>(area.id()));
+ feature->SetField("type", building);
+
+ std::string type = "";
+ if (area.from_way()) {
+ type += "w";
+ } else {
+ type += "r";
+ }
+ feature->SetField("type", type.c_str());
+
+ if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n";
+ }
+ }
+ }
+
+};
+
+/* ================================================== */
+
+void print_help() {
+ std::cout << "osmium_toogr [OPTIONS] [INFILE [OUTFILE]]\n\n" \
+ << "If INFILE is not given stdin is assumed.\n" \
+ << "If OUTFILE is not given 'ogr_out' is used.\n" \
+ << "\nOptions:\n" \
+ << " -h, --help This help message\n" \
+ << " -f, --format=FORMAT Output OGR format (Default: 'SQLite')\n";
+}
+
+int main(int argc, char* argv[]) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"format", required_argument, 0, 'f'},
+ {0, 0, 0, 0}
+ };
+
+ std::string output_format("SQLite");
+
+ while (true) {
+ int c = getopt_long(argc, argv, "hf:", long_options, 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'f':
+ output_format = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ std::string input_filename;
+ std::string output_filename("ogr_out");
+ int remaining_args = argc - optind;
+ if (remaining_args > 2) {
+ std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl;
+ exit(1);
+ } else if (remaining_args == 2) {
+ input_filename = argv[optind];
+ output_filename = argv[optind+1];
+ } else if (remaining_args == 1) {
+ input_filename = argv[optind];
+ } else {
+ input_filename = "-";
+ }
+
+ index_type index_pos;
+ location_handler_type location_handler(index_pos);
+ osmium::experimental::FlexReader<location_handler_type> exr(input_filename, location_handler, osmium::osm_entity_bits::object);
+
+ MyOGRHandler ogr_handler(output_format, output_filename);
+
+ while (auto buffer = exr.read()) {
+ osmium::apply(buffer, ogr_handler);
+ }
+
+ exr.close();
+
+ std::vector<const osmium::Relation*> incomplete_relations = exr.collector().get_incomplete_relations();
+ if (!incomplete_relations.empty()) {
+ std::cerr << "Warning! Some member ways missing for these multipolygon relations:";
+ for (const auto* relation : incomplete_relations) {
+ std::cerr << " " << relation->id();
+ }
+ std::cerr << "\n";
+ }
+
+ google::protobuf::ShutdownProtobufLibrary();
+}
+
diff --git a/examples/osmium_use_node_cache.cpp b/examples/osmium_use_node_cache.cpp
new file mode 100644
index 0000000..6b8f964
--- /dev/null
+++ b/examples/osmium_use_node_cache.cpp
@@ -0,0 +1,71 @@
+/*
+
+ 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <iostream>
+
+#include <osmium/io/pbf_input.hpp>
+#include <osmium/io/xml_input.hpp>
+
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/dense_file_array.hpp>
+#include <osmium/index/map/dense_mmap_array.hpp>
+
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+//typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+typedef osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> 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();
+
+ google::protobuf::ShutdownProtobufLibrary();
+
+ return 0;
+}
+
diff --git a/include/boost_unicode_iterator.hpp b/include/boost_unicode_iterator.hpp
new file mode 100644
index 0000000..3a7b68f
--- /dev/null
+++ b/include/boost_unicode_iterator.hpp
@@ -0,0 +1,776 @@
+/*
+ *
+ * Copyright (c) 2004
+ * John Maddock
+ *
+ * Use, modification and distribution are subject to the
+ * Boost Software License, Version 1.0. (See accompanying file
+ * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+ /*
+ * LOCATION: see http://www.boost.org for most recent version.
+ * FILE unicode_iterator.hpp
+ * VERSION see <boost/version.hpp>
+ * DESCRIPTION: Iterator adapters for converting between different Unicode encodings.
+ */
+
+/****************************************************************************
+
+Contents:
+~~~~~~~~~
+
+1) Read Only, Input Adapters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+template <class BaseIterator, class U8Type = ::boost::uint8_t>
+class u32_to_u8_iterator;
+
+Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-8.
+
+template <class BaseIterator, class U32Type = ::boost::uint32_t>
+class u8_to_u32_iterator;
+
+Adapts sequence of UTF-8 code points to "look like" a sequence of UTF-32.
+
+template <class BaseIterator, class U16Type = ::boost::uint16_t>
+class u32_to_u16_iterator;
+
+Adapts sequence of UTF-32 code points to "look like" a sequence of UTF-16.
+
+template <class BaseIterator, class U32Type = ::boost::uint32_t>
+class u16_to_u32_iterator;
+
+Adapts sequence of UTF-16 code points to "look like" a sequence of UTF-32.
+
+2) Single pass output iterator adapters:
+
+template <class BaseIterator>
+class utf8_output_iterator;
+
+Accepts UTF-32 code points and forwards them on as UTF-8 code points.
+
+template <class BaseIterator>
+class utf16_output_iterator;
+
+Accepts UTF-32 code points and forwards them on as UTF-16 code points.
+
+****************************************************************************/
+
+#ifndef BOOST_REGEX_UNICODE_ITERATOR_HPP
+#define BOOST_REGEX_UNICODE_ITERATOR_HPP
+#include <boost/cstdint.hpp>
+#include <boost/assert.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+#ifndef BOOST_NO_STD_LOCALE
+#include <sstream>
+#include <ios>
+#endif
+#include <limits.h> // CHAR_BIT
+
+namespace boost{
+
+namespace detail{
+
+static const ::boost::uint16_t high_surrogate_base = 0xD7C0u;
+static const ::boost::uint16_t low_surrogate_base = 0xDC00u;
+static const ::boost::uint32_t ten_bit_mask = 0x3FFu;
+
+inline bool is_high_surrogate(::boost::uint16_t v)
+{
+ return (v & 0xFFFFFC00u) == 0xd800u;
+}
+inline bool is_low_surrogate(::boost::uint16_t v)
+{
+ return (v & 0xFFFFFC00u) == 0xdc00u;
+}
+template <class T>
+inline bool is_surrogate(T v)
+{
+ return (v & 0xFFFFF800u) == 0xd800;
+}
+
+inline unsigned utf8_byte_count(boost::uint8_t c)
+{
+ // if the most significant bit with a zero in it is in position
+ // 8-N then there are N bytes in this UTF-8 sequence:
+ boost::uint8_t mask = 0x80u;
+ unsigned result = 0;
+ while(c & mask)
+ {
+ ++result;
+ mask >>= 1;
+ }
+ return (result == 0) ? 1 : ((result > 4) ? 4 : result);
+}
+
+inline unsigned utf8_trailing_byte_count(boost::uint8_t c)
+{
+ return utf8_byte_count(c) - 1;
+}
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable:4100)
+#endif
+inline void invalid_utf32_code_point(::boost::uint32_t val)
+{
+#ifndef BOOST_NO_STD_LOCALE
+ std::stringstream ss;
+ ss << "Invalid UTF-32 code point U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-16 sequence";
+ std::out_of_range e(ss.str());
+#else
+ std::out_of_range e("Invalid UTF-32 code point encountered while trying to encode UTF-16 sequence");
+#endif
+ boost::throw_exception(e);
+}
+#ifdef BOOST_MSVC
+#pragma warning(pop)
+#endif
+
+
+} // namespace detail
+
+template <class BaseIterator, class U16Type = ::boost::uint16_t>
+class u32_to_u16_iterator
+ : public boost::iterator_facade<u32_to_u16_iterator<BaseIterator, U16Type>, U16Type, std::bidirectional_iterator_tag, const U16Type>
+{
+ typedef boost::iterator_facade<u32_to_u16_iterator<BaseIterator, U16Type>, U16Type, std::bidirectional_iterator_tag, const U16Type> base_type;
+
+#if !defined(BOOST_NO_STD_ITERATOR_TRAITS) && !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
+ typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;
+
+ BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32);
+ BOOST_STATIC_ASSERT(sizeof(U16Type)*CHAR_BIT == 16);
+#endif
+
+public:
+ typename base_type::reference
+ dereference()const
+ {
+ if(m_current == 2)
+ extract_current();
+ return m_values[m_current];
+ }
+ bool equal(const u32_to_u16_iterator& that)const
+ {
+ if(m_position == that.m_position)
+ {
+ // Both m_currents must be equal, or both even
+ // this is the same as saying their sum must be even:
+ return (m_current + that.m_current) & 1u ? false : true;
+ }
+ return false;
+ }
+ void increment()
+ {
+ // if we have a pending read then read now, so that we know whether
+ // to skip a position, or move to a low-surrogate:
+ if(m_current == 2)
+ {
+ // pending read:
+ extract_current();
+ }
+ // move to the next surrogate position:
+ ++m_current;
+ // if we've reached the end skip a position:
+ if(m_values[m_current] == 0)
+ {
+ m_current = 2;
+ ++m_position;
+ }
+ }
+ void decrement()
+ {
+ if(m_current != 1)
+ {
+ // decrementing an iterator always leads to a valid position:
+ --m_position;
+ extract_current();
+ m_current = m_values[1] ? 1 : 0;
+ }
+ else
+ {
+ m_current = 0;
+ }
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+ // construct:
+ u32_to_u16_iterator() : m_position(), m_current(0)
+ {
+ m_values[0] = 0;
+ m_values[1] = 0;
+ m_values[2] = 0;
+ }
+ u32_to_u16_iterator(BaseIterator b) : m_position(b), m_current(2)
+ {
+ m_values[0] = 0;
+ m_values[1] = 0;
+ m_values[2] = 0;
+ }
+private:
+
+ void extract_current()const
+ {
+ // begin by checking for a code point out of range:
+ ::boost::uint32_t v = *m_position;
+ if(v >= 0x10000u)
+ {
+ if(v > 0x10FFFFu)
+ detail::invalid_utf32_code_point(*m_position);
+ // split into two surrogates:
+ m_values[0] = static_cast<U16Type>(v >> 10) + detail::high_surrogate_base;
+ m_values[1] = static_cast<U16Type>(v & detail::ten_bit_mask) + detail::low_surrogate_base;
+ m_current = 0;
+ BOOST_ASSERT(detail::is_high_surrogate(m_values[0]));
+ BOOST_ASSERT(detail::is_low_surrogate(m_values[1]));
+ }
+ else
+ {
+ // 16-bit code point:
+ m_values[0] = static_cast<U16Type>(*m_position);
+ m_values[1] = 0;
+ m_current = 0;
+ // value must not be a surrogate:
+ if(detail::is_surrogate(m_values[0]))
+ detail::invalid_utf32_code_point(*m_position);
+ }
+ }
+ BaseIterator m_position;
+ mutable U16Type m_values[3];
+ mutable unsigned m_current;
+};
+
+template <class BaseIterator, class U32Type = ::boost::uint32_t>
+class u16_to_u32_iterator
+ : public boost::iterator_facade<u16_to_u32_iterator<BaseIterator, U32Type>, U32Type, std::bidirectional_iterator_tag, const U32Type>
+{
+ typedef boost::iterator_facade<u16_to_u32_iterator<BaseIterator, U32Type>, U32Type, std::bidirectional_iterator_tag, const U32Type> base_type;
+ // special values for pending iterator reads:
+ BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu);
+
+#if !defined(BOOST_NO_STD_ITERATOR_TRAITS) && !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
+ typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;
+
+ BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 16);
+ BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32);
+#endif
+
+public:
+ typename base_type::reference
+ dereference()const
+ {
+ if(m_value == pending_read)
+ extract_current();
+ return m_value;
+ }
+ bool equal(const u16_to_u32_iterator& that)const
+ {
+ return m_position == that.m_position;
+ }
+ void increment()
+ {
+ // skip high surrogate first if there is one:
+ if(detail::is_high_surrogate(*m_position)) ++m_position;
+ ++m_position;
+ m_value = pending_read;
+ }
+ void decrement()
+ {
+ --m_position;
+ // if we have a low surrogate then go back one more:
+ if(detail::is_low_surrogate(*m_position))
+ --m_position;
+ m_value = pending_read;
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+ // construct:
+ u16_to_u32_iterator() : m_position()
+ {
+ m_value = pending_read;
+ }
+ u16_to_u32_iterator(BaseIterator b) : m_position(b)
+ {
+ m_value = pending_read;
+ }
+ //
+ // Range checked version:
+ //
+ u16_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)
+ {
+ m_value = pending_read;
+ //
+ // The range must not start with a low surrogate, or end in a high surrogate,
+ // otherwise we run the risk of running outside the underlying input range.
+ // Likewise b must not be located at a low surrogate.
+ //
+ boost::uint16_t val;
+ if(start != end)
+ {
+ if((b != start) && (b != end))
+ {
+ val = *b;
+ if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))
+ invalid_code_point(val);
+ }
+ val = *start;
+ if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))
+ invalid_code_point(val);
+ val = *--end;
+ if(detail::is_high_surrogate(val))
+ invalid_code_point(val);
+ }
+ }
+private:
+ static void invalid_code_point(::boost::uint16_t val)
+ {
+#ifndef BOOST_NO_STD_LOCALE
+ std::stringstream ss;
+ ss << "Misplaced UTF-16 surrogate U+" << std::showbase << std::hex << val << " encountered while trying to encode UTF-32 sequence";
+ std::out_of_range e(ss.str());
+#else
+ std::out_of_range e("Misplaced UTF-16 surrogate encountered while trying to encode UTF-32 sequence");
+#endif
+ boost::throw_exception(e);
+ }
+ void extract_current()const
+ {
+ m_value = static_cast<U32Type>(static_cast< ::boost::uint16_t>(*m_position));
+ // if the last value is a high surrogate then adjust m_position and m_value as needed:
+ if(detail::is_high_surrogate(*m_position))
+ {
+ // precondition; next value must have be a low-surrogate:
+ BaseIterator next(m_position);
+ ::boost::uint16_t t = *++next;
+ if((t & 0xFC00u) != 0xDC00u)
+ invalid_code_point(t);
+ m_value = (m_value - detail::high_surrogate_base) << 10;
+ m_value |= (static_cast<U32Type>(static_cast< ::boost::uint16_t>(t)) & detail::ten_bit_mask);
+ }
+ // postcondition; result must not be a surrogate:
+ if(detail::is_surrogate(m_value))
+ invalid_code_point(static_cast< ::boost::uint16_t>(m_value));
+ }
+ BaseIterator m_position;
+ mutable U32Type m_value;
+};
+
+template <class BaseIterator, class U8Type = ::boost::uint8_t>
+class u32_to_u8_iterator
+ : public boost::iterator_facade<u32_to_u8_iterator<BaseIterator, U8Type>, U8Type, std::bidirectional_iterator_tag, const U8Type>
+{
+ typedef boost::iterator_facade<u32_to_u8_iterator<BaseIterator, U8Type>, U8Type, std::bidirectional_iterator_tag, const U8Type> base_type;
+
+#if !defined(BOOST_NO_STD_ITERATOR_TRAITS) && !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
+ typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;
+
+ BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32);
+ BOOST_STATIC_ASSERT(sizeof(U8Type)*CHAR_BIT == 8);
+#endif
+
+public:
+ typename base_type::reference
+ dereference()const
+ {
+ if(m_current == 4)
+ extract_current();
+ return m_values[m_current];
+ }
+ bool equal(const u32_to_u8_iterator& that)const
+ {
+ if(m_position == that.m_position)
+ {
+ // either the m_current's must be equal, or one must be 0 and
+ // the other 4: which means neither must have bits 1 or 2 set:
+ return (m_current == that.m_current)
+ || (((m_current | that.m_current) & 3) == 0);
+ }
+ return false;
+ }
+ void increment()
+ {
+ // if we have a pending read then read now, so that we know whether
+ // to skip a position, or move to a low-surrogate:
+ if(m_current == 4)
+ {
+ // pending read:
+ extract_current();
+ }
+ // move to the next surrogate position:
+ ++m_current;
+ // if we've reached the end skip a position:
+ if(m_values[m_current] == 0)
+ {
+ m_current = 4;
+ ++m_position;
+ }
+ }
+ void decrement()
+ {
+ if((m_current & 3) == 0)
+ {
+ --m_position;
+ extract_current();
+ m_current = 3;
+ while(m_current && (m_values[m_current] == 0))
+ --m_current;
+ }
+ else
+ --m_current;
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+ // construct:
+ u32_to_u8_iterator() : m_position(), m_current(0)
+ {
+ m_values[0] = 0;
+ m_values[1] = 0;
+ m_values[2] = 0;
+ m_values[3] = 0;
+ m_values[4] = 0;
+ }
+ u32_to_u8_iterator(BaseIterator b) : m_position(b), m_current(4)
+ {
+ m_values[0] = 0;
+ m_values[1] = 0;
+ m_values[2] = 0;
+ m_values[3] = 0;
+ m_values[4] = 0;
+ }
+private:
+
+ void extract_current()const
+ {
+ boost::uint32_t c = *m_position;
+ if(c > 0x10FFFFu)
+ detail::invalid_utf32_code_point(c);
+ if(c < 0x80u)
+ {
+ m_values[0] = static_cast<unsigned char>(c);
+ m_values[1] = static_cast<unsigned char>(0u);
+ m_values[2] = static_cast<unsigned char>(0u);
+ m_values[3] = static_cast<unsigned char>(0u);
+ }
+ else if(c < 0x800u)
+ {
+ m_values[0] = static_cast<unsigned char>(0xC0u + (c >> 6));
+ m_values[1] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ m_values[2] = static_cast<unsigned char>(0u);
+ m_values[3] = static_cast<unsigned char>(0u);
+ }
+ else if(c < 0x10000u)
+ {
+ m_values[0] = static_cast<unsigned char>(0xE0u + (c >> 12));
+ m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));
+ m_values[2] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ m_values[3] = static_cast<unsigned char>(0u);
+ }
+ else
+ {
+ m_values[0] = static_cast<unsigned char>(0xF0u + (c >> 18));
+ m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));
+ m_values[2] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));
+ m_values[3] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ }
+ m_current= 0;
+ }
+ BaseIterator m_position;
+ mutable U8Type m_values[5];
+ mutable unsigned m_current;
+};
+
+template <class BaseIterator, class U32Type = ::boost::uint32_t>
+class u8_to_u32_iterator
+ : public boost::iterator_facade<u8_to_u32_iterator<BaseIterator, U32Type>, U32Type, std::bidirectional_iterator_tag, const U32Type>
+{
+ typedef boost::iterator_facade<u8_to_u32_iterator<BaseIterator, U32Type>, U32Type, std::bidirectional_iterator_tag, const U32Type> base_type;
+ // special values for pending iterator reads:
+ BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu);
+
+#if !defined(BOOST_NO_STD_ITERATOR_TRAITS) && !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
+ typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;
+
+ BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 8);
+ BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32);
+#endif
+
+public:
+ typename base_type::reference
+ dereference()const
+ {
+ if(m_value == pending_read)
+ extract_current();
+ return m_value;
+ }
+ bool equal(const u8_to_u32_iterator& that)const
+ {
+ return m_position == that.m_position;
+ }
+ void increment()
+ {
+ // We must not start with a continuation character:
+ if((static_cast<boost::uint8_t>(*m_position) & 0xC0) == 0x80)
+ invalid_sequence();
+ // skip high surrogate first if there is one:
+ unsigned c = detail::utf8_byte_count(*m_position);
+ if(m_value == pending_read)
+ {
+ // Since we haven't read in a value, we need to validate the code points:
+ for(unsigned i = 0; i < c; ++i)
+ {
+ ++m_position;
+ // We must have a continuation byte:
+ if((i != c - 1) && ((static_cast<boost::uint8_t>(*m_position) & 0xC0) != 0x80))
+ invalid_sequence();
+ }
+ }
+ else
+ {
+ std::advance(m_position, c);
+ }
+ m_value = pending_read;
+ }
+ void decrement()
+ {
+ // Keep backtracking until we don't have a trailing character:
+ unsigned count = 0;
+ while((*--m_position & 0xC0u) == 0x80u) ++count;
+ // now check that the sequence was valid:
+ if(count != detail::utf8_trailing_byte_count(*m_position))
+ invalid_sequence();
+ m_value = pending_read;
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+ // construct:
+ u8_to_u32_iterator() : m_position()
+ {
+ m_value = pending_read;
+ }
+ u8_to_u32_iterator(BaseIterator b) : m_position(b)
+ {
+ m_value = pending_read;
+ }
+ //
+ // Checked constructor:
+ //
+ u8_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)
+ {
+ m_value = pending_read;
+ //
+ // We must not start with a continuation character, or end with a
+ // truncated UTF-8 sequence otherwise we run the risk of going past
+ // the start/end of the underlying sequence:
+ //
+ if(start != end)
+ {
+ unsigned char v = *start;
+ if((v & 0xC0u) == 0x80u)
+ invalid_sequence();
+ if((b != start) && (b != end) && ((*b & 0xC0u) == 0x80u))
+ invalid_sequence();
+ BaseIterator pos = end;
+ do
+ {
+ v = *--pos;
+ }
+ while((start != pos) && ((v & 0xC0u) == 0x80u));
+ std::ptrdiff_t extra = detail::utf8_byte_count(v);
+ if(std::distance(pos, end) < extra)
+ invalid_sequence();
+ }
+ }
+private:
+ static void invalid_sequence()
+ {
+ std::out_of_range e("Invalid UTF-8 sequence encountered while trying to encode UTF-32 character");
+ boost::throw_exception(e);
+ }
+ void extract_current()const
+ {
+ m_value = static_cast<U32Type>(static_cast< ::boost::uint8_t>(*m_position));
+ // we must not have a continuation character:
+ if((m_value & 0xC0u) == 0x80u)
+ invalid_sequence();
+ // see how many extra bytes we have:
+ unsigned extra = detail::utf8_trailing_byte_count(*m_position);
+ // extract the extra bits, 6 from each extra byte:
+ BaseIterator next(m_position);
+ for(unsigned c = 0; c < extra; ++c)
+ {
+ ++next;
+ m_value <<= 6;
+ // We must have a continuation byte:
+ if((static_cast<boost::uint8_t>(*next) & 0xC0) != 0x80)
+ invalid_sequence();
+ m_value += static_cast<boost::uint8_t>(*next) & 0x3Fu;
+ }
+ // we now need to remove a few of the leftmost bits, but how many depends
+ // upon how many extra bytes we've extracted:
+ static const boost::uint32_t masks[4] =
+ {
+ 0x7Fu,
+ 0x7FFu,
+ 0xFFFFu,
+ 0x1FFFFFu,
+ };
+ m_value &= masks[extra];
+ // check the result:
+ if(m_value > static_cast<U32Type>(0x10FFFFu))
+ invalid_sequence();
+ }
+ BaseIterator m_position;
+ mutable U32Type m_value;
+};
+
+template <class BaseIterator>
+class utf16_output_iterator
+{
+public:
+ typedef void difference_type;
+ typedef void value_type;
+ typedef boost::uint32_t* pointer;
+ typedef boost::uint32_t& reference;
+ typedef std::output_iterator_tag iterator_category;
+
+ utf16_output_iterator(const BaseIterator& b)
+ : m_position(b){}
+ utf16_output_iterator(const utf16_output_iterator& that)
+ : m_position(that.m_position){}
+ utf16_output_iterator& operator=(const utf16_output_iterator& that)
+ {
+ m_position = that.m_position;
+ return *this;
+ }
+ const utf16_output_iterator& operator*()const
+ {
+ return *this;
+ }
+ void operator=(boost::uint32_t val)const
+ {
+ push(val);
+ }
+ utf16_output_iterator& operator++()
+ {
+ return *this;
+ }
+ utf16_output_iterator& operator++(int)
+ {
+ return *this;
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+private:
+ void push(boost::uint32_t v)const
+ {
+ if(v >= 0x10000u)
+ {
+ // begin by checking for a code point out of range:
+ if(v > 0x10FFFFu)
+ detail::invalid_utf32_code_point(v);
+ // split into two surrogates:
+ *m_position++ = static_cast<boost::uint16_t>(v >> 10) + detail::high_surrogate_base;
+ *m_position++ = static_cast<boost::uint16_t>(v & detail::ten_bit_mask) + detail::low_surrogate_base;
+ }
+ else
+ {
+ // 16-bit code point:
+ // value must not be a surrogate:
+ if(detail::is_surrogate(v))
+ detail::invalid_utf32_code_point(v);
+ *m_position++ = static_cast<boost::uint16_t>(v);
+ }
+ }
+ mutable BaseIterator m_position;
+};
+
+template <class BaseIterator>
+class utf8_output_iterator
+{
+public:
+ typedef void difference_type;
+ typedef void value_type;
+ typedef boost::uint32_t* pointer;
+ typedef boost::uint32_t& reference;
+ typedef std::output_iterator_tag iterator_category;
+
+ utf8_output_iterator(const BaseIterator& b)
+ : m_position(b){}
+ utf8_output_iterator(const utf8_output_iterator& that)
+ : m_position(that.m_position){}
+ utf8_output_iterator& operator=(const utf8_output_iterator& that)
+ {
+ m_position = that.m_position;
+ return *this;
+ }
+ const utf8_output_iterator& operator*()const
+ {
+ return *this;
+ }
+ void operator=(boost::uint32_t val)const
+ {
+ push(val);
+ }
+ utf8_output_iterator& operator++()
+ {
+ return *this;
+ }
+ utf8_output_iterator& operator++(int)
+ {
+ return *this;
+ }
+ BaseIterator base()const
+ {
+ return m_position;
+ }
+private:
+ void push(boost::uint32_t c)const
+ {
+ if(c > 0x10FFFFu)
+ detail::invalid_utf32_code_point(c);
+ if(c < 0x80u)
+ {
+ *m_position++ = static_cast<unsigned char>(c);
+ }
+ else if(c < 0x800u)
+ {
+ *m_position++ = static_cast<unsigned char>(0xC0u + (c >> 6));
+ *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ }
+ else if(c < 0x10000u)
+ {
+ *m_position++ = static_cast<unsigned char>(0xE0u + (c >> 12));
+ *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));
+ *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ }
+ else
+ {
+ *m_position++ = static_cast<unsigned char>(0xF0u + (c >> 18));
+ *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));
+ *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));
+ *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));
+ }
+ }
+ mutable BaseIterator m_position;
+};
+
+} // namespace boost
+
+#endif // BOOST_REGEX_UNICODE_ITERATOR_HPP
+
diff --git a/include/mmap_for_windows.hpp b/include/mmap_for_windows.hpp
new file mode 100644
index 0000000..abe62d6
--- /dev/null
+++ b/include/mmap_for_windows.hpp
@@ -0,0 +1,103 @@
+#ifndef MMAP_FOR_WINDOWS_HPP
+#define MMAP_FOR_WINDOWS_HPP
+
+/* mmap() replacement for Windows
+ *
+ * Author: Mike Frysinger <vapier at gentoo.org>
+ * Placed into the public domain
+ */
+
+/* References:
+ * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
+ * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
+ * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
+ * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
+ */
+
+#include <io.h>
+#include <windows.h>
+#include <sys/types.h>
+
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+/* This flag is only available in WinXP+ */
+#ifdef FILE_MAP_EXECUTE
+#define PROT_EXEC 0x4
+#else
+#define PROT_EXEC 0x0
+#define FILE_MAP_EXECUTE 0
+#endif
+
+#define MAP_SHARED 0x01
+#define MAP_PRIVATE 0x02
+#define MAP_ANONYMOUS 0x20
+#define MAP_ANON MAP_ANONYMOUS
+#define MAP_FAILED ((void *) -1)
+
+static DWORD dword_hi(uint64_t x) {
+ return static_cast<DWORD>(x >> 32);
+}
+
+static DWORD dword_lo(uint64_t x) {
+ return static_cast<DWORD>(x & 0xffffffff);
+}
+
+static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
+{
+ if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
+ return MAP_FAILED;
+ if (fd == -1) {
+ if (!(flags & MAP_ANON) || offset)
+ return MAP_FAILED;
+ } else if (flags & MAP_ANON)
+ return MAP_FAILED;
+
+ DWORD flProtect;
+ if (prot & PROT_WRITE) {
+ if (prot & PROT_EXEC)
+ flProtect = PAGE_EXECUTE_READWRITE;
+ else
+ flProtect = PAGE_READWRITE;
+ } else if (prot & PROT_EXEC) {
+ if (prot & PROT_READ)
+ flProtect = PAGE_EXECUTE_READ;
+ else if (prot & PROT_EXEC)
+ flProtect = PAGE_EXECUTE;
+ } else
+ flProtect = PAGE_READONLY;
+
+ uint64_t end = static_cast<uint64_t>(length) + offset;
+ HANDLE mmap_fd;
+ if (fd == -1)
+ mmap_fd = INVALID_HANDLE_VALUE;
+ else
+ mmap_fd = (HANDLE)_get_osfhandle(fd);
+
+ HANDLE h = CreateFileMapping(mmap_fd, NULL, flProtect, dword_hi(end), dword_lo(end), NULL);
+ if (h == NULL)
+ return MAP_FAILED;
+
+ DWORD dwDesiredAccess;
+ if (prot & PROT_WRITE)
+ dwDesiredAccess = FILE_MAP_WRITE;
+ else
+ dwDesiredAccess = FILE_MAP_READ;
+ if (prot & PROT_EXEC)
+ dwDesiredAccess |= FILE_MAP_EXECUTE;
+ if (flags & MAP_PRIVATE)
+ dwDesiredAccess |= FILE_MAP_COPY;
+ void *ret = MapViewOfFile(h, dwDesiredAccess, dword_hi(offset), dword_lo(offset), length);
+ if (ret == NULL) {
+ CloseHandle(h);
+ ret = MAP_FAILED;
+ }
+ return ret;
+}
+
+static int munmap(void *addr, size_t length)
+{
+ return UnmapViewOfFile(addr) ? 0 : -1;
+ /* ruh-ro, we leaked handle from CreateFileMapping() ... */
+}
+
+#endif
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
new file mode 100644
index 0000000..db769dd
--- /dev/null
+++ b/include/osmium/area/assembler.hpp
@@ -0,0 +1,783 @@
+#ifndef OSMIUM_AREA_ASSEMBLER_HPP
+#define OSMIUM_AREA_ASSEMBLER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <iostream>
+#include <iterator>
+#include <list>
+#include <map>
+#include <vector>
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/area.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/tags/filter.hpp>
+
+#include <osmium/area/detail/proto_ring.hpp>
+#include <osmium/area/detail/node_ref_segment.hpp>
+#include <osmium/area/detail/segment_list.hpp>
+#include <osmium/area/problem_reporter.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ using osmium::area::detail::ProtoRing;
+
+ struct AssemblerConfig {
+
+ osmium::area::ProblemReporter* problem_reporter;
+
+ // Enables debug output to stderr
+ bool debug;
+
+ explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d=false) :
+ problem_reporter(pr),
+ debug(d) {
+ }
+
+ /**
+ * Enable or disable debug output to stderr. This is for Osmium
+ * developers only.
+ */
+ void enable_debug_output(bool d=true) {
+ debug = d;
+ }
+
+ }; // struct AssemblerConfig
+
+ /**
+ * Assembles area objects from multipolygon relations and their
+ * members. This is called by the MultipolygonCollector object
+ * after all members have been collected.
+ */
+ class Assembler {
+
+ const AssemblerConfig m_config;
+
+ // The way segments
+ osmium::area::detail::SegmentList m_segment_list;
+
+ // The rings we are building from the way segments
+ std::list<ProtoRing> m_rings;
+
+ std::vector<ProtoRing*> m_outer_rings;
+ std::vector<ProtoRing*> m_inner_rings;
+
+ int m_inner_outer_mismatches { 0 };
+
+ bool debug() const {
+ return m_config.debug;
+ }
+
+ /**
+ * 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()) {
+ 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;
+ }
+
+ 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());
+ }
+ }
+
+ void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
+ std::map<std::string, size_t> counter;
+ for (const osmium::Way* way : ways) {
+ for (const auto& tag : way->tags()) {
+ std::string kv {tag.key()};
+ kv.append(1, '\0');
+ kv.append(tag.value());
+ ++counter[kv];
+ }
+ }
+
+ 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());
+ tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
+ }
+ }
+ }
+
+ struct MPFilter : public osmium::tags::KeyFilter {
+
+ MPFilter() : osmium::tags::KeyFilter(true) {
+ add(false, "type");
+ add(false, "created_by");
+ add(false, "source");
+ add(false, "note");
+ add(false, "test:id");
+ add(false, "test:section");
+ }
+
+ }; // struct MPFilter
+
+ static MPFilter& filter() {
+ static MPFilter filter;
+ return filter;
+ }
+
+ void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) const {
+ auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter());
+
+ if (debug()) {
+ std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
+ }
+
+ if (count > 0) {
+ if (debug()) {
+ std::cerr << " use tags from relation\n";
+ }
+
+ // 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());
+ }
+ }
+ } else {
+ if (debug()) {
+ std::cerr << " use tags from outer ways\n";
+ }
+ std::set<const osmium::Way*> ways;
+ for (const auto& ring : m_outer_rings) {
+ 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());
+ }
+ } else {
+ if (debug()) {
+ std::cerr << " multiple outer ways, get common tags\n";
+ }
+ osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
+ add_common_tags(tl_builder, ways);
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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_segment_front().first().location(), ring.get_segment_back().second().location());
+ }
+ }
+ }
+
+ 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_segment_back().second();
+
+ if (debug()) {
+ std::cerr << " possibly_combine_rings_back()\n";
+ }
+ for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
+ if (&*it != &ring && !it->closed()) {
+ if (has_same_location(nr, it->get_segment_front().first())) {
+ if (debug()) {
+ std::cerr << " ring.last=it->first\n";
+ }
+ ring.merge_ring(*it, debug());
+ m_rings.erase(it);
+ return true;
+ }
+ if (has_same_location(nr, it->get_segment_back().second())) {
+ if (debug()) {
+ std::cerr << " ring.last=it->last\n";
+ }
+ ring.merge_ring_reverse(*it, debug());
+ m_rings.erase(it);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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_segment_front().first();
+
+ if (debug()) {
+ std::cerr << " possibly_combine_rings_front()\n";
+ }
+ for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
+ if (&*it != &ring && !it->closed()) {
+ if (has_same_location(nr, it->get_segment_back().second())) {
+ if (debug()) {
+ std::cerr << " ring.first=it->last\n";
+ }
+ ring.swap_segments(*it);
+ ring.merge_ring(*it, debug());
+ m_rings.erase(it);
+ return true;
+ }
+ if (has_same_location(nr, it->get_segment_front().first())) {
+ if (debug()) {
+ std::cerr << " ring.first=it->first\n";
+ }
+ ring.reverse();
+ ring.merge_ring(*it, debug());
+ m_rings.erase(it);
+ return true;
+ }
+ }
+ }
+ 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";
+ }
+ ProtoRing new_ring(it_begin, it_end);
+ ring.remove_segments(it_begin, it_end);
+ if (debug()) {
+ std::cerr << " split into two rings:\n";
+ std::cerr << " " << new_ring << "\n";
+ std::cerr << " " << ring << "\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 (debug()) {
+ std::cerr << " has_closed_subring_back()\n";
+ }
+ 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;
+ }
+ }
+ return false;
+ }
+
+ bool has_closed_subring_front(ProtoRing& ring, const NodeRef& nr) {
+ if (ring.segments().size() < 3) {
+ return false;
+ }
+ if (debug()) {
+ std::cerr << " has_closed_subring_front()\n";
+ }
+ 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;
+ }
+
+ bool check_for_closed_subring(ProtoRing& ring) {
+ if (debug()) {
+ std::cerr << " check_for_closed_subring()\n";
+ }
+
+ osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size());
+ std::copy(ring.segments().begin(), ring.segments().end(), segments.begin());
+ std::sort(segments.begin(), segments.end());
+ 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;
+ }
+ auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1);
+ assert(r1 != ring.segments().end());
+ 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";
+ }
+
+ auto m = std::minmax(r1, r2);
+
+ ProtoRing new_ring(m.first, m.second);
+ ring.remove_segments(m.first, m.second);
+
+ if (debug()) {
+ std::cerr << " split ring1=" << new_ring << "\n";
+ std::cerr << " split ring2=" << ring << "\n";
+ }
+
+ m_rings.emplace_back(new_ring);
+
+ return true;
+ }
+
+ void combine_rings_front(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+ if (debug()) {
+ std::cerr << " => match at front of ring\n";
+ }
+ ring.add_segment_front(segment);
+ has_closed_subring_front(ring, segment.first());
+ if (possibly_combine_rings_front(ring)) {
+ check_for_closed_subring(ring);
+ }
+ }
+
+ void combine_rings_back(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+ if (debug()) {
+ std::cerr << " => match at back of ring\n";
+ }
+ ring.add_segment_back(segment);
+ has_closed_subring_back(ring, segment.second());
+ if (possibly_combine_rings_back(ring)) {
+ check_for_closed_subring(ring);
+ }
+ }
+
+ /**
+ * Append each outer ring together with its inner rings to the
+ * area in the buffer.
+ */
+ void add_rings_to_area(osmium::builder::AreaBuilder& builder) const {
+ for (const ProtoRing* ring : m_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_segment_front().first());
+ for (const auto& segment : ring->segments()) {
+ ring_builder.add_node_ref(segment.second());
+ }
+ }
+ for (ProtoRing* inner : ring->inner_rings()) {
+ osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder);
+ ring_builder.add_node_ref(inner->get_segment_front().first());
+ for (const auto& segment : inner->segments()) {
+ ring_builder.add_node_ref(segment.second());
+ }
+ }
+ }
+ }
+
+ bool add_to_existing_ring(osmium::area::detail::NodeRefSegment segment) {
+ int n=0;
+ for (auto& ring : m_rings) {
+ if (debug()) {
+ std::cerr << " check against ring " << n << " " << ring;
+ }
+ if (ring.closed()) {
+ if (debug()) {
+ std::cerr << " => ring CLOSED\n";
+ }
+ } else {
+ if (has_same_location(ring.get_segment_back().second(), segment.first())) {
+ combine_rings_back(segment, ring);
+ return true;
+ }
+ if (has_same_location(ring.get_segment_back().second(), segment.second())) {
+ segment.swap_locations();
+ combine_rings_back(segment, ring);
+ return true;
+ }
+ if (has_same_location(ring.get_segment_front().first(), segment.first())) {
+ segment.swap_locations();
+ combine_rings_front(segment, ring);
+ return true;
+ }
+ if (has_same_location(ring.get_segment_front().first(), segment.second())) {
+ combine_rings_front(segment, ring);
+ return true;
+ }
+ if (debug()) {
+ std::cerr << " => no match\n";
+ }
+ }
+
+ ++n;
+ }
+ return false;
+ }
+
+ void check_inner_outer(ProtoRing& ring) {
+ const osmium::NodeRef& min_node = ring.min_node();
+ if (debug()) {
+ std::cerr << " check_inner_outer min_node=" << min_node << "\n";
+ }
+
+ int count = 0;
+ int above = 0;
+
+ 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)) {
+ if (debug()) {
+ std::cerr << " segments for count: " << *it;
+ }
+ if (it->to_left_of(min_node.location())) {
+ ++count;
+ if (debug()) {
+ std::cerr << " counted\n";
+ }
+ } else {
+ if (debug()) {
+ std::cerr << " not counted\n";
+ }
+ }
+ if (it->first().location() == min_node.location()) {
+ if (it->second().location().y() > min_node.location().y()) {
+ ++above;
+ }
+ }
+ if (it->second().location() == min_node.location()) {
+ if (it->first().location().y() > min_node.location().y()) {
+ ++above;
+ }
+ }
+ }
+ }
+
+ if (debug()) {
+ std::cerr << " count=" << count << " above=" << above << "\n";
+ }
+
+ count += above % 2;
+
+ if (count % 2) {
+ ring.set_inner();
+ }
+ }
+
+ void check_inner_outer_roles() {
+ if (debug()) {
+ std::cerr << " check_inner_outer_roles\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";
+ }
+ if (m_config.problem_reporter) {
+ m_config.problem_reporter->report_role_should_be_outer(segment.way()->id(), segment.first().location(), segment.second().location());
+ }
+ }
+ }
+ }
+ for (const auto ringptr : m_inner_rings) {
+ for (const auto segment : ringptr->segments()) {
+ if (!segment.role_inner()) {
+ ++m_inner_outer_mismatches;
+ if (debug()) {
+ std::cerr << " segment " << segment << " from way " << segment.way()->id() << " should have role 'inner'\n";
+ }
+ if (m_config.problem_reporter) {
+ m_config.problem_reporter->report_role_should_be_inner(segment.way()->id(), segment.first().location(), segment.second().location());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Create rings from segments.
+ */
+ bool create_rings() {
+ m_segment_list.sort();
+ m_segment_list.erase_duplicate_segments();
+
+ // 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)) {
+ 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) {
+ if (debug()) {
+ std::cerr << " checking segment " << segment << "\n";
+ }
+ if (!add_to_existing_ring(segment)) {
+ if (debug()) {
+ std::cerr << " new ring for segment " << segment << "\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";
+ }
+ }
+
+ if (check_for_open_rings()) {
+ if (debug()) {
+ std::cerr << " not all rings are closed\n";
+ }
+ return false;
+ }
+
+ if (debug()) {
+ std::cerr << " Find inner/outer...\n";
+ }
+
+ if (m_rings.size() == 1) {
+ m_outer_rings.push_back(&m_rings.front());
+ } 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;
+ }
+ }
+ }
+ }
+ }
+
+ check_inner_outer_roles();
+
+ return true;
+ }
+
+ public:
+
+ typedef osmium::area::AssemblerConfig config_type;
+
+ explicit Assembler(const config_type& config) :
+ m_config(config),
+ m_segment_list(config.debug) {
+ }
+
+ ~Assembler() = 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.problem_reporter) {
+ m_config.problem_reporter->set_object(osmium::item_type::way, way.id());
+ }
+
+ if (!way.ends_have_same_id()) {
+ 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");
+
+ if (debug()) {
+ std::cerr << "\nBuild way id()=" << way.id() << " segments.size()=" << m_segment_list.size() << "\n";
+ }
+
+ // Now create the Area object and add the attributes and tags
+ // from the relation.
+ {
+ osmium::builder::AreaBuilder builder(out_buffer);
+ builder.initialize_from_object(way);
+
+ if (create_rings()) {
+ add_tags_to_area(builder, way);
+ add_rings_to_area(builder);
+ }
+ }
+ out_buffer.commit();
+ }
+
+ /**
+ * Assemble an area from the given relation and its members.
+ * All members are to be found in the in_buffer at the offsets
+ * given by the members parameter.
+ * The resulting area is put into the out_buffer.
+ */
+ void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
+ 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 (debug()) {
+ std::cerr << "\nBuild relation id()=" << relation.id() << " members.size()=" << members.size() << " segments.size()=" << m_segment_list.size() << "\n";
+ }
+
+ 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);
+ }
+ }
+ out_buffer.commit();
+
+ const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
+
+ // Find all closed ways that are inner rings and check their
+ // tags. If they are not the same as the tags of the area we
+ // just built, add them to a list and later build areas for
+ // them, too.
+ std::vector<const osmium::Way*> 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<const osmium::Way>(offset);
+ if (way.is_closed() && way.tags().size() > 0) {
+ auto d = std::count_if(way.tags().begin(), way.tags().end(), filter());
+ if (d > 0) {
+ osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().begin(), way.tags().end());
+ 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());
+
+ 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);
+ }
+ }
+ }
+ }
+ ++memit;
+ }
+ }
+
+ // Now build areas for all ways found in the last step.
+ for (const osmium::Way* way : ways_that_should_be_areas) {
+ Assembler assembler(m_config);
+ assembler(*way, out_buffer);
+ }
+ }
+
+ }; // class Assembler
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_ASSEMBLER_HPP
diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp
new file mode 100644
index 0000000..43569a8
--- /dev/null
+++ b/include/osmium/area/detail/node_ref_segment.hpp
@@ -0,0 +1,262 @@
+#ifndef OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
+#define OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iosfwd>
+#include <utility>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+
+namespace osmium {
+
+ class Way;
+
+ namespace area {
+
+ /**
+ * @brief Namespace for Osmium internal use
+ */
+ namespace detail {
+
+ /**
+ * 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.
+ */
+ class NodeRefSegment {
+
+ osmium::NodeRef m_first;
+ osmium::NodeRef m_second;
+
+ /// Role of the member this segment was from.
+ const char* m_role;
+
+ /// Way this segment was from.
+ const osmium::Way* m_way;
+
+ public:
+
+ void swap_locations() {
+ using std::swap;
+ swap(m_first, m_second);
+ }
+
+ explicit NodeRefSegment() noexcept :
+ m_first(),
+ m_second(),
+ m_role(nullptr),
+ m_way(nullptr) {
+ }
+
+ explicit NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, const char* role, const osmium::Way* way) :
+ m_first(nr1),
+ m_second(nr2),
+ m_role(role),
+ m_way(way) {
+ if (nr2.location() < nr1.location()) {
+ swap_locations();
+ }
+ }
+
+ NodeRefSegment(const NodeRefSegment&) = default;
+ NodeRefSegment(NodeRefSegment&&) = default;
+
+ NodeRefSegment& operator=(const NodeRefSegment&) = default;
+ NodeRefSegment& operator=(NodeRefSegment&&) = default;
+
+ ~NodeRefSegment() = default;
+
+ /// 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).
+ 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<osmium::Location, osmium::Location> 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;
+ }
+
+ 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;
+ }
+
+ bool role_outer() const noexcept {
+ return !strcmp(m_role, "outer");
+ }
+
+ bool role_inner() const noexcept {
+ return !strcmp(m_role, "inner");
+ }
+
+ const osmium::Way* way() const noexcept {
+ return m_way;
+ }
+
+ }; // 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();
+ }
+
+ inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ /**
+ * 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.
+ */
+ 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();
+ }
+
+ inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
+ return rhs < lhs;
+ }
+
+ inline bool operator<=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
+ return ! (rhs < lhs);
+ }
+
+ inline bool operator>=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
+ return ! (lhs < rhs);
+ }
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const NodeRefSegment& segment) {
+ return out << segment.first() << "--" << segment.second();
+ }
+
+ inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
+ if (s1.first().location().x() > s2.second().location().x()) {
+ return true;
+ }
+ return false;
+ }
+
+ inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) {
+ auto m1 = std::minmax(s1.first().location().y(), s1.second().location().y());
+ auto m2 = std::minmax(s2.first().location().y(), s2.second().location().y());
+ if (m1.first > m2.second || m2.first > m1.second) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Calculate the intersection between to NodeRefSegments. The result is returned
+ * as a Location. Note that because the Location uses integers with limited
+ * precision internally, the result might be slightly different than the
+ * numerically correct location.
+ *
+ * If the segments touch in one 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.
+ *
+ * @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()) {
+ return osmium::Location();
+ }
+
+ auto d = (static_cast<int64_t>(s2.second().y()) - static_cast<int64_t>(s2.first().y())) *
+ (static_cast<int64_t>(s1.second().x()) - static_cast<int64_t>(s1.first().x())) -
+ (static_cast<int64_t>(s2.second().x()) - static_cast<int64_t>(s2.first().x())) *
+ (static_cast<int64_t>(s1.second().y()) - static_cast<int64_t>(s1.first().y()));
+
+ if (d != 0) {
+ double denom = ((s2.second().lat() - s2.first().lat())*(s1.second().lon() - s1.first().lon())) -
+ ((s2.second().lon() - s2.first().lon())*(s1.second().lat() - s1.first().lat()));
+
+ double nume_a = ((s2.second().lon() - s2.first().lon())*(s1.first().lat() - s2.first().lat())) -
+ ((s2.second().lat() - s2.first().lat())*(s1.first().lon() - s2.first().lon()));
+
+ double nume_b = ((s1.second().lon() - s1.first().lon())*(s1.first().lat() - s2.first().lat())) -
+ ((s1.second().lat() - s1.first().lat())*(s1.first().lon() - s2.first().lon()));
+
+ if ((denom > 0 && nume_a >= 0 && nume_a <= denom && nume_b >= 0 && nume_b <= denom) ||
+ (denom < 0 && nume_a <= 0 && nume_a >= denom && nume_b <= 0 && nume_b >= denom)) {
+ double ua = nume_a / denom;
+ double ix = s1.first().lon() + ua*(s1.second().lon() - s1.first().lon());
+ double iy = s1.first().lat() + ua*(s1.second().lat() - s1.first().lat());
+ return osmium::Location(ix, iy);
+ }
+ }
+
+ return osmium::Location();
+ }
+
+ } // namespace detail
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp
new file mode 100644
index 0000000..c0f545c
--- /dev/null
+++ b/include/osmium/area/detail/proto_ring.hpp
@@ -0,0 +1,274 @@
+#ifndef OSMIUM_AREA_DETAIL_PROTO_RING_HPP
+#define OSMIUM_AREA_DETAIL_PROTO_RING_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <iostream>
+#include <list>
+#include <set>
+#include <vector>
+
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/area/detail/node_ref_segment.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ namespace detail {
+
+ /**
+ * A ring in the process of being built by the Assembler object.
+ */
+ class ProtoRing {
+
+ public:
+
+ typedef std::vector<NodeRefSegment> segments_type;
+
+ private:
+
+ // 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)
+ std::vector<ProtoRing*> 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<size_t>(std::distance(sbegin, send))) {
+ std::copy(sbegin, send, m_segments.begin());
+ }
+
+ bool outer() const noexcept {
+ return m_outer;
+ }
+
+ void set_inner() noexcept {
+ m_outer = false;
+ }
+
+ segments_type& segments() noexcept {
+ return m_segments;
+ }
+
+ const segments_type& segments() const noexcept {
+ return m_segments;
+ }
+
+ void remove_segments(segments_type::iterator sbegin, segments_type::iterator send) {
+ m_segments.erase(sbegin, send);
+ }
+
+ void add_segment_front(const NodeRefSegment& segment) {
+ m_segments.insert(m_segments.begin(), segment);
+ }
+
+ void add_segment_back(const NodeRefSegment& segment) {
+ m_segments.push_back(segment);
+ }
+
+ const NodeRefSegment& get_segment_front() const {
+ return m_segments.front();
+ }
+
+ NodeRefSegment& get_segment_front() {
+ return m_segments.front();
+ }
+
+ const NodeRefSegment& get_segment_back() const {
+ return m_segments.back();
+ }
+
+ NodeRefSegment& get_segment_back() {
+ return m_segments.back();
+ }
+
+ bool closed() const {
+ return m_segments.front().first().location() == m_segments.back().second().location();
+ }
+
+ int64_t sum() const {
+ int64_t sum = 0;
+
+ for (const auto& segment : m_segments) {
+ sum += static_cast<int64_t>(segment.first().location().x()) * static_cast<int64_t>(segment.second().location().y()) -
+ static_cast<int64_t>(segment.second().location().x()) * static_cast<int64_t>(segment.first().location().y());
+ }
+
+ return sum;
+ }
+
+ bool is_cw() const {
+ return sum() <= 0;
+ }
+
+ int64_t area() const {
+ return std::abs(sum()) / 2;
+ }
+
+ void swap_segments(ProtoRing& other) {
+ std::swap(m_segments, other.m_segments);
+ }
+
+ void add_inner_ring(ProtoRing* ring) {
+ m_inner.push_back(ring);
+ }
+
+ const std::vector<ProtoRing*>& inner_rings() const {
+ return m_inner;
+ }
+
+ 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;
+ }
+ out << "]";
+ }
+
+ void reverse() {
+ std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment& segment) {
+ segment.swap_locations();
+ });
+ 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 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<segments_type::difference_type>(n), [](NodeRefSegment segment) {
+ segment.swap_locations();
+ return segment;
+ });
+ if (debug) {
+ std::cerr << " result ring: ";
+ print(std::cerr);
+ std::cerr << "\n";
+ }
+ }
+
+ 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();
+ }
+ }
+
+ 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;
+ }
+ }
+
+ return is_in;
+ }
+
+ void get_ways(std::set<const osmium::Way*>& 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;
+ }
+ }
+ return false;
+ }
+
+ }; // class ProtoRing
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ProtoRing& ring) {
+ ring.print(out);
+ return out;
+ }
+
+ } // namespace detail
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_DETAIL_PROTO_RING_HPP
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
new file mode 100644
index 0000000..12ec97f
--- /dev/null
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -0,0 +1,216 @@
+#ifndef OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
+#define OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <iostream>
+#include <vector>
+
+#include <osmium/area/problem_reporter.hpp>
+#include <osmium/area/detail/node_ref_segment.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/way.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ namespace detail {
+
+ /**
+ * This is a helper class for the area assembler. It models
+ * a list of segments.
+ */
+ class SegmentList {
+
+ typedef std::vector<NodeRefSegment> slist_type;
+
+ slist_type m_segments;
+
+ bool m_debug;
+
+ public:
+
+ explicit SegmentList(bool debug) noexcept :
+ m_debug(debug) {
+ }
+
+ ~SegmentList() = default;
+
+ SegmentList(const SegmentList&) = delete;
+ SegmentList(SegmentList&&) = delete;
+
+ SegmentList& operator=(const SegmentList&) = delete;
+ SegmentList& operator=(SegmentList&&) = delete;
+
+ /// The number of segments in the list.
+ size_t size() const noexcept {
+ return m_segments.size();
+ }
+
+ bool empty() const noexcept {
+ return m_segments.empty();
+ }
+
+ typedef slist_type::const_iterator const_iterator;
+
+ const_iterator begin() const noexcept {
+ return m_segments.begin();
+ }
+
+ const_iterator end() const noexcept {
+ return m_segments.end();
+ }
+
+ /**
+ * Enable or disable debug output to stderr. This is for Osmium
+ * developers only.
+ */
+ void enable_debug_output(bool debug=true) noexcept {
+ 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());
+ }
+
+ /**
+ * 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?
+ */
+ 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;
+ }
+ }
+
+ /**
+ * 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<size_t>& 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<const osmium::Way>(offset);
+ extract_segments_from_way(way, member_it->role());
+ ++member_it;
+ }
+ }
+
+ /**
+ * Find duplicate segments (ie same start and end point) in the
+ * list and remove them. This will always remove pairs of the same
+ * segment. So if there are three, for instance, two will be
+ * removed and one will be left.
+ */
+ void erase_duplicate_segments() {
+ while (true) {
+ auto it = std::adjacent_find(m_segments.begin(), m_segments.end());
+ if (it == m_segments.end()) {
+ return;
+ }
+ if (m_debug) {
+ std::cerr << " erase duplicate segment: " << *it << "\n";
+ }
+ m_segments.erase(it, it+2);
+ }
+ }
+
+ /**
+ * Find intersection between segments.
+ *
+ * @param problem_reporter Any intersections found are reported to this object.
+ * @returns true if there are intersections.
+ */
+ bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
+ if (m_segments.empty()) {
+ return false;
+ }
+
+ bool found_intersections = false;
+
+ for (auto it1 = m_segments.begin(); it1 != m_segments.end()-1; ++it1) {
+ const NodeRefSegment& s1 = *it1;
+ for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) {
+ const NodeRefSegment& s2 = *it2;
+
+ assert(s1 != s2); // erase_duplicate_segments() should have made sure of that
+
+ if (outside_x_range(s2, s1)) {
+ break;
+ }
+
+ if (y_range_overlap(s1, s2)) {
+ osmium::Location intersection = calculate_intersection(s1, s2);
+ if (intersection) {
+ found_intersections = true;
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ return found_intersections;
+ }
+
+ }; // class SegmentList
+
+ } // namespace detail
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
new file mode 100644
index 0000000..0bd10f7
--- /dev/null
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -0,0 +1,212 @@
+#ifndef OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
+#define OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/relations/collector.hpp>
+#include <osmium/relations/detail/member_meta.hpp>
+
+namespace osmium {
+
+ struct invalid_location;
+
+ namespace relations {
+ class RelationMeta;
+ }
+
+ /**
+ * @brief Code related to the building of areas (multipolygons) from relations.
+ */
+ namespace area {
+
+ /**
+ * This class collects all data needed for creating areas from
+ * relations tagged with type=multipolygon or type=boundary.
+ * Most of its functionality is derived from the parent class
+ * osmium::relations::Collector.
+ *
+ * The actual assembling of the areas is done by the assembler
+ * class given as template argument.
+ *
+ * @tparam TAssembler Multipolygon Assembler class.
+ */
+ template <class TAssembler>
+ class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
+
+ typedef typename osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> collector_type;
+
+ typedef typename TAssembler::config_type assembler_config_type;
+ const assembler_config_type m_assembler_config;
+
+ osmium::memory::Buffer m_output_buffer;
+
+ static constexpr size_t initial_output_buffer_size = 1024 * 1024;
+ static constexpr size_t max_buffer_size_for_flush = 100 * 1024;
+
+ void flush_output_buffer() {
+ if (this->callback()) {
+ osmium::memory::Buffer buffer(initial_output_buffer_size);
+ std::swap(buffer, m_output_buffer);
+ this->callback()(std::move(buffer));
+ }
+ }
+
+ void possibly_flush_output_buffer() {
+ if (m_output_buffer.committed() > max_buffer_size_for_flush) {
+ flush_output_buffer();
+ }
+ }
+
+ public:
+
+ explicit MultipolygonCollector(const assembler_config_type& assembler_config) :
+ collector_type(),
+ m_assembler_config(assembler_config),
+ m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) {
+ }
+
+ /**
+ * We are interested in all relations tagged with type=multipolygon or
+ * type=boundary.
+ *
+ * Overwritten from the base class.
+ */
+ bool keep_relation(const osmium::Relation& relation) const {
+ const char* type = relation.tags().get_value_by_key("type");
+
+ // ignore relations without "type" tag
+ if (!type) {
+ return false;
+ }
+
+ if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Overwritten from the base class.
+ */
+ bool keep_member(const osmium::relations::RelationMeta& /*relation_meta*/, const osmium::RelationMember& member) const {
+ // We are only interested in members of type way.
+ return member.type() == osmium::item_type::way;
+ }
+
+ /**
+ * This is called when a way is not in any multipolygon
+ * relation.
+ *
+ * Overwritten from the base class.
+ */
+ void way_not_in_any_relation(const osmium::Way& way) {
+ if (way.ends_have_same_location() && way.nodes().size() > 3) {
+ // way is closed and has enough nodes, build simple multipolygon
+ try {
+ TAssembler assembler(m_assembler_config);
+ assembler(way, m_output_buffer);
+ possibly_flush_output_buffer();
+ } catch (osmium::invalid_location&) {
+ // XXX ignore
+ }
+ }
+ }
+
+ void complete_relation(osmium::relations::RelationMeta& relation_meta) {
+ const osmium::Relation& relation = this->get_relation(relation_meta);
+ std::vector<size_t> offsets;
+ for (const auto& member : relation.members()) {
+ if (member.ref() != 0) {
+ offsets.push_back(this->get_offset(member.type(), member.ref()));
+ }
+ }
+ try {
+ TAssembler assembler(m_assembler_config);
+ assembler(relation, offsets, this->members_buffer(), m_output_buffer);
+ possibly_flush_output_buffer();
+ } catch (osmium::invalid_location&) {
+ // XXX ignore
+ }
+
+ // clear member metas
+ for (const auto& member : relation.members()) {
+ if (member.ref() != 0) {
+ auto& mmv = this->member_meta(member.type());
+ auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(member.ref()));
+ assert(range.first != range.second);
+
+ // if this is the last time this object was needed
+ // then mark it as removed
+ if (osmium::relations::count_not_removed(range.first, range.second) == 1) {
+ this->get_member(range.first->buffer_offset()).set_removed(true);
+ }
+
+ for (auto it = range.first; it != range.second; ++it) {
+ if (!it->removed() && relation.id() == this->get_relation(it->relation_pos()).id()) {
+ it->remove();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void flush() {
+ flush_output_buffer();
+ }
+
+ osmium::memory::Buffer read() {
+ osmium::memory::Buffer buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes);
+ std::swap(buffer, m_output_buffer);
+ return buffer;
+ }
+
+ }; // class MultipolygonCollector
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
diff --git a/include/osmium/area/problem_reporter.hpp b/include/osmium/area/problem_reporter.hpp
new file mode 100644
index 0000000..4ae4bb2
--- /dev/null
+++ b/include/osmium/area/problem_reporter.hpp
@@ -0,0 +1,149 @@
+#ifndef OSMIUM_AREA_PROBLEM_REPORTER_HPP
+#define OSMIUM_AREA_PROBLEM_REPORTER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ /**
+ * When assembling a multipolygon/area from a multipolygon relation
+ * or a closed way several problems can be detected. This includes
+ * intersections between lines, wrong role attributes on relation
+ * members etc. These problems are reported by the area::Assembler
+ * class to the ProblemReporter class or one of its child classes.
+ *
+ * This is the parent class which does nothing with the reports.
+ * Child classes are expected to implement different ways of
+ * reporting the problems.
+ */
+ class ProblemReporter {
+
+ protected:
+
+ // Type of object we are currently working on
+ osmium::item_type m_object_type;
+
+ // ID of the relation/way we are currently working on
+ osmium::object_id_type m_object_id;
+
+ public:
+
+ ProblemReporter() = default;
+
+ virtual ~ProblemReporter() = default;
+
+ /**
+ * Set the object the next problem reports will be on.
+ *
+ * @param object_type The type of the object.
+ * @param object_id The ID of the object.
+ */
+ void set_object(osmium::item_type object_type, osmium::object_id_type object_id) noexcept {
+ m_object_type = object_type;
+ m_object_id = object_id;
+ }
+
+// 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
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+ /**
+ * 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.
+ */
+ virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) {
+ }
+
+ /**
+ * Report an intersection between two segments.
+ *
+ * @param way1_id ID of the first involved way.
+ * @param way1_seg_start Location where the segment of the first way with the intersection starts
+ * @param way1_seg_end Location where the segment of the first way with the intersection ends
+ * @param way2_id ID of the second involved way.
+ * @param way2_seg_start Location where the segment of the second way with the intersection starts
+ * @param way2_seg_end Location where the segment of the second way with the intersection ends
+ * @param intersection Location of the intersection. This might be slightly off the correct location due to rounding.
+ */
+ virtual 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) {
+ }
+
+ /**
+ * Report an open ring.
+ *
+ * @param end1 Location of the first open end.
+ * @param end2 Location of the second open end.
+ */
+ virtual void report_ring_not_closed(osmium::Location end1, osmium::Location end2) {
+ }
+
+ /**
+ * 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.
+ */
+ virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
+ }
+
+ /**
+ * Report a segment that should have role "inner", 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.
+ */
+ virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
+ }
+
+#pragma GCC diagnostic pop
+
+ }; // class ProblemReporter
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_PROBLEM_REPORTER_HPP
diff --git a/include/osmium/area/problem_reporter_exception.hpp b/include/osmium/area/problem_reporter_exception.hpp
new file mode 100644
index 0000000..5e743c6
--- /dev/null
+++ b/include/osmium/area/problem_reporter_exception.hpp
@@ -0,0 +1,96 @@
+#ifndef OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
+#define OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <sstream>
+#include <stdexcept>
+
+#include <osmium/area/problem_reporter_stream.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ class ProblemReporterException : public ProblemReporterStream {
+
+ std::stringstream m_sstream;
+
+ public:
+
+ ProblemReporterException() :
+ ProblemReporterStream(m_sstream) {
+ }
+
+ virtual ~ProblemReporterException() = default;
+
+ void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
+ m_sstream.str();
+ ProblemReporterStream::report_duplicate_node(node_id1, node_id2, 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();
+ ProblemReporterStream::report_intersection(way1_id, way1_seg_start, way1_seg_end, way2_id, way2_seg_start, way2_seg_end, intersection);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
+ m_sstream.str();
+ ProblemReporterStream::report_ring_not_closed(end1, end2);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
+ m_sstream.str();
+ ProblemReporterStream::report_role_should_be_outer(way_id, seg_start, seg_end);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
+ m_sstream.str();
+ ProblemReporterStream::report_role_should_be_inner(way_id, seg_start, seg_end);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ }; // class ProblemReporterException
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
diff --git a/include/osmium/area/problem_reporter_ogr.hpp b/include/osmium/area/problem_reporter_ogr.hpp
new file mode 100644
index 0000000..c437a3f
--- /dev/null
+++ b/include/osmium/area/problem_reporter_ogr.hpp
@@ -0,0 +1,223 @@
+#ifndef OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
+#define OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+/**
+ * @file
+ *
+ * This file contains code for reporting problems through OGR when
+ * assembling multipolygons.
+ *
+ * @attention If you include this file, you'll need to link with `libgdal`.
+ */
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4458)
+#else
+# pragma GCC diagnostic push
+# ifdef __clang__
+# pragma GCC diagnostic ignored "-Wdocumentation-unknown-command"
+# endif
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+# pragma GCC diagnostic ignored "-Wold-style-cast"
+# pragma GCC diagnostic ignored "-Wpadded"
+# pragma GCC diagnostic ignored "-Wredundant-decls"
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include <ogr_api.h>
+#include <ogrsf_frmts.h>
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#else
+# pragma GCC diagnostic pop
+#endif
+
+#include <memory>
+#include <stdexcept>
+
+#include <osmium/area/problem_reporter.hpp>
+#include <osmium/geom/ogr.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ /**
+ * Report problems when assembling areas by adding them to
+ * layers in an OGR datasource.
+ */
+ class ProblemReporterOGR : public ProblemReporter {
+
+ osmium::geom::OGRFactory<> m_ogr_factory;
+
+ OGRDataSource* m_data_source;
+
+ OGRLayer* m_layer_perror;
+ OGRLayer* m_layer_lerror;
+
+ void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_perror->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_ogr_factory.create_point(location);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id1", static_cast<double>(id1));
+ feature->SetField("id2", static_cast<double>(id2));
+ feature->SetField("problem_type", problem_type);
+
+ if (m_layer_perror->CreateFeature(feature) != OGRERR_NONE) {
+ std::runtime_error("Failed to create feature on layer 'perrors'");
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+
+ 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<OGRPoint> ogr_point1 = m_ogr_factory.create_point(loc1);
+ std::unique_ptr<OGRPoint> ogr_point2 = m_ogr_factory.create_point(loc2);
+ std::unique_ptr<OGRLineString> ogr_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
+ ogr_linestring->addPoint(ogr_point1.get());
+ ogr_linestring->addPoint(ogr_point2.get());
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_lerror->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id1", static_cast<double>(id1));
+ feature->SetField("id2", static_cast<double>(id2));
+ feature->SetField("problem_type", problem_type);
+
+ if (m_layer_lerror->CreateFeature(feature) != OGRERR_NONE) {
+ std::runtime_error("Failed to create feature on layer 'lerrors'");
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+
+ public:
+
+ explicit ProblemReporterOGR(OGRDataSource* data_source) :
+ m_data_source(data_source) {
+
+ OGRSpatialReference sparef;
+ sparef.SetWellKnownGeogCS("WGS84");
+
+ m_layer_perror = m_data_source->CreateLayer("perrors", &sparef, wkbPoint, nullptr);
+ if (!m_layer_perror) {
+ std::runtime_error("Layer creation failed for layer 'perrors'");
+ }
+
+ OGRFieldDefn layer_perror_field_id1("id1", OFTReal);
+ layer_perror_field_id1.SetWidth(10);
+
+ if (m_layer_perror->CreateField(&layer_perror_field_id1) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'id1' failed for layer 'perrors'");
+ }
+
+ OGRFieldDefn layer_perror_field_id2("id2", OFTReal);
+ layer_perror_field_id2.SetWidth(10);
+
+ if (m_layer_perror->CreateField(&layer_perror_field_id2) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'id2' failed for layer 'perrors'");
+ }
+
+ OGRFieldDefn layer_perror_field_problem_type("problem_type", OFTString);
+ layer_perror_field_problem_type.SetWidth(30);
+
+ if (m_layer_perror->CreateField(&layer_perror_field_problem_type) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'problem_type' failed for layer 'perrors'");
+ }
+
+ /**************/
+
+ m_layer_lerror = m_data_source->CreateLayer("lerrors", &sparef, wkbLineString, nullptr);
+ if (!m_layer_lerror) {
+ std::runtime_error("Layer creation failed for layer 'lerrors'");
+ }
+
+ OGRFieldDefn layer_lerror_field_id1("id1", OFTReal);
+ layer_lerror_field_id1.SetWidth(10);
+
+ if (m_layer_lerror->CreateField(&layer_lerror_field_id1) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'id1' failed for layer 'lerrors'");
+ }
+
+ OGRFieldDefn layer_lerror_field_id2("id2", OFTReal);
+ layer_lerror_field_id2.SetWidth(10);
+
+ if (m_layer_lerror->CreateField(&layer_lerror_field_id2) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'id2' failed for layer 'lerrors'");
+ }
+
+ OGRFieldDefn layer_lerror_field_problem_type("problem_type", OFTString);
+ layer_lerror_field_problem_type.SetWidth(30);
+
+ if (m_layer_lerror->CreateField(&layer_lerror_field_problem_type) != OGRERR_NONE) {
+ std::runtime_error("Creating field 'problem_type' failed for layer 'lerrors'");
+ }
+ }
+
+ virtual ~ProblemReporterOGR() = default;
+
+ void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
+ write_point("duplicate_node", node_id1, node_id2, location);
+ }
+
+ 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);
+ }
+
+ 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_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);
+ }
+
+ 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);
+ }
+
+ }; // class ProblemReporterOGR
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
diff --git a/include/osmium/area/problem_reporter_stream.hpp b/include/osmium/area/problem_reporter_stream.hpp
new file mode 100644
index 0000000..ddcb343
--- /dev/null
+++ b/include/osmium/area/problem_reporter_stream.hpp
@@ -0,0 +1,96 @@
+#ifndef OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
+#define OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <ostream>
+
+#include <osmium/area/problem_reporter.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ class ProblemReporterStream : public ProblemReporter {
+
+ std::ostream* m_out;
+
+ public:
+
+ explicit ProblemReporterStream(std::ostream& out) :
+ m_out(&out) {
+ }
+
+ virtual ~ProblemReporterStream() = default;
+
+ void header(const char* msg) {
+ *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << ": ";
+ }
+
+ void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
+ header("duplicate node");
+ *m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " 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");
+ *m_out << "way1_id=" << way1_id << " way1_seg_start=" << way1_seg_start << " way1_seg_end=" << way1_seg_end
+ << " 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 {
+ header("ring not closed");
+ *m_out << "end1=" << end1 << " end2=" << end2 << "\n";
+ }
+
+ void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
+ header("role should be outer");
+ *m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
+ }
+
+ void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
+ header("role should be inner");
+ *m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
+ }
+
+ }; // class ProblemReporterStream
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp
new file mode 100644
index 0000000..bf9cdcc
--- /dev/null
+++ b/include/osmium/builder/builder.hpp
@@ -0,0 +1,224 @@
+#ifndef OSMIUM_BUILDER_BUILDER_HPP
+#define OSMIUM_BUILDER_BUILDER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <new>
+#include <string>
+#include <type_traits>
+
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Classes for building OSM objects and other items in buffers
+ */
+ namespace builder {
+
+ class Builder {
+
+ osmium::memory::Buffer& m_buffer;
+ Builder* m_parent;
+ size_t m_item_offset;
+
+ Builder(const Builder&) = delete;
+ Builder(Builder&&) = delete;
+
+ Builder& operator=(const Builder&) = delete;
+ Builder& operator=(Builder&&) = delete;
+
+ protected:
+
+ explicit Builder(osmium::memory::Buffer& buffer, Builder* parent, osmium::memory::item_size_type size) :
+ m_buffer(buffer),
+ m_parent(parent),
+ m_item_offset(buffer.written()) {
+ m_buffer.reserve_space(size);
+ assert(buffer.is_aligned());
+ if (m_parent) {
+ m_parent->add_size(size);
+ }
+ }
+
+ ~Builder() = default;
+
+ osmium::memory::Item& item() const {
+ return *reinterpret_cast<osmium::memory::Item*>(m_buffer.data() + m_item_offset);
+ }
+
+ public:
+
+ /**
+ * Add padding to buffer (if needed) to align data properly.
+ *
+ * This calculates how many padding bytes are needed and adds
+ * as many zero bytes to the buffer. It also adds this number
+ * to the size of the current item (if the "self" param is
+ * true) and recursively to all the parent items.
+ *
+ * @param self If true add number of padding bytes to size
+ * of current item. Size is always added to
+ * parent item (if any).
+ *
+ */
+ void add_padding(bool self=false) {
+ 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) {
+ add_size(padding);
+ } else if (m_parent) {
+ m_parent->add_size(padding);
+ assert(m_parent->size() % osmium::memory::align_bytes == 0);
+ }
+ }
+ }
+
+ void add_size(uint32_t size) {
+ item().add_size(size);
+ if (m_parent) {
+ m_parent->add_size(size);
+ }
+ }
+
+ uint32_t size() const noexcept {
+ return item().byte_size();
+ }
+
+ void add_item(const osmium::memory::Item* item) {
+ unsigned char* target = m_buffer.reserve_space(item->padded_size());
+ std::copy_n(reinterpret_cast<const unsigned char*>(item), item->padded_size(), target);
+ add_size(item->padded_size());
+ }
+
+ /**
+ * Reserve space for an object of class T in buffer and return
+ * pointer to it.
+ */
+ template <class T>
+ T* reserve_space_for() {
+ assert(m_buffer.is_aligned());
+ return reinterpret_cast<T*>(m_buffer.reserve_space(sizeof(T)));
+ }
+
+ /**
+ * Append data to buffer.
+ *
+ * @param data Pointer to data.
+ * @param length Length of data in bytes. If data is a
+ * \0-terminated string, length must contain the
+ * \0 byte.
+ */
+ osmium::memory::item_size_type append(const char* data, const osmium::memory::item_size_type length) {
+ unsigned char* target = m_buffer.reserve_space(length);
+ std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
+ return length;
+ }
+
+ /**
+ * Append \0-terminated string to buffer.
+ */
+ osmium::memory::item_size_type append(const char* str) {
+ return append(str, static_cast<osmium::memory::item_size_type>(std::strlen(str) + 1));
+ }
+
+ /// Return the buffer this builder is using.
+ osmium::memory::Buffer& buffer() noexcept {
+ return m_buffer;
+ }
+
+ }; // class Builder
+
+ template <class TItem>
+ class ObjectBuilder : public Builder {
+
+ static_assert(std::is_base_of<osmium::memory::Item, TItem>::value,
+ "ObjectBuilder can only build objects derived from osmium::memory::Item");
+
+ public:
+
+ explicit ObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ Builder(buffer, parent, sizeof(TItem)) {
+ new (&item()) TItem();
+ }
+
+ TItem& object() noexcept {
+ return static_cast<TItem&>(item());
+ }
+
+ /**
+ * Add user name to buffer.
+ *
+ * @param user Pointer to user name.
+ * @param length Length of user name including \0 byte.
+ */
+ void add_user(const char* user, const string_size_type length) {
+ object().set_user_size(length);
+ add_size(append(user, length));
+ add_padding(true);
+ }
+
+ /**
+ * Add user name to buffer.
+ *
+ * @param user Pointer to \0-terminated user name.
+ */
+ void add_user(const char* user) {
+ add_user(user, static_cast_with_assert<string_size_type>(std::strlen(user) + 1));
+ }
+
+ /**
+ * Add user name to buffer.
+ *
+ * @param user User name.
+ */
+ void add_user(const std::string& user) {
+ add_user(user.data(), static_cast_with_assert<string_size_type>(user.size() + 1));
+ }
+
+ }; // class ObjectBuilder
+
+ } // namespace builder
+
+} // namespace osmium
+
+#endif // OSMIUM_BUILDER_BUILDER_HPP
diff --git a/include/osmium/builder/builder_helper.hpp b/include/osmium/builder/builder_helper.hpp
new file mode 100644
index 0000000..eebdf33
--- /dev/null
+++ b/include/osmium/builder/builder_helper.hpp
@@ -0,0 +1,103 @@
+#ifndef OSMIUM_BUILDER_BUILDER_HELPER_HPP
+#define OSMIUM_BUILDER_BUILDER_HELPER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <initializer_list>
+#include <functional>
+#include <map>
+#include <utility>
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/memory/buffer.hpp>
+
+namespace osmium {
+
+ class NodeRef;
+ class TagList;
+ class WayNodeList;
+
+ namespace builder {
+
+ inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list<osmium::NodeRef>& nodes) {
+ size_t pos = buffer.committed();
+ {
+ osmium::builder::WayNodeListBuilder wnl_builder(buffer);
+ for (const auto& node_ref : nodes) {
+ wnl_builder.add_node_ref(node_ref);
+ }
+ }
+ buffer.commit();
+ return buffer.get<const osmium::WayNodeList>(pos);
+ }
+
+ inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list<std::pair<const char*, const char*>>& tags) {
+ size_t pos = buffer.committed();
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer);
+ for (const auto& p : tags) {
+ tl_builder.add_tag(p.first, p.second);
+ }
+ }
+ buffer.commit();
+ return buffer.get<const osmium::TagList>(pos);
+ }
+
+ inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map<const char*, const char*>& tags) {
+ size_t pos = buffer.committed();
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer);
+ for (const auto& p : tags) {
+ tl_builder.add_tag(p.first, p.second);
+ }
+ }
+ buffer.commit();
+ return buffer.get<const osmium::TagList>(pos);
+ }
+
+ inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function<void(osmium::builder::TagListBuilder&)> func) {
+ size_t pos = buffer.committed();
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer);
+ func(tl_builder);
+ }
+ buffer.commit();
+ return buffer.get<const osmium::TagList>(pos);
+ }
+
+ } // namespace builder
+
+} // namespace osmium
+
+#endif // OSMIUM_BUILDER_BUILDER_HELPER_HPP
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
new file mode 100644
index 0000000..b88765e
--- /dev/null
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -0,0 +1,284 @@
+#ifndef OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
+#define OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstring>
+#include <initializer_list>
+#include <new>
+#include <string>
+#include <utility>
+
+#include <osmium/builder/builder.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ namespace memory {
+ class Buffer;
+ }
+
+ namespace builder {
+
+ class TagListBuilder : public ObjectBuilder<TagList> {
+
+ public:
+
+ explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ ObjectBuilder<TagList>(buffer, parent) {
+ }
+
+ ~TagListBuilder() {
+ add_padding();
+ }
+
+ /**
+ * Add tag to buffer.
+ *
+ * @param key Tag key.
+ * @param value Tag value.
+ */
+ void add_tag(const char* key, const char* value) {
+ add_size(append(key) + append(value));
+ }
+
+ /**
+ * Add tag to buffer.
+ *
+ * @param key Tag key.
+ * @param value Tag value.
+ */
+ void add_tag(const std::string& key, const std::string& value) {
+ add_size(append(key.data(), static_cast_with_assert<string_size_type>(key.size() + 1)) +
+ append(value.data(), static_cast_with_assert<string_size_type>(value.size() + 1)));
+ }
+
+ }; // class TagListBuilder
+
+ template <class T>
+ class NodeRefListBuilder : public ObjectBuilder<T> {
+
+ public:
+
+ explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ ObjectBuilder<T>(buffer, parent) {
+ }
+
+ ~NodeRefListBuilder() {
+ static_cast<Builder*>(this)->add_padding();
+ }
+
+ void add_node_ref(const NodeRef& node_ref) {
+ new (static_cast<Builder*>(this)->reserve_space_for<osmium::NodeRef>()) osmium::NodeRef(node_ref);
+ static_cast<Builder*>(this)->add_size(sizeof(osmium::NodeRef));
+ }
+
+ void add_node_ref(const object_id_type ref, const osmium::Location location=Location()) {
+ add_node_ref(NodeRef(ref, location));
+ }
+
+ }; // class NodeRefListBuilder
+
+ typedef NodeRefListBuilder<WayNodeList> WayNodeListBuilder;
+ typedef NodeRefListBuilder<OuterRing> OuterRingBuilder;
+ typedef NodeRefListBuilder<InnerRing> InnerRingBuilder;
+
+ class RelationMemberListBuilder : public ObjectBuilder<RelationMemberList> {
+
+ /**
+ * Add role to buffer.
+ *
+ * @param member Relation member object where the length of the role
+ * will be set.
+ * @param role The role.
+ * @param length Length of role string including \0 termination.
+ */
+ void add_role(osmium::RelationMember& member, const char* role, const string_size_type length) {
+ member.set_role_size(length);
+ add_size(append(role, length));
+ add_padding(true);
+ }
+
+ /**
+ * Add role to buffer.
+ *
+ * @param member Relation member object where the length of the role
+ * will be set.
+ * @param role \0-terminated role.
+ */
+ void add_role(osmium::RelationMember& member, const char* role) {
+ add_role(member, role, static_cast_with_assert<string_size_type>(std::strlen(role) + 1));
+ }
+
+ /**
+ * Add role to buffer.
+ *
+ * @param member Relation member object where the length of the role
+ * will be set.
+ * @param role Role.
+ */
+ void add_role(osmium::RelationMember& member, const std::string& role) {
+ add_role(member, role.data(), static_cast_with_assert<string_size_type>(role.size() + 1));
+ }
+
+ public:
+
+ explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ ObjectBuilder<RelationMemberList>(buffer, parent) {
+ }
+
+ ~RelationMemberListBuilder() {
+ add_padding();
+ }
+
+ /**
+ * Add a member to the relation.
+ *
+ * @param type The type (node, way, or relation).
+ * @param ref The ID of the member.
+ * @param role The role of the member.
+ * @param full_member Optional pointer to the member object. If it
+ * is available a copy will be added to the
+ * relation.
+ */
+ void add_member(osmium::item_type type, object_id_type ref, const char* role, const osmium::OSMObject* full_member = nullptr) {
+ osmium::RelationMember* member = reserve_space_for<osmium::RelationMember>();
+ new (member) osmium::RelationMember(ref, type, full_member != nullptr);
+ add_size(sizeof(RelationMember));
+ add_role(*member, role);
+ if (full_member) {
+ add_item(full_member);
+ }
+ }
+
+ /**
+ * Add a member to the relation.
+ *
+ * @param type The type (node, way, or relation).
+ * @param ref The ID of the member.
+ * @param role The role of the member.
+ * @param full_member Optional pointer to the member object. If it
+ * is available a copy will be added to the
+ * relation.
+ */
+ void add_member(osmium::item_type type, object_id_type ref, const std::string& role, const osmium::OSMObject* full_member = nullptr) {
+ osmium::RelationMember* member = reserve_space_for<osmium::RelationMember>();
+ new (member) osmium::RelationMember(ref, type, full_member != nullptr);
+ add_size(sizeof(RelationMember));
+ add_role(*member, role);
+ if (full_member) {
+ add_item(full_member);
+ }
+ }
+
+ }; // class RelationMemberListBuilder
+
+ template <class T>
+ class OSMObjectBuilder : public ObjectBuilder<T> {
+
+ public:
+
+ explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ ObjectBuilder<T>(buffer, parent) {
+ static_cast<Builder*>(this)->reserve_space_for<string_size_type>();
+ static_cast<Builder*>(this)->add_size(sizeof(string_size_type));
+ }
+
+ void add_tags(const std::initializer_list<std::pair<const char*, const char*>>& tags) {
+ osmium::builder::TagListBuilder tl_builder(static_cast<Builder*>(this)->buffer(), this);
+ for (const auto& p : tags) {
+ tl_builder.add_tag(p.first, p.second);
+ }
+ }
+
+ }; // class OSMObjectBuilder
+
+ typedef OSMObjectBuilder<osmium::Node> NodeBuilder;
+ typedef OSMObjectBuilder<osmium::Relation> RelationBuilder;
+
+ class WayBuilder : public OSMObjectBuilder<osmium::Way> {
+
+ public:
+
+ explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ OSMObjectBuilder<osmium::Way>(buffer, parent) {
+ }
+
+ void add_node_refs(const std::initializer_list<osmium::NodeRef>& nodes) {
+ osmium::builder::WayNodeListBuilder builder(buffer(), this);
+ for (const auto& node_ref : nodes) {
+ builder.add_node_ref(node_ref);
+ }
+ }
+
+ }; // class WayBuilder
+
+ class AreaBuilder : public OSMObjectBuilder<osmium::Area> {
+
+ public:
+
+ explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+ OSMObjectBuilder<osmium::Area>(buffer, parent) {
+ }
+
+ /**
+ * Initialize area attributes from the attributes of the given object.
+ */
+ void initialize_from_object(const osmium::OSMObject& source) {
+ osmium::Area& area = object();
+ area.set_id(osmium::object_id_to_area_id(source.id(), source.type()));
+ area.set_version(source.version());
+ area.set_changeset(source.changeset());
+ area.set_timestamp(source.timestamp());
+ area.set_visible(source.visible());
+ area.set_uid(source.uid());
+
+ add_user(source.user());
+ }
+
+ }; // class AreaBuilder
+
+ typedef ObjectBuilder<osmium::Changeset> ChangesetBuilder;
+
+ } // namespace builder
+
+} // namespace osmium
+
+#endif // OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
diff --git a/include/osmium/diff_handler.hpp b/include/osmium/diff_handler.hpp
new file mode 100644
index 0000000..4f9b3a1
--- /dev/null
+++ b/include/osmium/diff_handler.hpp
@@ -0,0 +1,67 @@
+#ifndef OSMIUM_DIFF_HANDLER_HPP
+#define OSMIUM_DIFF_HANDLER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/diff_object.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Osmium diff handlers provide access to differences between OSM object versions
+ */
+ namespace diff_handler {
+
+ class DiffHandler {
+
+ public:
+
+ DiffHandler() {
+ }
+
+ void node(const osmium::DiffNode&) const {
+ }
+
+ void way(const osmium::DiffWay&) const {
+ }
+
+ void relation(const osmium::DiffRelation&) const {
+ }
+
+ }; // class DiffHandler
+
+ } // namespace diff_handler
+
+} // namespace osmium
+
+#endif // OSMIUM_DIFF_HANDLER_HPP
diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp
new file mode 100644
index 0000000..0ddf7ff
--- /dev/null
+++ b/include/osmium/diff_iterator.hpp
@@ -0,0 +1,129 @@
+#ifndef OSMIUM_DIFF_ITERATOR_HPP
+#define OSMIUM_DIFF_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <iterator>
+#include <type_traits>
+
+#include <osmium/osm/diff_object.hpp>
+
+namespace osmium {
+
+ class OSMObject;
+
+ template <class TBasicIterator>
+ class DiffIterator : public std::iterator<std::input_iterator_tag, const osmium::DiffObject> {
+
+ static_assert(std::is_base_of<osmium::OSMObject, typename TBasicIterator::value_type>::value, "TBasicIterator::value_type must derive from osmium::OSMObject");
+
+ TBasicIterator m_prev;
+ TBasicIterator m_curr;
+ TBasicIterator m_next;
+
+ const TBasicIterator m_end;
+
+ mutable osmium::DiffObject m_diff;
+
+ void set_diff() const {
+ assert(m_curr != m_end);
+
+ TBasicIterator prev = m_prev;
+ if (prev->type() != m_curr->type() || prev->id() != m_curr->id()) {
+ prev = m_curr;
+ }
+
+ TBasicIterator next = m_next;
+ if (next == m_end || next->type() != m_curr->type() || next->id() != m_curr->id()) {
+ next = m_curr;
+ }
+
+ m_diff = osmium::DiffObject(*prev, *m_curr, *next);
+ }
+
+ public:
+
+ explicit DiffIterator(TBasicIterator begin, TBasicIterator end) :
+ m_prev(begin),
+ m_curr(begin),
+ m_next(begin == end ? begin : ++begin),
+ m_end(end) {
+ }
+
+ DiffIterator(const DiffIterator&) = default;
+ DiffIterator& operator=(const DiffIterator&) = default;
+
+ DiffIterator(DiffIterator&&) = default;
+ DiffIterator& operator=(DiffIterator&&) = default;
+
+ DiffIterator& operator++() {
+ m_prev = std::move(m_curr);
+ m_curr = m_next;
+
+ if (m_next != m_end) {
+ ++m_next;
+ }
+
+ return *this;
+ }
+
+ DiffIterator operator++(int) {
+ DiffIterator tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const DiffIterator& rhs) const {
+ return m_curr == rhs.m_curr && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const DiffIterator& rhs) const {
+ return !(*this == rhs);
+ }
+
+ reference operator*() const {
+ set_diff();
+ return m_diff;
+ }
+
+ pointer operator->() const {
+ set_diff();
+ return &m_diff;
+ }
+
+ }; // class DiffIterator
+
+} // namespace osmium
+
+#endif // OSMIUM_DIFF_ITERATOR_HPP
diff --git a/include/osmium/diff_visitor.hpp b/include/osmium/diff_visitor.hpp
new file mode 100644
index 0000000..5e72a7b
--- /dev/null
+++ b/include/osmium/diff_visitor.hpp
@@ -0,0 +1,104 @@
+#ifndef OSMIUM_DIFF_VISITOR_HPP
+#define OSMIUM_DIFF_VISITOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/diff_iterator.hpp>
+#include <osmium/io/input_iterator.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/diff_object.hpp>
+#include <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+ namespace detail {
+
+ template <class THandler>
+ inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler) {
+ switch (diff.type()) {
+ case osmium::item_type::node:
+ handler.node(static_cast<const osmium::DiffNode&>(diff));
+ break;
+ case osmium::item_type::way:
+ handler.way(static_cast<const osmium::DiffWay&>(diff));
+ break;
+ case osmium::item_type::relation:
+ handler.relation(static_cast<const osmium::DiffRelation&>(diff));
+ break;
+ default:
+ throw osmium::unknown_type();
+ }
+ }
+
+ template <class THandler, class ...TRest>
+ inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler, TRest&... more) {
+ apply_diff_iterator_recurse(diff, handler);
+ apply_diff_iterator_recurse(diff, more...);
+ }
+
+ } // namespace detail
+
+ template <class TIterator, class ...THandlers>
+ inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) {
+ typedef osmium::DiffIterator<TIterator> diff_iterator;
+
+ diff_iterator dit(it, end);
+ diff_iterator dend(end, end);
+
+ for (; dit != dend; ++dit) {
+ detail::apply_diff_iterator_recurse(*dit, handlers...);
+ }
+ }
+
+ class OSMObject;
+
+ template <class TSource, class ...THandlers>
+ inline void apply_diff(TSource& source, THandlers&... handlers) {
+ apply_diff(osmium::io::InputIterator<TSource, osmium::OSMObject> {source},
+ osmium::io::InputIterator<TSource, osmium::OSMObject> {},
+ handlers...);
+ }
+
+ template <class ...THandlers>
+ inline void apply_diff(osmium::memory::Buffer& buffer, THandlers&... handlers) {
+ apply_diff(buffer.begin(), buffer.end(), handlers...);
+ }
+
+ template <class ...THandlers>
+ inline void apply_diff(const osmium::memory::Buffer& buffer, THandlers&... handlers) {
+ apply_diff(buffer.cbegin(), buffer.cend(), handlers...);
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_DIFF_VISITOR_HPP
diff --git a/include/osmium/dynamic_handler.hpp b/include/osmium/dynamic_handler.hpp
new file mode 100644
index 0000000..9d0bd66
--- /dev/null
+++ b/include/osmium/dynamic_handler.hpp
@@ -0,0 +1,195 @@
+#ifndef OSMIUM_DYNAMIC_HANDLER_HPP
+#define OSMIUM_DYNAMIC_HANDLER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <memory>
+#include <utility>
+
+#include <osmium/handler.hpp>
+
+namespace osmium {
+
+ class Node;
+ class Way;
+ class Relation;
+ class Area;
+ class Changeset;
+
+ namespace handler {
+
+ namespace detail {
+
+ class HandlerWrapperBase {
+
+ public:
+
+ virtual ~HandlerWrapperBase() {
+ }
+
+ virtual void node(const osmium::Node&) {
+ }
+
+ virtual void way(const osmium::Way&) {
+ }
+
+ virtual void relation(const osmium::Relation&) {
+ }
+
+ virtual void area(const osmium::Area&) {
+ }
+
+ virtual void changeset(const osmium::Changeset&) {
+ }
+
+ virtual void flush() {
+ }
+
+ }; // class HandlerWrapperBase
+
+
+ // The following uses trick from
+ // http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+ // to either call handler style functions or visitor style operator().
+
+#define OSMIUM_DYNAMIC_HANDLER_DISPATCH(_name_, _type_) \
+template <class THandler> \
+auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, int) -> decltype(handler._name_(object), void()) { \
+ handler._name_(object); \
+} \
+template <class THandler> \
+auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> decltype(handler(object), void()) { \
+ handler(object); \
+}
+
+ OSMIUM_DYNAMIC_HANDLER_DISPATCH(node, Node)
+ OSMIUM_DYNAMIC_HANDLER_DISPATCH(way, Way)
+ OSMIUM_DYNAMIC_HANDLER_DISPATCH(relation, Relation)
+ OSMIUM_DYNAMIC_HANDLER_DISPATCH(changeset, Changeset)
+ OSMIUM_DYNAMIC_HANDLER_DISPATCH(area, Area)
+
+ template <class THandler>
+ auto flush_dispatch(THandler& handler, int) -> decltype(handler.flush(), void()) {
+ handler.flush();
+ }
+
+ template <class THandler>
+ void flush_dispatch(THandler&, long) {}
+
+ template <class THandler>
+ class HandlerWrapper : public HandlerWrapperBase {
+
+ THandler m_handler;
+
+ public:
+
+ template <class... TArgs>
+ HandlerWrapper(TArgs&&... args) :
+ m_handler(std::forward<TArgs>(args)...) {
+ }
+
+ void node(const osmium::Node& node) override final {
+ node_dispatch(m_handler, node, 0);
+ }
+
+ void way(const osmium::Way& way) override final {
+ way_dispatch(m_handler, way, 0);
+ }
+
+ void relation(const osmium::Relation& relation) override final {
+ relation_dispatch(m_handler, relation, 0);
+ }
+
+ void area(const osmium::Area& area) override final {
+ area_dispatch(m_handler, area, 0);
+ }
+
+ void changeset(const osmium::Changeset& changeset) override final {
+ changeset_dispatch(m_handler, changeset, 0);
+ }
+
+ void flush() override final {
+ flush_dispatch(m_handler, 0);
+ }
+
+ }; // class HandlerWrapper
+
+ } // namespace detail
+
+ class DynamicHandler : public osmium::handler::Handler {
+
+ typedef std::unique_ptr<osmium::handler::detail::HandlerWrapperBase> impl_ptr;
+ impl_ptr m_impl;
+
+ public:
+
+ DynamicHandler() :
+ m_impl(impl_ptr(new osmium::handler::detail::HandlerWrapperBase)) {
+ }
+
+ template <class THandler, class... TArgs>
+ void set(TArgs&&... args) {
+ m_impl = impl_ptr(new osmium::handler::detail::HandlerWrapper<THandler>(std::forward<TArgs>(args)...));
+ }
+
+ void node(const osmium::Node& node) {
+ m_impl->node(node);
+ }
+
+ void way(const osmium::Way& way) {
+ m_impl->way(way);
+ }
+
+ void relation(const osmium::Relation& relation) {
+ m_impl->relation(relation);
+ }
+
+ void area(const osmium::Area& area) {
+ m_impl->area(area);
+ }
+
+ void changeset(const osmium::Changeset& changeset) {
+ m_impl->changeset(changeset);
+ }
+
+ void flush() {
+ m_impl->flush();
+ }
+
+ }; // class DynamicHandler
+
+ } // namspace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_DYNAMIC_HANDLER_HPP
diff --git a/include/osmium/experimental/flex_reader.hpp b/include/osmium/experimental/flex_reader.hpp
new file mode 100644
index 0000000..f00a5ec
--- /dev/null
+++ b/include/osmium/experimental/flex_reader.hpp
@@ -0,0 +1,129 @@
+#ifndef OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
+#define OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/visitor.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/assembler.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Experimental code that is not "officially" supported.
+ */
+ namespace experimental {
+
+ template <class TLocationHandler>
+ class FlexReader {
+
+ bool m_with_areas;
+ osmium::osm_entity_bits::type m_entities;
+
+ TLocationHandler& m_location_handler;
+
+ osmium::io::Reader m_reader;
+ osmium::area::Assembler::config_type m_assembler_config;
+ osmium::area::MultipolygonCollector<osmium::area::Assembler> m_collector;
+
+ public:
+
+ explicit FlexReader(const osmium::io::File& file, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
+ m_with_areas((entities & osmium::osm_entity_bits::area) != 0),
+ m_entities((entities & ~osmium::osm_entity_bits::area) | (m_with_areas ? osmium::osm_entity_bits::node | osmium::osm_entity_bits::way : osmium::osm_entity_bits::nothing)),
+ m_location_handler(location_handler),
+ m_reader(file, m_entities),
+ m_assembler_config(),
+ m_collector(m_assembler_config)
+ {
+ m_location_handler.ignore_errors();
+ if (m_with_areas) {
+ osmium::io::Reader reader(file, osmium::osm_entity_bits::relation);
+ m_collector.read_relations(reader);
+ reader.close();
+ }
+ }
+
+ explicit FlexReader(const std::string& filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
+ FlexReader(osmium::io::File(filename), location_handler, entities) {
+ }
+
+ explicit FlexReader(const char* filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
+ FlexReader(osmium::io::File(filename), location_handler, entities) {
+ }
+
+ osmium::memory::Buffer read() {
+ osmium::memory::Buffer buffer = m_reader.read();
+
+ if (buffer) {
+ if (m_with_areas) {
+ std::vector<osmium::memory::Buffer> area_buffers;
+ osmium::apply(buffer, m_location_handler, m_collector.handler([&area_buffers](osmium::memory::Buffer&& area_buffer) {
+ area_buffers.push_back(std::move(area_buffer));
+ }));
+ for (const osmium::memory::Buffer& b : area_buffers) {
+ buffer.add_buffer(b);
+ buffer.commit();
+ }
+ } else if (m_entities & (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way)) {
+ osmium::apply(buffer, m_location_handler);
+ }
+ }
+
+ return buffer;
+ }
+
+ osmium::io::Header header() const {
+ return m_reader.header();
+ }
+
+ void close() {
+ return m_reader.close();
+ }
+
+ bool eof() const {
+ return m_reader.eof();
+ }
+
+ const osmium::area::MultipolygonCollector<osmium::area::Assembler>& collector() const {
+ return m_collector;
+ }
+
+ }; // class FlexReader
+
+ } // namespace experimental
+
+} // namespace osmium
+
+#endif // OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
diff --git a/include/osmium/geom/coordinates.hpp b/include/osmium/geom/coordinates.hpp
new file mode 100644
index 0000000..2bad57e
--- /dev/null
+++ b/include/osmium/geom/coordinates.hpp
@@ -0,0 +1,97 @@
+#ifndef OSMIUM_GEOM_COORDINATES_HPP
+#define OSMIUM_GEOM_COORDINATES_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <iosfwd>
+#include <string>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/util/double.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ struct Coordinates {
+
+ double x;
+ double y;
+
+ explicit Coordinates(double cx, double cy) noexcept : x(cx), y(cy) {
+ }
+
+ Coordinates(const osmium::Location& location) : x(location.lon()), y(location.lat()) {
+ }
+
+ void append_to_string(std::string& s, const char infix, int precision) const {
+ osmium::util::double2string(s, x, precision);
+ s += infix;
+ osmium::util::double2string(s, y, precision);
+ }
+
+ void append_to_string(std::string& s, const char prefix, const char infix, const char suffix, int precision) const {
+ s += prefix;
+ append_to_string(s, infix, precision);
+ s += suffix;
+ }
+
+ }; // struct coordinates
+
+ /**
+ * Compare whether two Coordinates are identical. Might not give the
+ * right result if the coordinates have been the result of some
+ * calculation that introduced rounding errors.
+ */
+ inline bool operator==(const Coordinates& lhs, const Coordinates& rhs) noexcept {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+#pragma GCC diagnostic pop
+ }
+
+ inline bool operator!=(const Coordinates& lhs, const Coordinates& rhs) noexcept {
+ return ! operator==(lhs, rhs);
+ }
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const Coordinates& c) {
+ return out << '(' << c.x << ',' << c.y << ')';
+ }
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_COORDINATES_HPP
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
new file mode 100644
index 0000000..a0a2080
--- /dev/null
+++ b/include/osmium/geom/factory.hpp
@@ -0,0 +1,328 @@
+#ifndef OSMIUM_GEOM_FACTORY_HPP
+#define OSMIUM_GEOM_FACTORY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/area.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/way.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when an invalid geometry is encountered. An example
+ * would be a linestring with less than two points.
+ */
+ struct geometry_error : public std::runtime_error {
+
+ geometry_error(const std::string& what) :
+ std::runtime_error(what) {
+ }
+
+ geometry_error(const char* what) :
+ std::runtime_error(what) {
+ }
+
+ }; // struct geometry_error
+
+ /**
+ * @brief Everything related to geometry handling.
+ */
+ namespace geom {
+
+ /**
+ * Which nodes of a way to use for a linestring.
+ */
+ enum class use_nodes : bool {
+ unique = true, ///< Remove consecutive nodes with same location.
+ all = false ///< Use all nodes.
+ }; // enum class use_nodes
+
+ /**
+ * Which direction the linestring created from a way
+ * should have.
+ */
+ enum class direction : bool {
+ backward = true, ///< Linestring has reverse direction.
+ forward = false ///< Linestring has same direction as way.
+ }; // enum class direction
+
+ /**
+ * This pseudo projection just returns its WGS84 input unchanged.
+ * Used as a template parameter if a real projection is not needed.
+ */
+ class IdentityProjection {
+
+ public:
+
+ Coordinates operator()(osmium::Location location) const {
+ return Coordinates{location.lon(), location.lat()};
+ }
+
+ int epsg() const noexcept {
+ return 4326;
+ }
+
+ std::string proj_string() const {
+ return "+proj=longlat +datum=WGS84 +no_defs";
+ }
+
+ }; // class IdentityProjection
+
+ /**
+ * Geometry factory.
+ */
+ template <class TGeomImpl, class TProjection = IdentityProjection>
+ class GeometryFactory {
+
+ /**
+ * Add all points of an outer or inner ring to a multipolygon.
+ */
+ void add_points(const osmium::OuterRing& nodes) {
+ osmium::Location last_location;
+ for (const osmium::NodeRef& node_ref : nodes) {
+ if (last_location != node_ref.location()) {
+ last_location = node_ref.location();
+ m_impl.multipolygon_add_location(m_projection(last_location));
+ }
+ }
+ }
+
+ TProjection m_projection;
+ TGeomImpl m_impl;
+
+ public:
+
+ /**
+ * Constructor for default initialized projection.
+ */
+ template <class... TArgs>
+ GeometryFactory<TGeomImpl, TProjection>(TArgs&&... args) :
+ m_projection(),
+ m_impl(std::forward<TArgs>(args)...) {
+ }
+
+ /**
+ * Constructor for explicitly initialized projection. Note that the
+ * projection is moved into the GeometryFactory.
+ */
+ template <class... TArgs>
+ GeometryFactory<TGeomImpl, TProjection>(TProjection&& projection, TArgs&&... args) :
+ m_projection(std::move(projection)),
+ m_impl(std::forward<TArgs>(args)...) {
+ }
+
+ 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;
+
+ int epsg() const {
+ return m_projection.epsg();
+ }
+
+ std::string proj_string() const {
+ return m_projection.proj_string();
+ }
+
+ /* Point */
+
+ point_type create_point(const osmium::Location location) const {
+ return m_impl.make_point(m_projection(location));
+ }
+
+ point_type create_point(const osmium::Node& node) {
+ return create_point(node.location());
+ }
+
+ point_type create_point(const osmium::NodeRef& node_ref) {
+ return create_point(node_ref.location());
+ }
+
+ /* LineString */
+
+ void linestring_start() {
+ m_impl.linestring_start();
+ }
+
+ template <class TIter>
+ size_t fill_linestring(TIter it, TIter end) {
+ size_t num_points = 0;
+ for (; it != end; ++it, ++num_points) {
+ m_impl.linestring_add_location(m_projection(it->location()));
+ }
+ return num_points;
+ }
+
+ template <class TIter>
+ size_t fill_linestring_unique(TIter it, TIter end) {
+ size_t num_points = 0;
+ osmium::Location last_location;
+ for (; it != end; ++it) {
+ if (last_location != it->location()) {
+ last_location = it->location();
+ m_impl.linestring_add_location(m_projection(last_location));
+ ++num_points;
+ }
+ }
+ return num_points;
+ }
+
+ linestring_type linestring_finish(size_t num_points) {
+ return m_impl.linestring_finish(num_points);
+ }
+
+ linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
+ linestring_start();
+ size_t num_points = 0;
+
+ if (un == use_nodes::unique) {
+ osmium::Location last_location;
+ switch (dir) {
+ case direction::forward:
+ num_points = fill_linestring_unique(wnl.cbegin(), wnl.cend());
+ break;
+ case direction::backward:
+ num_points = fill_linestring_unique(wnl.crbegin(), wnl.crend());
+ break;
+ }
+ } else {
+ switch (dir) {
+ case direction::forward:
+ num_points = fill_linestring(wnl.cbegin(), wnl.cend());
+ break;
+ case direction::backward:
+ num_points = fill_linestring(wnl.crbegin(), wnl.crend());
+ break;
+ }
+ }
+
+ if (num_points < 2) {
+ throw osmium::geometry_error("not enough 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) {
+ return create_linestring(way.nodes(), un, dir);
+ }
+
+ /* Polygon */
+
+ void polygon_start() {
+ m_impl.polygon_start();
+ }
+
+ template <class TIter>
+ size_t fill_polygon(TIter it, TIter end) {
+ size_t num_points = 0;
+ for (; it != end; ++it, ++num_points) {
+ m_impl.polygon_add_location(m_projection(it->location()));
+ }
+ return num_points;
+ }
+
+ template <class TIter>
+ size_t fill_polygon_unique(TIter it, TIter end) {
+ size_t num_points = 0;
+ osmium::Location last_location;
+ for (; it != end; ++it) {
+ if (last_location != it->location()) {
+ last_location = it->location();
+ m_impl.polygon_add_location(m_projection(last_location));
+ ++num_points;
+ }
+ }
+ return num_points;
+ }
+
+ polygon_type polygon_finish(size_t num_points) {
+ return m_impl.polygon_finish(num_points);
+ }
+
+ /* MultiPolygon */
+
+ multipolygon_type create_multipolygon(const osmium::Area& area) {
+ size_t num_polygons = 0;
+ size_t num_rings = 0;
+ m_impl.multipolygon_start();
+
+ for (auto it = area.cbegin(); it != area.cend(); ++it) {
+ const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
+ if (it->type() == osmium::item_type::outer_ring) {
+ if (num_polygons > 0) {
+ m_impl.multipolygon_polygon_finish();
+ }
+ m_impl.multipolygon_polygon_start();
+ m_impl.multipolygon_outer_ring_start();
+ add_points(ring);
+ m_impl.multipolygon_outer_ring_finish();
+ ++num_rings;
+ ++num_polygons;
+ } else if (it->type() == osmium::item_type::inner_ring) {
+ m_impl.multipolygon_inner_ring_start();
+ add_points(ring);
+ m_impl.multipolygon_inner_ring_finish();
+ ++num_rings;
+ }
+ }
+
+ // if there are no rings, this area is invalid
+ if (num_rings == 0) {
+ throw osmium::geometry_error("invalid area");
+ }
+
+ m_impl.multipolygon_polygon_finish();
+ return m_impl.multipolygon_finish();
+ }
+
+ }; // class GeometryFactory
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_FACTORY_HPP
diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp
new file mode 100644
index 0000000..7d59535
--- /dev/null
+++ b/include/osmium/geom/geojson.hpp
@@ -0,0 +1,154 @@
+#ifndef OSMIUM_GEOM_GEOJSON_HPP
+#define OSMIUM_GEOM_GEOJSON_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <string>
+#include <utility>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/factory.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ namespace detail {
+
+ class GeoJSONFactoryImpl {
+
+ std::string m_str;
+ int m_precision;
+
+ 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;
+
+ GeoJSONFactoryImpl(int precision = 7) :
+ m_precision(precision) {
+ }
+
+ /* Point */
+
+ // { "type": "Point", "coordinates": [100.0, 0.0] }
+ point_type make_point(const osmium::geom::Coordinates& xy) const {
+ std::string str {"{\"type\":\"Point\",\"coordinates\":"};
+ xy.append_to_string(str, '[', ',', ']', m_precision);
+ str += "}";
+ return str;
+ }
+
+ /* LineString */
+
+ // { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
+ void linestring_start() {
+ m_str = "{\"type\":\"LineString\",\"coordinates\":[";
+ }
+
+ void linestring_add_location(const osmium::geom::Coordinates& xy) {
+ xy.append_to_string(m_str, '[', ',', ']', m_precision);
+ m_str += ',';
+ }
+
+ linestring_type linestring_finish(size_t /* num_points */) {
+ assert(!m_str.empty());
+ std::string str;
+ std::swap(str, m_str);
+ str.back() = ']';
+ str += "}";
+ return str;
+ }
+
+ /* MultiPolygon */
+
+ void multipolygon_start() {
+ m_str = "{\"type\":\"MultiPolygon\",\"coordinates\":[";
+ }
+
+ void multipolygon_polygon_start() {
+ m_str += '[';
+ }
+
+ void multipolygon_polygon_finish() {
+ m_str += "],";
+ }
+
+ void multipolygon_outer_ring_start() {
+ m_str += '[';
+ }
+
+ void multipolygon_outer_ring_finish() {
+ assert(!m_str.empty());
+ m_str.back() = ']';
+ }
+
+ void multipolygon_inner_ring_start() {
+ m_str += ",[";
+ }
+
+ void multipolygon_inner_ring_finish() {
+ assert(!m_str.empty());
+ m_str.back() = ']';
+ }
+
+ void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
+ xy.append_to_string(m_str, '[', ',', ']', m_precision);
+ m_str += ',';
+ }
+
+ multipolygon_type multipolygon_finish() {
+ assert(!m_str.empty());
+ std::string str;
+ std::swap(str, m_str);
+ str.back() = ']';
+ str += "}";
+ return str;
+ }
+
+ }; // class GeoJSONFactoryImpl
+
+ } // namespace detail
+
+ template <class TProjection = IdentityProjection>
+ using GeoJSONFactory = GeometryFactory<osmium::geom::detail::GeoJSONFactoryImpl, TProjection>;
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_GEOJSON_HPP
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
new file mode 100644
index 0000000..3c73637
--- /dev/null
+++ b/include/osmium/geom/geos.hpp
@@ -0,0 +1,230 @@
+#ifndef OSMIUM_GEOM_GEOS_HPP
+#define OSMIUM_GEOM_GEOS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+/**
+ * @file
+ *
+ * This file contains code for conversion of OSM geometries into GDAL
+ * geometries.
+ *
+ * @attention If you include this file, you'll need to link with `libgeos`.
+ */
+
+#include <utility>
+
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/CoordinateSequence.h>
+#include <geos/geom/CoordinateSequenceFactory.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/LinearRing.h>
+#include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Point.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/PrecisionModel.h>
+#include <geos/util/GEOSException.h>
+
+#include <osmium/geom/factory.hpp>
+#include <osmium/geom/coordinates.hpp>
+
+// MSVC doesn't support throw_with_nested yet
+#ifdef _MSC_VER
+# define THROW throw
+#else
+# define THROW std::throw_with_nested
+#endif
+
+namespace osmium {
+
+ struct geos_geometry_error : public geometry_error {
+
+ geos_geometry_error() :
+ geometry_error("geometry creation failed in GEOS library, see nested exception for details") {
+ }
+
+ }; // struct geos_geometry_error
+
+ namespace geom {
+
+ namespace detail {
+
+ class GEOSFactoryImpl {
+
+ geos::geom::PrecisionModel m_precision_model;
+ geos::geom::GeometryFactory m_geos_factory;
+
+ std::unique_ptr<geos::geom::CoordinateSequence> m_coordinate_sequence;
+ std::vector<std::unique_ptr<geos::geom::LinearRing>> m_rings;
+ std::vector<std::unique_ptr<geos::geom::Polygon>> m_polygons;
+
+ public:
+
+ typedef std::unique_ptr<geos::geom::Point> point_type;
+ typedef std::unique_ptr<geos::geom::LineString> linestring_type;
+ typedef std::unique_ptr<geos::geom::Polygon> polygon_type;
+ typedef std::unique_ptr<geos::geom::MultiPolygon> multipolygon_type;
+ typedef std::unique_ptr<geos::geom::LinearRing> ring_type;
+
+ explicit GEOSFactoryImpl(int srid = -1) :
+ m_precision_model(),
+ m_geos_factory(&m_precision_model, srid) {
+ }
+
+ /* Point */
+
+ 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&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ /* LineString */
+
+ void linestring_start() {
+ try {
+ m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ 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&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ linestring_type linestring_finish(size_t /* num_points */) {
+ try {
+ return linestring_type(m_geos_factory.createLineString(m_coordinate_sequence.release()));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ /* MultiPolygon */
+
+ void multipolygon_start() {
+ m_polygons.clear();
+ }
+
+ void multipolygon_polygon_start() {
+ m_rings.clear();
+ }
+
+ void multipolygon_polygon_finish() {
+ try {
+ assert(!m_rings.empty());
+ auto inner_rings = new std::vector<geos::geom::Geometry*>;
+ std::transform(std::next(m_rings.begin(), 1), m_rings.end(), std::back_inserter(*inner_rings), [](std::unique_ptr<geos::geom::LinearRing>& r) {
+ return r.release();
+ });
+ m_polygons.emplace_back(m_geos_factory.createPolygon(m_rings[0].release(), inner_rings));
+ m_rings.clear();
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ void multipolygon_outer_ring_start() {
+ try {
+ m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ void multipolygon_outer_ring_finish() {
+ try {
+ m_rings.emplace_back(m_geos_factory.createLinearRing(m_coordinate_sequence.release()));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ void multipolygon_inner_ring_start() {
+ try {
+ m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ void multipolygon_inner_ring_finish() {
+ try {
+ m_rings.emplace_back(m_geos_factory.createLinearRing(m_coordinate_sequence.release()));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ 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&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ multipolygon_type multipolygon_finish() {
+ try {
+ auto polygons = new std::vector<geos::geom::Geometry*>;
+ std::transform(m_polygons.begin(), m_polygons.end(), std::back_inserter(*polygons), [](std::unique_ptr<geos::geom::Polygon>& p) {
+ return p.release();
+ });
+ m_polygons.clear();
+ return multipolygon_type(m_geos_factory.createMultiPolygon(polygons));
+ } catch (geos::util::GEOSException&) {
+ THROW(osmium::geos_geometry_error());
+ }
+ }
+
+ }; // class GEOSFactoryImpl
+
+ } // namespace detail
+
+ template <class TProjection = IdentityProjection>
+ using GEOSFactory = GeometryFactory<osmium::geom::detail::GEOSFactoryImpl, TProjection>;
+
+ } // namespace geom
+
+} // namespace osmium
+
+#undef THROW
+
+#endif // OSMIUM_GEOM_GEOS_HPP
diff --git a/include/osmium/geom/haversine.hpp b/include/osmium/geom/haversine.hpp
new file mode 100644
index 0000000..14e18c5
--- /dev/null
+++ b/include/osmium/geom/haversine.hpp
@@ -0,0 +1,94 @@
+#ifndef OSMIUM_GEOM_HAVERSINE_HPP
+#define OSMIUM_GEOM_HAVERSINE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cmath>
+#include <iterator>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/util.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/way.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ /**
+ * @brief Functions to calculate arc distance on Earth using the haversine formula.
+ *
+ * See http://en.wikipedia.org/wiki/Haversine_formula
+ *
+ * Implementation derived from
+ * http://blog.julien.cayzac.name/2008/10/arc-and-distance-between-two-points-on.html
+ */
+ namespace haversine {
+
+ /// @brief Earth's quadratic mean radius for WGS84
+ constexpr double EARTH_RADIUS_IN_METERS = 6372797.560856;
+
+ /**
+ * Calculate distance in meters between two sets of coordinates.
+ */
+ inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) {
+ double lonh = sin(deg_to_rad(c1.x - c2.x) * 0.5);
+ lonh *= lonh;
+ double lath = sin(deg_to_rad(c1.y - c2.y) * 0.5);
+ lath *= lath;
+ const double tmp = cos(deg_to_rad(c1.y)) * cos(deg_to_rad(c2.y));
+ return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(lath + tmp*lonh));
+ }
+
+ /**
+ * Calculate length of way.
+ */
+ inline double distance(const osmium::WayNodeList& wnl) {
+ double sum_length=0;
+
+ for (auto it = wnl.begin(); it != wnl.end(); ++it) {
+ if (std::next(it) != wnl.end()) {
+ sum_length += distance(it->location(), std::next(it)->location());
+ }
+ }
+
+ return sum_length;
+ }
+
+ } // namespace haversine
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_HAVERSINE_HPP
diff --git a/include/osmium/geom/mercator_projection.hpp b/include/osmium/geom/mercator_projection.hpp
new file mode 100644
index 0000000..a6d1d57
--- /dev/null
+++ b/include/osmium/geom/mercator_projection.hpp
@@ -0,0 +1,109 @@
+#ifndef OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
+#define OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cmath>
+#include <string>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/util.hpp>
+#include <osmium/osm/location.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ namespace detail {
+
+ constexpr double earth_radius_for_epsg3857 = 6378137.0;
+
+ constexpr inline double lon_to_x(double lon) {
+ return earth_radius_for_epsg3857 * deg_to_rad(lon);
+ }
+
+ inline double lat_to_y(double lat) { // not constexpr because math functions aren't
+ return earth_radius_for_epsg3857 * std::log(std::tan(osmium::geom::PI/4 + deg_to_rad(lat)/2));
+ }
+
+ constexpr inline double x_to_lon(double x) {
+ return rad_to_deg(x) / earth_radius_for_epsg3857;
+ }
+
+ inline double y_to_lat(double y) { // not constexpr because math functions aren't
+ return rad_to_deg(2 * std::atan(std::exp(y / earth_radius_for_epsg3857)) - osmium::geom::PI/2);
+ }
+
+ } // namespace detail
+
+ /**
+ * The maximum latitude that can be projected with the Web Mercator
+ * (EPSG:3857) projection.
+ */
+ constexpr double MERCATOR_MAX_LAT = 85.0511288;
+
+ inline Coordinates lonlat_to_mercator(const Coordinates& c) {
+ return Coordinates(detail::lon_to_x(c.x), detail::lat_to_y(c.y));
+ }
+
+ inline Coordinates mercator_to_lonlat(const Coordinates& c) {
+ return Coordinates(detail::x_to_lon(c.x), detail::y_to_lat(c.y));
+ }
+
+ /**
+ * Functor that does projection from WGS84 (EPSG:4326) to "Web
+ * Mercator" (EPSG:3857)
+ */
+ class MercatorProjection {
+
+ public:
+
+ Coordinates operator()(osmium::Location location) const {
+ return Coordinates {detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())};
+ }
+
+ int epsg() const noexcept {
+ return 3857;
+ }
+
+ std::string proj_string() const {
+ return "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs";
+ }
+
+ }; // class MercatorProjection
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp
new file mode 100644
index 0000000..f33971c
--- /dev/null
+++ b/include/osmium/geom/ogr.hpp
@@ -0,0 +1,206 @@
+#ifndef OSMIUM_GEOM_OGR_HPP
+#define OSMIUM_GEOM_OGR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+/**
+ * @file
+ *
+ * This file contains code for conversion of OSM geometries into OGR
+ * geometries.
+ *
+ * @attention If you include this file, you'll need to link with `libgdal`.
+ */
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <utility>
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4458)
+# pragma warning(disable : 4251)
+#else
+# pragma GCC diagnostic push
+# ifdef __clang__
+# pragma GCC diagnostic ignored "-Wdocumentation-unknown-command"
+# endif
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+# pragma GCC diagnostic ignored "-Wold-style-cast"
+# pragma GCC diagnostic ignored "-Wpadded"
+# pragma GCC diagnostic ignored "-Wredundant-decls"
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+/* Strictly speaking the following include would be enough here,
+ but everybody using this file will very likely need the other includes,
+ so we are adding them here, so that not everybody will need all those
+ pragmas to disable warnings. */
+//#include <ogr_geometry.h>
+#include <ogr_api.h>
+#include <ogrsf_frmts.h>
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#else
+# pragma GCC diagnostic pop
+#endif
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/factory.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ namespace detail {
+
+ class OGRFactoryImpl {
+
+ public:
+
+ typedef std::unique_ptr<OGRPoint> point_type;
+ typedef std::unique_ptr<OGRLineString> linestring_type;
+ typedef std::unique_ptr<OGRPolygon> polygon_type;
+ typedef std::unique_ptr<OGRMultiPolygon> multipolygon_type;
+ typedef std::unique_ptr<OGRLinearRing> ring_type;
+
+ private:
+
+ linestring_type m_linestring;
+ multipolygon_type m_multipolygon;
+ polygon_type m_polygon;
+ ring_type m_ring;
+
+ public:
+
+ OGRFactoryImpl() = default;
+
+ /* Point */
+
+ point_type make_point(const osmium::geom::Coordinates& xy) const {
+ return point_type(new OGRPoint(xy.x, xy.y));
+ }
+
+ /* LineString */
+
+ void linestring_start() {
+ m_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
+ }
+
+ void linestring_add_location(const osmium::geom::Coordinates& xy) {
+ assert(!!m_linestring);
+ m_linestring->addPoint(xy.x, xy.y);
+ }
+
+ linestring_type linestring_finish(size_t /* num_points */) {
+ return std::move(m_linestring);
+ }
+
+ /* Polygon */
+
+ void polygon_start() {
+ m_ring = std::unique_ptr<OGRLinearRing>(new OGRLinearRing());
+ }
+
+ void polygon_add_location(const osmium::geom::Coordinates& xy) {
+ assert(!!m_ring);
+ m_ring->addPoint(xy.x, xy.y);
+ }
+
+ polygon_type polygon_finish(size_t /* num_points */) {
+ std::unique_ptr<OGRPolygon> polygon = std::unique_ptr<OGRPolygon>(new OGRPolygon());
+ polygon->addRingDirectly(m_ring.release());
+ return polygon;
+ }
+
+ /* MultiPolygon */
+
+ void multipolygon_start() {
+ m_multipolygon.reset(new OGRMultiPolygon());
+ }
+
+ void multipolygon_polygon_start() {
+ m_polygon.reset(new OGRPolygon());
+ }
+
+ void multipolygon_polygon_finish() {
+ assert(!!m_multipolygon);
+ assert(!!m_polygon);
+ m_multipolygon->addGeometryDirectly(m_polygon.release());
+ }
+
+ void multipolygon_outer_ring_start() {
+ m_ring.reset(new OGRLinearRing());
+ }
+
+ void multipolygon_outer_ring_finish() {
+ assert(!!m_polygon);
+ assert(!!m_ring);
+ m_polygon->addRingDirectly(m_ring.release());
+ }
+
+ void multipolygon_inner_ring_start() {
+ m_ring.reset(new OGRLinearRing());
+ }
+
+ void multipolygon_inner_ring_finish() {
+ assert(!!m_polygon);
+ assert(!!m_ring);
+ m_polygon->addRingDirectly(m_ring.release());
+ }
+
+ void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
+ assert(!!m_polygon);
+ assert(!!m_ring);
+ m_ring->addPoint(xy.x, xy.y);
+ }
+
+ multipolygon_type multipolygon_finish() {
+ assert(!!m_multipolygon);
+ return std::move(m_multipolygon);
+ }
+
+ }; // class OGRFactoryImpl
+
+ } // namespace detail
+
+ template <class TProjection = IdentityProjection>
+ using OGRFactory = GeometryFactory<osmium::geom::detail::OGRFactoryImpl, TProjection>;
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_OGR_HPP
diff --git a/include/osmium/geom/projection.hpp b/include/osmium/geom/projection.hpp
new file mode 100644
index 0000000..6419101
--- /dev/null
+++ b/include/osmium/geom/projection.hpp
@@ -0,0 +1,167 @@
+#ifndef OSMIUM_GEOM_PROJECTION_HPP
+#define OSMIUM_GEOM_PROJECTION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+/**
+ * @file
+ *
+ * This file contains code for projecting OSM locations to arbitrary
+ * coordinate reference systems. It is based on the Proj.4 library.
+ *
+ * @attention If you include this file, you'll need to link with `libproj`.
+ */
+
+#include <memory>
+#include <string>
+
+#include <proj_api.h>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/util.hpp>
+#include <osmium/osm/location.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ /**
+ * C++ wrapper for a Coordinate Reference System of the proj library.
+ */
+ class CRS {
+
+ struct ProjCRSDeleter {
+ void operator()(void* crs) {
+ pj_free(crs);
+ }
+ }; // struct ProjCRSDeleter
+
+ std::unique_ptr<void, ProjCRSDeleter> m_crs;
+
+ projPJ get() const {
+ return m_crs.get();
+ }
+
+ public:
+
+ 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()));
+ }
+ }
+
+ CRS(int epsg) :
+ CRS(std::string("+init=epsg:") + std::to_string(epsg)) {
+ }
+
+ bool is_latlong() const {
+ return pj_is_latlong(m_crs.get()) != 0;
+ }
+
+ bool is_geocent() const {
+ return pj_is_geocent(m_crs.get()) != 0;
+ }
+
+ /**
+ * Transform coordinates from one CRS into another. Wraps the same function
+ * of the proj library.
+ *
+ * Coordinates have to be in radians and are produced in radians.
+ *
+ * @throws osmmium::projection_error if the projection fails
+ */
+ friend Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) {
+ int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr);
+ if (result != 0) {
+ throw osmium::projection_error(std::string("projection failed: ") + pj_strerrno(result));
+ }
+ return c;
+ }
+
+ }; // class CRS
+
+ /**
+ * Functor that does projection from WGS84 (EPSG:4326) to the given
+ * CRS.
+ */
+ class Projection {
+
+ int m_epsg;
+ std::string m_proj_string;
+ CRS m_crs_wgs84 {4326};
+ CRS m_crs_user;
+
+ public:
+
+ Projection(const std::string& proj_string) :
+ m_epsg(-1),
+ m_proj_string(proj_string),
+ m_crs_user(proj_string) {
+ }
+
+ Projection(int epsg) :
+ m_epsg(epsg),
+ m_proj_string(std::string("+init=epsg:") + std::to_string(epsg)),
+ m_crs_user(epsg) {
+ }
+
+ Coordinates operator()(osmium::Location location) const {
+ Coordinates c {location.lon(), location.lat()};
+
+ if (m_epsg != 4326) {
+ c = transform(m_crs_wgs84, m_crs_user, Coordinates(deg_to_rad(location.lon()), deg_to_rad(location.lat())));
+ if (m_crs_user.is_latlong()) {
+ c.x = rad_to_deg(c.x);
+ c.y = rad_to_deg(c.y);
+ }
+ }
+
+ return c;
+ }
+
+ int epsg() const noexcept {
+ return m_epsg;
+ }
+
+ std::string proj_string() const {
+ return m_proj_string;
+ }
+
+ }; // class Projection
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_PROJECTION_HPP
diff --git a/include/osmium/geom/relations.hpp b/include/osmium/geom/relations.hpp
new file mode 100644
index 0000000..e9e2aa4
--- /dev/null
+++ b/include/osmium/geom/relations.hpp
@@ -0,0 +1,57 @@
+#ifndef OSMIUM_GEOM_RELATIONS_HPP
+#define OSMIUM_GEOM_RELATIONS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/box.hpp>
+#include <osmium/osm/location.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ /**
+ * Check whether one geometry contains another.
+ */
+ inline bool contains(const osmium::Box& a, const osmium::Box& b) {
+ return ((a.bottom_left().x() >= b.bottom_left().x()) &&
+ (a.top_right().x() <= b.top_right().x()) &&
+ (a.bottom_left().y() >= b.bottom_left().y()) &&
+ (a.top_right().y() <= b.top_right().y()));
+ }
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_RELATIONS_HPP
diff --git a/include/osmium/geom/util.hpp b/include/osmium/geom/util.hpp
new file mode 100644
index 0000000..5e9f822
--- /dev/null
+++ b/include/osmium/geom/util.hpp
@@ -0,0 +1,75 @@
+#ifndef OSMIUM_GEOM_UTIL_HPP
+#define OSMIUM_GEOM_UTIL_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <stdexcept>
+#include <string>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when a projection object can not be initialized or the
+ * projection of some coordinates can not be calculated.
+ */
+ struct projection_error : public std::runtime_error {
+
+ projection_error(const std::string& what) :
+ std::runtime_error(what) {
+ }
+
+ projection_error(const char* what) :
+ std::runtime_error(what) {
+ }
+
+ }; // struct projection_error
+
+ namespace geom {
+
+ constexpr double PI = 3.14159265358979323846;
+
+ /// Convert angle from degrees to radians.
+ inline constexpr double deg_to_rad(double degree) noexcept {
+ return degree * (PI / 180.0);
+ }
+
+ /// Convert angle from radians to degrees.
+ inline constexpr double rad_to_deg(double radians) noexcept {
+ return radians * (180.0 / PI);
+ }
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_UTIL_HPP
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
new file mode 100644
index 0000000..31fce71
--- /dev/null
+++ b/include/osmium/geom/wkb.hpp
@@ -0,0 +1,277 @@
+#ifndef OSMIUM_GEOM_WKB_HPP
+#define OSMIUM_GEOM_WKB_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <cstdint>
+#include <string>
+
+// Windows is only available for little endian architectures
+// http://stackoverflow.com/questions/6449468/can-i-safely-assume-that-windows-installations-will-always-be-little-endian
+#if !defined(_WIN32) && !defined(__APPLE__)
+# include <endian.h>
+#else
+# define __LITTLE_ENDIAN 1234
+# define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/factory.hpp>
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ enum class wkb_type : bool {
+ wkb = false,
+ ewkb = true
+ }; // enum class wkb_type
+
+ enum class out_type : bool {
+ binary = false,
+ hex = true
+ }; // enum class out_type
+
+ namespace detail {
+
+ template <typename T>
+ inline void str_push(std::string& str, T data) {
+ size_t size = str.size();
+ str.resize(size + sizeof(T));
+ std::copy_n(reinterpret_cast<char*>(&data), sizeof(T), &str[size]);
+ }
+
+ inline std::string convert_to_hex(const std::string& str) {
+ static const char* lookup_hex = "0123456789ABCDEF";
+ std::string out;
+
+ for (char c : str) {
+ out += lookup_hex[(c >> 4) & 0xf];
+ out += lookup_hex[c & 0xf];
+ }
+
+ return out;
+ }
+
+ class WKBFactoryImpl {
+
+ /// OSM data always uses SRID 4326 (WGS84).
+ static constexpr uint32_t srid = 4326;
+
+ /**
+ * Type of WKB geometry.
+ * These definitions are from
+ * 99-049_OpenGIS_Simple_Features_Specification_For_SQL_Rev_1.1.pdf (for WKB)
+ * and http://trac.osgeo.org/postgis/browser/trunk/doc/ZMSgeoms.txt (for EWKB).
+ * They are used to encode geometries into the WKB format.
+ */
+ enum wkbGeometryType : uint32_t {
+ wkbPoint = 1,
+ wkbLineString = 2,
+ wkbPolygon = 3,
+ wkbMultiPoint = 4,
+ wkbMultiLineString = 5,
+ wkbMultiPolygon = 6,
+ wkbGeometryCollection = 7,
+
+ // SRID-presence flag (EWKB)
+ wkbSRID = 0x20000000
+ }; // enum wkbGeometryType
+
+ /**
+ * Byte order marker in WKB geometry.
+ */
+ enum class wkb_byte_order_type : uint8_t {
+ XDR = 0, // Big Endian
+ NDR = 1 // Little Endian
+ }; // enum class wkb_byte_order_type
+
+ std::string m_data;
+ uint32_t m_points {0};
+ wkb_type m_wkb_type;
+ out_type m_out_type;
+
+ size_t m_linestring_size_offset = 0;
+ size_t m_polygons = 0;
+ size_t m_rings = 0;
+ size_t m_multipolygon_size_offset = 0;
+ size_t m_polygon_size_offset = 0;
+ size_t m_ring_size_offset = 0;
+
+ size_t header(std::string& str, wkbGeometryType type, bool add_length) const {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ str_push(str, wkb_byte_order_type::NDR);
+#else
+ str_push(str, wkb_byte_order_type::XDR);
+#endif
+ if (m_wkb_type == wkb_type::ewkb) {
+ str_push(str, type | wkbSRID);
+ str_push(str, srid);
+ } else {
+ str_push(str, type);
+ }
+ size_t offset = str.size();
+ if (add_length) {
+ str_push(str, static_cast<uint32_t>(0));
+ }
+ return offset;
+ }
+
+ void set_size(const size_t offset, const size_t size) {
+ *reinterpret_cast<uint32_t*>(&m_data[offset]) = static_cast_with_assert<uint32_t>(size);
+ }
+
+ 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;
+
+ explicit WKBFactoryImpl(wkb_type wtype=wkb_type::wkb, out_type otype=out_type::binary) :
+ m_wkb_type(wtype),
+ m_out_type(otype) {
+ }
+
+ /* Point */
+
+ point_type make_point(const osmium::geom::Coordinates& xy) const {
+ std::string data;
+ header(data, wkbPoint, false);
+ str_push(data, xy.x);
+ str_push(data, xy.y);
+
+ if (m_out_type == out_type::hex) {
+ return convert_to_hex(data);
+ } else {
+ return data;
+ }
+ }
+
+ /* LineString */
+
+ void linestring_start() {
+ m_data.clear();
+ m_linestring_size_offset = header(m_data, wkbLineString, true);
+ }
+
+ void linestring_add_location(const osmium::geom::Coordinates& xy) {
+ str_push(m_data, xy.x);
+ str_push(m_data, xy.y);
+ }
+
+ linestring_type linestring_finish(size_t num_points) {
+ set_size(m_linestring_size_offset, num_points);
+ std::string data;
+ std::swap(data, m_data);
+
+ if (m_out_type == out_type::hex) {
+ return convert_to_hex(data);
+ } else {
+ return data;
+ }
+ }
+
+ /* MultiPolygon */
+
+ void multipolygon_start() {
+ m_data.clear();
+ m_polygons = 0;
+ m_multipolygon_size_offset = header(m_data, wkbMultiPolygon, true);
+ }
+
+ void multipolygon_polygon_start() {
+ ++m_polygons;
+ m_rings = 0;
+ m_polygon_size_offset = header(m_data, wkbPolygon, true);
+ }
+
+ void multipolygon_polygon_finish() {
+ set_size(m_polygon_size_offset, m_rings);
+ }
+
+ void multipolygon_outer_ring_start() {
+ ++m_rings;
+ m_points = 0;
+ m_ring_size_offset = m_data.size();
+ str_push(m_data, static_cast<uint32_t>(0));
+ }
+
+ void multipolygon_outer_ring_finish() {
+ set_size(m_ring_size_offset, m_points);
+ }
+
+ void multipolygon_inner_ring_start() {
+ ++m_rings;
+ m_points = 0;
+ m_ring_size_offset = m_data.size();
+ str_push(m_data, static_cast<uint32_t>(0));
+ }
+
+ void multipolygon_inner_ring_finish() {
+ set_size(m_ring_size_offset, m_points);
+ }
+
+ void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
+ str_push(m_data, xy.x);
+ str_push(m_data, xy.y);
+ ++m_points;
+ }
+
+ multipolygon_type multipolygon_finish() {
+ set_size(m_multipolygon_size_offset, m_polygons);
+ std::string data;
+ std::swap(data, m_data);
+
+ if (m_out_type == out_type::hex) {
+ return convert_to_hex(data);
+ } else {
+ return data;
+ }
+ }
+
+ }; // class WKBFactoryImpl
+
+ } // namespace detail
+
+ template <class TProjection = IdentityProjection>
+ using WKBFactory = GeometryFactory<osmium::geom::detail::WKBFactoryImpl, TProjection>;
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_WKB_HPP
diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp
new file mode 100644
index 0000000..4fea96b
--- /dev/null
+++ b/include/osmium/geom/wkt.hpp
@@ -0,0 +1,150 @@
+#ifndef OSMIUM_GEOM_WKT_HPP
+#define OSMIUM_GEOM_WKT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstddef>
+#include <string>
+#include <utility>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/geom/factory.hpp>
+
+namespace osmium {
+
+ namespace geom {
+
+ namespace detail {
+
+ class WKTFactoryImpl {
+
+ std::string m_str;
+ int m_precision;
+
+ 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) {
+ }
+
+ /* Point */
+
+ point_type make_point(const osmium::geom::Coordinates& xy) const {
+ std::string str {"POINT"};
+ xy.append_to_string(str, '(', ' ', ')', m_precision);
+ return str;
+ }
+
+ /* LineString */
+
+ void linestring_start() {
+ m_str = "LINESTRING(";
+ }
+
+ void linestring_add_location(const osmium::geom::Coordinates& xy) {
+ xy.append_to_string(m_str, ' ', m_precision);
+ m_str += ',';
+ }
+
+ linestring_type linestring_finish(size_t /* num_points */) {
+ assert(!m_str.empty());
+ std::string str;
+ std::swap(str, m_str);
+ str.back() = ')';
+ return str;
+ }
+
+ /* MultiPolygon */
+
+ void multipolygon_start() {
+ m_str = "MULTIPOLYGON(";
+ }
+
+ void multipolygon_polygon_start() {
+ m_str += '(';
+ }
+
+ void multipolygon_polygon_finish() {
+ m_str += "),";
+ }
+
+ void multipolygon_outer_ring_start() {
+ m_str += '(';
+ }
+
+ void multipolygon_outer_ring_finish() {
+ assert(!m_str.empty());
+ m_str.back() = ')';
+ }
+
+ void multipolygon_inner_ring_start() {
+ m_str += ",(";
+ }
+
+ void multipolygon_inner_ring_finish() {
+ assert(!m_str.empty());
+ m_str.back() = ')';
+ }
+
+ void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
+ xy.append_to_string(m_str, ' ', m_precision);
+ m_str += ',';
+ }
+
+ multipolygon_type multipolygon_finish() {
+ assert(!m_str.empty());
+ std::string str;
+ std::swap(str, m_str);
+ str.back() = ')';
+ return str;
+ }
+
+ }; // class WKTFactoryImpl
+
+ } // namespace detail
+
+ template <class TProjection = IdentityProjection>
+ using WKTFactory = GeometryFactory<osmium::geom::detail::WKTFactoryImpl, TProjection>;
+
+ } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_WKT_HPP
diff --git a/include/osmium/handler.hpp b/include/osmium/handler.hpp
new file mode 100644
index 0000000..34d8785
--- /dev/null
+++ b/include/osmium/handler.hpp
@@ -0,0 +1,101 @@
+#ifndef OSMIUM_HANDLER_HPP
+#define OSMIUM_HANDLER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+namespace osmium {
+
+ class OSMObject;
+ class Node;
+ class Way;
+ class Relation;
+ class Area;
+ class Changeset;
+ class TagList;
+ class WayNodeList;
+ class RelationMemberList;
+ class OuterRing;
+ class InnerRing;
+
+ /**
+ * @brief Osmium handlers provide callbacks for OSM objects
+ */
+ namespace handler {
+
+ class Handler {
+
+ public:
+
+ void osm_object(const osmium::OSMObject&) const {
+ }
+
+ void node(const osmium::Node&) const {
+ }
+
+ void way(const osmium::Way&) const {
+ }
+
+ void relation(const osmium::Relation&) const {
+ }
+
+ void area(const osmium::Area&) const {
+ }
+
+ void changeset(const osmium::Changeset&) const {
+ }
+
+ void tag_list(const osmium::TagList&) const {
+ }
+
+ void way_node_list(const osmium::WayNodeList&) const {
+ }
+
+ void relation_member_list(const osmium::RelationMemberList&) const {
+ }
+
+ void outer_ring(const osmium::OuterRing&) const {
+ }
+
+ void inner_ring(const osmium::InnerRing&) const {
+ }
+
+ void flush() const {
+ }
+
+ }; // class Handler
+
+ } // namspace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_HPP
diff --git a/include/osmium/handler/chain.hpp b/include/osmium/handler/chain.hpp
new file mode 100644
index 0000000..1af3962
--- /dev/null
+++ b/include/osmium/handler/chain.hpp
@@ -0,0 +1,128 @@
+#ifndef OSMIUM_HANDLER_CHAIN_HPP
+#define OSMIUM_HANDLER_CHAIN_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <tuple>
+
+#include <osmium/handler.hpp>
+
+#define OSMIUM_CHAIN_HANDLER_CALL(_func_, _type_) \
+ template <int N, int SIZE, class THandlers> \
+ struct call_ ## _func_ { \
+ void operator()(THandlers& handlers, osmium::_type_& object) { \
+ std::get<N>(handlers)._func_(object); \
+ call_ ## _func_<N+1, SIZE, THandlers>()(handlers, object); \
+ } \
+ }; \
+ template <int SIZE, class THandlers> \
+ struct call_ ## _func_<SIZE, SIZE, THandlers> { \
+ void operator()(THandlers&, osmium::_type_&) {} \
+ };
+
+namespace osmium {
+
+ class Node;
+ class Way;
+ class Relation;
+ class Area;
+ class Changeset;
+
+ namespace handler {
+
+ /**
+ * This handler allows chaining of any number of handlers into a single
+ * handler.
+ */
+ template <class ...THandler>
+ class ChainHandler : public osmium::handler::Handler {
+
+ typedef std::tuple<THandler&...> handlers_type;
+ handlers_type m_handlers;
+
+ template <int N, int SIZE, class THandlers>
+ struct call_flush {
+ void operator()(THandlers& handlers) {
+ std::get<N>(handlers).flush();
+ call_flush<N+1, SIZE, THandlers>()(handlers);
+ }
+ }; // struct call_flush
+
+ template <int SIZE, class THandlers>
+ struct call_flush<SIZE, SIZE, THandlers> {
+ void operator()(THandlers&) {}
+ }; // struct call_flush
+
+ OSMIUM_CHAIN_HANDLER_CALL(node, Node)
+ OSMIUM_CHAIN_HANDLER_CALL(way, Way)
+ OSMIUM_CHAIN_HANDLER_CALL(relation, Relation)
+ OSMIUM_CHAIN_HANDLER_CALL(changeset, Changeset)
+ OSMIUM_CHAIN_HANDLER_CALL(area, Area)
+
+ public:
+
+ explicit ChainHandler(THandler&... handlers) :
+ m_handlers(handlers...) {
+ }
+
+ void node(osmium::Node& node) {
+ call_node<0, sizeof...(THandler), handlers_type>()(m_handlers, node);
+ }
+
+ void way(osmium::Way& way) {
+ call_way<0, sizeof...(THandler), handlers_type>()(m_handlers, way);
+ }
+
+ void relation(osmium::Relation& relation) {
+ call_relation<0, sizeof...(THandler), handlers_type>()(m_handlers, relation);
+ }
+
+ void changeset( osmium::Changeset& changeset) {
+ call_changeset<0, sizeof...(THandler), handlers_type>()(m_handlers, changeset);
+ }
+
+ void area(osmium::Area& area) {
+ call_area<0, sizeof...(THandler), handlers_type>()(m_handlers, area);
+ }
+
+ void flush() {
+ call_flush<0, sizeof...(THandler), handlers_type>()(m_handlers);
+ }
+
+ }; // class ChainHandler
+
+ } // namespace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_CHAIN_HPP
diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp
new file mode 100644
index 0000000..ccae596
--- /dev/null
+++ b/include/osmium/handler/disk_store.hpp
@@ -0,0 +1,111 @@
+#ifndef OSMIUM_HANDLER_DISK_STORE_HPP
+#define OSMIUM_HANDLER_DISK_STORE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+
+#include <osmium/handler.hpp>
+#include <osmium/index/map.hpp>
+#include <osmium/io/detail/read_write.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/item_iterator.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/visitor.hpp>
+
+namespace osmium {
+
+ namespace handler {
+
+ /**
+ *
+ * Note: This handler will only work if either all object IDs are
+ * positive or all object IDs are negative.
+ */
+ class DiskStore : public osmium::handler::Handler {
+
+ typedef osmium::index::map::Map<unsigned_object_id_type, size_t> offset_index_type;
+
+ size_t m_offset = 0;
+ int m_data_fd;
+
+ offset_index_type& m_node_index;
+ offset_index_type& m_way_index;
+ offset_index_type& m_relation_index;
+
+ public:
+
+ explicit DiskStore(int data_fd, offset_index_type& node_index, offset_index_type& way_index, offset_index_type& relation_index) :
+ m_data_fd(data_fd),
+ m_node_index(node_index),
+ m_way_index(way_index),
+ m_relation_index(relation_index) {
+ }
+
+ DiskStore(const DiskStore&) = delete;
+ DiskStore& operator=(const DiskStore&) = delete;
+
+ ~DiskStore() noexcept = default;
+
+ void node(const osmium::Node& node) {
+ m_node_index.set(node.positive_id(), m_offset);
+ m_offset += node.byte_size();
+ }
+
+ void way(const osmium::Way& way) {
+ m_way_index.set(way.positive_id(), m_offset);
+ m_offset += way.byte_size();
+ }
+
+ void relation(const osmium::Relation& relation) {
+ m_relation_index.set(relation.positive_id(), m_offset);
+ m_offset += relation.byte_size();
+ }
+
+ // XXX
+ void operator()(const osmium::memory::Buffer& buffer) {
+ osmium::io::detail::reliable_write(m_data_fd, buffer.data(), buffer.committed());
+
+ osmium::apply(buffer.begin(), buffer.end(), *this);
+ }
+
+ }; // class DiskStore
+
+ } // namespace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_DISK_STORE_HPP
diff --git a/include/osmium/handler/dump.hpp b/include/osmium/handler/dump.hpp
new file mode 100644
index 0000000..564035c
--- /dev/null
+++ b/include/osmium/handler/dump.hpp
@@ -0,0 +1,294 @@
+#ifndef OSMIUM_HANDLER_DUMP_HPP
+#define OSMIUM_HANDLER_DUMP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iomanip>
+#include <iostream>
+#include <string>
+
+#include <osmium/handler.hpp>
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/area.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/changeset.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/visitor.hpp>
+
+namespace osmium {
+
+ namespace handler {
+
+ class Dump : public osmium::handler::Handler {
+
+ std::ostream* m_out;
+ bool m_with_size;
+ std::string m_prefix;
+
+ void print_title(const char* title, const osmium::memory::Item& item) {
+ *m_out << m_prefix
+ << title
+ << ":";
+
+ if (m_with_size) {
+ *m_out << " ["
+ << item.byte_size()
+ << "]";
+ }
+
+ *m_out << "\n";
+ }
+
+ void print_meta(const osmium::OSMObject& object) {
+ *m_out << m_prefix
+ << " id="
+ << object.id()
+ << "\n";
+ *m_out << m_prefix
+ << " version="
+ << object.version()
+ << "\n";
+ *m_out << m_prefix
+ << " uid="
+ << object.uid()
+ << "\n";
+ *m_out << m_prefix
+ << " user=|"
+ << object.user()
+ << "|\n";
+ *m_out << m_prefix
+ << " changeset="
+ << object.changeset()
+ << "\n";
+ *m_out << m_prefix
+ << " timestamp="
+ << object.timestamp().to_iso()
+ << "\n";
+ *m_out << m_prefix
+ << " visible="
+ << (object.visible() ? "yes" : "no")
+ << "\n";
+
+ Dump dump(*m_out, m_with_size, m_prefix + " ");
+ osmium::apply(object.cbegin(), object.cend(), dump);
+ }
+
+ void print_location(const osmium::Node& node) {
+ const osmium::Location& location = node.location();
+
+ if (location) {
+ *m_out << m_prefix
+ << " lon="
+ << std::fixed
+ << std::setprecision(7)
+ << location.lon_without_check()
+ << "\n";
+ *m_out << m_prefix
+ << " lat="
+ << location.lat_without_check()
+ << "\n";
+ } else {
+ *m_out << m_prefix
+ << " lon=\n"
+ << m_prefix
+ << " lat=\n";
+ }
+ }
+
+ public:
+
+ explicit Dump(std::ostream& out, bool with_size=true, const std::string& prefix="") :
+ m_out(&out),
+ m_with_size(with_size),
+ m_prefix(prefix) {
+ }
+
+ void tag_list(const osmium::TagList& tags) {
+ print_title("TAGS", tags);
+ for (const auto& tag : tags) {
+ *m_out << m_prefix
+ << " k=|"
+ << tag.key()
+ << "| v=|"
+ << tag.value()
+ << "|"
+ << "\n";
+ }
+ }
+
+ void way_node_list(const osmium::WayNodeList& wnl) {
+ print_title("NODES", wnl);
+ for (const auto& node_ref : wnl) {
+ *m_out << m_prefix
+ << " ref="
+ << node_ref.ref();
+ if (node_ref.location()) {
+ *m_out << " pos="
+ << node_ref.location();
+ }
+ *m_out << "\n";
+ }
+ }
+
+ void relation_member_list(const osmium::RelationMemberList& rml) {
+ print_title("MEMBERS", rml);
+ for (const auto& member : rml) {
+ *m_out << m_prefix
+ << " type="
+ << item_type_to_name(member.type())
+ << " ref="
+ << member.ref()
+ << " role=|"
+ << member.role()
+ << "|\n";
+ if (member.full_member()) {
+ Dump dump(*m_out, m_with_size, m_prefix + " | ");
+ osmium::apply_item(member.get_object(), dump);
+ }
+ }
+ }
+
+ void outer_ring(const osmium::OuterRing& ring) {
+ print_title("OUTER RING", ring);
+ for (const auto& node_ref : ring) {
+ *m_out << m_prefix
+ << " ref="
+ << node_ref.ref();
+ if (node_ref.location()) {
+ *m_out << " pos="
+ << node_ref.location();
+ }
+ *m_out << "\n";
+ }
+ }
+
+ void inner_ring(const osmium::InnerRing& ring) {
+ print_title("INNER RING", ring);
+ for (const auto& node_ref : ring) {
+ *m_out << m_prefix
+ << " ref="
+ << node_ref.ref();
+ if (node_ref.location()) {
+ *m_out << " pos="
+ << node_ref.location();
+ }
+ *m_out << "\n";
+ }
+ }
+
+ void node(const osmium::Node& node) {
+ print_title("NODE", node);
+ print_meta(node);
+ print_location(node);
+ }
+
+ void way(const osmium::Way& way) {
+ print_title("WAY", way);
+ print_meta(way);
+ }
+
+ void relation(const osmium::Relation& relation) {
+ print_title("RELATION", relation);
+ print_meta(relation);
+ }
+
+ void area(const osmium::Area& area) {
+ print_title("AREA", area);
+ print_meta(area);
+ }
+
+ void changeset(const osmium::Changeset& changeset) {
+ print_title("CHANGESET", changeset);
+ *m_out << m_prefix
+ << " id="
+ << changeset.id()
+ << "\n";
+ *m_out << m_prefix
+ << " num_changes="
+ << changeset.num_changes()
+ << "\n";
+ *m_out << m_prefix
+ << " uid="
+ << changeset.uid()
+ << "\n";
+ *m_out << m_prefix
+ << " user=|"
+ << changeset.user()
+ << "|\n";
+ *m_out << m_prefix
+ << " created_at="
+ << changeset.created_at().to_iso()
+ << "\n";
+ *m_out << m_prefix
+ << " closed_at="
+ << changeset.closed_at().to_iso()
+ << "\n";
+ *m_out << m_prefix
+ << " bounds=";
+
+ if (changeset.bounds()) {
+ *m_out << '('
+ << changeset.bounds().bottom_left().lon_without_check()
+ << ','
+ << changeset.bounds().bottom_left().lat_without_check()
+ << ','
+ << changeset.bounds().top_right().lon_without_check()
+ << ','
+ << changeset.bounds().top_right().lat_without_check()
+ << ')';
+ } else {
+ *m_out << "(undefined)";
+ }
+
+ *m_out << "\n";
+
+ Dump dump(*m_out, m_with_size, m_prefix + " ");
+ osmium::apply(changeset.cbegin(), changeset.cend(), dump);
+ }
+
+ }; // class Dump
+
+ } // namespace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_DUMP_HPP
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
new file mode 100644
index 0000000..d1224a0
--- /dev/null
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -0,0 +1,182 @@
+#ifndef OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
+#define OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <type_traits>
+
+#include <osmium/handler.hpp>
+#include <osmium/index/index.hpp>
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
+
+#include <osmium/index/node_locations_map.hpp>
+
+namespace osmium {
+
+ namespace handler {
+
+ typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> dummy_type;
+
+ /**
+ * Handler to retrieve locations from nodes and add them to ways.
+ *
+ * @tparam TStoragePosIDs Class that handles the actual storage of the node locations
+ * (for positive IDs). It must support the set(id, value) and
+ * get(id) methods.
+ * @tparam TStorageNegIDs Same but for negative IDs.
+ */
+ template <class TStoragePosIDs, class TStorageNegIDs = dummy_type>
+ class NodeLocationsForWays : public osmium::handler::Handler {
+
+ static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStoragePosIDs>::value,
+ "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
+
+ static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStorageNegIDs>::value,
+ "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
+
+ public:
+
+ typedef TStoragePosIDs index_pos_type;
+ typedef TStorageNegIDs index_neg_type;
+
+ private:
+
+ /// Object that handles the actual storage of the node locations (with positive IDs).
+ TStoragePosIDs& m_storage_pos;
+
+ /// Object that handles the actual storage of the node locations (with negative IDs).
+ TStorageNegIDs& m_storage_neg;
+
+ bool m_ignore_errors {false};
+
+ bool m_must_sort {false};
+
+ // It is okay to have this static dummy instance, even when using several threads,
+ // because it is read-only.
+ static dummy_type& get_dummy() {
+ static dummy_type instance;
+ return instance;
+ }
+
+ public:
+
+ explicit NodeLocationsForWays(TStoragePosIDs& storage_pos,
+ TStorageNegIDs& storage_neg = get_dummy()) :
+ m_storage_pos(storage_pos),
+ m_storage_neg(storage_neg) {
+ }
+
+ NodeLocationsForWays(const NodeLocationsForWays&) = delete;
+ NodeLocationsForWays& operator=(const NodeLocationsForWays&) = delete;
+
+ NodeLocationsForWays(NodeLocationsForWays&&) = default;
+ NodeLocationsForWays& operator=(NodeLocationsForWays&&) = default;
+
+ ~NodeLocationsForWays() noexcept = default;
+
+ void ignore_errors() {
+ m_ignore_errors = true;
+ }
+
+ /**
+ * Store the location of the node in the storage.
+ */
+ void node(const osmium::Node& node) {
+ m_must_sort = true;
+ const osmium::object_id_type id = node.id();
+ if (id >= 0) {
+ m_storage_pos.set(static_cast<osmium::unsigned_object_id_type>( id), node.location());
+ } else {
+ m_storage_neg.set(static_cast<osmium::unsigned_object_id_type>(-id), node.location());
+ }
+ }
+
+ /**
+ * Get location of node with given id.
+ */
+ osmium::Location get_node_location(const osmium::object_id_type id) const {
+ if (id >= 0) {
+ return m_storage_pos.get(static_cast<osmium::unsigned_object_id_type>( id));
+ } else {
+ return m_storage_neg.get(static_cast<osmium::unsigned_object_id_type>(-id));
+ }
+ }
+
+ /**
+ * Retrieve locations of all nodes in the way from storage and add
+ * them to the way object.
+ */
+ void way(osmium::Way& way) {
+ if (m_must_sort) {
+ m_storage_pos.sort();
+ m_storage_neg.sort();
+ m_must_sort = false;
+ }
+ bool error = false;
+ for (auto& node_ref : way.nodes()) {
+ try {
+ node_ref.set_location(get_node_location(node_ref.ref()));
+ if (!node_ref.location()) {
+ error = true;
+ }
+ } catch (osmium::not_found&) {
+ error = true;
+ }
+ }
+ if (error && !m_ignore_errors) {
+ throw osmium::not_found("location for one or more nodes not found in node location index");
+ }
+ }
+
+ /**
+ * Call clear on the location indexes. Makes the
+ * NodeLocationsForWays handler unusable. Used to explicitly free
+ * memory if thats needed.
+ */
+ void clear() {
+ m_storage_pos.clear();
+ m_storage_neg.clear();
+ }
+
+ }; // class NodeLocationsForWays
+
+ } // namespace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp
new file mode 100644
index 0000000..dc4aa45
--- /dev/null
+++ b/include/osmium/handler/object_relations.hpp
@@ -0,0 +1,106 @@
+#ifndef OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
+#define OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/handler.hpp>
+#include <osmium/index/multimap.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
+
+namespace osmium {
+
+ namespace handler {
+
+ /**
+ *
+ * Note: This handler will only work if either all object IDs are
+ * positive or all object IDs are negative.
+ */
+ class ObjectRelations : public osmium::handler::Handler {
+
+ typedef osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type> index_type;
+
+ index_type& m_index_n2w;
+ index_type& m_index_n2r;
+ index_type& m_index_w2r;
+ index_type& m_index_r2r;
+
+ public:
+
+ explicit ObjectRelations(index_type& n2w, index_type& n2r, index_type& w2r, index_type& r2r) :
+ m_index_n2w(n2w),
+ m_index_n2r(n2r),
+ m_index_w2r(w2r),
+ m_index_r2r(r2r) {
+ }
+
+ ObjectRelations(const ObjectRelations&) = delete;
+ ObjectRelations& operator=(const ObjectRelations&) = delete;
+
+ ~ObjectRelations() noexcept = default;
+
+ void way(const osmium::Way& way) {
+ for (const auto& node_ref : way.nodes()) {
+ m_index_n2w.set(node_ref.positive_ref(), way.positive_id());
+ }
+ }
+
+ void relation(const osmium::Relation& relation) {
+ for (const auto& member : relation.members()) {
+ switch (member.type()) {
+ case osmium::item_type::node:
+ m_index_n2r.set(member.positive_ref(), relation.positive_id());
+ break;
+ case osmium::item_type::way:
+ m_index_w2r.set(member.positive_ref(), relation.positive_id());
+ break;
+ case osmium::item_type::relation:
+ m_index_r2r.set(member.positive_ref(), relation.positive_id());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ }; // class ObjectRelations
+
+ } // namespace handler
+
+} // namespace osmium
+
+#endif // OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
diff --git a/include/osmium/index/detail/create_map_with_fd.hpp b/include/osmium/index/detail/create_map_with_fd.hpp
new file mode 100644
index 0000000..29dc1dc
--- /dev/null
+++ b/include/osmium/index/detail/create_map_with_fd.hpp
@@ -0,0 +1,73 @@
+#ifndef OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
+#define OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cerrno>
+#include <cstring>
+#include <fcntl.h>
+#include <stdexcept>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace detail {
+
+ template <class T>
+ inline T* create_map_with_fd(const std::vector<std::string>& config) {
+ if (config.size() == 1) {
+ return new T();
+ } else {
+ assert(config.size() > 1);
+ const std::string& filename = config[1];
+ int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644);
+ if (fd == -1) {
+ throw std::runtime_error(std::string("can't open file '") + filename + "': " + strerror(errno));
+ }
+ return new T(fd);
+ }
+ }
+
+ } // namespace detail
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/index/detail/mmap_vector_anon.hpp
new file mode 100644
index 0000000..0ea4f9d
--- /dev/null
+++ b/include/osmium/index/detail/mmap_vector_anon.hpp
@@ -0,0 +1,78 @@
+#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifdef __linux__
+
+#include <cstddef>
+
+#include <osmium/index/detail/typed_mmap.hpp>
+#include <osmium/index/detail/mmap_vector_base.hpp>
+
+namespace osmium {
+
+ namespace detail {
+
+ /**
+ * This class looks and behaves like STL vector, but uses mmap internally.
+ */
+ template <typename T>
+ class mmap_vector_anon : public mmap_vector_base<T, mmap_vector_anon> {
+
+ public:
+
+ mmap_vector_anon() :
+ mmap_vector_base<T, osmium::detail::mmap_vector_anon>(
+ -1,
+ osmium::detail::mmap_vector_size_increment,
+ 0,
+ osmium::detail::typed_mmap<T>::map(osmium::detail::mmap_vector_size_increment)) {
+ }
+
+ void reserve(size_t new_capacity) {
+ if (new_capacity > this->capacity()) {
+ this->data(osmium::detail::typed_mmap<T>::remap(this->data(), this->capacity(), new_capacity));
+ this->m_capacity = new_capacity;
+ }
+ }
+
+ }; // class mmap_vector_anon
+
+ } // namespace detail
+
+} // namespace osmium
+
+#endif // __linux__
+
+#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp
new file mode 100644
index 0000000..3aff26d
--- /dev/null
+++ b/include/osmium/index/detail/mmap_vector_base.hpp
@@ -0,0 +1,184 @@
+#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
+#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <new>
+#include <stdexcept>
+
+#include <osmium/index/detail/typed_mmap.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ namespace detail {
+
+ constexpr size_t mmap_vector_size_increment = 1024 * 1024;
+
+ /**
+ * This is a base class for implementing classes that look like
+ * STL vector but use mmap internally. This class can not be used
+ * on it's own. Use the derived classes mmap_vector_anon or
+ * mmap_vector_file.
+ */
+ template <typename T, template <typename> class TDerived>
+ class mmap_vector_base {
+
+ protected:
+
+ int m_fd;
+ size_t m_capacity;
+ size_t m_size;
+ T* m_data;
+
+ explicit mmap_vector_base(int fd, size_t capacity, size_t size, T* data) noexcept :
+ m_fd(fd),
+ m_capacity(capacity),
+ m_size(size),
+ m_data(data) {
+ }
+
+ explicit mmap_vector_base(int fd, size_t capacity, size_t size) :
+ m_fd(fd),
+ m_capacity(capacity),
+ m_size(size),
+ m_data(osmium::detail::typed_mmap<T>::grow_and_map(capacity, m_fd)) {
+ }
+
+ void data(T* data) {
+ m_data = data;
+ }
+
+ public:
+
+ 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;
+
+ ~mmap_vector_base() {
+ osmium::detail::typed_mmap<T>::unmap(m_data, m_capacity);
+ }
+
+ size_t capacity() const noexcept {
+ return m_capacity;
+ }
+
+ size_t size() const noexcept {
+ return m_size;
+ }
+
+ bool empty() const noexcept {
+ return m_size == 0;
+ }
+
+ const T* data() const noexcept {
+ return m_data;
+ }
+
+ T* data() noexcept {
+ return m_data;
+ }
+
+ T& operator[](size_t n) {
+ return m_data[n];
+ }
+
+ T at(size_t n) const {
+ if (n >= m_size) {
+ throw std::out_of_range("out of range");
+ }
+ return m_data[n];
+ }
+
+ void clear() noexcept {
+ m_size = 0;
+ }
+
+ void shrink_to_fit() {
+ // XXX do something here
+ }
+
+ void push_back(const T& value) {
+ if (m_size >= m_capacity) {
+ resize(m_size+1);
+ }
+ m_data[m_size] = value;
+ ++m_size;
+ }
+
+ void resize(size_t new_size) {
+ if (new_size > capacity()) {
+ static_cast<TDerived<T>*>(this)->reserve(new_size + osmium::detail::mmap_vector_size_increment);
+ }
+ if (new_size > size()) {
+ new (data() + size()) T[new_size - size()];
+ }
+ m_size = new_size;
+ }
+
+ iterator begin() noexcept {
+ return m_data;
+ }
+
+ iterator end() noexcept {
+ return m_data + m_size;
+ }
+
+ const_iterator begin() const noexcept {
+ return m_data;
+ }
+
+ const_iterator end() const noexcept {
+ return m_data + m_size;
+ }
+
+ const_iterator cbegin() noexcept {
+ return m_data;
+ }
+
+ const_iterator cend() noexcept {
+ return m_data + m_size;
+ }
+
+ }; // class mmap_vector_base
+
+ } // namespace detail
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
new file mode 100644
index 0000000..55077d1
--- /dev/null
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -0,0 +1,85 @@
+#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
+#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+
+#include <osmium/index/detail/typed_mmap.hpp>
+#include <osmium/index/detail/mmap_vector_base.hpp>
+#include <osmium/index/detail/tmpfile.hpp>
+
+namespace osmium {
+
+ namespace detail {
+
+ /**
+ * This class looks and behaves like STL vector, but mmap's a file
+ * internally.
+ */
+ template <typename T>
+ class mmap_vector_file : public mmap_vector_base<T, mmap_vector_file> {
+
+ public:
+
+ explicit mmap_vector_file() :
+ mmap_vector_base<T, osmium::detail::mmap_vector_file>(
+ osmium::detail::create_tmp_file(),
+ osmium::detail::mmap_vector_size_increment,
+ 0) {
+ }
+
+ explicit mmap_vector_file(int fd) :
+ mmap_vector_base<T, osmium::detail::mmap_vector_file>(
+ fd,
+ osmium::detail::typed_mmap<T>::file_size(fd) == 0 ?
+ osmium::detail::mmap_vector_size_increment :
+ osmium::detail::typed_mmap<T>::file_size(fd),
+ osmium::detail::typed_mmap<T>::file_size(fd)) {
+ }
+
+ void reserve(size_t new_capacity) {
+ if (new_capacity > this->capacity()) {
+ typed_mmap<T>::unmap(this->data(), this->capacity());
+ this->data(typed_mmap<T>::grow_and_map(new_capacity, this->m_fd));
+ this->m_capacity = new_capacity;
+ }
+ }
+
+ }; // class mmap_vector_file
+
+ } // namespace detail
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
diff --git a/include/osmium/index/detail/tmpfile.hpp b/include/osmium/index/detail/tmpfile.hpp
new file mode 100644
index 0000000..06cab65
--- /dev/null
+++ b/include/osmium/index/detail/tmpfile.hpp
@@ -0,0 +1,62 @@
+#ifndef OSMIUM_INDEX_DETAIL_TMPFILE_HPP
+#define OSMIUM_INDEX_DETAIL_TMPFILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cerrno>
+#include <cstdio>
+#include <system_error>
+
+namespace osmium {
+
+ namespace detail {
+
+ /**
+ * Create and open a temporary file. It is removed after opening.
+ *
+ * @returns File descriptor of temporary file.
+ * @throws std::system_error if something went wrong.
+ */
+ inline int create_tmp_file() {
+ FILE* file = ::tmpfile();
+ if (!file) {
+ throw std::system_error(errno, std::system_category(), "tempfile failed");
+ }
+ return fileno(file);
+ }
+
+ } // namespace detail
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_TMPFILE_HPP
diff --git a/include/osmium/index/detail/typed_mmap.hpp b/include/osmium/index/detail/typed_mmap.hpp
new file mode 100644
index 0000000..77b065e
--- /dev/null
+++ b/include/osmium/index/detail/typed_mmap.hpp
@@ -0,0 +1,229 @@
+#ifndef OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
+#define OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cerrno>
+#include <cstddef>
+#include <stdexcept>
+#include <system_error>
+
+#include <sys/stat.h>
+
+#ifndef _WIN32
+# include <sys/mman.h>
+#else
+# include <mmap_for_windows.hpp>
+#endif
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#else
+# define ftruncate _chsize
+#endif
+
+// for bsd systems
+#ifndef MAP_ANONYMOUS
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Namespace for Osmium internal use
+ */
+ namespace detail {
+
+ /**
+ * This is a helper class for working with memory mapped files and
+ * anonymous shared memory. It wraps the necessary system calls
+ * adding:
+ * - error checking: all functions throw exceptions where needed
+ * - internal casts and size calculations allow use with user defined
+ * type T instead of void*
+ *
+ * This class only contains static functions. It should never be
+ * instantiated.
+ *
+ * @tparam T Type of objects we want to store.
+ */
+ template <typename T>
+ class typed_mmap {
+
+ public:
+
+ /**
+ * Create anonymous private memory mapping with enough space for size
+ * objects of type T.
+ *
+ * Note that no constructor is called for any of the objects in this memory!
+ *
+ * @param size Number of objects of type T that should fit into this memory
+ * @returns Pointer to mapped memory
+ * @throws std::system_error If mmap(2) failed
+ */
+ static T* map(size_t size) {
+ void* addr = ::mmap(nullptr, sizeof(T) * size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ if (addr == MAP_FAILED) {
+ throw std::system_error(errno, std::system_category(), "mmap failed");
+ }
+#pragma GCC diagnostic pop
+ return reinterpret_cast<T*>(addr);
+ }
+
+ /**
+ * Create shared memory mapping of a file with enough space for size
+ * objects of type T. The file must already have at least the
+ * required size.
+ *
+ * Note that no constructor is called for any of the objects in this memory!
+ *
+ * @param size Number of objects of type T that should fit into this memory
+ * @param fd File descriptor
+ * @param write True if data should be writable
+ * @returns Pointer to mapped memory
+ * @throws std::system_error If mmap(2) failed
+ */
+ static T* map(size_t size, int fd, bool write = false) {
+ int prot = PROT_READ;
+ if (write) {
+ prot |= PROT_WRITE;
+ }
+ void* addr = ::mmap(nullptr, sizeof(T) * size, prot, MAP_SHARED, fd, 0);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ if (addr == MAP_FAILED) {
+ throw std::system_error(errno, std::system_category(), "mmap failed");
+ }
+#pragma GCC diagnostic pop
+ return reinterpret_cast<T*>(addr);
+ }
+
+// mremap(2) is only available on linux systems
+#ifdef __linux__
+ /**
+ * Grow memory mapping created with map().
+ *
+ * Note that no constructor is called for any of the objects in this memory!
+ *
+ * @param data Pointer to current mapping (as returned by typed_mmap())
+ * @param old_size Number of objects currently stored in this memory
+ * @param new_size Number of objects we want to have space for
+ * @throws std::system_error If mremap(2) call failed
+ */
+ static T* remap(T* data, size_t old_size, size_t new_size) {
+ void* addr = ::mremap(reinterpret_cast<void*>(data), sizeof(T) * old_size, sizeof(T) * new_size, MREMAP_MAYMOVE);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ if (addr == MAP_FAILED) {
+ throw std::system_error(errno, std::system_category(), "mremap failed");
+ }
+#pragma GCC diagnostic pop
+ return reinterpret_cast<T*>(addr);
+ }
+#endif
+
+ /**
+ * Release memory from map() call.
+ *
+ * Note that no destructor is called for the objects in this memory!
+ *
+ * @param data Pointer to the data
+ * @param size Number of objects of type T stored
+ * @throws std::system_error If munmap(2) call failed
+ */
+ static void unmap(T* data, size_t size) {
+ if (::munmap(reinterpret_cast<void*>(data), sizeof(T) * size) != 0) {
+ throw std::system_error(errno, std::system_category(), "munmap failed");
+ }
+ }
+
+ /**
+ * Get number of objects of type T that would fit into a file.
+ *
+ * @param fd File descriptor
+ * @returns Number of objects of type T in this file
+ * @throws std::system_error If fstat(2) call failed
+ * @throws std::length_error If size of the file isn't a multiple of sizeof(T)
+ */
+ static size_t file_size(int fd) {
+ struct stat s;
+ if (fstat(fd, &s) < 0) {
+ throw std::system_error(errno, std::system_category(), "fstat failed");
+ }
+ if (static_cast<size_t>(s.st_size) % sizeof(T) != 0) {
+ throw std::length_error("file size has to be multiple of object size");
+ }
+ return static_cast<size_t>(s.st_size) / sizeof(T);
+ }
+
+ /**
+ * Grow file so there is enough space for at least new_size objects
+ * of type T. If the file is large enough already, nothing is done.
+ * The file is never shrunk.
+ *
+ * @param new_size Number of objects of type T that should fit into this file
+ * @param fd File descriptor
+ * @throws std::system_error If ftruncate(2) call failed
+ */
+ static void grow_file(size_t new_size, int fd) {
+ if (file_size(fd) < new_size) {
+ if (::ftruncate(fd, static_cast_with_assert<off_t>(sizeof(T) * new_size)) < 0) {
+ throw std::system_error(errno, std::system_category(), "ftruncate failed");
+ }
+ }
+ }
+
+ /**
+ * Grow file to given size (if it is smaller) and mmap it.
+ *
+ * @param size Number of objects of type T that should fit into this file
+ * @param fd File descriptor
+ * @throws Errors thrown by grow_file() or map()
+ */
+ static T* grow_and_map(size_t size, int fd) {
+ grow_file(size, fd);
+ return map(size, fd, true);
+ }
+
+ }; // class typed_mmap
+
+ } // namespace detail
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
new file mode 100644
index 0000000..73c5a37
--- /dev/null
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -0,0 +1,238 @@
+#ifndef OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
+#define OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <stdexcept>
+#include <utility>
+
+#include <osmium/index/index.hpp>
+#include <osmium/index/map.hpp>
+#include <osmium/io/detail/read_write.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <class TVector, typename TId, typename TValue>
+ class VectorBasedDenseMap : public Map<TId, TValue> {
+
+ TVector m_vector;
+
+ public:
+
+ typedef TValue element_type;
+ typedef TVector vector_type;
+ typedef typename vector_type::iterator iterator;
+ typedef typename vector_type::const_iterator const_iterator;
+
+ VectorBasedDenseMap() :
+ m_vector() {
+ }
+
+ explicit VectorBasedDenseMap(int fd) :
+ m_vector(fd) {
+ }
+
+ ~VectorBasedDenseMap() {}
+
+ void reserve(const size_t size) override final {
+ m_vector.reserve(size);
+ }
+
+ void set(const TId id, const TValue value) override final {
+ if (size() <= id) {
+ m_vector.resize(id+1);
+ }
+ m_vector[id] = value;
+ }
+
+ const TValue get(const TId id) const override final {
+ try {
+ const TValue& value = m_vector.at(id);
+ if (value == osmium::index::empty_value<TValue>()) {
+ not_found_error(id);
+ }
+ return value;
+ } catch (std::out_of_range&) {
+ not_found_error(id);
+ }
+ }
+
+ size_t size() const override final {
+ return m_vector.size();
+ }
+
+ size_t used_memory() const override final {
+ return sizeof(TValue) * size();
+ }
+
+ void clear() override final {
+ m_vector.clear();
+ m_vector.shrink_to_fit();
+ }
+
+ iterator begin() {
+ return m_vector.begin();
+ }
+
+ iterator end() {
+ return m_vector.end();
+ }
+
+ const_iterator cbegin() const {
+ return m_vector.cbegin();
+ }
+
+ const_iterator cend() const {
+ return m_vector.cend();
+ }
+
+ const_iterator begin() const {
+ return m_vector.cbegin();
+ }
+
+ const_iterator end() const {
+ return m_vector.cend();
+ }
+
+ }; // class VectorBasedDenseMap
+
+
+ template <typename TId, typename TValue, template<typename...> class TVector>
+ class VectorBasedSparseMap : public Map<TId, TValue> {
+
+ public:
+
+ typedef typename std::pair<TId, TValue> element_type;
+ typedef TVector<element_type> vector_type;
+ typedef typename vector_type::iterator iterator;
+ typedef typename vector_type::const_iterator const_iterator;
+
+ private:
+
+ vector_type m_vector;
+
+ public:
+
+ VectorBasedSparseMap() :
+ m_vector() {
+ }
+
+ VectorBasedSparseMap(int fd) :
+ m_vector(fd) {
+ }
+
+ ~VectorBasedSparseMap() override final = default;
+
+ void set(const TId id, const TValue value) override final {
+ m_vector.push_back(element_type(id, value));
+ }
+
+ const TValue get(const TId id) const override final {
+ const element_type element {
+ id,
+ osmium::index::empty_value<TValue>()
+ };
+ const auto result = std::lower_bound(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
+ return a.first < b.first;
+ });
+ if (result == m_vector.end() || result->first != id) {
+ not_found_error(id);
+ } else {
+ return result->second;
+ }
+ }
+
+ size_t size() const override final {
+ return m_vector.size();
+ }
+
+ size_t byte_size() const {
+ return m_vector.size() * sizeof(element_type);
+ }
+
+ size_t used_memory() const override final {
+ return sizeof(element_type) * size();
+ }
+
+ void clear() override final {
+ m_vector.clear();
+ m_vector.shrink_to_fit();
+ }
+
+ void sort() override final {
+ std::sort(m_vector.begin(), m_vector.end());
+ }
+
+ void dump_as_list(const int fd) override final {
+ osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
+ }
+
+ iterator begin() {
+ return m_vector.begin();
+ }
+
+ iterator end() {
+ return m_vector.end();
+ }
+
+ const_iterator cbegin() const {
+ return m_vector.cbegin();
+ }
+
+ const_iterator cend() const {
+ return m_vector.cend();
+ }
+
+ const_iterator begin() const {
+ return m_vector.cbegin();
+ }
+
+ const_iterator end() const {
+ return m_vector.cend();
+ }
+
+ }; // class VectorBasedSparseMap
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp
new file mode 100644
index 0000000..c2b2e1f
--- /dev/null
+++ b/include/osmium/index/detail/vector_multimap.hpp
@@ -0,0 +1,152 @@
+#ifndef OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
+#define OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include <osmium/index/index.hpp>
+#include <osmium/index/multimap.hpp>
+#include <osmium/io/detail/read_write.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ template <typename TId, typename TValue, template<typename...> class TVector>
+ class VectorBasedSparseMultimap : public Multimap<TId, TValue> {
+
+ public:
+
+ typedef typename std::pair<TId, TValue> element_type;
+ typedef TVector<element_type> vector_type;
+ typedef typename vector_type::iterator iterator;
+ typedef typename vector_type::const_iterator const_iterator;
+
+ private:
+
+ vector_type m_vector;
+
+ static bool is_removed(element_type& element) {
+ return element.second == osmium::index::empty_value<TValue>();
+ }
+
+ public:
+
+ void set(const TId id, const TValue value) override final {
+ m_vector.push_back(element_type(id, value));
+ }
+
+ void unsorted_set(const TId id, const TValue value) {
+ m_vector.push_back(element_type(id, value));
+ }
+
+ std::pair<iterator, iterator> get_all(const TId id) {
+ const element_type element {
+ id,
+ osmium::index::empty_value<TValue>()
+ };
+ return std::equal_range(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
+ return a.first < b.first;
+ });
+ }
+
+ std::pair<const_iterator, const_iterator> get_all(const TId id) const {
+ const element_type element {
+ id,
+ osmium::index::empty_value<TValue>()
+ };
+ return std::equal_range(m_vector.cbegin(), m_vector.cend(), element, [](const element_type& a, const element_type& b) {
+ return a.first < b.first;
+ });
+ }
+
+ size_t size() const override final {
+ return m_vector.size();
+ }
+
+ size_t byte_size() const {
+ return m_vector.size() * sizeof(element_type);
+ }
+
+ size_t used_memory() const override final {
+ return sizeof(element_type) * size();
+ }
+
+ void clear() override final {
+ m_vector.clear();
+ m_vector.shrink_to_fit();
+ }
+
+ void sort() override final {
+ std::sort(m_vector.begin(), m_vector.end());
+ }
+
+ void remove(const TId id, const TValue value) {
+ auto r = get_all(id);
+ for (auto it = r.first; it != r.second; ++it) {
+ if (it->second == value) {
+ it->second = 0;
+ return;
+ }
+ }
+ }
+
+ void consolidate() {
+ std::sort(m_vector.begin(), m_vector.end());
+ }
+
+ void erase_removed() {
+ m_vector.erase(
+ std::remove_if(m_vector.begin(), m_vector.end(), is_removed),
+ m_vector.end()
+ );
+ }
+
+ void dump_as_list(const int fd) override final {
+ osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
+ }
+
+ }; // class VectorBasedSparseMultimap
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
diff --git a/include/osmium/index/index.hpp b/include/osmium/index/index.hpp
new file mode 100644
index 0000000..b73b319
--- /dev/null
+++ b/include/osmium/index/index.hpp
@@ -0,0 +1,100 @@
+#ifndef OSMIUM_INDEX_INDEX_HPP
+#define OSMIUM_INDEX_INDEX_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <limits>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception signaling that an element could not be
+ * found in an index.
+ */
+ struct not_found : public std::runtime_error {
+
+ not_found(const std::string& what) :
+ std::runtime_error(what) {
+ }
+
+ not_found(const char* what) :
+ std::runtime_error(what) {
+ }
+
+ }; // struct not_found
+
+ /**
+ * @brief Indexing of OSM data, Locations, etc.
+ */
+ namespace index {
+
+ template <typename TKey>
+ OSMIUM_NORETURN void not_found_error(TKey key) {
+ std::stringstream s;
+ s << "id " << key << " no found";
+ throw not_found(s.str());
+ }
+
+ /**
+ * Some of the index classes need an "empty" value that can
+ * never appear in real data. This function must return this
+ * empty value for any class used as a value in an index.
+ * The default implementation returns a default constructed
+ * object, but it can be specialized.
+ */
+ template <typename T>
+ inline constexpr T empty_value() {
+ return T{};
+ }
+
+ /**
+ * The size_t value in indexes is usually used for offsets
+ * into a buffer or file. It is unlikely that we ever need
+ * the full range, so the max value is a good "empty" value.
+ */
+ template <>
+ inline OSMIUM_CONSTEXPR size_t empty_value<size_t>() {
+ return std::numeric_limits<size_t>::max();
+ }
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_INDEX_HPP
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
new file mode 100644
index 0000000..a9fe0c3
--- /dev/null
+++ b/include/osmium/index/map.hpp
@@ -0,0 +1,255 @@
+#ifndef OSMIUM_INDEX_MAP_HPP
+#define OSMIUM_INDEX_MAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <functional>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <osmium/util/compatibility.hpp>
+#include <osmium/util/string.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ /**
+ * @brief Key-value containers with unique integer values for a key
+ */
+ namespace map {
+
+ /**
+ * This abstract class defines an interface to storage classes
+ * intended for storing small pieces of data (such as coordinates)
+ * indexed by a positive integer (such as an object ID). The
+ * storage must be very space efficient and able to scale to billions
+ * of objects.
+ *
+ * Subclasses have different implementations that store the
+ * data in different ways in memory and/or on disk. Some storage
+ * classes are better suited when working with the whole planet,
+ * some are better for data extracts.
+ *
+ * Note that these classes are not required to track "empty" fields.
+ * When reading data you have to be sure you have put something in
+ * there before.
+ *
+ * A typical use for this and derived classes is storage of node
+ * locations indexed by node ID. These indexes will only work
+ * on 64 bit systems if used in this case. 32 bit systems just
+ * can't address that much memory!
+ *
+ * @tparam TId Id type, usually osmium::unsigned_object_id_type,
+ * must be an unsigned integral type.
+ * @tparam TValue Value type, usually osmium::Location or size_t.
+ * Copied by value, so should be "small" type.
+ */
+ template <typename TId, typename TValue>
+ class Map {
+
+ static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value,
+ "TId template parameter for class Map must be unsigned integral type");
+
+ Map(const Map&) = delete;
+ Map& operator=(const Map&) = delete;
+
+ protected:
+
+ Map(Map&&) = default;
+ Map& operator=(Map&&) = default;
+
+ public:
+
+ /// The "key" type, usually osmium::unsigned_object_id_type.
+ typedef TId key_type;
+
+ /// The "value" type, usually a Location or size_t.
+ typedef TValue value_type;
+
+ Map() = default;
+
+ virtual ~Map() = default;
+
+ virtual void reserve(const size_t) {
+ // default implementation is empty
+ }
+
+ /// Set the field with id to value.
+ virtual void set(const TId id, const TValue value) = 0;
+
+ /// Retrieve value by id. Does not check for overflow or empty fields.
+ virtual const TValue get(const TId id) const = 0;
+
+ /**
+ * Get the approximate number of items in the storage. The storage
+ * might allocate memory in blocks, so this size might not be
+ * accurate. You can not use this to find out how much memory the
+ * storage uses. Use used_memory() for that.
+ */
+ virtual size_t size() const = 0;
+
+ /**
+ * Get the memory used for this storage in bytes. Note that this
+ * is not necessarily entirely accurate but an approximation.
+ * For storage classes that store the data in memory, this is
+ * the main memory used, for storage classes storing data on disk
+ * this is the memory used on disk.
+ */
+ virtual size_t used_memory() const = 0;
+
+ /**
+ * Clear memory used for this storage. After this you can not
+ * use the storage container any more.
+ */
+ virtual void clear() = 0;
+
+ /**
+ * Sort data in map. Call this after writing all data and
+ * before reading. Not all implementations need this.
+ */
+ virtual void sort() {
+ // default implementation is empty
+ }
+
+ virtual void dump_as_list(const int /*fd*/) {
+ std::runtime_error("can't dump as list");
+ }
+
+ }; // class Map
+
+ } // namespace map
+
+ template <typename TId, typename TValue>
+ class MapFactory {
+
+ public:
+
+ typedef TId id_type;
+ typedef TValue value_type;
+ typedef osmium::index::map::Map<id_type, value_type> map_type;
+ typedef std::function<map_type*(const std::vector<std::string>&)> create_map_func;
+
+ private:
+
+ std::map<const std::string, create_map_func> m_callbacks;
+
+ MapFactory() = default;
+
+ MapFactory(const MapFactory&) = delete;
+ MapFactory& operator=(const MapFactory&) = delete;
+
+ MapFactory(MapFactory&&) = delete;
+ MapFactory& operator=(MapFactory&&) = delete;
+
+ OSMIUM_NORETURN static void error(const std::string& map_type_name) {
+ std::string error_message {"Support for map type '"};
+ error_message += map_type_name;
+ error_message += "' not compiled into this binary.";
+ throw std::runtime_error(error_message);
+ }
+
+ public:
+
+ static MapFactory<id_type, value_type>& instance() {
+ static MapFactory<id_type, value_type> factory;
+ return factory;
+ }
+
+ bool register_map(const std::string& map_type_name, create_map_func func) {
+ return m_callbacks.emplace(map_type_name, func).second;
+ }
+
+ std::vector<std::string> map_types() const {
+ std::vector<std::string> result;
+
+ for (const auto& cb : m_callbacks) {
+ result.push_back(cb.first);
+ }
+
+ std::sort(result.begin(), result.end());
+
+ return result;
+ }
+
+ std::unique_ptr<map_type> create_map(const std::string& config_string) const {
+ std::vector<std::string> config = osmium::split_string(config_string, ',');
+
+ if (config.empty()) {
+ throw std::runtime_error("Need non-empty map type name.");
+ }
+
+ auto it = m_callbacks.find(config[0]);
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<map_type>((it->second)(config));
+ }
+
+ error(config[0]);
+ }
+
+ }; // class MapFactory
+
+ namespace map {
+
+ template <typename TId, typename TValue, template<typename, typename> class TMap>
+ struct create_map {
+ TMap<TId, TValue>* operator()(const std::vector<std::string>&) {
+ return new TMap<TId, TValue>();
+ }
+ };
+
+ } // namespace map
+
+ template <typename TId, typename TValue, template<typename, typename> class TMap>
+ inline bool register_map(const std::string& name) {
+ return osmium::index::MapFactory<TId, TValue>::instance().register_map(name, [](const std::vector<std::string>& config) {
+ return map::create_map<TId, TValue, TMap>()(config);
+ });
+ }
+
+#define REGISTER_MAP(id, value, klass, name) \
+namespace { \
+ const bool registered_index_map_##name = osmium::index::register_map<id, value, klass>(#name); \
+}
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_HPP
diff --git a/include/osmium/index/map/all.hpp b/include/osmium/index/map/all.hpp
new file mode 100644
index 0000000..9ffadc0
--- /dev/null
+++ b/include/osmium/index/map/all.hpp
@@ -0,0 +1,46 @@
+#ifndef OSMIUM_INDEX_MAP_ALL_HPP
+#define OSMIUM_INDEX_MAP_ALL_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/index/map/dense_file_array.hpp> // IWYU pragma: keep
+#include <osmium/index/map/dense_mem_array.hpp> // IWYU pragma: keep
+#include <osmium/index/map/dense_mmap_array.hpp> // IWYU pragma: keep
+#include <osmium/index/map/dummy.hpp> // IWYU pragma: keep
+#include <osmium/index/map/sparse_file_array.hpp> // IWYU pragma: keep
+#include <osmium/index/map/sparse_mem_array.hpp> // IWYU pragma: keep
+#include <osmium/index/map/sparse_mem_map.hpp> // IWYU pragma: keep
+#include <osmium/index/map/sparse_mem_table.hpp> // IWYU pragma: keep
+#include <osmium/index/map/sparse_mmap_array.hpp> // IWYU pragma: keep
+
+#endif // OSMIUM_INDEX_MAP_ALL_HPP
diff --git a/include/osmium/index/map/dense_file_array.hpp b/include/osmium/index/map/dense_file_array.hpp
new file mode 100644
index 0000000..d209a87
--- /dev/null
+++ b/include/osmium/index/map/dense_file_array.hpp
@@ -0,0 +1,67 @@
+#ifndef OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <string>
+#include <vector>
+
+#include <osmium/index/detail/mmap_vector_file.hpp>
+#include <osmium/index/detail/vector_map.hpp>
+#include <osmium/index/detail/create_map_with_fd.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_DENSE_FILE_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename TId, typename TValue>
+ using DenseFileArray = VectorBasedDenseMap<osmium::detail::mmap_vector_file<TValue>, TId, TValue>;
+
+ template <typename TId, typename TValue>
+ struct create_map<TId, TValue, DenseFileArray> {
+ DenseFileArray<TId, TValue>* operator()(const std::vector<std::string>& config) {
+ return osmium::index::detail::create_map_with_fd<DenseFileArray<TId, TValue>>(config);
+ }
+ };
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
diff --git a/include/osmium/index/map/dense_mem_array.hpp b/include/osmium/index/map/dense_mem_array.hpp
new file mode 100644
index 0000000..b45eec4
--- /dev/null
+++ b/include/osmium/index/map/dense_mem_array.hpp
@@ -0,0 +1,57 @@
+#ifndef OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <vector>
+
+#include <osmium/index/detail/vector_map.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_DENSE_MEM_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename TId, typename TValue>
+ using DenseMemArray = VectorBasedDenseMap<std::vector<TValue>, TId, TValue>;
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
diff --git a/include/osmium/index/map/dense_mmap_array.hpp b/include/osmium/index/map/dense_mmap_array.hpp
new file mode 100644
index 0000000..fc60a1e
--- /dev/null
+++ b/include/osmium/index/map/dense_mmap_array.hpp
@@ -0,0 +1,60 @@
+#ifndef OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifdef __linux__
+
+#include <osmium/index/detail/mmap_vector_anon.hpp>
+#include <osmium/index/detail/vector_map.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_DENSE_MMAP_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename TId, typename TValue>
+ using DenseMmapArray = VectorBasedDenseMap<osmium::detail::mmap_vector_anon<TValue>, TId, TValue>;
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // __linux__
+
+#endif // OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
diff --git a/include/osmium/index/map/dummy.hpp b/include/osmium/index/map/dummy.hpp
new file mode 100644
index 0000000..de05d1d
--- /dev/null
+++ b/include/osmium/index/map/dummy.hpp
@@ -0,0 +1,88 @@
+#ifndef OSMIUM_INDEX_MAP_DUMMY_HPP
+#define OSMIUM_INDEX_MAP_DUMMY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+
+#include <osmium/index/index.hpp>
+#include <osmium/index/map.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ /**
+ * Pseudo map.
+ * Use this class if you don't need a map, but you
+ * need an object that behaves like one.
+ */
+ template <typename TId, typename TValue>
+ class Dummy : public osmium::index::map::Map<TId, TValue> {
+
+ public:
+
+ Dummy() = default;
+
+ ~Dummy() override final = default;
+
+ void set(const TId, const TValue) override final {
+ // intentionally left blank
+ }
+
+ const TValue get(const TId id) const override final {
+ not_found_error(id);
+ }
+
+ size_t size() const override final {
+ return 0;
+ }
+
+ size_t used_memory() const override final {
+ return 0;
+ }
+
+ void clear() override final {
+ }
+
+ }; // class Dummy
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_DUMMY_HPP
diff --git a/include/osmium/index/map/sparse_file_array.hpp b/include/osmium/index/map/sparse_file_array.hpp
new file mode 100644
index 0000000..2ba9315
--- /dev/null
+++ b/include/osmium/index/map/sparse_file_array.hpp
@@ -0,0 +1,67 @@
+#ifndef OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <string>
+#include <vector>
+
+#include <osmium/index/detail/mmap_vector_file.hpp>
+#include <osmium/index/detail/vector_map.hpp>
+#include <osmium/index/detail/create_map_with_fd.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_SPARSE_FILE_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename TId, typename TValue>
+ using SparseFileArray = VectorBasedSparseMap<TId, TValue, osmium::detail::mmap_vector_file>;
+
+ template <typename TId, typename TValue>
+ struct create_map<TId, TValue, SparseFileArray> {
+ SparseFileArray<TId, TValue>* operator()(const std::vector<std::string>& config) {
+ return osmium::index::detail::create_map_with_fd<SparseFileArray<TId, TValue>>(config);
+ }
+ };
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
diff --git a/include/osmium/index/map/sparse_mem_array.hpp b/include/osmium/index/map/sparse_mem_array.hpp
new file mode 100644
index 0000000..9adf41f
--- /dev/null
+++ b/include/osmium/index/map/sparse_mem_array.hpp
@@ -0,0 +1,60 @@
+#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <vector>
+
+#include <osmium/index/detail/vector_map.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename T>
+ using StdVectorWrap = std::vector<T>;
+
+ template <typename TId, typename TValue>
+ using SparseMemArray = VectorBasedSparseMap<TId, TValue, StdVectorWrap>;
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp
new file mode 100644
index 0000000..d053155
--- /dev/null
+++ b/include/osmium/index/map/sparse_mem_map.hpp
@@ -0,0 +1,115 @@
+#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
+#define OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <map>
+#include <stdexcept>
+#include <vector>
+
+#include <osmium/index/map.hpp>
+#include <osmium/index/index.hpp>
+#include <osmium/io/detail/read_write.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_MAP
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ /**
+ * This implementation uses std::map internally. It uses rather a
+ * lot of memory, but might make sense for small maps.
+ */
+ template <typename TId, typename TValue>
+ class SparseMemMap : public osmium::index::map::Map<TId, TValue> {
+
+ // This is a rough estimate for the memory needed for each
+ // element in the map (id + value + pointers to left, right,
+ // and parent plus some overhead for color of red-black-tree
+ // or similar).
+ static constexpr size_t element_size = sizeof(TId) + sizeof(TValue) + sizeof(void*) * 4;
+
+ std::map<TId, TValue> m_elements;
+
+ public:
+
+ SparseMemMap() = default;
+
+ ~SparseMemMap() override final = default;
+
+ void set(const TId id, const TValue value) override final {
+ m_elements[id] = value;
+ }
+
+ const TValue get(const TId id) const override final {
+ try {
+ return m_elements.at(id);
+ } catch (std::out_of_range&) {
+ not_found_error(id);
+ }
+ }
+
+ size_t size() const override final {
+ return m_elements.size();
+ }
+
+ size_t used_memory() const override final {
+ return element_size * m_elements.size();
+ }
+
+ void clear() override final {
+ m_elements.clear();
+ }
+
+ void dump_as_list(const int fd) override final {
+ typedef typename std::map<TId, TValue>::value_type t;
+ std::vector<t> v;
+ std::copy(m_elements.begin(), m_elements.end(), std::back_inserter(v));
+ osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(t) * v.size());
+ }
+
+ }; // class SparseMemMap
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
diff --git a/include/osmium/index/map/sparse_mem_table.hpp b/include/osmium/index/map/sparse_mem_table.hpp
new file mode 100644
index 0000000..8998a91
--- /dev/null
+++ b/include/osmium/index/map/sparse_mem_table.hpp
@@ -0,0 +1,146 @@
+#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP
+#define OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifdef OSMIUM_WITH_SPARSEHASH
+
+#include <cstddef>
+#include <utility>
+#include <vector>
+
+#include <google/sparsetable>
+
+#include <osmium/index/index.hpp>
+#include <osmium/index/map.hpp>
+#include <osmium/io/detail/read_write.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_TABLE
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ /**
+ * The SparseMemTable index stores elements in a Google sparsetable,
+ * a data structure that can hold sparsly filled tables in a
+ * space efficient way. It will resize automatically.
+ *
+ * Use this index if the ID space is only sparsly
+ * populated, such as when working with smaller OSM files (like
+ * country extracts).
+ *
+ * This will only work on 64 bit machines.
+ */
+ template <typename TId, typename TValue>
+ class SparseMemTable : public osmium::index::map::Map<TId, TValue> {
+
+ TId m_grow_size;
+
+ google::sparsetable<TValue> m_elements;
+
+ static_assert(sizeof(typename google::sparsetable<TValue>::size_type) >= 8, "google::sparsetable needs 64bit machine");
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param grow_size The initial size of the index (ie number of
+ * elements that fit into the index).
+ * The storage will grow by at least this size
+ * every time it runs out of space.
+ */
+ explicit SparseMemTable(const TId grow_size=10000) :
+ m_grow_size(grow_size),
+ m_elements(grow_size) {
+ }
+
+ ~SparseMemTable() override final = default;
+
+ void set(const TId id, const TValue value) override final {
+ if (id >= m_elements.size()) {
+ m_elements.resize(id + m_grow_size);
+ }
+ m_elements[id] = value;
+ }
+
+ const TValue get(const TId id) const override final {
+ if (id >= m_elements.size()) {
+ not_found_error(id);
+ }
+ if (m_elements[id] == osmium::index::empty_value<TValue>()) {
+ not_found_error(id);
+ }
+ return m_elements[id];
+ }
+
+ size_t size() const override final {
+ return m_elements.size();
+ }
+
+ size_t used_memory() const override final {
+ // unused elements use 1 bit, used elements sizeof(TValue) bytes
+ // http://google-sparsehash.googlecode.com/svn/trunk/doc/sparsetable.html
+ return (m_elements.size() / 8) + (m_elements.num_nonempty() * sizeof(TValue));
+ }
+
+ void clear() override final {
+ m_elements.clear();
+ }
+
+ void dump_as_list(const int fd) override final {
+ std::vector<std::pair<TId, TValue>> v;
+ int n=0;
+ for (const TValue value : m_elements) {
+ if (value != osmium::index::empty_value<TValue>()) {
+ v.emplace_back(n, value);
+ }
+ ++n;
+ }
+ osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(std::pair<TId, TValue>) * v.size());
+ }
+
+ }; // class SparseMemTable
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_WITH_SPARSEHASH
+
+#endif // OSMIUM_INDEX_BYID_SPARSE_MEM_TABLE_HPP
diff --git a/include/osmium/index/map/sparse_mmap_array.hpp b/include/osmium/index/map/sparse_mmap_array.hpp
new file mode 100644
index 0000000..c85e2c9
--- /dev/null
+++ b/include/osmium/index/map/sparse_mmap_array.hpp
@@ -0,0 +1,60 @@
+#ifndef OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
+#define OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifdef __linux__
+
+#include <osmium/index/detail/mmap_vector_anon.hpp>
+#include <osmium/index/detail/vector_map.hpp>
+
+#define OSMIUM_HAS_INDEX_MAP_SPARSE_MMAP_ARRAY
+
+namespace osmium {
+
+ namespace index {
+
+ namespace map {
+
+ template <typename TId, typename TValue>
+ using SparseMmapArray = VectorBasedSparseMap<TId, TValue, osmium::detail::mmap_vector_anon>;
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // __linux__
+
+#endif // OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp
new file mode 100644
index 0000000..e5c6b4f
--- /dev/null
+++ b/include/osmium/index/multimap.hpp
@@ -0,0 +1,128 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_HPP
+#define OSMIUM_INDEX_MULTIMAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+namespace osmium {
+
+ namespace index {
+
+ /**
+ * @brief Key-value containers with multiple values for an integer key
+ */
+ namespace multimap {
+
+ template <typename TId, typename TValue>
+ class Multimap {
+
+ static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value,
+ "TId template parameter for class Multimap must be unsigned integral type");
+
+ typedef typename std::pair<TId, TValue> element_type;
+
+ Multimap(const Multimap&) = delete;
+ Multimap& operator=(const Multimap&) = delete;
+
+ protected:
+
+ Multimap(Multimap&&) = default;
+ Multimap& operator=(Multimap&&) = default;
+
+ public:
+
+ /// The "key" type, usually osmium::unsigned_object_id_type.
+ typedef TId key_type;
+
+ /// The "value" type, usually a Location or size_t.
+ typedef TValue value_type;
+
+ Multimap() = default;
+
+ virtual ~Multimap() noexcept = default;
+
+ /// Set the field with id to value.
+ virtual void set(const TId id, const TValue value) = 0;
+
+ typedef element_type* iterator;
+
+// virtual std::pair<iterator, iterator> get_all(const TId id) const = 0;
+
+ /**
+ * Get the approximate number of items in the storage. The storage
+ * might allocate memory in blocks, so this size might not be
+ * accurate. You can not use this to find out how much memory the
+ * storage uses. Use used_memory() for that.
+ */
+ virtual size_t size() const = 0;
+
+ /**
+ * Get the memory used for this storage in bytes. Note that this
+ * is not necessarily entirely accurate but an approximation.
+ * For storage classes that store the data in memory, this is
+ * the main memory used, for storage classes storing data on disk
+ * this is the memory used on disk.
+ */
+ virtual size_t used_memory() const = 0;
+
+ /**
+ * Clear memory used for this storage. After this you can not
+ * use the storage container any more.
+ */
+ virtual void clear() = 0;
+
+ /**
+ * Sort data in map. Call this after writing all data and
+ * before reading. Not all implementations need this.
+ */
+ virtual void sort() {
+ // default implementation is empty
+ }
+
+ virtual void dump_as_list(const int /*fd*/) {
+ std::runtime_error("can't dump as list");
+ }
+
+ }; // class Multimap
+
+ } // namespace map
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MULTIMAP_HPP
diff --git a/include/osmium/index/multimap/all.hpp b/include/osmium/index/multimap/all.hpp
new file mode 100644
index 0000000..8b0ae99
--- /dev/null
+++ b/include/osmium/index/multimap/all.hpp
@@ -0,0 +1,41 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_ALL_HPP
+#define OSMIUM_INDEX_MULTIMAP_ALL_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/index/multimap/sparse_file_array.hpp> // IWYU pragma: keep
+#include <osmium/index/multimap/sparse_mem_array.hpp> // IWYU pragma: keep
+#include <osmium/index/multimap/sparse_mem_multimap.hpp> // IWYU pragma: keep
+#include <osmium/index/multimap/sparse_mmap_array.hpp> // IWYU pragma: keep
+
+#endif // OSMIUM_INDEX_MULTIMAP_ALL_HPP
diff --git a/include/osmium/index/multimap/hybrid.hpp b/include/osmium/index/multimap/hybrid.hpp
new file mode 100644
index 0000000..ac2d964
--- /dev/null
+++ b/include/osmium/index/multimap/hybrid.hpp
@@ -0,0 +1,200 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
+#define OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <utility>
+
+#include <osmium/index/index.hpp>
+#include <osmium/index/multimap.hpp>
+#include <osmium/index/multimap/sparse_mem_array.hpp>
+#include <osmium/index/multimap/sparse_mem_multimap.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ template <typename TId, typename TValue>
+ class HybridIterator {
+
+ typedef SparseMemArray<TId, TValue> main_map_type;
+ typedef SparseMemMultimap<TId, TValue> extra_map_type;
+
+ typedef typename std::pair<TId, TValue> element_type;
+
+ typename main_map_type::iterator m_begin_main;
+ typename main_map_type::iterator m_end_main;
+ typename extra_map_type::iterator m_begin_extra;
+ typename extra_map_type::iterator m_end_extra;
+
+ public:
+
+ explicit HybridIterator(typename main_map_type::iterator begin_main,
+ typename main_map_type::iterator end_main,
+ typename extra_map_type::iterator begin_extra,
+ typename extra_map_type::iterator end_extra) :
+ m_begin_main(begin_main),
+ m_end_main(end_main),
+ m_begin_extra(begin_extra),
+ m_end_extra(end_extra) {
+ }
+
+ HybridIterator& operator++() {
+ if (m_begin_main == m_end_main) {
+ ++m_begin_extra;
+ } else {
+ ++m_begin_main;
+ while (m_begin_main != m_end_main && m_begin_main->second == osmium::index::empty_value<TValue>()) { // ignore removed elements
+ ++m_begin_main;
+ }
+ }
+ return *this;
+ }
+
+ HybridIterator<TId, TValue> operator++(int) {
+ auto tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const HybridIterator& rhs) const {
+ return m_begin_main == rhs.m_begin_main &&
+ m_end_main == rhs.m_end_main &&
+ m_begin_extra == rhs.m_begin_extra &&
+ m_end_extra == rhs.m_end_extra;
+ }
+
+ bool operator!=(const HybridIterator& rhs) const {
+ return ! operator==(rhs);
+ }
+
+ const element_type& operator*() {
+ if (m_begin_main == m_end_main) {
+ return *m_begin_extra;
+ } else {
+ return *m_begin_main;
+ }
+ }
+
+ const element_type* operator->() {
+ return &operator*();
+ }
+
+ }; // class HybridIterator
+
+ template <typename TId, typename TValue>
+ class Hybrid : public Multimap<TId, TValue> {
+
+ typedef SparseMemArray<TId, TValue> main_map_type;
+ typedef SparseMemMultimap<TId, TValue> extra_map_type;
+
+ main_map_type m_main;
+ extra_map_type m_extra;
+
+ public:
+
+ typedef HybridIterator<TId, TValue> iterator;
+ typedef const HybridIterator<TId, TValue> const_iterator;
+
+ Hybrid() :
+ m_main(),
+ m_extra() {
+ }
+
+ size_t size() const override final {
+ return m_main.size() + m_extra.size();
+ }
+
+ size_t used_memory() const override final {
+ return m_main.used_memory() + m_extra.used_memory();
+ }
+
+ void reserve(const size_t size) {
+ m_main.reserve(size);
+ }
+
+ void unsorted_set(const TId id, const TValue value) {
+ m_main.set(id, value);
+ }
+
+ void set(const TId id, const TValue value) override final {
+ m_extra.set(id, value);
+ }
+
+ std::pair<iterator, iterator> get_all(const TId id) {
+ auto result_main = m_main.get_all(id);
+ auto result_extra = m_extra.get_all(id);
+ return std::make_pair(iterator(result_main.first, result_main.second, result_extra.first, result_extra.second),
+ iterator(result_main.second, result_main.second, result_extra.second, result_extra.second));
+ }
+
+ void remove(const TId id, const TValue value) {
+ m_main.remove(id, value);
+ m_extra.remove(id, value);
+ }
+
+ void consolidate() {
+ m_main.erase_removed();
+ for (const auto& element : m_extra) {
+ m_main.set(element.first, element.second);
+ }
+ m_extra.clear();
+ m_main.sort();
+ }
+
+ void dump_as_list(const int fd) override final {
+ consolidate();
+ m_main.dump_as_list(fd);
+ }
+
+ void clear() override final {
+ m_main.clear();
+ m_extra.clear();
+ }
+
+ void sort() override final {
+ m_main.sort();
+ }
+
+ }; // class Hybrid
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
diff --git a/include/osmium/index/multimap/sparse_file_array.hpp b/include/osmium/index/multimap/sparse_file_array.hpp
new file mode 100644
index 0000000..0b9ae92
--- /dev/null
+++ b/include/osmium/index/multimap/sparse_file_array.hpp
@@ -0,0 +1,54 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
+#define OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/index/detail/mmap_vector_file.hpp>
+#include <osmium/index/detail/vector_multimap.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ template <typename TId, typename TValue>
+ using SparseFileArray = VectorBasedSparseMultimap<TId, TValue, osmium::detail::mmap_vector_file>;
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
diff --git a/include/osmium/index/multimap/sparse_mem_array.hpp b/include/osmium/index/multimap/sparse_mem_array.hpp
new file mode 100644
index 0000000..c4140cb
--- /dev/null
+++ b/include/osmium/index/multimap/sparse_mem_array.hpp
@@ -0,0 +1,58 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
+#define OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <vector>
+
+#include <osmium/index/detail/vector_multimap.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ template <typename T>
+ using StdVectorWrap = std::vector<T>;
+
+ template <typename TId, typename TValue>
+ using SparseMemArray = VectorBasedSparseMultimap<TId, TValue, StdVectorWrap>;
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
diff --git a/include/osmium/index/multimap/sparse_mem_multimap.hpp b/include/osmium/index/multimap/sparse_mem_multimap.hpp
new file mode 100644
index 0000000..5b47152
--- /dev/null
+++ b/include/osmium/index/multimap/sparse_mem_multimap.hpp
@@ -0,0 +1,151 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
+#define OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <osmium/index/multimap.hpp>
+#include <osmium/io/detail/read_write.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ /**
+ * This implementation uses std::multimap internally. It uses rather a
+ * lot of memory, but might make sense for small maps.
+ */
+ template <typename TId, typename TValue>
+ class SparseMemMultimap : public osmium::index::multimap::Multimap<TId, TValue> {
+
+ // This is a rough estimate for the memory needed for each
+ // element in the map (id + value + pointers to left, right,
+ // and parent plus some overhead for color of red-black-tree
+ // or similar).
+ static constexpr size_t element_size = sizeof(TId) + sizeof(TValue) + sizeof(void*) * 4;
+
+ public:
+
+ typedef typename std::multimap<const TId, TValue> 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<TId, TValue> element_type;
+
+ private:
+
+ collection_type m_elements;
+
+ public:
+
+ SparseMemMultimap() = default;
+
+ ~SparseMemMultimap() noexcept override final = default;
+
+ void unsorted_set(const TId id, const TValue value) {
+ m_elements.emplace(id, value);
+ }
+
+ void set(const TId id, const TValue value) override final {
+ m_elements.emplace(id, value);
+ }
+
+ std::pair<iterator, iterator> get_all(const TId id) {
+ return m_elements.equal_range(id);
+ }
+
+ std::pair<const_iterator, const_iterator> get_all(const TId id) const {
+ return m_elements.equal_range(id);
+ }
+
+ void remove(const TId id, const TValue value) {
+ std::pair<iterator, iterator> r = get_all(id);
+ for (iterator it = r.first; it != r.second; ++it) {
+ if (it->second == value) {
+ m_elements.erase(it);
+ return;
+ }
+ }
+ }
+
+ iterator begin() {
+ return m_elements.begin();
+ }
+
+ iterator end() {
+ return m_elements.end();
+ }
+
+ size_t size() const override final {
+ return m_elements.size();
+ }
+
+ size_t used_memory() const override final {
+ return element_size * m_elements.size();
+ }
+
+ void clear() override final {
+ m_elements.clear();
+ }
+
+ void consolidate() {
+ // intentionally left blank
+ }
+
+ void dump_as_list(const int fd) override final {
+ std::vector<element_type> v;
+ for (const auto& element : m_elements) {
+ v.emplace_back(element.first, element.second);
+ }
+// std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v));
+ std::sort(v.begin(), v.end());
+ osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(element_type) * v.size());
+ }
+
+ }; // class SparseMemMultimap
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
diff --git a/include/osmium/index/multimap/sparse_mmap_array.hpp b/include/osmium/index/multimap/sparse_mmap_array.hpp
new file mode 100644
index 0000000..9f92555
--- /dev/null
+++ b/include/osmium/index/multimap/sparse_mmap_array.hpp
@@ -0,0 +1,58 @@
+#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
+#define OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifdef __linux__
+
+#include <osmium/index/detail/mmap_vector_anon.hpp>
+#include <osmium/index/detail/vector_multimap.hpp>
+
+namespace osmium {
+
+ namespace index {
+
+ namespace multimap {
+
+ template <typename TId, typename TValue>
+ using SparseMmapArray = VectorBasedSparseMultimap<TId, TValue, osmium::detail::mmap_vector_anon>;
+
+ } // namespace multimap
+
+ } // namespace index
+
+} // namespace osmium
+
+#endif // __linux__
+
+#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
diff --git a/include/osmium/index/node_locations_map.hpp b/include/osmium/index/node_locations_map.hpp
new file mode 100644
index 0000000..ca4b136
--- /dev/null
+++ b/include/osmium/index/node_locations_map.hpp
@@ -0,0 +1,70 @@
+#ifndef OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
+#define OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/index/map.hpp> // IWYU pragma: keep
+
+#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_FILE_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseFileArray, dense_file_array)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_MEM_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMemArray, dense_mem_array)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_MMAP_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMmapArray, dense_mmap_array)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_FILE_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseFileArray, sparse_file_array)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemArray, sparse_mem_array)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_MAP
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemMap, sparse_mem_map)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_TABLE
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemTable, sparse_mem_table)
+#endif
+
+#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MMAP_ARRAY
+ REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMmapArray, sparse_mmap_array)
+#endif
+
+#endif // OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
diff --git a/include/osmium/io/any_compression.hpp b/include/osmium/io/any_compression.hpp
new file mode 100644
index 0000000..00e8ee2
--- /dev/null
+++ b/include/osmium/io/any_compression.hpp
@@ -0,0 +1,48 @@
+#ifndef OSMIUM_IO_ANY_COMPRESSION_HPP
+#define OSMIUM_IO_ANY_COMPRESSION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 or write compressed OSM XML files.
+ *
+ * @attention If you include this file, you'll need to link with `libz`
+ * and `libbz2`.
+ */
+
+#include <osmium/io/bzip2_compression.hpp> // IWYU pragma: export
+#include <osmium/io/gzip_compression.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_ANY_COMPRESSION_HPP
diff --git a/include/osmium/io/any_input.hpp b/include/osmium/io/any_input.hpp
new file mode 100644
index 0000000..633fab3
--- /dev/null
+++ b/include/osmium/io/any_input.hpp
@@ -0,0 +1,51 @@
+#ifndef OSMIUM_IO_ANY_INPUT_HPP
+#define OSMIUM_IO_ANY_INPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 all kinds of OSM files.
+ *
+ * @attention If you include this file, you'll need to link with
+ * `libprotobuf-lite`, `libosmpbf`, `ws2_32` (Windows only),
+ * `libexpat`, `libz`, `libbz2`, and enable multithreading.
+ */
+
+#include <osmium/io/any_compression.hpp> // IWYU pragma: export
+
+#include <osmium/io/pbf_input.hpp> // IWYU pragma: export
+#include <osmium/io/xml_input.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_ANY_INPUT_HPP
diff --git a/include/osmium/io/any_output.hpp b/include/osmium/io/any_output.hpp
new file mode 100644
index 0000000..63de3ff
--- /dev/null
+++ b/include/osmium/io/any_output.hpp
@@ -0,0 +1,52 @@
+#ifndef OSMIUM_IO_ANY_OUTPUT_HPP
+#define OSMIUM_IO_ANY_OUTPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 write all kinds of OSM files.
+ *
+ * @attention If you include this file, you'll need to link with
+ * `libprotobuf-lite`, `libosmpbf`, `ws2_32` (Windows only),
+ * `libz`, `libbz2`, and enable multithreading.
+ */
+
+#include <osmium/io/any_compression.hpp> // IWYU pragma: export
+
+#include <osmium/io/opl_output.hpp> // IWYU pragma: export
+#include <osmium/io/pbf_output.hpp> // IWYU pragma: export
+#include <osmium/io/xml_output.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_ANY_OUTPUT_HPP
diff --git a/include/osmium/io/bzip2_compression.hpp b/include/osmium/io/bzip2_compression.hpp
new file mode 100644
index 0000000..de1364c
--- /dev/null
+++ b/include/osmium/io/bzip2_compression.hpp
@@ -0,0 +1,289 @@
+#ifndef OSMIUM_IO_BZIP2_COMPRESSION_HPP
+#define OSMIUM_IO_BZIP2_COMPRESSION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 or write bzip2-compressed OSM XML
+ * files.
+ *
+ * @attention If you include this file, you'll need to link with `libbz2`.
+ */
+
+#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <stdexcept>
+#include <string>
+
+#include <bzlib.h>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#endif
+
+#include <osmium/io/compression.hpp>
+#include <osmium/io/file_compression.hpp>
+#include <osmium/util/cast.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when there are problems compressing or
+ * decompressing bzip2 files.
+ */
+ struct bzip2_error : public std::runtime_error {
+
+ int bzip2_error_code;
+ int system_errno;
+
+ bzip2_error(const std::string& what, int error_code) :
+ std::runtime_error(what),
+ bzip2_error_code(error_code),
+ system_errno(error_code == BZ_IO_ERROR ? errno : 0) {
+ }
+
+ }; // struct bzip2_error
+
+ namespace io {
+
+ namespace detail {
+
+ OSMIUM_NORETURN inline void throw_bzip2_error(BZFILE* bzfile, const char* msg, int bzlib_error=0) {
+ std::string error("bzip2 error: ");
+ error += msg;
+ error += ": ";
+ int errnum = bzlib_error;
+ if (bzlib_error) {
+ error += std::to_string(bzlib_error);
+ } else {
+ error += ::BZ2_bzerror(bzfile, &errnum);
+ }
+ throw osmium::bzip2_error(error, errnum);
+ }
+
+ } // namespace detail
+
+ class Bzip2Compressor : public Compressor {
+
+ FILE* m_file;
+ int m_bzerror;
+ BZFILE* m_bzfile;
+
+ public:
+
+ explicit Bzip2Compressor(int fd) :
+ Compressor(),
+ m_file(fdopen(dup(fd), "wb")),
+ m_bzerror(BZ_OK),
+ m_bzfile(::BZ2_bzWriteOpen(&m_bzerror, m_file, 6, 0, 0)) {
+ if (!m_bzfile) {
+ detail::throw_bzip2_error(m_bzfile, "write open failed", m_bzerror);
+ }
+ }
+
+ ~Bzip2Compressor() override final {
+ close();
+ }
+
+ void write(const std::string& data) override final {
+ int error;
+ ::BZ2_bzWrite(&error, m_bzfile, const_cast<char*>(data.data()), static_cast_with_assert<int>(data.size()));
+ if (error != BZ_OK && error != BZ_STREAM_END) {
+ detail::throw_bzip2_error(m_bzfile, "write failed", error);
+ }
+ }
+
+ void close() override final {
+ if (m_bzfile) {
+ int error;
+ ::BZ2_bzWriteClose(&error, m_bzfile, 0, nullptr, nullptr);
+ m_bzfile = nullptr;
+ if (m_file) {
+ fclose(m_file);
+ }
+ if (error != BZ_OK) {
+ detail::throw_bzip2_error(m_bzfile, "write close failed", error);
+ }
+ }
+ }
+
+ }; // class Bzip2Compressor
+
+ class Bzip2Decompressor : public Decompressor {
+
+ FILE* m_file;
+ int m_bzerror;
+ BZFILE* m_bzfile;
+ bool m_stream_end {false};
+
+ public:
+
+ Bzip2Decompressor(int fd) :
+ Decompressor(),
+ 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) {
+ detail::throw_bzip2_error(m_bzfile, "read open failed", m_bzerror);
+ }
+ }
+
+ ~Bzip2Decompressor() override final {
+ close();
+ }
+
+ std::string read() override final {
+ std::string buffer;
+
+ if (!m_stream_end) {
+ buffer.resize(osmium::io::Decompressor::input_buffer_size);
+ int error;
+ int nread = ::BZ2_bzRead(&error, m_bzfile, const_cast<char*>(buffer.data()), static_cast_with_assert<int>(buffer.size()));
+ if (error != BZ_OK && error != BZ_STREAM_END) {
+ detail::throw_bzip2_error(m_bzfile, "read failed", error);
+ }
+ if (error == BZ_STREAM_END) {
+ void* unused;
+ int nunused;
+ if (! feof(m_file)) {
+ ::BZ2_bzReadGetUnused(&error, m_bzfile, &unused, &nunused);
+ if (error != BZ_OK) {
+ detail::throw_bzip2_error(m_bzfile, "get unused failed", error);
+ }
+ std::string unused_data(static_cast<const char*>(unused), static_cast<std::string::size_type>(nunused));
+ ::BZ2_bzReadClose(&error, m_bzfile);
+ if (error != BZ_OK) {
+ detail::throw_bzip2_error(m_bzfile, "read close failed", error);
+ }
+ m_bzfile = ::BZ2_bzReadOpen(&error, m_file, 0, 0, const_cast<void*>(static_cast<const void*>(unused_data.data())), static_cast_with_assert<int>(unused_data.size()));
+ if (error != BZ_OK) {
+ detail::throw_bzip2_error(m_bzfile, "read open failed", error);
+ }
+ } else {
+ m_stream_end = true;
+ }
+ }
+ buffer.resize(static_cast<std::string::size_type>(nread));
+ }
+
+ return buffer;
+ }
+
+ void close() override final {
+ if (m_bzfile) {
+ int error;
+ ::BZ2_bzReadClose(&error, m_bzfile);
+ m_bzfile = nullptr;
+ if (m_file) {
+ fclose(m_file);
+ }
+ if (error != BZ_OK) {
+ detail::throw_bzip2_error(m_bzfile, "read close failed", error);
+ }
+ }
+ }
+
+ }; // class Bzip2Decompressor
+
+ class Bzip2BufferDecompressor : public Decompressor {
+
+ const char* m_buffer;
+ size_t m_buffer_size;
+ bz_stream m_bzstream;
+
+ public:
+
+ Bzip2BufferDecompressor(const char* buffer, size_t size) :
+ m_buffer(buffer),
+ m_buffer_size(size),
+ m_bzstream() {
+ m_bzstream.next_in = const_cast<char*>(buffer);
+ m_bzstream.avail_in = static_cast_with_assert<unsigned int>(size);
+ int result = BZ2_bzDecompressInit(&m_bzstream, 0, 0);
+ if (result != BZ_OK) {
+ std::string message("bzip2 error: decompression init failed: ");
+ throw bzip2_error(message, result);
+ }
+ }
+
+ ~Bzip2BufferDecompressor() override final {
+ BZ2_bzDecompressEnd(&m_bzstream);
+ }
+
+ std::string read() override final {
+ std::string output;
+
+ if (m_buffer) {
+ const size_t buffer_size = 10240;
+ output.resize(buffer_size);
+ m_bzstream.next_out = const_cast<char*>(output.data());
+ m_bzstream.avail_out = buffer_size;
+ int result = BZ2_bzDecompress(&m_bzstream);
+
+ if (result != BZ_OK) {
+ m_buffer = nullptr;
+ m_buffer_size = 0;
+ }
+
+ if (result != BZ_OK && result != BZ_STREAM_END) {
+ std::string message("bzip2 error: decompress failed: ");
+ throw bzip2_error(message, result);
+ }
+
+ output.resize(static_cast<unsigned long>(m_bzstream.next_out - output.data()));
+ }
+
+ return output;
+ }
+
+ }; // class Bzip2BufferDecompressor
+
+ namespace {
+
+ const bool registered_bzip2_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::bzip2,
+ [](int fd) { return new osmium::io::Bzip2Compressor(fd); },
+ [](int fd) { return new osmium::io::Bzip2Decompressor(fd); },
+ [](const char* buffer, size_t size) { return new osmium::io::Bzip2BufferDecompressor(buffer, size); }
+ );
+
+ } // anonymous namespace
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_BZIP2_COMPRESSION_HPP
diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp
new file mode 100644
index 0000000..c1f8de2
--- /dev/null
+++ b/include/osmium/io/compression.hpp
@@ -0,0 +1,281 @@
+#ifndef OSMIUM_IO_COMPRESSION_HPP
+#define OSMIUM_IO_COMPRESSION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cerrno>
+#include <functional>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+#include <utility>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+
+#include <osmium/io/detail/read_write.hpp>
+#include <osmium/io/file_compression.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ class Compressor {
+
+ public:
+
+ Compressor() = default;
+
+ virtual ~Compressor() {
+ }
+
+ virtual void write(const std::string& data) = 0;
+
+ virtual void close() = 0;
+
+ }; // class Compressor
+
+ class Decompressor {
+
+ public:
+
+ static constexpr unsigned int input_buffer_size = 1024 * 1024;
+
+ Decompressor() = default;
+
+ Decompressor(const Decompressor&) = delete;
+ Decompressor& operator=(const Decompressor&) = delete;
+
+ Decompressor(Decompressor&&) = delete;
+ Decompressor& operator=(Decompressor&&) = delete;
+
+ virtual ~Decompressor() {
+ }
+
+ virtual std::string read() = 0;
+
+ virtual void close() {
+ }
+
+ }; // class Decompressor
+
+ /**
+ * This singleton factory class is used to register compression
+ * algorithms used for reading and writing OSM files.
+ *
+ * For each algorithm we store two functions that construct
+ * a compressor and decompressor object, respectively.
+ */
+ class CompressionFactory {
+
+ public:
+
+ typedef std::function<osmium::io::Compressor*(int)> create_compressor_type;
+ typedef std::function<osmium::io::Decompressor*(int)> create_decompressor_type_fd;
+ typedef std::function<osmium::io::Decompressor*(const char*, size_t)> create_decompressor_type_buffer;
+
+ private:
+
+ typedef std::map<const osmium::io::file_compression, std::tuple<create_compressor_type, create_decompressor_type_fd, create_decompressor_type_buffer>> compression_map_type;
+
+ compression_map_type m_callbacks;
+
+ CompressionFactory() = default;
+
+ CompressionFactory(const CompressionFactory&) = delete;
+ CompressionFactory& operator=(const CompressionFactory&) = delete;
+
+ CompressionFactory(CompressionFactory&&) = delete;
+ CompressionFactory& operator=(CompressionFactory&&) = delete;
+
+ OSMIUM_NORETURN void error(osmium::io::file_compression compression) {
+ std::string error_message {"Support for compression '"};
+ error_message += as_string(compression);
+ error_message += "' not compiled into this binary.";
+ throw std::runtime_error(error_message);
+ }
+
+ public:
+
+ static CompressionFactory& instance() {
+ static CompressionFactory factory;
+ return factory;
+ }
+
+ bool register_compression(
+ osmium::io::file_compression compression,
+ create_compressor_type create_compressor,
+ create_decompressor_type_fd create_decompressor_fd,
+ create_decompressor_type_buffer create_decompressor_buffer) {
+
+ compression_map_type::value_type cc(compression, std::make_tuple(create_compressor, create_decompressor_fd, create_decompressor_buffer));
+ return m_callbacks.insert(cc).second;
+ }
+
+ std::unique_ptr<osmium::io::Compressor> create_compressor(osmium::io::file_compression compression, int fd) {
+ auto it = m_callbacks.find(compression);
+
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<osmium::io::Compressor>(std::get<0>(it->second)(fd));
+ }
+
+ error(compression);
+ }
+
+ std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, int fd) {
+ auto it = m_callbacks.find(compression);
+
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<osmium::io::Decompressor>(std::get<1>(it->second)(fd));
+ }
+
+ error(compression);
+ }
+
+ std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, const char* buffer, size_t size) {
+ auto it = m_callbacks.find(compression);
+
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<osmium::io::Decompressor>(std::get<2>(it->second)(buffer, size));
+ }
+
+ error(compression);
+ }
+
+ }; // class CompressionFactory
+
+ class NoCompressor : public Compressor {
+
+ int m_fd;
+
+ public:
+
+ NoCompressor(int fd) :
+ Compressor(),
+ m_fd(fd) {
+ }
+
+ ~NoCompressor() override final {
+ close();
+ }
+
+ void write(const std::string& data) override final {
+ osmium::io::detail::reliable_write(m_fd, data.data(), data.size());
+ }
+
+ void close() override final {
+ if (m_fd >= 0) {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+ }
+
+ }; // class NoCompressor
+
+ class NoDecompressor : public Decompressor {
+
+ int m_fd;
+ const char *m_buffer;
+ size_t m_buffer_size;
+
+ public:
+
+ NoDecompressor(int fd) :
+ Decompressor(),
+ m_fd(fd),
+ m_buffer(nullptr),
+ m_buffer_size(0) {
+ }
+
+ NoDecompressor(const char* buffer, size_t size) :
+ Decompressor(),
+ m_fd(-1),
+ m_buffer(buffer),
+ m_buffer_size(size) {
+ }
+
+ ~NoDecompressor() override final {
+ close();
+ }
+
+ std::string read() override final {
+ std::string buffer;
+
+ if (m_buffer) {
+ if (m_buffer_size != 0) {
+ size_t size = m_buffer_size;
+ m_buffer_size = 0;
+ buffer.append(m_buffer, size);
+ }
+ } else {
+ buffer.resize(osmium::io::Decompressor::input_buffer_size);
+ auto nread = ::read(m_fd, const_cast<char*>(buffer.data()), osmium::io::Decompressor::input_buffer_size);
+ if (nread < 0) {
+ throw std::system_error(errno, std::system_category(), "Read failed");
+ }
+ buffer.resize(nread);
+ }
+
+ return buffer;
+ }
+
+ void close() override final {
+ if (m_fd >= 0) {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+ }
+
+ }; // class NoDecompressor
+
+ namespace {
+
+ const bool registered_no_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::none,
+ [](int fd) { return new osmium::io::NoCompressor(fd); },
+ [](int fd) { return new osmium::io::NoDecompressor(fd); },
+ [](const char* buffer, size_t size) { return new osmium::io::NoDecompressor(buffer, size); }
+ );
+
+ } // anonymous namespace
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_COMPRESSION_HPP
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
new file mode 100644
index 0000000..03e1190
--- /dev/null
+++ b/include/osmium/io/detail/input_format.hpp
@@ -0,0 +1,158 @@
+#ifndef OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <functional>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include <osmium/io/file.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/entity_bits.hpp>
+
+namespace osmium {
+
+ namespace thread {
+ template <typename T> class Queue;
+ } // namespace thread
+
+ namespace io {
+
+ namespace detail {
+
+ /**
+ * Virtual base class for all classes reading OSM files in different
+ * formats.
+ *
+ * Do not use this class or derived classes directly. Use the
+ * osmium::io::Reader class instead.
+ */
+ class InputFormat {
+
+ protected:
+
+ osmium::io::File m_file;
+ osmium::osm_entity_bits::type m_read_which_entities;
+ osmium::io::Header m_header;
+
+ explicit InputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities) :
+ m_file(file),
+ m_read_which_entities(read_which_entities) {
+ m_header.set_has_multiple_object_versions(m_file.has_multiple_object_versions());
+ }
+
+ InputFormat(const InputFormat&) = delete;
+ InputFormat(InputFormat&&) = delete;
+
+ InputFormat& operator=(const InputFormat&) = delete;
+ InputFormat& operator=(InputFormat&&) = delete;
+
+ public:
+
+ virtual ~InputFormat() {
+ }
+
+ virtual osmium::memory::Buffer read() = 0;
+
+ virtual void close() {
+ }
+
+ virtual osmium::io::Header header() {
+ return m_header;
+ }
+
+ }; // class InputFormat
+
+ /**
+ * This factory class is used to create objects that read OSM data
+ * written in a specified format.
+ *
+ * Do not use this class directly. Instead use the osmium::io::Reader
+ * class.
+ */
+ class InputFormatFactory {
+
+ public:
+
+ typedef std::function<osmium::io::detail::InputFormat*(const osmium::io::File&, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>&)> create_input_type;
+
+ private:
+
+ typedef std::map<osmium::io::file_format, create_input_type> map_type;
+
+ map_type m_callbacks;
+
+ InputFormatFactory() :
+ m_callbacks() {
+ }
+
+ public:
+
+ static InputFormatFactory& instance() {
+ static InputFormatFactory factory;
+ return factory;
+ }
+
+ bool register_input_format(osmium::io::file_format format, create_input_type create_function) {
+ if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) {
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<osmium::io::detail::InputFormat> create_input(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>& input_queue) {
+ file.check();
+
+ auto it = m_callbacks.find(file.format());
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<osmium::io::detail::InputFormat>((it->second)(file, read_which_entities, input_queue));
+ }
+
+ throw std::runtime_error(std::string("Support for input format '") + as_string(file.format()) + "' not compiled into this binary.");
+ }
+
+ }; // class InputFormatFactory
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/opl_output_format.hpp b/include/osmium/io/detail/opl_output_format.hpp
new file mode 100644
index 0000000..cf92e13
--- /dev/null
+++ b/include/osmium/io/detail/opl_output_format.hpp
@@ -0,0 +1,317 @@
+#ifndef OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <chrono>
+#include <cinttypes>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <future>
+#include <iterator>
+#include <memory>
+#include <ratio>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <boost/version.hpp>
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmissing-noreturn"
+# pragma clang diagnostic ignored "-Wsign-conversion"
+#endif
+
+#if BOOST_VERSION >= 104800
+# include <boost/regex/pending/unicode_iterator.hpp>
+#else
+# include <boost_unicode_iterator.hpp>
+#endif
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+#include <osmium/handler.hpp>
+#include <osmium/io/detail/output_format.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/collection.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/changeset.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/thread/pool.hpp>
+#include <osmium/visitor.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ class File;
+
+ namespace detail {
+
+ /**
+ * Writes out one buffer with OSM data in OPL format.
+ */
+ class OPLOutputBlock : public osmium::handler::Handler {
+
+ static constexpr size_t tmp_buffer_size = 100;
+
+ std::shared_ptr<osmium::memory::Buffer> m_input_buffer;
+
+ std::shared_ptr<std::string> m_out;
+
+ char m_tmp_buffer[tmp_buffer_size+1];
+
+ template <typename... TArgs>
+ void output_formatted(const char* format, TArgs&&... args) {
+#ifndef NDEBUG
+ int len =
+#endif
+#ifndef _MSC_VER
+ snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward<TArgs>(args)...);
+#else
+ _snprintf(m_tmp_buffer, tmp_buffer_size, format, std::forward<TArgs>(args)...);
+#endif
+ assert(len > 0 && static_cast<size_t>(len) < tmp_buffer_size);
+ *m_out += m_tmp_buffer;
+ }
+
+ void append_encoded_string(const std::string& data) {
+ boost::u8_to_u32_iterator<std::string::const_iterator> it(data.cbegin(), data.cbegin(), data.cend());
+ boost::u8_to_u32_iterator<std::string::const_iterator> end(data.cend(), data.cend(), data.cend());
+ boost::utf8_output_iterator<std::back_insert_iterator<std::string>> oit(std::back_inserter(*m_out));
+
+ for (; it != end; ++it) {
+ uint32_t c = *it;
+
+ // This is a list of Unicode code points that we let
+ // through instead of escaping them. It is incomplete
+ // and can be extended later.
+ // Generally we don't want to let through any character
+ // that has special meaning in the OPL format such as
+ // space, comma, @, etc. and any non-printing characters.
+ if ((0x0021 <= c && c <= 0x0024) ||
+ (0x0026 <= c && c <= 0x002b) ||
+ (0x002d <= c && c <= 0x003c) ||
+ (0x003e <= c && c <= 0x003f) ||
+ (0x0041 <= c && c <= 0x007e) ||
+ (0x00a1 <= c && c <= 0x00ac) ||
+ (0x00ae <= c && c <= 0x05ff)) {
+ *oit = c;
+ } else {
+ *m_out += '%';
+ output_formatted("%04x", c);
+ }
+ }
+ }
+
+ void write_meta(const osmium::OSMObject& object) {
+ output_formatted("%" PRId64 " v%d d", object.id(), object.version());
+ *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());
+ 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());
+ }
+ }
+
+ void write_location(const osmium::Location location, const char x, const char y) {
+ if (location) {
+ output_formatted(" %c%.7f %c%.7f", x, location.lon_without_check(), y, location.lat_without_check());
+ } else {
+ *m_out += ' ';
+ *m_out += x;
+ *m_out += ' ';
+ *m_out += y;
+ }
+ }
+
+ public:
+
+ explicit OPLOutputBlock(osmium::memory::Buffer&& buffer) :
+ m_input_buffer(std::make_shared<osmium::memory::Buffer>(std::move(buffer))),
+ m_out(std::make_shared<std::string>()),
+ m_tmp_buffer() {
+ }
+
+ OPLOutputBlock(const OPLOutputBlock&) = default;
+ OPLOutputBlock& operator=(const OPLOutputBlock&) = default;
+
+ OPLOutputBlock(OPLOutputBlock&&) = default;
+ OPLOutputBlock& operator=(OPLOutputBlock&&) = default;
+
+ ~OPLOutputBlock() = default;
+
+ std::string operator()() {
+ osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
+
+ std::string out;
+ std::swap(out, *m_out);
+ return out;
+ }
+
+ void node(const osmium::Node& node) {
+ *m_out += 'n';
+ write_meta(node);
+ write_location(node.location(), 'x', 'y');
+ *m_out += '\n';
+ }
+
+ void way(const osmium::Way& way) {
+ *m_out += 'w';
+ write_meta(way);
+
+ *m_out += " N";
+ bool first = true;
+ for (const auto& node_ref : way.nodes()) {
+ if (first) {
+ first = false;
+ } else {
+ *m_out += ',';
+ }
+ output_formatted("n%" PRId64, node_ref.ref());
+ }
+ *m_out += '\n';
+ }
+
+ void relation(const osmium::Relation& relation) {
+ *m_out += 'r';
+ write_meta(relation);
+
+ *m_out += " M";
+ bool first = true;
+ for (const auto& member : relation.members()) {
+ if (first) {
+ first = false;
+ } else {
+ *m_out += ',';
+ }
+ *m_out += item_type_to_char(member.type());
+ output_formatted("%" PRId64 "@", member.ref());
+ *m_out += 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(" i%d u", changeset.uid());
+ 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());
+ }
+
+ *m_out += '\n';
+ }
+
+ }; // OPLOutputBlock
+
+ class OPLOutputFormat : public osmium::io::detail::OutputFormat {
+
+ OPLOutputFormat(const OPLOutputFormat&) = delete;
+ OPLOutputFormat& operator=(const OPLOutputFormat&) = delete;
+
+ public:
+
+ OPLOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) :
+ OutputFormat(file, output_queue) {
+ }
+
+ void write_buffer(osmium::memory::Buffer&& buffer) override final {
+ m_output_queue.push(osmium::thread::Pool::instance().submit(OPLOutputBlock{std::move(buffer)}));
+ }
+
+ void close() override final {
+ std::string out;
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(out);
+ }
+
+ }; // class OPLOutputFormat
+
+ namespace {
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+ const bool registered_opl_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::opl,
+ [](const osmium::io::File& file, data_queue_type& output_queue) {
+ return new osmium::io::detail::OPLOutputFormat(file, output_queue);
+ });
+#pragma GCC diagnostic pop
+
+ } // anonymous namespace
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp
new file mode 100644
index 0000000..529a189
--- /dev/null
+++ b/include/osmium/io/detail/output_format.hpp
@@ -0,0 +1,156 @@
+#ifndef OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <functional>
+#include <future>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include <osmium/io/file.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/thread/queue.hpp>
+
+namespace osmium {
+
+ namespace memory {
+ class Buffer;
+ }
+
+ namespace io {
+
+ namespace detail {
+
+ typedef osmium::thread::Queue<std::future<std::string>> data_queue_type;
+
+ /**
+ * Virtual base class for all classes writing OSM files in different
+ * formats.
+ *
+ * Do not use this class or derived classes directly. Use the
+ * osmium::io::Writer class instead.
+ */
+ class OutputFormat {
+
+ protected:
+
+ osmium::io::File m_file;
+ data_queue_type& m_output_queue;
+
+ public:
+
+ explicit OutputFormat(const osmium::io::File& file, data_queue_type& output_queue) :
+ m_file(file),
+ m_output_queue(output_queue) {
+ }
+
+ OutputFormat(const OutputFormat&) = delete;
+ OutputFormat(OutputFormat&&) = delete;
+
+ OutputFormat& operator=(const OutputFormat&) = delete;
+ OutputFormat& operator=(OutputFormat&&) = delete;
+
+ virtual ~OutputFormat() {
+ }
+
+ virtual void write_header(const osmium::io::Header&) {
+ }
+
+ virtual void write_buffer(osmium::memory::Buffer&&) = 0;
+
+ virtual void close() = 0;
+
+ }; // class OutputFormat
+
+ /**
+ * This factory class is used to create objects that write OSM data
+ * into a specified output format.
+ *
+ * Do not use this class directly. Instead use the osmium::io::Writer
+ * class.
+ */
+ class OutputFormatFactory {
+
+ public:
+
+ typedef std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, data_queue_type&)> create_output_type;
+
+ private:
+
+ typedef std::map<osmium::io::file_format, create_output_type> map_type;
+
+ map_type m_callbacks;
+
+ OutputFormatFactory() :
+ m_callbacks() {
+ }
+
+ public:
+
+ static OutputFormatFactory& instance() {
+ static OutputFormatFactory factory;
+ return factory;
+ }
+
+ bool register_output_format(osmium::io::file_format format, create_output_type create_function) {
+ if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) {
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<osmium::io::detail::OutputFormat> create_output(const osmium::io::File& file, data_queue_type& output_queue) {
+ file.check();
+
+ auto it = m_callbacks.find(file.format());
+ if (it != m_callbacks.end()) {
+ return std::unique_ptr<osmium::io::detail::OutputFormat>((it->second)(file, output_queue));
+ }
+
+ throw std::runtime_error(std::string("Support for output format '") + as_string(file.format()) + "' not compiled into this binary.");
+ }
+
+ }; // class OutputFormatFactory
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/pbf.hpp b/include/osmium/io/detail/pbf.hpp
new file mode 100644
index 0000000..e64e51a
--- /dev/null
+++ b/include/osmium/io/detail/pbf.hpp
@@ -0,0 +1,98 @@
+#ifndef OSMIUM_IO_DETAIL_PBF_HPP
+#define OSMIUM_IO_DETAIL_PBF_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <stdexcept>
+
+#include <osmpbf/osmpbf.h>
+
+// needed for htonl and ntohl
+#ifndef _WIN32
+# include <netinet/in.h>
+#else
+# include <winsock2.h>
+#endif
+
+#include <osmium/io/error.hpp>
+#include <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+// avoid g++ false positive
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreturn-type"
+ inline item_type osmpbf_membertype_to_item_type(const OSMPBF::Relation::MemberType mt) {
+ switch (mt) {
+ case OSMPBF::Relation::NODE:
+ return item_type::node;
+ case OSMPBF::Relation::WAY:
+ return item_type::way;
+ case OSMPBF::Relation::RELATION:
+ return item_type::relation;
+ }
+ }
+#pragma GCC diagnostic pop
+
+ inline OSMPBF::Relation::MemberType item_type_to_osmpbf_membertype(const item_type type) {
+ switch (type) {
+ case item_type::node:
+ return OSMPBF::Relation::NODE;
+ case item_type::way:
+ return OSMPBF::Relation::WAY;
+ case item_type::relation:
+ return OSMPBF::Relation::RELATION;
+ default:
+ throw std::runtime_error("Unknown relation member type");
+ }
+ }
+
+ /**
+ * Exception thrown when there was a problem with parsing the PBF format of
+ * a file.
+ */
+ struct pbf_error : public io_error {
+
+ pbf_error(const std::string& what) :
+ io_error(std::string("PBF error: ") + what) {
+ }
+
+ pbf_error(const char* what) :
+ io_error(std::string("PBF error: ") + what) {
+ }
+
+ }; // struct pbf_error
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_PBF_HPP
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
new file mode 100644
index 0000000..8b997ec
--- /dev/null
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -0,0 +1,245 @@
+#ifndef OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <future>
+#include <memory>
+#include <ratio>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <thread>
+#include <type_traits>
+
+#include <osmium/io/detail/input_format.hpp>
+#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_parser.hpp>
+#include <osmium/io/error.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/entity_bits.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/thread/pool.hpp>
+#include <osmium/thread/queue.hpp>
+#include <osmium/thread/util.hpp>
+#include <osmium/util/cast.hpp>
+#include <osmium/util/config.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ class File;
+
+ namespace detail {
+
+ typedef osmium::thread::Queue<std::future<osmium::memory::Buffer>> queue_type;
+
+ /**
+ * Class for parsing PBF files.
+ */
+ class PBFInputFormat : public osmium::io::detail::InputFormat {
+
+ bool m_use_thread_pool;
+ queue_type m_queue;
+ std::atomic<bool> m_done;
+ std::thread m_reader;
+ osmium::thread::Queue<std::string>& m_input_queue;
+ std::string m_input_buffer;
+
+ /**
+ * Read the given number of bytes from the input queue.
+ *
+ * @param size Number of bytes to read
+ * @returns String with the data
+ * @throws osmium::pbf_error If size bytes can't be read
+ */
+ std::string read_from_input_queue(size_t size) {
+ while (m_input_buffer.size() < size) {
+ std::string new_data;
+ m_input_queue.wait_and_pop(new_data);
+ if (new_data.empty()) {
+ throw osmium::pbf_error("truncated data (EOF encountered)");
+ }
+ m_input_buffer += new_data;
+ }
+
+ std::string output { m_input_buffer.substr(size) };
+ m_input_buffer.resize(size);
+ std::swap(output, m_input_buffer);
+ return output;
+ }
+
+ /**
+ * Read BlobHeader by first reading the size and then the
+ * BlobHeader. The BlobHeader contains a type field (which is
+ * checked against the expected type) and a size field.
+ *
+ * @param expected_type Expected type of data ("OSMHeader" or
+ * "OSMData").
+ * @returns Size of the data read from BlobHeader (0 on EOF).
+ */
+ size_t read_blob_header(const char* expected_type) {
+ uint32_t size_in_network_byte_order;
+
+ try {
+ std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order));
+ size_in_network_byte_order = *reinterpret_cast<const uint32_t*>(input_data.data());
+ } catch (osmium::pbf_error&) {
+ return 0; // EOF
+ }
+
+ uint32_t size = ntohl(size_in_network_byte_order);
+ if (size > static_cast<uint32_t>(OSMPBF::max_blob_header_size)) {
+ throw osmium::pbf_error("invalid BlobHeader size (> max_blob_header_size)");
+ }
+
+ OSMPBF::BlobHeader blob_header;
+ if (!blob_header.ParseFromString(read_from_input_queue(size))) {
+ throw osmium::pbf_error("failed to parse BlobHeader");
+ }
+
+ if (blob_header.type() != expected_type) {
+ throw osmium::pbf_error("blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)");
+ }
+
+ return static_cast<size_t>(blob_header.datasize());
+ }
+
+ void parse_osm_data(osmium::osm_entity_bits::type read_types) {
+ osmium::thread::set_thread_name("_osmium_pbf_in");
+ int n=0;
+ while (auto size = read_blob_header("OSMData")) {
+
+ if (m_use_thread_pool) {
+ m_queue.push(osmium::thread::Pool::instance().submit(DataBlobParser{read_from_input_queue(size), read_types}));
+ } else {
+ std::promise<osmium::memory::Buffer> promise;
+ m_queue.push(promise.get_future());
+ DataBlobParser data_blob_parser{read_from_input_queue(size), read_types};
+ promise.set_value(data_blob_parser());
+ }
+ ++n;
+
+ if (m_done) {
+ return;
+ }
+ }
+ m_done = true;
+ }
+
+ public:
+
+ /**
+ * Instantiate PBF Parser
+ *
+ * @param file osmium::io::File instance describing file to be read from.
+ * @param read_which_entities Which types of OSM entities (nodes, ways, relations, changesets) should be parsed?
+ * @param input_queue String queue where data is read from.
+ */
+ PBFInputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>& input_queue) :
+ osmium::io::detail::InputFormat(file, read_which_entities),
+ m_use_thread_pool(osmium::config::use_pool_threads_for_pbf_parsing()),
+ m_queue(20, "pbf_parser_results"), // XXX
+ m_done(false),
+ m_input_queue(input_queue),
+ m_input_buffer() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ // handle OSMHeader
+ auto size = read_blob_header("OSMHeader");
+ m_header = parse_header_blob(read_from_input_queue(size));
+
+ if (m_read_which_entities != osmium::osm_entity_bits::nothing) {
+ m_reader = std::thread(&PBFInputFormat::parse_osm_data, this, m_read_which_entities);
+ }
+ }
+
+ ~PBFInputFormat() {
+ m_done = true;
+ if (m_reader.joinable()) {
+ m_reader.join();
+ }
+ }
+
+ /**
+ * Returns the next buffer with OSM data read from the PBF file.
+ * Blocks if data is not available yet.
+ * Returns an empty buffer at end of input.
+ */
+ osmium::memory::Buffer read() override {
+ if (!m_done || !m_queue.empty()) {
+ std::future<osmium::memory::Buffer> buffer_future;
+ m_queue.wait_and_pop(buffer_future);
+ try {
+ return buffer_future.get();
+ } catch (...) {
+ m_done = true;
+ throw;
+ }
+ }
+
+ return osmium::memory::Buffer();
+ }
+
+ }; // class PBFInputFormat
+
+ namespace {
+
+ const bool registered_pbf_input = osmium::io::detail::InputFormatFactory::instance().register_input_format(osmium::io::file_format::pbf,
+ [](const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>& input_queue) {
+ return new osmium::io::detail::PBFInputFormat(file, read_which_entities, input_queue);
+ });
+
+ } // anonymous namespace
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
new file mode 100644
index 0000000..73bc616
--- /dev/null
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -0,0 +1,944 @@
+#ifndef OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+/*
+
+About the .osm.pbf file format
+This is an excerpt of <http://wiki.openstreetmap.org/wiki/PBF_Format>
+
+The .osm.pbf format and it's derived formats (.osh.pbf and .osc.pbf) are encoded
+using googles protobuf library for the low-level storage. They are constructed
+by nesting data on two levels:
+
+On the lower level the file is constructed using BlobHeaders and Blobs. A .osm.pbf
+file contains multiple sequences of
+ 1. a 4-byte header size, stored in network-byte-order
+ 2. a BlobHeader of exactly this size
+ 3. a Blob
+
+The BlobHeader tells the reader about the type and size of the following Blob. The
+Blob can contain data in raw or zlib-compressed form. After uncompressing the blob
+it is treated differently depending on the type specified in the BlobHeader.
+
+The contents of the Blob belongs to the higher level. It contains either an HeaderBlock
+(type="OSMHeader") or an PrimitiveBlock (type="OSMData"). The file needs to have
+at least one HeaderBlock before the first PrimitiveBlock.
+
+The HeaderBlock contains meta-information like the writing program or a bbox. It may
+also contain multiple "required features" that describe what kinds of input a
+reading program needs to handle in order to fully understand the files' contents.
+
+The PrimitiveBlock can store multiple types of objects (i.e. 5 nodes, 2 ways and
+1 relation). It contains one or more PrimitiveGroup which in turn contain multiple
+nodes, ways or relations. A PrimitiveGroup should only contain one kind of object.
+
+There's a special kind of "object type" called dense-nodes. It is used to store nodes
+in a very dense format, avoiding message overheads and using delta-encoding for nearly
+all ids.
+
+All Strings are stored as indexes to rows in a StringTable. The StringTable contains
+one row for each used string, so strings that are used multiple times need to be
+stored only once. The StringTable is sorted by usage-count, so the most often used
+string is stored at index 1.
+
+A simple outline of a .osm.pbf file could look like this:
+
+ 4-bytes header size
+ BlobHeader
+ Blob
+ HeaderBlock
+ 4-bytes header size
+ BlobHeader
+ Blob
+ PrimitiveBlock
+ StringTable
+ PrimitiveGroup
+ 5 nodes
+ PrimitiveGroup
+ 2 ways
+ PrimitiveGroup
+ 1 relation
+
+More complete outlines of real .osm.pbf files can be created using the osmpbf-outline tool:
+ <https://github.com/MaZderMind/OSM-binary/tree/osmpbf-outline>
+*/
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <future>
+#include <iostream>
+#include <memory>
+#include <ratio>
+#include <string>
+#include <thread>
+#include <time.h>
+#include <utility>
+
+#include <osmium/handler.hpp>
+#include <osmium/io/detail/output_format.hpp>
+#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_stringtable.hpp>
+#include <osmium/io/detail/zlib.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/collection.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/util/cast.hpp>
+#include <osmium/visitor.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ namespace {
+
+ /**
+ * Serialize a protobuf message into a Blob, optionally apply compression
+ * and return it together with a BlobHeader ready to be written to a file.
+ *
+ * @param type Type-string used in the BlobHeader.
+ * @param msg Protobuf-message.
+ * @param use_compression Should the output be compressed using zlib?
+ */
+ std::string serialize_blob(const std::string& type, const google::protobuf::MessageLite& msg, bool use_compression) {
+ OSMPBF::Blob pbf_blob;
+
+ {
+ std::string content;
+ msg.SerializeToString(&content);
+
+ pbf_blob.set_raw_size(static_cast_with_assert<::google::protobuf::int32>(content.size()));
+
+ if (use_compression) {
+ pbf_blob.set_zlib_data(osmium::io::detail::zlib_compress(content));
+ } else {
+ pbf_blob.set_raw(content);
+ }
+ }
+
+ std::string blob_data;
+ pbf_blob.SerializeToString(&blob_data);
+
+ OSMPBF::BlobHeader pbf_blob_header;
+ pbf_blob_header.set_type(type);
+ pbf_blob_header.set_datasize(static_cast_with_assert<::google::protobuf::int32>(blob_data.size()));
+
+ std::string blob_header_data;
+ pbf_blob_header.SerializeToString(&blob_header_data);
+
+ uint32_t sz = htonl(static_cast_with_assert<uint32_t>(blob_header_data.size()));
+
+ // write to output: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob
+ std::string output;
+ output.reserve(sizeof(sz) + blob_header_data.size() + blob_data.size());
+ output.append(reinterpret_cast<const char*>(&sz), sizeof(sz));
+ output.append(blob_header_data);
+ output.append(blob_data);
+
+ return output;
+ }
+
+ } // anonymous namespace
+
+ class PBFOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
+
+ /**
+ * This class models a variable that keeps track of the value
+ * it was last set to and returns the delta between old and
+ * new value from the update() call.
+ */
+ template <typename T>
+ class Delta {
+
+ T m_value;
+
+ public:
+
+ Delta() :
+ m_value(0) {
+ }
+
+ void clear() {
+ m_value = 0;
+ }
+
+ T update(T new_value) {
+ using std::swap;
+ swap(m_value, new_value);
+ return m_value - new_value;
+ }
+
+ }; // class Delta
+
+ /**
+ * Maximum number of items in a primitive block.
+ *
+ * The uncompressed length of a Blob *should* be less
+ * than 16 megabytes and *must* be less than 32 megabytes.
+ *
+ * A block may contain any number of entities, as long as
+ * the size limits for the surrounding blob are obeyed.
+ * However, for simplicity, the current Osmosis (0.38)
+ * as well as Osmium implementation always
+ * uses at most 8k entities in a block.
+ */
+ static constexpr uint32_t max_block_contents = 8000;
+
+ /**
+ * The output buffer (block) will be filled to about
+ * 95% and then written to disk. This leaves more than
+ * enough space for the string table (which typically
+ * needs about 0.1 to 0.3% of the block size).
+ */
+ static constexpr int64_t buffer_fill_percent = 95;
+
+ /**
+ * protobuf-struct of a HeaderBlock
+ */
+ OSMPBF::HeaderBlock pbf_header_block;
+
+ /**
+ * protobuf-struct of a PrimitiveBlock
+ */
+ OSMPBF::PrimitiveBlock pbf_primitive_block;
+
+ /**
+ * pointer to PrimitiveGroups inside the current PrimitiveBlock,
+ * used for writing nodes, ways or relations
+ */
+ OSMPBF::PrimitiveGroup* pbf_nodes;
+ OSMPBF::PrimitiveGroup* pbf_ways;
+ OSMPBF::PrimitiveGroup* pbf_relations;
+
+ /**
+ * To flexibly handle multiple resolutions, the granularity, or
+ * resolution used for representing locations is adjustable in
+ * multiples of 1 nanodegree. The default scaling factor is 100
+ * nanodegrees, corresponding to about ~1cm at the equator.
+ * This is the current resolution of the OSM database.
+ */
+ int m_location_granularity;
+
+ /**
+ * The granularity used for representing timestamps is also adjustable in
+ * multiples of 1 millisecond. The default scaling factor is 1000
+ * milliseconds, which is the current resolution of the OSM database.
+ */
+ int m_date_granularity;
+
+ /**
+ * should nodes be serialized into the dense format?
+ *
+ * nodes can be encoded one of two ways, as a Node
+ * (m_use_dense_nodes = false) and a special dense format.
+ * In the dense format, all information is stored 'column wise',
+ * as an array of ID's, array of latitudes, and array of
+ * longitudes. Each column is delta-encoded. This reduces
+ * header overheads and allows delta-coding to work very effectively.
+ */
+ bool m_use_dense_nodes {true};
+
+ /**
+ * should the PBF blobs contain zlib compressed data?
+ *
+ * the zlib compression is optional, it's possible to store the
+ * blobs in raw format. Disabling the compression can improve the
+ * writing speed a little but the output will be 2x to 3x bigger.
+ */
+ bool m_use_compression {true};
+
+ /**
+ * While the .osm.pbf-format is able to carry all meta information, it is
+ * also able to omit this information to reduce size.
+ */
+ bool m_should_add_metadata {true};
+
+ /**
+ * Should the visible flag be added on objects?
+ */
+ bool m_add_visible;
+
+ /**
+ * counter used to quickly check the number of objects stored inside
+ * the current PrimitiveBlock. When the counter reaches max_block_contents
+ * the PrimitiveBlock is serialized into a Blob and flushed to the file.
+ *
+ * this check is performed in check_block_contents_counter() which is
+ * called once for each object.
+ */
+ uint16_t primitive_block_contents;
+ int primitive_block_size;
+
+ // StringTable management
+ StringTable string_table;
+
+ /**
+ * These variables are used to calculate the
+ * delta-encoding while storing dense-nodes. It holds the last seen values
+ * from which the difference is stored into the protobuf.
+ */
+ Delta<int64_t> m_delta_id;
+ Delta<int64_t> m_delta_lat;
+ Delta<int64_t> m_delta_lon;
+ Delta<int64_t> m_delta_timestamp;
+ Delta<int64_t> m_delta_changeset;
+ Delta<int64_t> m_delta_uid;
+ Delta<::google::protobuf::int32> m_delta_user_sid;
+
+ bool debug;
+
+ bool has_debug_level(int) {
+ return false;
+ }
+
+ ///// Blob writing /////
+
+ /**
+ * Before a PrimitiveBlock gets serialized, all interim StringTable-ids needs to be
+ * mapped to the associated real StringTable ids. This is done in this function.
+ *
+ * This function needs to know about the concrete structure of all item types to find
+ * all occurrences of string-ids.
+ */
+ void map_string_ids() {
+ // test, if the node-block has been allocated
+ if (pbf_nodes) {
+ // iterate over all nodes, passing them to the map_common_string_ids function
+ for (int i=0, l=pbf_nodes->nodes_size(); i<l; ++i) {
+ map_common_string_ids(pbf_nodes->mutable_nodes(i));
+ }
+
+ // test, if the node-block has a densenodes structure
+ if (pbf_nodes->has_dense()) {
+ // get a pointer to the densenodes structure
+ OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense();
+
+ // in the densenodes structure keys and vals are encoded in an intermixed
+ // array, individual nodes are seperated by a value of 0 (0 in the StringTable
+ // is always unused). String-ids of 0 are thus kept alone.
+ for (int i=0, l=dense->keys_vals_size(); i<l; ++i) {
+ // map interim string-ids > 0 to real string ids
+ auto sid = dense->keys_vals(i);
+ if (sid > 0) {
+ dense->set_keys_vals(i, string_table.map_string_id(sid));
+ }
+ }
+
+ // test if the densenodes block has meta infos
+ if (dense->has_denseinfo()) {
+ // get a pointer to the denseinfo structure
+ OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo();
+
+ // iterate over all username string-ids
+ for (int i=0, l=denseinfo->user_sid_size(); i<l; ++i) {
+ // map interim string-ids > 0 to real string ids
+ auto user_sid = string_table.map_string_id(denseinfo->user_sid(i));
+
+ // delta encode the string-id
+ denseinfo->set_user_sid(i, m_delta_user_sid.update(user_sid));
+ }
+ }
+ }
+ }
+
+ // test, if the ways-block has been allocated
+ if (pbf_ways) {
+ // iterate over all ways, passing them to the map_common_string_ids function
+ for (int i=0, l=pbf_ways->ways_size(); i<l; ++i) {
+ map_common_string_ids(pbf_ways->mutable_ways(i));
+ }
+ }
+
+ // test, if the relations-block has been allocated
+ if (pbf_relations) {
+ // iterate over all relations
+ for (int i=0, l=pbf_relations->relations_size(); i<l; ++i) {
+ // get a pointer to the relation
+ OSMPBF::Relation* relation = pbf_relations->mutable_relations(i);
+
+ // pass them to the map_common_string_ids function
+ map_common_string_ids(relation);
+
+ // iterate over all relation members, mapping the interim string-ids
+ // of the role to real string ids
+ for (int mi=0; mi < relation->roles_sid_size(); ++mi) {
+ relation->set_roles_sid(mi, string_table.map_string_id(relation->roles_sid(mi)));
+ }
+ }
+ }
+ }
+
+ /**
+ * a helper function used in map_string_ids to map common interim string-ids of the
+ * user name and all tags to real string ids.
+ *
+ * TPBFObject is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
+ */
+ template <class TPBFObject>
+ void map_common_string_ids(TPBFObject* in) {
+ // if the object has meta-info attached
+ if (in->has_info()) {
+ // map the interim-id of the user name to a real id
+ OSMPBF::Info* info = in->mutable_info();
+ info->set_user_sid(string_table.map_string_id(info->user_sid()));
+ }
+
+ // iterate over all tags and map the interim-ids of the key and the value to real ids
+ for (int i=0, l=in->keys_size(); i<l; ++i) {
+ in->set_keys(i, string_table.map_string_id(in->keys(i)));
+ in->set_vals(i, string_table.map_string_id(in->vals(i)));
+ }
+ }
+
+
+ ///// MetaData helper /////
+
+ /**
+ * convert a double lat or lon value to an int, respecting the current blocks granularity
+ */
+ int64_t lonlat2int(double lonlat) {
+ return static_cast<int64_t>(std::round(lonlat * OSMPBF::lonlat_resolution / location_granularity()));
+ }
+
+ /**
+ * convert a timestamp to an int, respecting the current blocks granularity
+ */
+ int64_t timestamp2int(time_t timestamp) {
+ return static_cast<int64_t>(std::round(timestamp * (1000.0 / date_granularity())));
+ }
+
+ /**
+ * helper function used in the write()-calls to apply common information from an osmium-object
+ * onto a pbf-object.
+ *
+ * TPBFObject is either OSMPBF::Node, OSMPBF::Way or OSMPBF::Relation.
+ */
+ template <class TPBFObject>
+ void apply_common_info(const osmium::OSMObject& in, TPBFObject* out) {
+ // set the object-id
+ out->set_id(in.id());
+
+ // iterate over all tags and set the keys and vals, recording the strings in the
+ // interim StringTable and storing the interim ids
+ for (const auto& tag : in.tags()) {
+ out->add_keys(string_table.record_string(tag.key()));
+ out->add_vals(string_table.record_string(tag.value()));
+ }
+
+ if (m_should_add_metadata) {
+ // add an info-section to the pbf object and set the meta-info on it
+ OSMPBF::Info* out_info = out->mutable_info();
+ if (m_add_visible) {
+ out_info->set_visible(in.visible());
+ }
+ out_info->set_version(static_cast<::google::protobuf::int32>(in.version()));
+ out_info->set_timestamp(timestamp2int(in.timestamp()));
+ out_info->set_changeset(in.changeset());
+ out_info->set_uid(static_cast<::google::protobuf::int32>(in.uid()));
+ out_info->set_user_sid(string_table.record_string(in.user()));
+ }
+ }
+
+
+ ///// High-Level Block writing /////
+
+ /**
+ * store the current pbf_header_block into a Blob and clear this struct afterwards.
+ */
+ void store_header_block() {
+ if (debug && has_debug_level(1)) {
+ std::cerr << "storing header block" << std::endl;
+ }
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(serialize_blob("OSMHeader", pbf_header_block, m_use_compression));
+
+ pbf_header_block.Clear();
+ }
+
+ /**
+ * store the interim StringTable to the current pbf_primitive_block, map all interim string ids
+ * to real StringTable ids and then store the current pbf_primitive_block into a Blob and clear
+ * this struct and all related pointers and maps afterwards.
+ */
+ void store_primitive_block() {
+ if (debug && has_debug_level(1)) {
+ std::cerr << "storing primitive block with " << primitive_block_contents << " items" << std::endl;
+ }
+
+ // set the granularity
+ pbf_primitive_block.set_granularity(location_granularity());
+ pbf_primitive_block.set_date_granularity(date_granularity());
+
+ // store the interim StringTable into the protobuf object
+ string_table.store_stringtable(pbf_primitive_block.mutable_stringtable());
+
+ // map all interim string ids to real ids
+ map_string_ids();
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(serialize_blob("OSMData", pbf_primitive_block, m_use_compression));
+
+ // clear the PrimitiveBlock struct
+ pbf_primitive_block.Clear();
+
+ // clear the interim StringTable and its id map
+ string_table.clear();
+
+ // reset the delta variables
+ m_delta_id.clear();
+ m_delta_lat.clear();
+ m_delta_lon.clear();
+ m_delta_timestamp.clear();
+ m_delta_changeset.clear();
+ m_delta_uid.clear();
+ m_delta_user_sid.clear();
+
+ // reset the contents-counter to zero
+ primitive_block_contents = 0;
+ primitive_block_size = 0;
+
+ // reset the node/way/relation pointers to nullptr
+ pbf_nodes = nullptr;
+ pbf_ways = nullptr;
+ pbf_relations = nullptr;
+ }
+
+ /**
+ * this little function checks primitive_block_contents counter against its maximum and calls
+ * store_primitive_block to flush the block to the disk when it's reached. It's also responsible
+ * for increasing this counter.
+ *
+ * this function also checks the estimated size of the current block and calls store_primitive_block
+ * when the estimated size reaches buffer_fill_percent of the maximum uncompressed blob size.
+ */
+ void check_block_contents_counter() {
+ if (primitive_block_contents >= max_block_contents) {
+ store_primitive_block();
+ } else if (primitive_block_size > OSMPBF::max_uncompressed_blob_size * buffer_fill_percent / 100) {
+ if (debug && has_debug_level(1)) {
+ std::cerr << "storing primitive_block with only " << primitive_block_contents << " items, because its ByteSize (" << primitive_block_size << ") reached " <<
+ (static_cast<float>(primitive_block_size) / static_cast<float>(OSMPBF::max_uncompressed_blob_size) * 100.0) << "% of the maximum blob-size" << std::endl;
+ }
+
+ store_primitive_block();
+ }
+
+ ++primitive_block_contents;
+ }
+
+
+ ///// Block content writing /////
+
+ /**
+ * Add a node to the block.
+ *
+ * @param node The node to add.
+ */
+ void write_node(const osmium::Node& node) {
+ // add a way to the group
+ OSMPBF::Node* pbf_node = pbf_nodes->add_nodes();
+
+ // copy the common meta-info from the osmium-object to the pbf-object
+ apply_common_info(node, pbf_node);
+
+ // modify lat & lon to integers, respecting the block's granularity and copy
+ // the ints to the pbf-object
+ pbf_node->set_lon(lonlat2int(node.location().lon_without_check()));
+ pbf_node->set_lat(lonlat2int(node.location().lat_without_check()));
+ }
+
+ /**
+ * Add a node to the block using DenseNodes.
+ *
+ * @param node The node to add.
+ */
+ void write_dense_node(const osmium::Node& node) {
+ // add a DenseNodes-Section to the PrimitiveGroup
+ OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense();
+
+ // copy the id, delta encoded
+ dense->add_id(m_delta_id.update(node.id()));
+
+ // copy the longitude, delta encoded
+ dense->add_lon(m_delta_lon.update(lonlat2int(node.location().lon_without_check())));
+
+ // copy the latitude, delta encoded
+ dense->add_lat(m_delta_lat.update(lonlat2int(node.location().lat_without_check())));
+
+ // in the densenodes structure keys and vals are encoded in an intermixed
+ // array, individual nodes are seperated by a value of 0 (0 in the StringTable
+ // is always unused)
+ // so for three nodes the keys_vals array may look like this: 3 5 2 1 0 0 8 5
+ // the first node has two tags (3=>5 and 2=>1), the second node does not
+ // have any tags and the third node has a single tag (8=>5)
+ for (const auto& tag : node.tags()) {
+ dense->add_keys_vals(string_table.record_string(tag.key()));
+ dense->add_keys_vals(string_table.record_string(tag.value()));
+ }
+ dense->add_keys_vals(0);
+
+ if (m_should_add_metadata) {
+ // add a DenseInfo-Section to the PrimitiveGroup
+ OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo();
+
+ denseinfo->add_version(static_cast<::google::protobuf::int32>(node.version()));
+
+ if (m_add_visible) {
+ denseinfo->add_visible(node.visible());
+ }
+
+ // copy the timestamp, delta encoded
+ denseinfo->add_timestamp(m_delta_timestamp.update(timestamp2int(node.timestamp())));
+
+ // copy the changeset, delta encoded
+ denseinfo->add_changeset(m_delta_changeset.update(node.changeset()));
+
+ // copy the user id, delta encoded
+ denseinfo->add_uid(static_cast<::google::protobuf::int32>(m_delta_uid.update(node.uid())));
+
+ // record the user-name to the interim stringtable and copy the
+ // interim string-id to the pbf-object
+ denseinfo->add_user_sid(string_table.record_string(node.user()));
+ }
+ }
+
+ /**
+ * Add a way to the block.
+ *
+ * @param way The way to add.
+ */
+ void write_way(const osmium::Way& way) {
+ // add a way to the group
+ OSMPBF::Way* pbf_way = pbf_ways->add_ways();
+
+ // copy the common meta-info from the osmium-object to the pbf-object
+ apply_common_info(way, pbf_way);
+
+ // last way-node-id used for delta-encoding
+ Delta<int64_t> delta_id;
+
+ for (const auto& node_ref : way.nodes()) {
+ // copy the way-node-id, delta encoded
+ pbf_way->add_refs(delta_id.update(node_ref.ref()));
+ }
+
+ // count up blob size by the size of the Way
+ primitive_block_size += pbf_way->ByteSize();
+ }
+
+ /**
+ * Add a relation to the block.
+ *
+ * @param relation The relation to add.
+ */
+ void write_relation(const osmium::Relation& relation) {
+ // add a relation to the group
+ OSMPBF::Relation* pbf_relation = pbf_relations->add_relations();
+
+ // copy the common meta-info from the osmium-object to the pbf-object
+ apply_common_info(relation, pbf_relation);
+
+ Delta<int64_t> delta_id;
+
+ for (const auto& member : relation.members()) {
+ // record the relation-member role to the interim stringtable and copy the
+ // interim string-id to the pbf-object
+ pbf_relation->add_roles_sid(string_table.record_string(member.role()));
+
+ // copy the relation-member-id, delta encoded
+ pbf_relation->add_memids(delta_id.update(member.ref()));
+
+ // copy the relation-member-type, mapped to the OSMPBF enum
+ pbf_relation->add_types(item_type_to_osmpbf_membertype(member.type()));
+ }
+
+ // count up blob size by the size of the Relation
+ primitive_block_size += pbf_relation->ByteSize();
+ }
+
+ // objects of this class can't be copied
+ PBFOutputFormat(const PBFOutputFormat&) = delete;
+ PBFOutputFormat& operator=(const PBFOutputFormat&) = delete;
+
+ public:
+
+ /**
+ * Create PBFOutputFormat object from File.
+ */
+ explicit PBFOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) :
+ OutputFormat(file, output_queue),
+ pbf_header_block(),
+ pbf_primitive_block(),
+ pbf_nodes(nullptr),
+ pbf_ways(nullptr),
+ pbf_relations(nullptr),
+ m_location_granularity(pbf_primitive_block.granularity()),
+ m_date_granularity(pbf_primitive_block.date_granularity()),
+ m_add_visible(file.has_multiple_object_versions()),
+ primitive_block_contents(0),
+ primitive_block_size(0),
+ string_table(),
+ m_delta_id(),
+ m_delta_lat(),
+ m_delta_lon(),
+ m_delta_timestamp(),
+ m_delta_changeset(),
+ m_delta_uid(),
+ m_delta_user_sid(),
+ debug(true) {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ if (file.get("pbf_dense_nodes") == "false") {
+ m_use_dense_nodes = false;
+ }
+ if (file.get("pbf_compression") == "none" || file.get("pbf_compression") == "false") {
+ m_use_compression = false;
+ }
+ if (file.get("pbf_add_metadata") == "false") {
+ m_should_add_metadata = false;
+ }
+ }
+
+ void write_buffer(osmium::memory::Buffer&& buffer) override final {
+ osmium::apply(buffer.cbegin(), buffer.cend(), *this);
+ }
+
+
+ /**
+ * getter to access the granularity
+ */
+ int location_granularity() const {
+ return m_location_granularity;
+ }
+
+ /**
+ * setter to set the granularity
+ */
+ PBFOutputFormat& location_granularity(int g) {
+ m_location_granularity = g;
+ return *this;
+ }
+
+
+ /**
+ * getter to access the date_granularity
+ */
+ int date_granularity() const {
+ return m_date_granularity;
+ }
+
+ /**
+ * Set date granularity.
+ */
+ PBFOutputFormat& date_granularity(int g) {
+ m_date_granularity = g;
+ return *this;
+ }
+
+
+ /**
+ * Initialize the writing process.
+ *
+ * This initializes the header-block, sets the required-features and
+ * the writing-program and adds the obligatory StringTable-Index 0.
+ */
+ void write_header(const osmium::io::Header& header) override final {
+ // add the schema version as required feature to the HeaderBlock
+ pbf_header_block.add_required_features("OsmSchema-V0.6");
+
+ // when the densenodes-feature is used, add DenseNodes as required feature
+ if (m_use_dense_nodes) {
+ pbf_header_block.add_required_features("DenseNodes");
+ }
+
+ // when the resulting file will carry history information, add
+ // HistoricalInformation as required feature
+ if (m_file.has_multiple_object_versions()) {
+ pbf_header_block.add_required_features("HistoricalInformation");
+ }
+
+ // set the writing program
+ pbf_header_block.set_writingprogram(header.get("generator"));
+
+ if (!header.boxes().empty()) {
+ OSMPBF::HeaderBBox* pbf_bbox = pbf_header_block.mutable_bbox();
+ osmium::Box box = header.joined_boxes();
+ pbf_bbox->set_left(static_cast<::google::protobuf::int64>(box.bottom_left().lon() * OSMPBF::lonlat_resolution));
+ pbf_bbox->set_bottom(static_cast<::google::protobuf::int64>(box.bottom_left().lat() * OSMPBF::lonlat_resolution));
+ pbf_bbox->set_right(static_cast<::google::protobuf::int64>(box.top_right().lon() * OSMPBF::lonlat_resolution));
+ pbf_bbox->set_top(static_cast<::google::protobuf::int64>(box.top_right().lat() * OSMPBF::lonlat_resolution));
+ }
+
+ 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.set_osmosis_replication_timestamp(ts);
+ }
+
+ std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number");
+ if (!osmosis_replication_sequence_number.empty()) {
+ pbf_header_block.set_osmosis_replication_sequence_number(std::atoll(osmosis_replication_sequence_number.c_str()));
+ }
+
+ std::string osmosis_replication_base_url = header.get("osmosis_replication_base_url");
+ if (!osmosis_replication_base_url.empty()) {
+ pbf_header_block.set_osmosis_replication_base_url(osmosis_replication_base_url);
+ }
+
+ store_header_block();
+ }
+
+ /**
+ * Add a node to the pbf.
+ *
+ * A call to this method won't write the node to the file directly but
+ * cache it for later bulk-writing. Calling final() ensures that everything
+ * gets written and every file pointer is closed.
+ */
+ void node(const osmium::Node& node) {
+ // first of we check the contents-counter which may flush the cached nodes to
+ // disk if the limit is reached. This call also increases the contents-counter
+ check_block_contents_counter();
+
+ if (debug && has_debug_level(2)) {
+ std::cerr << "node " << node.id() << " v" << node.version() << std::endl;
+ }
+
+ // if no PrimitiveGroup for nodes has been added, add one and save the pointer
+ if (!pbf_nodes) {
+ pbf_nodes = pbf_primitive_block.add_primitivegroup();
+ }
+
+ if (m_use_dense_nodes) {
+ write_dense_node(node);
+ } else {
+ write_node(node);
+ }
+ }
+
+ /**
+ * Add a way to the pbf.
+ *
+ * A call to this method won't write the way to the file directly but
+ * cache it for later bulk-writing. Calling final() ensures that everything
+ * gets written and every file pointer is closed.
+ */
+ void way(const osmium::Way& way) {
+ // first of we check the contents-counter which may flush the cached ways to
+ // disk if the limit is reached. This call also increases the contents-counter
+ check_block_contents_counter();
+
+ // if no PrimitiveGroup for nodes has been added, add one and save the pointer
+ if (!pbf_ways) {
+ pbf_ways = pbf_primitive_block.add_primitivegroup();
+ }
+
+ write_way(way);
+ }
+
+ /**
+ * Add a relation to the pbf.
+ *
+ * A call to this method won't write the way to the file directly but
+ * cache it for later bulk-writing. Calling final() ensures that everything
+ * gets written and every file pointer is closed.
+ */
+ void relation(const osmium::Relation& relation) {
+ // first of we check the contents-counter which may flush the cached relations to
+ // disk if the limit is reached. This call also increases the contents-counter
+ check_block_contents_counter();
+
+ // if no PrimitiveGroup for relations has been added, add one and save the pointer
+ if (!pbf_relations) {
+ pbf_relations = pbf_primitive_block.add_primitivegroup();
+ }
+
+ write_relation(relation);
+ }
+
+ /**
+ * Finalize the writing process, flush any open primitive blocks to the file and
+ * close the file.
+ */
+ void close() override final {
+ if (debug && has_debug_level(1)) {
+ std::cerr << "finishing" << std::endl;
+ }
+
+ // if the current block contains any elements, flush it to the protobuf
+ if (primitive_block_contents > 0) {
+ store_primitive_block();
+ }
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(std::string());
+ }
+
+ }; // class PBFOutputFormat
+
+ namespace {
+
+ const bool registered_pbf_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::pbf,
+ [](const osmium::io::File& file, data_queue_type& output_queue) {
+ return new osmium::io::detail::PBFOutputFormat(file, output_queue);
+ });
+
+ } // anonymous namespace
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/pbf_parser.hpp b/include/osmium/io/detail/pbf_parser.hpp
new file mode 100644
index 0000000..d99819d
--- /dev/null
+++ b/include/osmium/io/detail/pbf_parser.hpp
@@ -0,0 +1,455 @@
+#ifndef OSMIUM_IO_DETAIL_PBF_PRIMITIVE_BLOCK_PARSER_HPP
+#define OSMIUM_IO_DETAIL_PBF_PRIMITIVE_BLOCK_PARSER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <algorithm>
+
+#include <osmpbf/osmpbf.h>
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/zlib.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/entity_bits.hpp>
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ class PBFPrimitiveBlockParser {
+
+ static constexpr size_t initial_buffer_size = 2 * 1024 * 1024;
+
+ const std::string& m_data;
+
+ const OSMPBF::StringTable* m_stringtable;
+ int64_t m_lon_offset;
+ int64_t m_lat_offset;
+ int64_t m_date_factor;
+ int32_t m_granularity;
+
+ osmium::osm_entity_bits::type m_read_types;
+
+ osmium::memory::Buffer m_buffer;
+
+ PBFPrimitiveBlockParser(const PBFPrimitiveBlockParser&) = delete;
+ PBFPrimitiveBlockParser(PBFPrimitiveBlockParser&&) = delete;
+
+ PBFPrimitiveBlockParser& operator=(const PBFPrimitiveBlockParser&) = delete;
+ PBFPrimitiveBlockParser& operator=(PBFPrimitiveBlockParser&&) = delete;
+
+ public:
+
+ explicit PBFPrimitiveBlockParser(const std::string& data, osmium::osm_entity_bits::type read_types) :
+ m_data(data),
+ m_stringtable(nullptr),
+ m_lon_offset(0),
+ m_lat_offset(0),
+ m_date_factor(1000),
+ m_granularity(100),
+ m_read_types(read_types),
+ m_buffer(initial_buffer_size) {
+ }
+
+ ~PBFPrimitiveBlockParser() = default;
+
+ osmium::memory::Buffer operator()() {
+ OSMPBF::PrimitiveBlock pbf_primitive_block;
+ if (!pbf_primitive_block.ParseFromString(m_data)) {
+ throw osmium::pbf_error("failed to parse PrimitiveBlock");
+ }
+
+ m_stringtable = &pbf_primitive_block.stringtable();
+ m_lon_offset = pbf_primitive_block.lon_offset();
+ m_lat_offset = pbf_primitive_block.lat_offset();
+ m_date_factor = pbf_primitive_block.date_granularity() / 1000;
+ m_granularity = pbf_primitive_block.granularity();
+
+ for (int i=0; i < pbf_primitive_block.primitivegroup_size(); ++i) {
+ const OSMPBF::PrimitiveGroup& group = pbf_primitive_block.primitivegroup(i);
+
+ if (group.has_dense()) {
+ if (m_read_types & osmium::osm_entity_bits::node) parse_dense_node_group(group);
+ } else if (group.ways_size() != 0) {
+ if (m_read_types & osmium::osm_entity_bits::way) parse_way_group(group);
+ } else if (group.relations_size() != 0) {
+ if (m_read_types & osmium::osm_entity_bits::relation) parse_relation_group(group);
+ } else if (group.nodes_size() != 0) {
+ if (m_read_types & osmium::osm_entity_bits::node) parse_node_group(group);
+ } else {
+ throw osmium::pbf_error("group of unknown type");
+ }
+ }
+
+ return std::move(m_buffer);
+ }
+
+ private:
+
+ template <class TBuilder, class TPBFObject>
+ void parse_attributes(TBuilder& builder, const TPBFObject& pbf_object) {
+ auto& object = builder.object();
+
+ object.set_id(pbf_object.id());
+
+ if (pbf_object.has_info()) {
+ object.set_version(static_cast_with_assert<object_version_type>(pbf_object.info().version()))
+ .set_changeset(static_cast_with_assert<changeset_id_type>(pbf_object.info().changeset()))
+ .set_timestamp(pbf_object.info().timestamp() * m_date_factor)
+ .set_uid_from_signed(pbf_object.info().uid());
+ if (pbf_object.info().has_visible()) {
+ object.set_visible(pbf_object.info().visible());
+ }
+ builder.add_user(m_stringtable->s(static_cast_with_assert<int>(pbf_object.info().user_sid())));
+ } else {
+ builder.add_user("", 1);
+ }
+ }
+
+ void parse_node_group(const OSMPBF::PrimitiveGroup& group) {
+ for (int i=0; i < group.nodes_size(); ++i) {
+ osmium::builder::NodeBuilder builder(m_buffer);
+ const OSMPBF::Node& pbf_node = group.nodes(i);
+ parse_attributes(builder, pbf_node);
+
+ if (builder.object().visible()) {
+ builder.object().set_location(osmium::Location(
+ (pbf_node.lon() * m_granularity + m_lon_offset) / (OSMPBF::lonlat_resolution / osmium::Location::coordinate_precision),
+ (pbf_node.lat() * m_granularity + m_lat_offset) / (OSMPBF::lonlat_resolution / osmium::Location::coordinate_precision)));
+ }
+
+ if (pbf_node.keys_size() > 0) {
+ osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
+ for (int tag=0; tag < pbf_node.keys_size(); ++tag) {
+ tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_node.keys(tag))),
+ m_stringtable->s(static_cast<int>(pbf_node.vals(tag))));
+ }
+ }
+
+ m_buffer.commit();
+ }
+ }
+
+ void parse_way_group(const OSMPBF::PrimitiveGroup& group) {
+ for (int i=0; i < group.ways_size(); ++i) {
+ osmium::builder::WayBuilder builder(m_buffer);
+ const OSMPBF::Way& pbf_way = group.ways(i);
+ parse_attributes(builder, pbf_way);
+
+ if (pbf_way.refs_size() > 0) {
+ osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
+ int64_t ref = 0;
+ for (int n=0; n < pbf_way.refs_size(); ++n) {
+ ref += pbf_way.refs(n);
+ wnl_builder.add_node_ref(ref);
+ }
+ }
+
+ if (pbf_way.keys_size() > 0) {
+ osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
+ for (int tag=0; tag < pbf_way.keys_size(); ++tag) {
+ tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_way.keys(tag))),
+ m_stringtable->s(static_cast<int>(pbf_way.vals(tag))));
+ }
+ }
+
+ m_buffer.commit();
+ }
+ }
+
+ void parse_relation_group(const OSMPBF::PrimitiveGroup& group) {
+ for (int i=0; i < group.relations_size(); ++i) {
+ osmium::builder::RelationBuilder builder(m_buffer);
+ const OSMPBF::Relation& pbf_relation = group.relations(i);
+ parse_attributes(builder, pbf_relation);
+
+ if (pbf_relation.types_size() > 0) {
+ osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
+ int64_t ref = 0;
+ for (int n=0; n < pbf_relation.types_size(); ++n) {
+ ref += pbf_relation.memids(n);
+ rml_builder.add_member(osmpbf_membertype_to_item_type(pbf_relation.types(n)), ref, m_stringtable->s(pbf_relation.roles_sid(n)));
+ }
+ }
+
+ if (pbf_relation.keys_size() > 0) {
+ osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
+ for (int tag=0; tag < pbf_relation.keys_size(); ++tag) {
+ tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_relation.keys(tag))),
+ m_stringtable->s(static_cast<int>(pbf_relation.vals(tag))));
+ }
+ }
+
+ m_buffer.commit();
+ }
+ }
+
+ int add_tags(const OSMPBF::DenseNodes& dense, int n, osmium::builder::NodeBuilder* builder) {
+ if (n >= dense.keys_vals_size()) {
+ return n;
+ }
+
+ if (dense.keys_vals(n) == 0) {
+ return n+1;
+ }
+
+ osmium::builder::TagListBuilder tl_builder(m_buffer, builder);
+
+ while (n < dense.keys_vals_size()) {
+ int tag_key_pos = dense.keys_vals(n++);
+
+ if (tag_key_pos == 0) {
+ break;
+ }
+
+ tl_builder.add_tag(m_stringtable->s(tag_key_pos),
+ m_stringtable->s(dense.keys_vals(n)));
+
+ ++n;
+ }
+
+ return n;
+ }
+
+ void parse_dense_node_group(const OSMPBF::PrimitiveGroup& group) {
+ int64_t last_dense_id = 0;
+ int64_t last_dense_latitude = 0;
+ int64_t last_dense_longitude = 0;
+ int64_t last_dense_uid = 0;
+ int64_t last_dense_user_sid = 0;
+ int64_t last_dense_changeset = 0;
+ int64_t last_dense_timestamp = 0;
+ int last_dense_tag = 0;
+
+ const OSMPBF::DenseNodes& dense = group.dense();
+
+ for (int i=0; i < dense.id_size(); ++i) {
+ bool visible = true;
+
+ last_dense_id += dense.id(i);
+ last_dense_latitude += dense.lat(i);
+ last_dense_longitude += dense.lon(i);
+
+ if (dense.has_denseinfo()) {
+ last_dense_changeset += dense.denseinfo().changeset(i);
+ last_dense_timestamp += dense.denseinfo().timestamp(i);
+ last_dense_uid += dense.denseinfo().uid(i);
+ last_dense_user_sid += dense.denseinfo().user_sid(i);
+ if (dense.denseinfo().visible_size() > 0) {
+ visible = dense.denseinfo().visible(i);
+ }
+ assert(last_dense_changeset >= 0);
+ assert(last_dense_timestamp >= 0);
+ assert(last_dense_uid >= -1);
+ assert(last_dense_user_sid >= 0);
+ }
+
+ osmium::builder::NodeBuilder builder(m_buffer);
+ osmium::Node& node = builder.object();
+
+ node.set_id(last_dense_id);
+
+ if (dense.has_denseinfo()) {
+ auto v = dense.denseinfo().version(i);
+ assert(v > 0);
+ node.set_version(static_cast<osmium::object_version_type>(v));
+ node.set_changeset(static_cast<osmium::changeset_id_type>(last_dense_changeset));
+ node.set_timestamp(last_dense_timestamp * m_date_factor);
+ node.set_uid_from_signed(static_cast<osmium::signed_user_id_type>(last_dense_uid));
+ node.set_visible(visible);
+ builder.add_user(m_stringtable->s(static_cast<int>(last_dense_user_sid)));
+ } else {
+ builder.add_user("", 1);
+ }
+
+ if (visible) {
+ builder.object().set_location(osmium::Location(
+ (last_dense_longitude * m_granularity + m_lon_offset) / (OSMPBF::lonlat_resolution / osmium::Location::coordinate_precision),
+ (last_dense_latitude * m_granularity + m_lat_offset) / (OSMPBF::lonlat_resolution / osmium::Location::coordinate_precision)));
+ }
+
+ last_dense_tag = add_tags(dense, last_dense_tag, &builder);
+ m_buffer.commit();
+ }
+ }
+
+ }; // class PBFPrimitiveBlockParser
+
+ /**
+ * PBF blobs can optionally be packed with the zlib algorithm.
+ * This function returns the raw data (if it was unpacked) or
+ * the unpacked data (if it was packed).
+ *
+ * @param input_data Reference to input data.
+ * @returns Unpacked data
+ * @throws osmium::pbf_error If there was a problem parsing the PBF
+ */
+ inline std::unique_ptr<const std::string> unpack_blob(const std::string& input_data) {
+ OSMPBF::Blob pbf_blob;
+ if (!pbf_blob.ParseFromString(input_data)) {
+ throw osmium::pbf_error("failed to parse blob");
+ }
+
+ if (pbf_blob.has_raw()) {
+ return std::unique_ptr<std::string>(pbf_blob.release_raw());
+ } else if (pbf_blob.has_zlib_data()) {
+ auto raw_size = pbf_blob.raw_size();
+ assert(raw_size >= 0);
+ assert(raw_size <= OSMPBF::max_uncompressed_blob_size);
+ return osmium::io::detail::zlib_uncompress(pbf_blob.zlib_data(), static_cast<unsigned long>(raw_size));
+ } else if (pbf_blob.has_lzma_data()) {
+ throw osmium::pbf_error("lzma blobs not implemented");
+ } else {
+ throw osmium::pbf_error("blob contains no data");
+ }
+ }
+
+ /**
+ * Parse blob as a HeaderBlock.
+ *
+ * @param input_buffer Blob data
+ * @returns Header object
+ * @throws osmium::pbf_error If there was a parsing error
+ */
+ inline osmium::io::Header parse_header_blob(const std::string& input_buffer) {
+ const std::unique_ptr<const std::string> data = unpack_blob(input_buffer);
+
+ OSMPBF::HeaderBlock pbf_header_block;
+ if (!pbf_header_block.ParseFromString(*data)) {
+ throw osmium::pbf_error("failed to parse HeaderBlock");
+ }
+
+ osmium::io::Header header;
+ for (int i=0; i < pbf_header_block.required_features_size(); ++i) {
+ const std::string& feature = pbf_header_block.required_features(i);
+
+ if (feature == "OsmSchema-V0.6") continue;
+ if (feature == "DenseNodes") {
+ header.set("pbf_dense_nodes", true);
+ continue;
+ }
+ if (feature == "HistoricalInformation") {
+ header.set_has_multiple_object_versions(true);
+ continue;
+ }
+
+ throw osmium::pbf_error(std::string("required feature not supported: ") + feature);
+ }
+
+ for (int i=0; i < pbf_header_block.optional_features_size(); ++i) {
+ const std::string& feature = pbf_header_block.optional_features(i);
+ header.set("pbf_optional_feature_" + std::to_string(i), feature);
+ }
+
+ if (pbf_header_block.has_writingprogram()) {
+ header.set("generator", pbf_header_block.writingprogram());
+ }
+
+ if (pbf_header_block.has_bbox()) {
+ const OSMPBF::HeaderBBox& pbf_bbox = pbf_header_block.bbox();
+ const int64_t resolution_convert = OSMPBF::lonlat_resolution / osmium::Location::coordinate_precision;
+ osmium::Box box;
+ box.extend(osmium::Location(pbf_bbox.left() / resolution_convert, pbf_bbox.bottom() / resolution_convert));
+ box.extend(osmium::Location(pbf_bbox.right() / resolution_convert, pbf_bbox.top() / resolution_convert));
+ header.add_box(box);
+ }
+
+ if (pbf_header_block.has_osmosis_replication_timestamp()) {
+ header.set("osmosis_replication_timestamp", osmium::Timestamp(pbf_header_block.osmosis_replication_timestamp()).to_iso());
+ }
+
+ if (pbf_header_block.has_osmosis_replication_sequence_number()) {
+ header.set("osmosis_replication_sequence_number", std::to_string(pbf_header_block.osmosis_replication_sequence_number()));
+ }
+
+ if (pbf_header_block.has_osmosis_replication_base_url()) {
+ header.set("osmosis_replication_base_url", pbf_header_block.osmosis_replication_base_url());
+ }
+
+ return header;
+ }
+
+ class DataBlobParser {
+
+ std::shared_ptr<std::string> m_input_buffer;
+ osmium::osm_entity_bits::type m_read_types;
+
+ public:
+
+ DataBlobParser(std::string&& input_buffer, osmium::osm_entity_bits::type read_types) :
+ m_input_buffer(std::make_shared<std::string>(std::move(input_buffer))),
+ m_read_types(read_types) {
+ if (input_buffer.size() > OSMPBF::max_uncompressed_blob_size) {
+ throw osmium::pbf_error(std::string("invalid blob size: " + std::to_string(input_buffer.size())));
+ }
+ }
+/*
+ DataBlobParser(const DataBlobParser& other) :
+ m_input_buffer(std::move(other.m_input_buffer)),
+ m_read_types(other.m_read_types) {
+ }*/
+
+ DataBlobParser(const DataBlobParser&) = default;
+ DataBlobParser& operator=(const DataBlobParser&) = default;
+
+ DataBlobParser(DataBlobParser&&) = default;
+ DataBlobParser& operator=(DataBlobParser&&) = default;
+
+ ~DataBlobParser() = default;
+
+ osmium::memory::Buffer operator()() {
+ const std::unique_ptr<const std::string> data = unpack_blob(*m_input_buffer);
+ PBFPrimitiveBlockParser parser(*data, m_read_types);
+ return parser();
+ }
+
+ }; // class DataBlobParser
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_PBF_PRIMITIVE_BLOCK_PARSER_HPP
diff --git a/include/osmium/io/detail/pbf_stringtable.hpp b/include/osmium/io/detail/pbf_stringtable.hpp
new file mode 100644
index 0000000..c56d549
--- /dev/null
+++ b/include/osmium/io/detail/pbf_stringtable.hpp
@@ -0,0 +1,204 @@
+#ifndef OSMIUM_IO_DETAIL_PBF_STRINGTABLE_HPP
+#define OSMIUM_IO_DETAIL_PBF_STRINGTABLE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <osmpbf/osmpbf.h>
+
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ /**
+ * StringTable management for PBF writer
+ *
+ * All strings are stored as indexes to rows in a StringTable. The StringTable contains
+ * one row for each used string, so strings that are used multiple times need to be
+ * stored only once. The StringTable is sorted by usage-count, so the most often used
+ * string is stored at index 1.
+ */
+ class StringTable {
+
+ public:
+
+ /// type for string IDs (interim and final)
+ typedef uint16_t string_id_type;
+
+ private:
+
+ /**
+ * this is the struct used to build the StringTable. It is stored as
+ * the value-part in the strings-map.
+ *
+ * when a new string is added to the map, its count is set to 0 and
+ * the interim_id is set to the current size of the map. This interim_id
+ * is then stored into the pbf-objects.
+ *
+ * before the PrimitiveBlock is serialized, the map is sorted by count
+ * and stored into the pbf-StringTable. Afterwards the interim-ids are
+ * mapped to the "real" id in the StringTable.
+ *
+ * this way often used strings get lower ids in the StringTable. As the
+ * protobuf-serializer stores numbers in variable bit-lengths, lower
+ * IDs means less used space in the resulting file.
+ */
+ struct string_info {
+
+ /// number of occurrences of this string
+ uint16_t count;
+
+ /// an intermediate-id
+ string_id_type interim_id;
+
+ }; // struct string_info
+
+ /**
+ * Interim StringTable, storing all strings that should be written to
+ * the StringTable once the block is written to disk.
+ */
+ typedef std::map<std::string, string_info> string2string_info_type;
+ string2string_info_type m_strings;
+
+ /**
+ * This vector is used to map the interim IDs to real StringTable IDs after
+ * writing all strings to the StringTable.
+ */
+ typedef std::vector<string_id_type> interim_id2id_type;
+ interim_id2id_type m_id2id_map;
+
+ size_t m_size = 0;
+
+ public:
+
+ StringTable() {
+ }
+
+ friend bool operator<(const string_info& lhs, const string_info& rhs) {
+ return lhs.count > rhs.count;
+ }
+
+ /**
+ * record a string in the interim StringTable if it's missing, otherwise just increase its counter,
+ * return the interim-id assigned to the string.
+ */
+ string_id_type record_string(const std::string& string) {
+ string_info& info = m_strings[string];
+ if (info.interim_id == 0) {
+ ++m_size;
+ info.interim_id = static_cast_with_assert<string_id_type>(m_size);
+ } else {
+ info.count++;
+ }
+ return info.interim_id;
+ }
+
+ /**
+ * Sort the interim StringTable and store it to the real protobuf StringTable.
+ * while storing to the real table, this function fills the id2id_map with
+ * pairs, mapping the interim-ids to final and real StringTable ids.
+ *
+ * Note that the m_strings table is a std::map and as such is sorted lexicographically.
+ * When the transformation into the sortedby multimap is done, it gets sorted by
+ * the count. The end result (at least with the glibc standard container/algorithm
+ * implementation) is that the string table is sorted first by reverse count (ie descending)
+ * and then by reverse lexicographic order.
+ */
+ void store_stringtable(OSMPBF::StringTable* st) {
+ // add empty StringTable entry at index 0
+ // StringTable index 0 is reserved as delimiter in the densenodes key/value list
+ // this line also ensures that there's always a valid StringTable
+ st->add_s("");
+
+ std::multimap<string_info, std::string> sortedbycount;
+
+ m_id2id_map.resize(m_size+1);
+
+ std::transform(m_strings.begin(), m_strings.end(),
+ std::inserter(sortedbycount, sortedbycount.begin()),
+ [](const std::pair<std::string, string_info>& p) {
+ return std::pair<string_info, std::string>(p.second, p.first);
+ });
+
+ string_id_type n=0;
+
+ for (const auto& mapping : sortedbycount) {
+ // add the string of the current item to the pbf StringTable
+ st->add_s(mapping.second);
+
+ // store the mapping from the interim-id to the real id
+ m_id2id_map[mapping.first.interim_id] = ++n;
+ }
+ }
+
+ /**
+ * Map from an interim ID to a real string ID.
+ */
+ string_id_type map_string_id(const string_id_type interim_id) const {
+ return m_id2id_map[interim_id];
+ }
+
+ template <typename T>
+ string_id_type map_string_id(const T interim_id) const {
+ return map_string_id(static_cast_with_assert<string_id_type>(interim_id));
+ }
+
+ /**
+ * Clear the stringtable, preparing for the next block.
+ */
+ void clear() {
+ m_strings.clear();
+ m_id2id_map.clear();
+ m_size = 0;
+ }
+
+ }; // class StringTable
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_PBF_STRINGTABLE_HPP
diff --git a/include/osmium/io/detail/read_thread.hpp b/include/osmium/io/detail/read_thread.hpp
new file mode 100644
index 0000000..bce4f55
--- /dev/null
+++ b/include/osmium/io/detail/read_thread.hpp
@@ -0,0 +1,106 @@
+#ifndef OSMIUM_IO_DETAIL_READ_THREAD_HPP
+#define OSMIUM_IO_DETAIL_READ_THREAD_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <atomic>
+#include <chrono>
+#include <ratio>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <osmium/io/compression.hpp>
+#include <osmium/thread/queue.hpp>
+#include <osmium/thread/util.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ class ReadThread {
+
+ osmium::thread::Queue<std::string>& m_queue;
+ osmium::io::Decompressor* m_decompressor;
+
+ // If this is set in the main thread, we have to wrap up at the
+ // next possible moment.
+ std::atomic<bool>& m_done;
+
+ public:
+
+ explicit ReadThread(osmium::thread::Queue<std::string>& queue, osmium::io::Decompressor* decompressor, std::atomic<bool>& done) :
+ m_queue(queue),
+ m_decompressor(decompressor),
+ m_done(done) {
+ }
+
+ bool operator()() {
+ osmium::thread::set_thread_name("_osmium_input");
+
+ try {
+ while (!m_done) {
+ std::string data {m_decompressor->read()};
+ if (data.empty()) {
+ m_queue.push(std::move(data));
+ break;
+ }
+ m_queue.push(std::move(data));
+ while (m_queue.size() > 10 && !m_done) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ }
+
+ m_decompressor->close();
+ } catch (...) {
+ // If there is an exception in this thread, we make sure
+ // to push an empty string onto the queue to signal the
+ // end-of-data to the reading thread so that it will not
+ // hang. Then we re-throw the exception.
+ m_queue.push(std::string());
+ throw;
+ }
+ return true;
+ }
+
+ }; // class ReadThread
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_READ_THREAD_HPP
diff --git a/include/osmium/io/detail/read_write.hpp b/include/osmium/io/detail/read_write.hpp
new file mode 100644
index 0000000..6651cce
--- /dev/null
+++ b/include/osmium/io/detail/read_write.hpp
@@ -0,0 +1,160 @@
+#ifndef OSMIUM_IO_DETAIL_READ_WRITE_HPP
+#define OSMIUM_IO_DETAIL_READ_WRITE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cerrno>
+#include <cstddef>
+#include <fcntl.h>
+#include <string>
+#include <system_error>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+
+#include <osmium/io/overwrite.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * @brief Namespace for Osmium internal use
+ */
+ namespace detail {
+
+ /**
+ * Open file for writing. If the file exists, it is truncated, if
+ * not, it is created. If the file name is empty or "-", no file
+ * is opened and the stdout file descriptor (1) is returned.
+ *
+ * @param filename Name of file to be opened.
+ * @param allow_overwrite If the file exists, should it be overwritten?
+ * @returns File descriptor of open file.
+ * @throws system_error if the file can't be opened.
+ */
+ inline int open_for_writing(const std::string& filename, osmium::io::overwrite allow_overwrite = osmium::io::overwrite::no) {
+ if (filename == "" || filename == "-") {
+ return 1; // stdout
+ } else {
+ int flags = O_WRONLY | O_CREAT;
+ if (allow_overwrite == osmium::io::overwrite::allow) {
+ flags |= O_TRUNC;
+ } else {
+ flags |= O_EXCL;
+ }
+#ifdef _WIN32
+ flags |= O_BINARY;
+#endif
+ int fd = ::open(filename.c_str(), flags, 0666);
+ if (fd < 0) {
+ throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'");
+ }
+ return fd;
+ }
+ }
+
+ /**
+ * Open file for reading. If the file name is empty or "-", no file
+ * is opened and the stdin file descriptor (0) is returned.
+ *
+ * @param filename Name of file to be opened.
+ * @returns File descriptor of open file.
+ * @throws system_error if the file can't be opened.
+ */
+ inline int open_for_reading(const std::string& filename) {
+ if (filename == "" || filename == "-") {
+ return 0; // stdin
+ } else {
+ int flags = O_RDONLY;
+#ifdef _WIN32
+ flags |= O_BINARY;
+#endif
+ int fd = ::open(filename.c_str(), flags);
+ if (fd < 0) {
+ throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'");
+ }
+ return fd;
+ }
+ }
+
+ /**
+ * Writes the given number of bytes from the output_buffer to the file descriptor.
+ * This is just a wrapper around write(2), because write(2) can write less than
+ * the given number of bytes.
+ *
+ * @param fd File descriptor.
+ * @param output_buffer Buffer with data to be written. Must be at least size bytes long.
+ * @param size Number of bytes to write.
+ * @throws std::system_error On error.
+ */
+ inline void reliable_write(const int fd, const unsigned char* output_buffer, const size_t size) {
+ constexpr size_t max_write = 100 * 1024 * 1024; // Max 100 MByte per write
+ size_t offset = 0;
+ do {
+ auto write_count = size - offset;
+ if (write_count > max_write) {
+ write_count = max_write;
+ }
+ auto length = ::write(fd, output_buffer + offset, static_cast<unsigned int>(write_count));
+ if (length < 0) {
+ throw std::system_error(errno, std::system_category(), "Write failed");
+ }
+ offset += static_cast<size_t>(length);
+ } while (offset < size);
+ }
+
+ /**
+ * Writes the given number of bytes from the output_buffer to the file descriptor.
+ * This is just a wrapper around write(2), because write(2) can write less than
+ * the given number of bytes.
+ *
+ * @param fd File descriptor.
+ * @param output_buffer Buffer with data to be written. Must be at least size bytes long.
+ * @param size Number of bytes to write.
+ * @throws std::system_error On error.
+ */
+ inline void reliable_write(const int fd, const char* output_buffer, const size_t size) {
+ reliable_write(fd, reinterpret_cast<const unsigned char*>(output_buffer), size);
+ }
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_READ_WRITE_HPP
diff --git a/include/osmium/io/detail/write_thread.hpp b/include/osmium/io/detail/write_thread.hpp
new file mode 100644
index 0000000..fad22ed
--- /dev/null
+++ b/include/osmium/io/detail/write_thread.hpp
@@ -0,0 +1,86 @@
+#ifndef OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
+#define OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <future>
+#include <string>
+
+#include <osmium/io/compression.hpp>
+#include <osmium/io/detail/output_format.hpp>
+#include <osmium/thread/util.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ class WriteThread {
+
+ typedef osmium::io::detail::data_queue_type data_queue_type;
+
+ data_queue_type& m_input_queue;
+ osmium::io::Compressor* m_compressor;
+
+ public:
+
+ explicit WriteThread(data_queue_type& input_queue, osmium::io::Compressor* compressor) :
+ m_input_queue(input_queue),
+ m_compressor(compressor) {
+ }
+
+ bool operator()() {
+ osmium::thread::set_thread_name("_osmium_output");
+
+ std::future<std::string> data_future;
+ std::string data;
+ do {
+ m_input_queue.wait_and_pop(data_future);
+ data = data_future.get();
+ m_compressor->write(data);
+ } while (!data.empty());
+
+ m_compressor->close();
+ return true;
+ }
+
+ }; // class WriteThread
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
new file mode 100644
index 0000000..96003d8
--- /dev/null
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -0,0 +1,724 @@
+#ifndef OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include <future>
+#include <iostream>
+#include <memory>
+#include <ratio>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <expat.h>
+
+#include <osmium/builder/builder.hpp>
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/io/detail/input_format.hpp>
+#include <osmium/io/error.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/entity_bits.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/thread/queue.hpp>
+#include <osmium/thread/util.hpp>
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when the XML parser failed. The exception contains
+ * information about the place where the error happened and the type of
+ * error.
+ */
+ struct xml_error : public io_error {
+
+ unsigned long line;
+ unsigned long column;
+ XML_Error error_code;
+ std::string error_string;
+
+ xml_error(XML_Parser parser) :
+ io_error(std::string("XML parsing error at line ")
+ + std::to_string(XML_GetCurrentLineNumber(parser))
+ + ", column "
+ + std::to_string(XML_GetCurrentColumnNumber(parser))
+ + ": "
+ + XML_ErrorString(XML_GetErrorCode(parser))),
+ line(XML_GetCurrentLineNumber(parser)),
+ column(XML_GetCurrentColumnNumber(parser)),
+ error_code(XML_GetErrorCode(parser)),
+ error_string(XML_ErrorString(error_code)) {
+ }
+
+ }; // struct xml_error
+
+ struct format_version_error : public io_error {
+
+ std::string version;
+
+ explicit format_version_error() :
+ io_error("Can not read file without version (missing version attribute on osm element)."),
+ version() {
+ }
+
+ explicit format_version_error(const char* v) :
+ io_error(std::string("Can not read file with version ") + v),
+ version(v) {
+ }
+
+ }; // struct format_version_error
+
+ namespace io {
+
+ class File;
+
+ namespace detail {
+
+ /**
+ * Once the header is fully parsed this exception will be thrown if
+ * the caller is not interested in anything else except the header.
+ * It will break off the parsing at this point.
+ *
+ * This exception is never seen by user code, it is caught internally.
+ */
+ class ParserIsDone : std::exception {
+ };
+
+ class XMLParser {
+
+ static constexpr int buffer_size = 10 * 1000 * 1000;
+
+ enum class context {
+ root,
+ top,
+ node,
+ way,
+ relation,
+ changeset,
+ ignored_node,
+ ignored_way,
+ ignored_relation,
+ ignored_changeset,
+ in_object
+ }; // enum class context
+
+ context m_context;
+ context m_last_context;
+
+ /**
+ * This is used only for change files which contain create, modify,
+ * and delete sections.
+ */
+ bool m_in_delete_section;
+
+ osmium::io::Header m_header;
+
+ osmium::memory::Buffer m_buffer;
+
+ std::unique_ptr<osmium::builder::NodeBuilder> m_node_builder;
+ std::unique_ptr<osmium::builder::WayBuilder> m_way_builder;
+ std::unique_ptr<osmium::builder::RelationBuilder> m_relation_builder;
+ std::unique_ptr<osmium::builder::ChangesetBuilder> m_changeset_builder;
+
+ std::unique_ptr<osmium::builder::TagListBuilder> m_tl_builder;
+ std::unique_ptr<osmium::builder::WayNodeListBuilder> m_wnl_builder;
+ std::unique_ptr<osmium::builder::RelationMemberListBuilder> m_rml_builder;
+
+ osmium::thread::Queue<std::string>& m_input_queue;
+ osmium::thread::Queue<osmium::memory::Buffer>& m_queue;
+ std::promise<osmium::io::Header>& m_header_promise;
+
+ osmium::osm_entity_bits::type m_read_types;
+
+ std::atomic<bool>& m_done;
+
+ /**
+ * A C++ wrapper for the Expat parser that makes sure no memory is leaked.
+ */
+ template <class T>
+ class ExpatXMLParser {
+
+ XML_Parser m_parser;
+
+ static void XMLCALL start_element_wrapper(void* data, const XML_Char* element, const XML_Char** attrs) {
+ static_cast<XMLParser*>(data)->start_element(element, attrs);
+ }
+
+ static void XMLCALL end_element_wrapper(void* data, const XML_Char* element) {
+ static_cast<XMLParser*>(data)->end_element(element);
+ }
+
+ public:
+
+ ExpatXMLParser(T* callback_object) :
+ m_parser(XML_ParserCreate(nullptr)) {
+ if (!m_parser) {
+ throw osmium::io_error("Internal error: Can not create parser");
+ }
+ XML_SetUserData(m_parser, callback_object);
+ XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper);
+ }
+
+ ExpatXMLParser(const ExpatXMLParser&) = delete;
+ ExpatXMLParser(ExpatXMLParser&&) = delete;
+
+ ExpatXMLParser& operator=(const ExpatXMLParser&) = delete;
+ ExpatXMLParser& operator=(ExpatXMLParser&&) = delete;
+
+ ~ExpatXMLParser() {
+ XML_ParserFree(m_parser);
+ }
+
+ void operator()(const std::string& data, bool last) {
+ if (XML_Parse(m_parser, data.data(), static_cast_with_assert<int>(data.size()), last) == XML_STATUS_ERROR) {
+ throw osmium::xml_error(m_parser);
+ }
+ }
+
+ }; // class ExpatXMLParser
+
+ /**
+ * A helper class that makes sure a promise is kept. It stores
+ * a reference to some piece of data and to a promise and, on
+ * destruction, sets the value of the promise from the data.
+ */
+ template <class T>
+ class PromiseKeeper {
+
+ T& m_data;
+ std::promise<T>& m_promise;
+
+ public:
+
+ PromiseKeeper(T& data, std::promise<T>& promise) :
+ m_data(data),
+ m_promise(promise) {
+ }
+
+ ~PromiseKeeper() {
+ m_promise.set_value(m_data);
+ }
+
+ }; // class PromiseKeeper
+
+ public:
+
+ explicit XMLParser(osmium::thread::Queue<std::string>& input_queue, osmium::thread::Queue<osmium::memory::Buffer>& queue, std::promise<osmium::io::Header>& header_promise, osmium::osm_entity_bits::type read_types, std::atomic<bool>& done) :
+ m_context(context::root),
+ m_last_context(context::root),
+ m_in_delete_section(false),
+ m_header(),
+ m_buffer(buffer_size),
+ m_node_builder(),
+ m_way_builder(),
+ m_relation_builder(),
+ m_changeset_builder(),
+ m_tl_builder(),
+ m_wnl_builder(),
+ m_rml_builder(),
+ m_input_queue(input_queue),
+ m_queue(queue),
+ m_header_promise(header_promise),
+ m_read_types(read_types),
+ m_done(done) {
+ }
+
+ /**
+ * The copy constructor is needed for storing XMLParser in a std::function.
+ * The copy will look the same as if it has been initialized with the
+ * same parameters as the original. Any state changes in the original will
+ * not be reflected in the copy.
+ */
+ XMLParser(const XMLParser& other) :
+ m_context(context::root),
+ m_last_context(context::root),
+ m_in_delete_section(false),
+ m_header(),
+ m_buffer(buffer_size),
+ m_node_builder(),
+ m_way_builder(),
+ m_relation_builder(),
+ m_changeset_builder(),
+ m_tl_builder(),
+ m_wnl_builder(),
+ m_rml_builder(),
+ m_input_queue(other.m_input_queue),
+ m_queue(other.m_queue),
+ m_header_promise(other.m_header_promise),
+ m_read_types(other.m_read_types),
+ m_done(other.m_done) {
+ }
+
+ XMLParser(XMLParser&&) = default;
+
+ XMLParser& operator=(const XMLParser&) = delete;
+
+ XMLParser& operator=(XMLParser&&) = default;
+
+ ~XMLParser() = default;
+
+ bool operator()() {
+ ExpatXMLParser<XMLParser> parser(this);
+ PromiseKeeper<osmium::io::Header> promise_keeper(m_header, m_header_promise);
+ bool last;
+ do {
+ std::string data;
+ m_input_queue.wait_and_pop(data);
+ last = data.empty();
+ try {
+ parser(data, last);
+ } catch (ParserIsDone&) {
+ return true;
+ } catch (...) {
+ m_queue.push(osmium::memory::Buffer()); // empty buffer to signify eof
+ throw;
+ }
+ } while (!last && !m_done);
+ if (m_buffer.committed() > 0) {
+ m_queue.push(std::move(m_buffer));
+ }
+ m_queue.push(osmium::memory::Buffer()); // empty buffer to signify eof
+ return true;
+ }
+
+ private:
+
+ const char* init_object(osmium::OSMObject& object, const XML_Char** attrs) {
+ static const char* empty = "";
+ const char* user = empty;
+
+ if (m_in_delete_section) {
+ object.set_visible(false);
+ }
+
+ osmium::Location location;
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "lon")) {
+ location.set_lon(std::atof(attrs[count+1])); // XXX doesn't detect garbage after the number
+ } else if (!strcmp(attrs[count], "lat")) {
+ location.set_lat(std::atof(attrs[count+1])); // XXX doesn't detect garbage after the number
+ } else if (!strcmp(attrs[count], "user")) {
+ user = attrs[count+1];
+ } else {
+ object.set_attribute(attrs[count], attrs[count+1]);
+ }
+ }
+
+ if (location && object.type() == osmium::item_type::node) {
+ static_cast<osmium::Node&>(object).set_location(location);
+ }
+
+ return user;
+ }
+
+ void init_changeset(osmium::builder::ChangesetBuilder* builder, const XML_Char** attrs) {
+ static const char* empty = "";
+ const char* user = empty;
+ osmium::Changeset& new_changeset = builder->object();
+
+ osmium::Location min;
+ osmium::Location max;
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "min_lon")) {
+ min.set_lon(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "min_lat")) {
+ min.set_lat(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "max_lon")) {
+ max.set_lon(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "max_lat")) {
+ max.set_lat(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "user")) {
+ user = attrs[count+1];
+ } else {
+ new_changeset.set_attribute(attrs[count], attrs[count+1]);
+ }
+ }
+
+ new_changeset.bounds().extend(min);
+ new_changeset.bounds().extend(max);
+
+ builder->add_user(user);
+ }
+
+ void check_tag(osmium::builder::Builder* builder, const XML_Char* element, const XML_Char** attrs) {
+ if (!strcmp(element, "tag")) {
+ m_wnl_builder.reset();
+ m_rml_builder.reset();
+
+ const char* key = "";
+ const char* value = "";
+ for (int count = 0; attrs[count]; count += 2) {
+ if (attrs[count][0] == 'k' && attrs[count][1] == 0) {
+ key = attrs[count+1];
+ } else if (attrs[count][0] == 'v' && attrs[count][1] == 0) {
+ value = attrs[count+1];
+ }
+ }
+ if (!m_tl_builder) {
+ m_tl_builder = std::unique_ptr<osmium::builder::TagListBuilder>(new osmium::builder::TagListBuilder(m_buffer, builder));
+ }
+ m_tl_builder->add_tag(key, value);
+ }
+ }
+
+ void header_is_done() {
+ if (m_read_types == osmium::osm_entity_bits::nothing) {
+ throw ParserIsDone();
+ }
+ }
+
+ 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")) {
+ m_header.set_has_multiple_object_versions(true);
+ }
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "version")) {
+ m_header.set("version", attrs[count+1]);
+ if (strcmp(attrs[count+1], "0.6")) {
+ throw osmium::format_version_error(attrs[count+1]);
+ }
+ } else if (!strcmp(attrs[count], "generator")) {
+ m_header.set("generator", attrs[count+1]);
+ }
+ }
+ if (m_header.get("version") == "") {
+ throw osmium::format_version_error();
+ }
+ }
+ m_context = context::top;
+ break;
+ case context::top:
+ assert(!m_tl_builder);
+ if (!strcmp(element, "node")) {
+ header_is_done();
+ if (m_read_types & osmium::osm_entity_bits::node) {
+ m_node_builder = std::unique_ptr<osmium::builder::NodeBuilder>(new osmium::builder::NodeBuilder(m_buffer));
+ m_node_builder->add_user(init_object(m_node_builder->object(), attrs));
+ m_context = context::node;
+ } else {
+ m_context = context::ignored_node;
+ }
+ } else if (!strcmp(element, "way")) {
+ header_is_done();
+ if (m_read_types & osmium::osm_entity_bits::way) {
+ m_way_builder = std::unique_ptr<osmium::builder::WayBuilder>(new osmium::builder::WayBuilder(m_buffer));
+ m_way_builder->add_user(init_object(m_way_builder->object(), attrs));
+ m_context = context::way;
+ } else {
+ m_context = context::ignored_way;
+ }
+ } else if (!strcmp(element, "relation")) {
+ header_is_done();
+ if (m_read_types & osmium::osm_entity_bits::relation) {
+ m_relation_builder = std::unique_ptr<osmium::builder::RelationBuilder>(new osmium::builder::RelationBuilder(m_buffer));
+ m_relation_builder->add_user(init_object(m_relation_builder->object(), attrs));
+ m_context = context::relation;
+ } else {
+ m_context = context::ignored_relation;
+ }
+ } else if (!strcmp(element, "changeset")) {
+ header_is_done();
+ if (m_read_types & osmium::osm_entity_bits::changeset) {
+ m_changeset_builder = std::unique_ptr<osmium::builder::ChangesetBuilder>(new osmium::builder::ChangesetBuilder(m_buffer));
+ init_changeset(m_changeset_builder.get(), attrs);
+ m_context = context::changeset;
+ } else {
+ m_context = context::ignored_changeset;
+ }
+ } else if (!strcmp(element, "bounds")) {
+ osmium::Location min;
+ osmium::Location max;
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "minlon")) {
+ min.set_lon(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "minlat")) {
+ min.set_lat(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "maxlon")) {
+ max.set_lon(atof(attrs[count+1]));
+ } else if (!strcmp(attrs[count], "maxlat")) {
+ max.set_lat(atof(attrs[count+1]));
+ }
+ }
+ osmium::Box box;
+ box.extend(min).extend(max);
+ m_header.add_box(box);
+ } else if (!strcmp(element, "delete")) {
+ m_in_delete_section = true;
+ }
+ break;
+ case context::node:
+ m_last_context = context::node;
+ m_context = context::in_object;
+ check_tag(m_node_builder.get(), element, attrs);
+ break;
+ case context::way:
+ m_last_context = context::way;
+ m_context = context::in_object;
+ if (!strcmp(element, "nd")) {
+ m_tl_builder.reset();
+
+ if (!m_wnl_builder) {
+ m_wnl_builder = std::unique_ptr<osmium::builder::WayNodeListBuilder>(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get()));
+ }
+
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "ref")) {
+ m_wnl_builder->add_node_ref(osmium::string_to_object_id(attrs[count+1]));
+ }
+ }
+ } else {
+ check_tag(m_way_builder.get(), element, attrs);
+ }
+ break;
+ case context::relation:
+ m_last_context = context::relation;
+ m_context = context::in_object;
+ if (!strcmp(element, "member")) {
+ m_tl_builder.reset();
+
+ if (!m_rml_builder) {
+ m_rml_builder = std::unique_ptr<osmium::builder::RelationMemberListBuilder>(new osmium::builder::RelationMemberListBuilder(m_buffer, m_relation_builder.get()));
+ }
+
+ char type = 'x';
+ object_id_type ref = 0;
+ const char* role = "";
+ for (int count = 0; attrs[count]; count += 2) {
+ if (!strcmp(attrs[count], "type")) {
+ type = static_cast<char>(attrs[count+1][0]);
+ } else if (!strcmp(attrs[count], "ref")) {
+ ref = osmium::string_to_object_id(attrs[count+1]);
+ } else if (!strcmp(attrs[count], "role")) {
+ role = static_cast<const char*>(attrs[count+1]);
+ }
+ }
+ // XXX assert type, ref, role are set
+ m_rml_builder->add_member(char_to_item_type(type), ref, role);
+ } else {
+ check_tag(m_relation_builder.get(), element, attrs);
+ }
+ break;
+ case context::changeset:
+ m_last_context = context::changeset;
+ m_context = context::in_object;
+ check_tag(m_changeset_builder.get(), element, attrs);
+ break;
+ case context::ignored_node:
+ break;
+ case context::ignored_way:
+ break;
+ case context::ignored_relation:
+ break;
+ case context::ignored_changeset:
+ break;
+ case context::in_object:
+ assert(false); // should never be here
+ break;
+ }
+ }
+
+ void end_element(const XML_Char* element) {
+ switch (m_context) {
+ case context::root:
+ assert(false); // should never be here
+ break;
+ case context::top:
+ if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
+ header_is_done();
+ m_context = context::root;
+ } else if (!strcmp(element, "delete")) {
+ m_in_delete_section = false;
+ }
+ break;
+ case context::node:
+ assert(!strcmp(element, "node"));
+ m_tl_builder.reset();
+ m_node_builder.reset();
+ m_buffer.commit();
+ m_context = context::top;
+ flush_buffer();
+ break;
+ case context::way:
+ assert(!strcmp(element, "way"));
+ m_tl_builder.reset();
+ m_wnl_builder.reset();
+ m_way_builder.reset();
+ m_buffer.commit();
+ m_context = context::top;
+ flush_buffer();
+ break;
+ case context::relation:
+ assert(!strcmp(element, "relation"));
+ m_tl_builder.reset();
+ m_rml_builder.reset();
+ m_relation_builder.reset();
+ m_buffer.commit();
+ m_context = context::top;
+ flush_buffer();
+ break;
+ case context::changeset:
+ assert(!strcmp(element, "changeset"));
+ m_tl_builder.reset();
+ m_changeset_builder.reset();
+ m_buffer.commit();
+ m_context = context::top;
+ flush_buffer();
+ break;
+ case context::in_object:
+ m_context = m_last_context;
+ break;
+ case context::ignored_node:
+ if (!strcmp(element, "node")) {
+ m_context = context::top;
+ }
+ break;
+ case context::ignored_way:
+ if (!strcmp(element, "way")) {
+ m_context = context::top;
+ }
+ break;
+ case context::ignored_relation:
+ if (!strcmp(element, "relation")) {
+ m_context = context::top;
+ }
+ break;
+ case context::ignored_changeset:
+ if (!strcmp(element, "changeset")) {
+ m_context = context::top;
+ }
+ break;
+ }
+ }
+
+ void flush_buffer() {
+ if (m_buffer.capacity() - m_buffer.committed() < 1000 * 1000) {
+ m_queue.push(std::move(m_buffer));
+ osmium::memory::Buffer buffer(buffer_size);
+ std::swap(m_buffer, buffer);
+ }
+ }
+
+ }; // class XMLParser
+
+ class XMLInputFormat : public osmium::io::detail::InputFormat {
+
+ static constexpr size_t max_queue_size = 100;
+
+ osmium::thread::Queue<osmium::memory::Buffer> m_queue;
+ std::atomic<bool> m_done;
+ std::promise<osmium::io::Header> m_header_promise;
+ std::future<bool> m_parser_future;
+
+ public:
+
+ /**
+ * Instantiate XML Parser
+ *
+ * @param file osmium::io::File instance describing file to be read from.
+ * @param read_which_entities Which types of OSM entities (nodes, ways, relations, changesets) should be parsed?
+ * @param input_queue String queue where data is read from.
+ */
+ explicit XMLInputFormat(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>& input_queue) :
+ osmium::io::detail::InputFormat(file, read_which_entities),
+ m_queue(max_queue_size, "xml_parser_results"),
+ m_done(false),
+ m_header_promise(),
+ m_parser_future(std::async(std::launch::async, XMLParser(input_queue, m_queue, m_header_promise, read_which_entities, m_done))) {
+ }
+
+ ~XMLInputFormat() {
+ try {
+ close();
+ } catch (...) {
+ // ignore any exceptions at this point because destructor should not throw
+ }
+ }
+
+ virtual osmium::io::Header header() override final {
+ osmium::thread::check_for_exception(m_parser_future);
+ return m_header_promise.get_future().get();
+ }
+
+ osmium::memory::Buffer read() override {
+ osmium::memory::Buffer buffer;
+ if (!m_done || !m_queue.empty()) {
+ m_queue.wait_and_pop(buffer);
+ }
+
+ osmium::thread::check_for_exception(m_parser_future);
+ return buffer;
+ }
+
+ void close() override {
+ m_done = true;
+ osmium::thread::wait_until_done(m_parser_future);
+ }
+
+ }; // class XMLInputFormat
+
+ namespace {
+
+ const bool registered_xml_input = osmium::io::detail::InputFormatFactory::instance().register_input_format(osmium::io::file_format::xml,
+ [](const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities, osmium::thread::Queue<std::string>& input_queue) {
+ return new osmium::io::detail::XMLInputFormat(file, read_which_entities, input_queue);
+ });
+
+ } // anonymous namespace
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp
new file mode 100644
index 0000000..73a7263
--- /dev/null
+++ b/include/osmium/io/detail/xml_output_format.hpp
@@ -0,0 +1,484 @@
+#ifndef OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
+#define OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <chrono>
+#include <cinttypes>
+#include <cstddef>
+#include <cstdio>
+#include <future>
+#include <iterator>
+#include <memory>
+#include <ratio>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <osmium/handler.hpp>
+#include <osmium/io/detail/output_format.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/collection.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/changeset.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/thread/pool.hpp>
+#include <osmium/visitor.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ struct XMLWriteError {};
+
+ namespace {
+
+ void xml_string(std::string& out, const char* in) {
+ for (; *in != '\0'; ++in) {
+ switch(*in) {
+ case '&': out += "&"; break;
+ case '\"': out += """; break;
+ case '\'': out += "'"; break;
+ case '<': out += "<"; break;
+ case '>': out += ">"; break;
+ default: out += *in; break;
+ }
+ }
+ }
+
+ const size_t tmp_buffer_size = 100;
+
+ template <typename T>
+ void oprintf(std::string& out, const char* format, T value) {
+ char buffer[tmp_buffer_size+1];
+ size_t max_size = sizeof(buffer)/sizeof(char);
+#ifndef NDEBUG
+ int len =
+#endif
+#ifndef _MSC_VER
+ snprintf(buffer, max_size, format, value);
+#else
+ _snprintf(buffer, max_size, format, value);
+#endif
+ assert(len > 0 && static_cast<size_t>(len) < max_size);
+ out += buffer;
+ }
+
+ } // anonymous namespace
+
+ class XMLOutputBlock : public osmium::handler::Handler {
+
+ // operation (create, modify, delete) for osc files
+ enum class operation {
+ op_none = 0,
+ op_create = 1,
+ op_modify = 2,
+ op_delete = 3
+ }; // enum class operation
+
+ std::shared_ptr<osmium::memory::Buffer> m_input_buffer;
+
+ std::shared_ptr<std::string> m_out;
+
+ operation m_last_op {operation::op_none};
+
+ const bool m_write_visible_flag;
+ const bool m_write_change_ops;
+
+ void write_spaces(int num) {
+ for (; num!=0; --num) {
+ *m_out += ' ';
+ }
+ }
+
+ void write_prefix() {
+ if (m_write_change_ops) {
+ write_spaces(4);
+ } else {
+ write_spaces(2);
+ }
+ }
+
+ void write_meta(const osmium::OSMObject& object) {
+ oprintf(*m_out, " id=\"%" PRId64 "\"", object.id());
+
+ if (object.version()) {
+ oprintf(*m_out, " version=\"%d\"", object.version());
+ }
+
+ if (object.timestamp()) {
+ *m_out += " timestamp=\"";
+ *m_out += object.timestamp().to_iso();
+ *m_out += "\"";
+ }
+
+ if (!object.user_is_anonymous()) {
+ oprintf(*m_out, " uid=\"%d\" user=\"", object.uid());
+ xml_string(*m_out, object.user());
+ *m_out += "\"";
+ }
+
+ if (object.changeset()) {
+ oprintf(*m_out, " changeset=\"%d\"", object.changeset());
+ }
+
+ if (m_write_visible_flag) {
+ if (object.visible()) {
+ *m_out += " visible=\"true\"";
+ } else {
+ *m_out += " visible=\"false\"";
+ }
+ }
+ }
+
+ void write_tags(const osmium::TagList& tags) {
+ for (const auto& tag : tags) {
+ write_prefix();
+ *m_out += " <tag k=\"";
+ xml_string(*m_out, tag.key());
+ *m_out += "\" v=\"";
+ xml_string(*m_out, tag.value());
+ *m_out += "\"/>\n";
+ }
+ }
+
+ void open_close_op_tag(const operation op = operation::op_none) {
+ if (op == m_last_op) {
+ return;
+ }
+
+ switch (m_last_op) {
+ case operation::op_none:
+ break;
+ case operation::op_create:
+ *m_out += " </create>\n";
+ break;
+ case operation::op_modify:
+ *m_out += " </modify>\n";
+ break;
+ case operation::op_delete:
+ *m_out += " </delete>\n";
+ break;
+ }
+
+ switch (op) {
+ case operation::op_none:
+ break;
+ case operation::op_create:
+ *m_out += " <create>\n";
+ break;
+ case operation::op_modify:
+ *m_out += " <modify>\n";
+ break;
+ case operation::op_delete:
+ *m_out += " <delete>\n";
+ break;
+ }
+
+ m_last_op = op;
+ }
+
+ public:
+
+ explicit XMLOutputBlock(osmium::memory::Buffer&& buffer, bool write_visible_flag, bool write_change_ops) :
+ m_input_buffer(std::make_shared<osmium::memory::Buffer>(std::move(buffer))),
+ m_out(std::make_shared<std::string>()),
+ m_write_visible_flag(write_visible_flag && !write_change_ops),
+ m_write_change_ops(write_change_ops) {
+ }
+
+ XMLOutputBlock(const XMLOutputBlock&) = default;
+ XMLOutputBlock& operator=(const XMLOutputBlock&) = default;
+
+ XMLOutputBlock(XMLOutputBlock&&) = default;
+ XMLOutputBlock& operator=(XMLOutputBlock&&) = default;
+
+ ~XMLOutputBlock() = default;
+
+ std::string operator()() {
+ osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
+
+ if (m_write_change_ops) {
+ open_close_op_tag();
+ }
+
+ std::string out;
+ std::swap(out, *m_out);
+ return out;
+ }
+
+ void node(const osmium::Node& node) {
+ if (m_write_change_ops) {
+ open_close_op_tag(node.visible() ? (node.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
+ }
+
+ write_prefix();
+ *m_out += "<node";
+
+ write_meta(node);
+
+ if (node.location()) {
+ *m_out += " lat=\"";
+ osmium::util::double2string(std::back_inserter(*m_out), node.location().lat_without_check(), 7);
+ *m_out += "\" lon=\"";
+ osmium::util::double2string(std::back_inserter(*m_out), node.location().lon_without_check(), 7);
+ *m_out += "\"";
+ }
+
+ if (node.tags().empty()) {
+ *m_out += "/>\n";
+ return;
+ }
+
+ *m_out += ">\n";
+
+ write_tags(node.tags());
+
+ write_prefix();
+ *m_out += "</node>\n";
+ }
+
+ void way(const osmium::Way& way) {
+ if (m_write_change_ops) {
+ open_close_op_tag(way.visible() ? (way.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
+ }
+
+ write_prefix();
+ *m_out += "<way";
+ write_meta(way);
+
+ if (way.tags().empty() && way.nodes().empty()) {
+ *m_out += "/>\n";
+ return;
+ }
+
+ *m_out += ">\n";
+
+ for (const auto& node_ref : way.nodes()) {
+ write_prefix();
+ oprintf(*m_out, " <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
+ }
+
+ write_tags(way.tags());
+
+ write_prefix();
+ *m_out += "</way>\n";
+ }
+
+ void relation(const osmium::Relation& relation) {
+ if (m_write_change_ops) {
+ open_close_op_tag(relation.visible() ? (relation.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
+ }
+
+ write_prefix();
+ *m_out += "<relation";
+ write_meta(relation);
+
+ if (relation.tags().empty() && relation.members().empty()) {
+ *m_out += "/>\n";
+ return;
+ }
+
+ *m_out += ">\n";
+
+ for (const auto& member : relation.members()) {
+ write_prefix();
+ *m_out += " <member type=\"";
+ *m_out += item_type_to_name(member.type());
+ oprintf(*m_out, "\" ref=\"%" PRId64 "\" role=\"", member.ref());
+ xml_string(*m_out, member.role());
+ *m_out += "\"/>\n";
+ }
+
+ write_tags(relation.tags());
+
+ write_prefix();
+ *m_out += "</relation>\n";
+ }
+
+ void changeset(const osmium::Changeset& changeset) {
+ write_prefix();
+ *m_out += "<changeset";
+
+ oprintf(*m_out, " id=\"%" PRId32 "\"", changeset.id());
+
+ if (changeset.created_at()) {
+ *m_out += " created_at=\"";
+ *m_out += changeset.created_at().to_iso();
+ *m_out += "\"";
+ }
+
+ oprintf(*m_out, " num_changes=\"%" PRId32 "\"", changeset.num_changes());
+
+ if (changeset.closed_at()) {
+ *m_out += " closed_at=\"";
+ *m_out += changeset.closed_at().to_iso();
+ *m_out += "\" open=\"false\"";
+ } else {
+ *m_out += " open=\"true\"";
+ }
+
+ if (changeset.bounds()) {
+ oprintf(*m_out, " min_lon=\"%.7f\"", changeset.bounds().bottom_left().lon_without_check());
+ oprintf(*m_out, " min_lat=\"%.7f\"", changeset.bounds().bottom_left().lat_without_check());
+ oprintf(*m_out, " max_lon=\"%.7f\"", changeset.bounds().top_right().lon_without_check());
+ oprintf(*m_out, " max_lat=\"%.7f\"", changeset.bounds().top_right().lat_without_check());
+ }
+
+ if (!changeset.user_is_anonymous()) {
+ *m_out += " user=\"";
+ xml_string(*m_out, changeset.user());
+ oprintf(*m_out, "\" uid=\"%d\"", changeset.uid());
+ }
+
+ if (changeset.tags().empty()) {
+ *m_out += "/>\n";
+ return;
+ }
+
+ *m_out += ">\n";
+
+ write_tags(changeset.tags());
+
+ write_prefix();
+ *m_out += "</changeset>\n";
+ }
+
+ }; // class XMLOutputBlock
+
+ class XMLOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
+
+ bool m_write_visible_flag;
+
+ public:
+
+ XMLOutputFormat(const osmium::io::File& file, data_queue_type& output_queue) :
+ OutputFormat(file, output_queue),
+ m_write_visible_flag(file.has_multiple_object_versions() || m_file.is_true("force_visible_flag")) {
+ }
+
+ XMLOutputFormat(const XMLOutputFormat&) = delete;
+ XMLOutputFormat& operator=(const XMLOutputFormat&) = delete;
+
+ ~XMLOutputFormat() override final {
+ }
+
+ void write_buffer(osmium::memory::Buffer&& buffer) override final {
+ m_output_queue.push(osmium::thread::Pool::instance().submit(XMLOutputBlock{std::move(buffer), m_write_visible_flag, m_file.is_true("xml_change_format")}));
+ }
+
+ void write_header(const osmium::io::Header& header) override final {
+ std::string out = "<?xml version='1.0' encoding='UTF-8'?>\n";
+
+ if (m_file.is_true("xml_change_format")) {
+ out += "<osmChange version=\"0.6\" generator=\"";
+ xml_string(out, header.get("generator").c_str());
+ out += "\">\n";
+ } else {
+ out += "<osm version=\"0.6\"";
+
+ std::string xml_josm_upload = header.get("xml_josm_upload");
+ if (xml_josm_upload == "true" || xml_josm_upload == "false") {
+ out += " upload=\"";
+ out += xml_josm_upload;
+ out += "\"";
+ }
+ out += " generator=\"";
+ xml_string(out, header.get("generator").c_str());
+ out += "\">\n";
+ }
+
+ for (const auto& box : header.boxes()) {
+ out += " <bounds";
+ oprintf(out, " minlon=\"%.7f\"", box.bottom_left().lon());
+ oprintf(out, " minlat=\"%.7f\"", box.bottom_left().lat());
+ oprintf(out, " maxlon=\"%.7f\"", box.top_right().lon());
+ oprintf(out, " maxlat=\"%.7f\"/>\n", box.top_right().lat());
+ }
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(std::move(out));
+ }
+
+ void close() override final {
+ {
+ std::string out;
+ if (m_file.is_true("xml_change_format")) {
+ out += "</osmChange>\n";
+ } else {
+ out += "</osm>\n";
+ }
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(std::move(out));
+ }
+
+ std::promise<std::string> promise;
+ m_output_queue.push(promise.get_future());
+ promise.set_value(std::string());
+ }
+
+ }; // class XMLOutputFormat
+
+ namespace {
+
+ const bool registered_xml_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::xml,
+ [](const osmium::io::File& file, data_queue_type& output_queue) {
+ return new osmium::io::detail::XMLOutputFormat(file, output_queue);
+ });
+
+ } // anonymous namespace
+
+ } // namespace detail
+
+ } // namespace output
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
diff --git a/include/osmium/io/detail/zlib.hpp b/include/osmium/io/detail/zlib.hpp
new file mode 100644
index 0000000..a402bf7
--- /dev/null
+++ b/include/osmium/io/detail/zlib.hpp
@@ -0,0 +1,113 @@
+#ifndef OSMIUM_IO_DETAIL_ZLIB_HPP
+#define OSMIUM_IO_DETAIL_ZLIB_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <memory>
+#include <stdexcept>
+#include <string>
+
+#include <zlib.h>
+
+#include <osmium/util/cast.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ namespace detail {
+
+ /**
+ * Compress data using zlib.
+ *
+ * Note that this function can not compress data larger than
+ * what fits in an unsigned long, on Windows this is usually 32bit.
+ *
+ * @param input Data to compress.
+ * @returns Compressed data.
+ */
+ inline std::string zlib_compress(const std::string& input) {
+ unsigned long output_size = ::compressBound(osmium::static_cast_with_assert<unsigned long>(input.size()));
+
+ std::string output(output_size, '\0');
+
+ auto result = ::compress(
+ reinterpret_cast<unsigned char*>(const_cast<char *>(output.data())),
+ &output_size,
+ reinterpret_cast<const unsigned char*>(input.data()),
+ osmium::static_cast_with_assert<unsigned long>(input.size())
+ );
+
+ if (result != Z_OK) {
+ throw std::runtime_error(std::string("failed to compress data: ") + zError(result));
+ }
+
+ output.resize(output_size);
+
+ return output;
+ }
+
+ /**
+ * Uncompress data using zlib.
+ *
+ * Note that this function can not uncompress data larger than
+ * what fits in an unsigned long, on Windows this is usually 32bit.
+ *
+ * @param input Compressed input data.
+ * @param raw_size Size of uncompressed data.
+ * @returns Uncompressed data.
+ */
+ inline std::unique_ptr<std::string> zlib_uncompress(const std::string& input, unsigned long raw_size) {
+ auto output = std::unique_ptr<std::string>(new std::string(raw_size, '\0'));
+
+ auto result = ::uncompress(
+ reinterpret_cast<unsigned char*>(const_cast<char *>(output->data())),
+ &raw_size,
+ reinterpret_cast<const unsigned char*>(input.data()),
+ osmium::static_cast_with_assert<unsigned long>(input.size())
+ );
+
+ if (result != Z_OK) {
+ throw std::runtime_error(std::string("failed to uncompress data: ") + zError(result));
+ }
+
+ return output;
+ }
+
+ } // namespace detail
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_DETAIL_ZLIB_HPP
diff --git a/include/osmium/io/error.hpp b/include/osmium/io/error.hpp
new file mode 100644
index 0000000..07652bc
--- /dev/null
+++ b/include/osmium/io/error.hpp
@@ -0,0 +1,58 @@
+#ifndef OSMIUM_IO_ERROR_HPP
+#define OSMIUM_IO_ERROR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <stdexcept>
+#include <string>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when some kind of input/output operation failed.
+ */
+ struct io_error : public std::runtime_error {
+
+ io_error(const std::string& what) :
+ std::runtime_error(what) {
+ }
+
+ io_error(const char* what) :
+ std::runtime_error(what) {
+ }
+
+ }; // struct io_error
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_ERROR_HPP
diff --git a/include/osmium/io/file.hpp b/include/osmium/io/file.hpp
new file mode 100644
index 0000000..5b6c02f
--- /dev/null
+++ b/include/osmium/io/file.hpp
@@ -0,0 +1,344 @@
+#ifndef OSMIUM_IO_FILE_HPP
+#define OSMIUM_IO_FILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <stdexcept>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <osmium/io/file_format.hpp>
+#include <osmium/io/file_compression.hpp>
+#include <osmium/util/options.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Everything related to input and output of OSM data.
+ */
+ namespace io {
+
+ namespace detail {
+
+ inline std::vector<std::string> split(const std::string& in, const char delim) {
+ std::vector<std::string> result;
+ std::stringstream ss(in);
+ std::string item;
+ while (std::getline(ss, item, delim)) {
+ result.push_back(item);
+ }
+ return result;
+ }
+
+ } // namespace detail
+
+ /**
+ * This class describes an OSM file in one of several different formats.
+ *
+ * If the filename is empty or "-", this means stdin or stdout is used.
+ */
+ class File : public osmium::util::Options {
+
+ private:
+
+ std::string m_filename;
+
+ const char* m_buffer;
+ size_t m_buffer_size;
+
+ std::string m_format_string;
+
+ file_format m_file_format {file_format::unknown};
+
+ file_compression m_file_compression {file_compression::none};
+
+ bool m_has_multiple_object_versions {false};
+
+ public:
+
+ /**
+ * Create File using type and encoding from filename or given
+ * format specification.
+ *
+ * @param filename Filename including suffix. The type and encoding
+ * of the file will be taken from the suffix.
+ * An empty filename or "-" means stdin or stdout.
+ * @param format File format as string. See the description of the
+ * parse_format() function for details.
+ */
+ explicit File(const std::string& filename = "", const std::string& format = "") :
+ Options(),
+ m_filename(filename),
+ m_buffer(nullptr),
+ m_buffer_size(0),
+ m_format_string(format) {
+
+ // stdin/stdout
+ if (filename == "" || filename == "-") {
+ m_filename = "";
+ default_settings_for_stdinout();
+ }
+
+ // filename is actually a URL
+ std::string protocol = m_filename.substr(0, m_filename.find_first_of(':'));
+ if (protocol == "http" || protocol == "https") {
+ default_settings_for_url();
+ }
+
+ detect_format_from_suffix(m_filename);
+
+ if (format != "") {
+ parse_format(format);
+ }
+ }
+
+ /**
+ * Create File using buffer pointer and size and type and encoding
+ * from given format specification.
+ *
+ * @param buffer Pointer to buffer with data.
+ * @param size Size of buffer.
+ * @param format File format as string. See the description of the
+ * parse_format() function for details.
+ */
+ explicit File(const char* buffer, size_t size, const std::string& format = "") :
+ Options(),
+ m_filename(),
+ m_buffer(buffer),
+ m_buffer_size(size),
+ m_format_string(format) {
+
+ default_settings_for_stdinout();
+
+ if (format != "") {
+ parse_format(format);
+ }
+ }
+
+ File(const File&) = default;
+ File& operator=(const File&) = default;
+
+ File(File&&) = default;
+ File& operator=(File&&) = default;
+
+ ~File() = default;
+
+ const char* buffer() const noexcept {
+ return m_buffer;
+ }
+
+ size_t buffer_size() const noexcept {
+ return m_buffer_size;
+ }
+
+ void parse_format(const std::string& format) {
+ std::vector<std::string> options = detail::split(format, ',');
+
+ // if the first item in the format list doesn't contain
+ // an equals sign, it is a format
+ if (!options.empty() && options[0].find_first_of('=') == std::string::npos) {
+ detect_format_from_suffix(options[0]);
+ options.erase(options.begin());
+ }
+
+ for (auto& option : options) {
+ size_t pos = option.find_first_of('=');
+ if (pos == std::string::npos) {
+ set(option, true);
+ } else {
+ std::string value = option.substr(pos+1);
+ option.erase(pos);
+ set(option, value);
+ }
+ }
+
+ if (get("history") == "true") {
+ m_has_multiple_object_versions = true;
+ } else if (get("history") == "false") {
+ m_has_multiple_object_versions = false;
+ }
+ }
+
+ void detect_format_from_suffix(const std::string& name) {
+ std::vector<std::string> suffixes = detail::split(name, '.');
+
+ if (suffixes.empty()) return;
+
+ // if the last suffix is one of a known set of compressions,
+ // set that compression
+ if (suffixes.back() == "gz") {
+ m_file_compression = file_compression::gzip;
+ suffixes.pop_back();
+ } else if (suffixes.back() == "bz2") {
+ m_file_compression = file_compression::bzip2;
+ suffixes.pop_back();
+ }
+
+ if (suffixes.empty()) return;
+
+ // if the last suffix is one of a known set of formats,
+ // set that format
+ if (suffixes.back() == "pbf") {
+ m_file_format = file_format::pbf;
+ suffixes.pop_back();
+ } else if (suffixes.back() == "xml") {
+ m_file_format = file_format::xml;
+ suffixes.pop_back();
+ } else if (suffixes.back() == "opl") {
+ m_file_format = file_format::opl;
+ suffixes.pop_back();
+ }
+
+ if (suffixes.empty()) return;
+
+ if (suffixes.back() == "osm") {
+ if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
+ suffixes.pop_back();
+ } else if (suffixes.back() == "osh") {
+ if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
+ m_has_multiple_object_versions = true;
+ suffixes.pop_back();
+ } else if (suffixes.back() == "osc") {
+ if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
+ m_has_multiple_object_versions = true;
+ set("xml_change_format", true);
+ suffixes.pop_back();
+ }
+ }
+
+ /**
+ * Check file format etc. for consistency and throw exception if there
+ * is a problem.
+ *
+ * @throws std::runtime_error
+ */
+ void check() const {
+ if (m_file_format == file_format::unknown) {
+ std::string msg = "Could not detect file format";
+ if (!m_format_string.empty()) {
+ msg += " from format string '";
+ msg += m_format_string;
+ msg += "'";
+ }
+ if (m_filename.empty()) {
+ msg += " for stdin/stdout";
+ } else {
+ msg += " for filename '";
+ msg += m_filename;
+ msg += "'";
+ }
+ msg += ".";
+ throw std::runtime_error(msg);
+ }
+ }
+
+ /**
+ * Set default settings for type and encoding when the filename is
+ * empty or "-". If you want to have a different default setting
+ * override this in a subclass.
+ */
+ void default_settings_for_stdinout() {
+ m_file_format = file_format::unknown;
+ m_file_compression = file_compression::none;
+ }
+
+ /**
+ * Set default settings for type and encoding when the filename is
+ * a normal file. If you want to have a different default setting
+ * override this in a subclass.
+ */
+ void default_settings_for_file() {
+ m_file_format = file_format::unknown;
+ m_file_compression = file_compression::none;
+ }
+
+ /**
+ * Set default settings for type and encoding when the filename is a URL.
+ * If you want to have a different default setting override this in a
+ * subclass.
+ */
+ void default_settings_for_url() {
+ m_file_format = file_format::xml;
+ m_file_compression = file_compression::none;
+ }
+
+ file_format format() const noexcept {
+ return m_file_format;
+ }
+
+ File& set_format(file_format format) noexcept {
+ m_file_format = format;
+ return *this;
+ }
+
+ file_compression compression() const noexcept {
+ return m_file_compression;
+ }
+
+ File& set_compression(file_compression compression) noexcept {
+ m_file_compression = compression;
+ return *this;
+ }
+
+ bool has_multiple_object_versions() const noexcept {
+ return m_has_multiple_object_versions;
+ }
+
+ File& set_has_multiple_object_versions(bool value) noexcept {
+ m_has_multiple_object_versions = value;
+ return *this;
+ }
+
+ File& filename(const std::string& filename) {
+ if (filename == "-") {
+ m_filename = "";
+ } else {
+ m_filename = filename;
+ }
+ return *this;
+ }
+
+ const std::string& filename() const noexcept {
+ return m_filename;
+ }
+
+ }; // class File
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_FILE_HPP
diff --git a/include/osmium/io/file_compression.hpp b/include/osmium/io/file_compression.hpp
new file mode 100644
index 0000000..292ddcf
--- /dev/null
+++ b/include/osmium/io/file_compression.hpp
@@ -0,0 +1,72 @@
+#ifndef OSMIUM_IO_FILE_COMPRESSION_HPP
+#define OSMIUM_IO_FILE_COMPRESSION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iosfwd>
+
+namespace osmium {
+
+ namespace io {
+
+ enum class file_compression {
+ none = 0,
+ gzip = 1,
+ bzip2 = 2
+ };
+
+// avoid g++ false positive
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreturn-type"
+ inline const char* as_string(file_compression compression) {
+ switch (compression) {
+ case file_compression::none:
+ return "none";
+ case file_compression::gzip:
+ return "gzip";
+ case file_compression::bzip2:
+ return "bzip2";
+ }
+ }
+#pragma GCC diagnostic pop
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const file_compression compression) {
+ return out << as_string(compression);
+ }
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_FILE_COMPRESSION_HPP
diff --git a/include/osmium/io/file_format.hpp b/include/osmium/io/file_format.hpp
new file mode 100644
index 0000000..1a63a5e
--- /dev/null
+++ b/include/osmium/io/file_format.hpp
@@ -0,0 +1,78 @@
+#ifndef OSMIUM_IO_FILE_FORMAT_HPP
+#define OSMIUM_IO_FILE_FORMAT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iosfwd>
+
+namespace osmium {
+
+ namespace io {
+
+ enum class file_format {
+ unknown = 0,
+ xml = 1,
+ pbf = 2,
+ opl = 3,
+ json = 4
+ };
+
+// avoid g++ false positive
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreturn-type"
+ inline const char* as_string(file_format format) {
+ switch (format) {
+ case file_format::unknown:
+ return "unknown";
+ case file_format::xml:
+ return "XML";
+ case file_format::pbf:
+ return "PBF";
+ case file_format::opl:
+ return "OPL";
+ case file_format::json:
+ return "JSON";
+ }
+ }
+#pragma GCC diagnostic pop
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const file_format format) {
+ return out << as_string(format);
+ }
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_FILE_FORMAT_HPP
diff --git a/include/osmium/io/gzip_compression.hpp b/include/osmium/io/gzip_compression.hpp
new file mode 100644
index 0000000..8bc1e5a
--- /dev/null
+++ b/include/osmium/io/gzip_compression.hpp
@@ -0,0 +1,246 @@
+#ifndef OSMIUM_IO_GZIP_COMPRESSION_HPP
+#define OSMIUM_IO_GZIP_COMPRESSION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 or write gzip-compressed OSM XML
+ * files.
+ *
+ * @attention If you include this file, you'll need to link with `libz`.
+ */
+
+#include <stdexcept>
+#include <string>
+
+#include <errno.h>
+#include <zlib.h>
+
+#include <osmium/io/compression.hpp>
+#include <osmium/io/file_compression.hpp>
+#include <osmium/util/cast.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception thrown when there are problems compressing or
+ * decompressing gzip files.
+ */
+ struct gzip_error : public std::runtime_error {
+
+ int gzip_error_code;
+ int system_errno;
+
+ gzip_error(const std::string& what, int error_code) :
+ std::runtime_error(what),
+ gzip_error_code(error_code),
+ system_errno(error_code == Z_ERRNO ? errno : 0) {
+ }
+
+ }; // struct gzip_error
+
+ namespace io {
+
+ namespace detail {
+
+ OSMIUM_NORETURN inline void throw_gzip_error(gzFile gzfile, const char* msg, int zlib_error=0) {
+ std::string error("gzip error: ");
+ error += msg;
+ error += ": ";
+ int errnum = zlib_error;
+ if (zlib_error) {
+ error += std::to_string(zlib_error);
+ } else {
+ error += ::gzerror(gzfile, &errnum);
+ }
+ throw osmium::gzip_error(error, errnum);
+ }
+
+ } // namespace detail
+
+ class GzipCompressor : public Compressor {
+
+ gzFile m_gzfile;
+
+ public:
+
+ explicit GzipCompressor(int fd) :
+ Compressor(),
+ m_gzfile(::gzdopen(fd, "w")) {
+ if (!m_gzfile) {
+ detail::throw_gzip_error(m_gzfile, "write initialization failed");
+ }
+ }
+
+ ~GzipCompressor() override final {
+ close();
+ }
+
+ void write(const std::string& data) override final {
+ if (!data.empty()) {
+ int nwrite = ::gzwrite(m_gzfile, data.data(), static_cast_with_assert<unsigned int>(data.size()));
+ if (nwrite == 0) {
+ detail::throw_gzip_error(m_gzfile, "write failed");
+ }
+ }
+ }
+
+ void close() override final {
+ if (m_gzfile) {
+ int result = ::gzclose(m_gzfile);
+ m_gzfile = nullptr;
+ if (result != Z_OK) {
+ detail::throw_gzip_error(m_gzfile, "write close failed", result);
+ }
+ }
+ }
+
+ }; // class GzipCompressor
+
+ class GzipDecompressor : public Decompressor {
+
+ gzFile m_gzfile;
+
+ public:
+
+ explicit GzipDecompressor(int fd) :
+ Decompressor(),
+ m_gzfile(::gzdopen(fd, "r")) {
+ if (!m_gzfile) {
+ detail::throw_gzip_error(m_gzfile, "read initialization failed");
+ }
+ }
+
+ ~GzipDecompressor() override final {
+ close();
+ }
+
+ std::string read() override final {
+ std::string buffer(osmium::io::Decompressor::input_buffer_size, '\0');
+ int nread = ::gzread(m_gzfile, const_cast<char*>(buffer.data()), static_cast_with_assert<unsigned int>(buffer.size()));
+ if (nread < 0) {
+ detail::throw_gzip_error(m_gzfile, "read failed");
+ }
+ buffer.resize(static_cast<std::string::size_type>(nread));
+ return buffer;
+ }
+
+ void close() override final {
+ if (m_gzfile) {
+ int result = ::gzclose(m_gzfile);
+ m_gzfile = nullptr;
+ if (result != Z_OK) {
+ detail::throw_gzip_error(m_gzfile, "read close failed", result);
+ }
+ }
+ }
+
+ }; // class GzipDecompressor
+
+ class GzipBufferDecompressor : public Decompressor {
+
+ const char* m_buffer;
+ size_t m_buffer_size;
+ z_stream m_zstream;
+
+ public:
+
+ GzipBufferDecompressor(const char* buffer, size_t size) :
+ m_buffer(buffer),
+ m_buffer_size(size),
+ m_zstream() {
+ m_zstream.next_in = reinterpret_cast<unsigned char*>(const_cast<char*>(buffer));
+ m_zstream.avail_in = static_cast_with_assert<unsigned int>(size);
+ int result = inflateInit2(&m_zstream, MAX_WBITS | 32);
+ if (result != Z_OK) {
+ std::string message("gzip error: decompression init failed: ");
+ if (m_zstream.msg) {
+ message.append(m_zstream.msg);
+ }
+ throw osmium::gzip_error(message, result);
+ }
+ }
+
+ ~GzipBufferDecompressor() override final {
+ inflateEnd(&m_zstream);
+ }
+
+ std::string read() override final {
+ std::string output;
+
+ if (m_buffer) {
+ const size_t buffer_size = 10240;
+ output.append(buffer_size, '\0');
+ m_zstream.next_out = reinterpret_cast<unsigned char*>(const_cast<char*>(output.data()));
+ m_zstream.avail_out = buffer_size;
+ int result = inflate(&m_zstream, Z_SYNC_FLUSH);
+
+ if (result != Z_OK) {
+ m_buffer = nullptr;
+ m_buffer_size = 0;
+ }
+
+ if (result != Z_OK && result != Z_STREAM_END) {
+ std::string message("gzip error: inflate failed: ");
+ if (m_zstream.msg) {
+ message.append(m_zstream.msg);
+ }
+ throw osmium::gzip_error(message, result);
+ }
+
+ output.resize(static_cast<unsigned long>(m_zstream.next_out - reinterpret_cast<const unsigned char*>(output.data())));
+ }
+
+ return output;
+ }
+
+ }; // class GzipBufferDecompressor
+
+ namespace {
+
+ const bool registered_gzip_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::gzip,
+ [](int fd) { return new osmium::io::GzipCompressor(fd); },
+ [](int fd) { return new osmium::io::GzipDecompressor(fd); },
+ [](const char* buffer, size_t size) { return new osmium::io::GzipBufferDecompressor(buffer, size); }
+ );
+
+ } // anonymous namespace
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_GZIP_COMPRESSION_HPP
diff --git a/include/osmium/io/header.hpp b/include/osmium/io/header.hpp
new file mode 100644
index 0000000..4b0830a
--- /dev/null
+++ b/include/osmium/io/header.hpp
@@ -0,0 +1,122 @@
+#ifndef OSMIUM_IO_HEADER_HPP
+#define OSMIUM_IO_HEADER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <initializer_list>
+#include <vector>
+
+#include <osmium/osm/box.hpp>
+#include <osmium/util/options.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * Meta information from the header of an OSM file.
+ */
+ class Header : public osmium::util::Options {
+
+ /// Bounding boxes
+ std::vector<osmium::Box> m_boxes;
+
+ /**
+ * Are there possibly multiple versions of the same object in this stream of objects?
+ * This is true for history files and for change files, but not for normal OSM files.
+ */
+ bool m_has_multiple_object_versions = false;
+
+ public:
+
+ Header() = default;
+
+ explicit Header(const std::initializer_list<osmium::util::Options::value_type>& values) :
+ Options(values) {
+ }
+
+ Header(const Header&) = default;
+ Header& operator=(const Header&) = default;
+
+ Header(Header&&) = default;
+ Header& operator=(Header&&) = default;
+
+ ~Header() = default;
+
+ std::vector<osmium::Box>& boxes() noexcept {
+ return m_boxes;
+ }
+
+ const std::vector<osmium::Box>& boxes() const noexcept {
+ return m_boxes;
+ }
+
+ Header& boxes(const std::vector<osmium::Box>& boxes) noexcept {
+ m_boxes = boxes;
+ return *this;
+ }
+
+ osmium::Box box() const {
+ return m_boxes.empty() ? osmium::Box() : m_boxes.front();
+ }
+
+ osmium::Box joined_boxes() const {
+ osmium::Box box;
+ for (const auto& b : m_boxes) {
+ box.extend(b.bottom_left());
+ box.extend(b.top_right());
+ }
+ return box;
+ }
+
+ Header& add_box(const osmium::Box& box) {
+ m_boxes.push_back(box);
+ return *this;
+ }
+
+ bool has_multiple_object_versions() const noexcept {
+ return m_has_multiple_object_versions;
+ }
+
+ Header& set_has_multiple_object_versions(bool value) noexcept {
+ m_has_multiple_object_versions = value;
+ return *this;
+ }
+
+ }; // class Header
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_HEADER_HPP
diff --git a/include/osmium/io/input_iterator.hpp b/include/osmium/io/input_iterator.hpp
new file mode 100644
index 0000000..f619729
--- /dev/null
+++ b/include/osmium/io/input_iterator.hpp
@@ -0,0 +1,140 @@
+#ifndef OSMIUM_IO_INPUT_ITERATOR_HPP
+#define OSMIUM_IO_INPUT_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * This iterator class allows you to iterate over all items from a
+ * source. It hides all the buffer handling and makes the contents of a
+ * source accessible as a normal STL input iterator.
+ */
+ template <class TSource, class TItem = osmium::memory::Item>
+ class InputIterator {
+
+ static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, "TItem must derive from osmium::buffer::Item");
+
+ typedef typename osmium::memory::Buffer::t_iterator<TItem> item_iterator;
+
+ TSource* m_source;
+ std::shared_ptr<osmium::memory::Buffer> m_buffer;
+ item_iterator m_iter {};
+
+ void update_buffer() {
+ do {
+ m_buffer = std::make_shared<osmium::memory::Buffer>(std::move(m_source->read()));
+ if (!m_buffer || !*m_buffer) { // end of input
+ m_source = nullptr;
+ m_buffer.reset();
+ m_iter = item_iterator();
+ return;
+ }
+ m_iter = m_buffer->begin<TItem>();
+ } while (m_iter == m_buffer->end<TItem>());
+ }
+
+ public:
+
+ typedef std::input_iterator_tag iterator_category;
+ typedef TItem value_type;
+ typedef ptrdiff_t difference_type;
+ typedef TItem* pointer;
+ typedef TItem& reference;
+
+ explicit InputIterator(TSource& source) :
+ m_source(&source) {
+ update_buffer();
+ }
+
+ // end iterator
+ InputIterator() noexcept :
+ m_source(nullptr) {
+ }
+
+ InputIterator& operator++() {
+ assert(m_source);
+ assert(m_buffer);
+ assert(m_iter);
+ ++m_iter;
+ if (m_iter == m_buffer->end<TItem>()) {
+ update_buffer();
+ }
+ return *this;
+ }
+
+ InputIterator operator++(int) {
+ InputIterator tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const InputIterator& rhs) const noexcept {
+ return m_source == rhs.m_source &&
+ m_buffer == rhs.m_buffer &&
+ m_iter == rhs.m_iter;
+ }
+
+ bool operator!=(const InputIterator& rhs) const noexcept {
+ return !(*this == rhs);
+ }
+
+ reference operator*() const {
+ assert(m_iter);
+ return static_cast<reference>(*m_iter);
+ }
+
+ pointer operator->() const {
+ assert(m_iter);
+ return &static_cast<reference>(*m_iter);
+ }
+
+ }; // class InputIterator
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_INPUT_ITERATOR_HPP
diff --git a/include/osmium/io/opl_output.hpp b/include/osmium/io/opl_output.hpp
new file mode 100644
index 0000000..04385d9
--- /dev/null
+++ b/include/osmium/io/opl_output.hpp
@@ -0,0 +1,39 @@
+#ifndef OSMIUM_IO_OPL_OUTPUT_HPP
+#define OSMIUM_IO_OPL_OUTPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/io/writer.hpp> // IWYU pragma: export
+#include <osmium/io/detail/opl_output_format.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_OPL_OUTPUT_HPP
diff --git a/include/osmium/io/output_iterator.hpp b/include/osmium/io/output_iterator.hpp
new file mode 100644
index 0000000..608852f
--- /dev/null
+++ b/include/osmium/io/output_iterator.hpp
@@ -0,0 +1,124 @@
+#ifndef OSMIUM_IO_OUTPUT_ITERATOR_HPP
+#define OSMIUM_IO_OUTPUT_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/diff_object.hpp>
+
+namespace osmium {
+
+ namespace memory {
+ class Item;
+ } // namespace memory
+
+ namespace io {
+
+ template <class TDest>
+ class OutputIterator : public std::iterator<std::output_iterator_tag, osmium::memory::Item> {
+
+ struct buffer_wrapper {
+
+ osmium::memory::Buffer buffer;
+
+ buffer_wrapper(size_t buffer_size) :
+ buffer(buffer_size, osmium::memory::Buffer::auto_grow::no) {
+ }
+
+ }; // struct buffer_wrapper
+
+ static constexpr size_t default_buffer_size = 10 * 1024 * 1024;
+
+ TDest* m_destination;
+
+ std::shared_ptr<buffer_wrapper> m_buffer_wrapper;
+
+ public:
+
+ explicit OutputIterator(TDest& destination, const size_t buffer_size = default_buffer_size) :
+ m_destination(&destination),
+ m_buffer_wrapper(std::make_shared<buffer_wrapper>(buffer_size)) {
+ }
+
+ OutputIterator(const OutputIterator&) = default;
+ OutputIterator(OutputIterator&&) = default;
+
+ OutputIterator& operator=(const OutputIterator&) = default;
+ OutputIterator& operator=(OutputIterator&&) = default;
+
+ ~OutputIterator() = default;
+
+ void flush() {
+ osmium::memory::Buffer buffer(m_buffer_wrapper->buffer.capacity(), osmium::memory::Buffer::auto_grow::no);
+ std::swap(m_buffer_wrapper->buffer, buffer);
+ (*m_destination)(std::move(buffer));
+ }
+
+ OutputIterator& operator=(const osmium::memory::Item& item) {
+ try {
+ m_buffer_wrapper->buffer.push_back(item);
+ } catch (osmium::buffer_is_full&) {
+ flush();
+ m_buffer_wrapper->buffer.push_back(item);
+ }
+ return *this;
+ }
+
+ OutputIterator& operator=(const osmium::DiffObject& diff) {
+ return this->operator=(diff.curr());
+ }
+
+ OutputIterator& operator*() {
+ return *this;
+ }
+
+ OutputIterator& operator++() {
+ return *this;
+ }
+
+ OutputIterator& operator++(int) {
+ return *this;
+ }
+
+ }; // class OutputIterator
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_OUTPUT_ITERATOR_HPP
diff --git a/include/osmium/io/overwrite.hpp b/include/osmium/io/overwrite.hpp
new file mode 100644
index 0000000..e33894b
--- /dev/null
+++ b/include/osmium/io/overwrite.hpp
@@ -0,0 +1,52 @@
+#ifndef OSMIUM_IO_OVERWRITE_HPP
+#define OSMIUM_IO_OVERWRITE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * Allow overwriting of existing file.
+ */
+ enum class overwrite : bool {
+ no = false,
+ allow = true
+ };
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_OVERWRITE_HPP
diff --git a/include/osmium/io/pbf_input.hpp b/include/osmium/io/pbf_input.hpp
new file mode 100644
index 0000000..766153e
--- /dev/null
+++ b/include/osmium/io/pbf_input.hpp
@@ -0,0 +1,49 @@
+#ifndef OSMIUM_IO_PBF_INPUT_HPP
+#define OSMIUM_IO_PBF_INPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 PBF files.
+ *
+ * @attention If you include this file, you'll need to link with
+ * `libprotobuf-lite`, `libosmpbf`, `ws2_32` (Windows only),
+ * `libz`, and enable multithreading.
+ */
+
+#include <osmium/io/reader.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_input_format.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_PBF_INPUT_HPP
diff --git a/include/osmium/io/pbf_output.hpp b/include/osmium/io/pbf_output.hpp
new file mode 100644
index 0000000..5f46ede
--- /dev/null
+++ b/include/osmium/io/pbf_output.hpp
@@ -0,0 +1,49 @@
+#ifndef OSMIUM_IO_PBF_OUTPUT_HPP
+#define OSMIUM_IO_PBF_OUTPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 write OSM PBF files.
+ *
+ * @attention If you include this file, you'll need to link with
+ * `libprotobuf-lite`, `libosmpbf`, `ws2_32` (Windows only),
+ * `libz`, and enable multithreading.
+ */
+
+#include <osmium/io/writer.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_output_format.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_PBF_OUTPUT_HPP
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
new file mode 100644
index 0000000..0bb4f1c
--- /dev/null
+++ b/include/osmium/io/reader.hpp
@@ -0,0 +1,312 @@
+#ifndef OSMIUM_IO_READER_HPP
+#define OSMIUM_IO_READER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <atomic>
+#include <cerrno>
+#include <cstdlib>
+#include <fcntl.h>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <thread>
+#include <utility>
+
+#ifndef _WIN32
+# include <sys/wait.h>
+#endif
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#endif
+
+#include <osmium/io/compression.hpp>
+#include <osmium/io/detail/input_format.hpp>
+#include <osmium/io/detail/read_thread.hpp>
+#include <osmium/io/detail/read_write.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/entity_bits.hpp>
+#include <osmium/thread/util.hpp>
+#include <osmium/thread/queue.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * 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
+ * and then call read() on it in a loop until it returns an invalid
+ * Buffer.
+ */
+ class Reader {
+
+ osmium::io::File m_file;
+ osmium::osm_entity_bits::type m_read_which_entities;
+ std::atomic<bool> m_input_done;
+ int m_childpid;
+
+ osmium::thread::Queue<std::string> m_input_queue;
+
+ std::unique_ptr<osmium::io::Decompressor> m_decompressor;
+ std::future<bool> m_read_future;
+
+ std::unique_ptr<osmium::io::detail::InputFormat> m_input;
+
+#ifndef _WIN32
+ /**
+ * Fork and execute the given command in the child.
+ * A pipe is created between the child and the parent.
+ * The child writes to the pipe, the parent reads from it.
+ * This function never returns in the child.
+ *
+ * @param command Command to execute in the child.
+ * @param filename Filename to give to command as argument.
+ * @returns File descriptor of pipe in the parent.
+ * @throws std::system_error if a system call fails.
+ */
+ static int execute(const std::string& command, const std::string& filename, int* childpid) {
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ throw std::system_error(errno, std::system_category(), "opening pipe failed");
+ }
+ pid_t pid = fork();
+ if (pid < 0) {
+ throw std::system_error(errno, std::system_category(), "fork failed");
+ }
+ if (pid == 0) { // child
+ // close all file descriptors except one end of the pipe
+ for (int i=0; i < 32; ++i) {
+ if (i != pipefd[1]) {
+ ::close(i);
+ }
+ }
+ if (dup2(pipefd[1], 1) < 0) { // put end of pipe as stdout/stdin
+ exit(1);
+ }
+
+ ::open("/dev/null", O_RDONLY); // stdin
+ ::open("/dev/null", O_WRONLY); // stderr
+ // hack: -g switches off globbing in curl which allows [] to be used in file names
+ // this is important for XAPI URLs
+ // in theory this execute() function could be used for other commands, but it is
+ // only used for curl at the moment, so this is okay.
+ if (::execlp(command.c_str(), command.c_str(), "-g", filename.c_str(), nullptr) < 0) {
+ exit(1);
+ }
+ }
+ // parent
+ *childpid = pid;
+ ::close(pipefd[1]);
+ return pipefd[0];
+ }
+#endif
+
+ /**
+ * Open File for reading. Handles URLs or normal files. URLs
+ * are opened by executing the "curl" program (which must be installed)
+ * and reading from its output.
+ *
+ * @returns File descriptor of open file or pipe.
+ * @throws std::system_error if a system call fails.
+ */
+ static int open_input_file_or_url(const std::string& filename, int* childpid) {
+ std::string protocol = filename.substr(0, filename.find_first_of(':'));
+ if (protocol == "http" || protocol == "https" || protocol == "ftp" || protocol == "file") {
+#ifndef _WIN32
+ return execute("curl", filename, childpid);
+#else
+ throw std::runtime_error("Reading OSM files from the network currently not supported on Windows.");
+#endif
+ } else {
+ return osmium::io::detail::open_for_reading(filename);
+ }
+ }
+
+ public:
+
+ /**
+ * Create new Reader object.
+ *
+ * @param file The file we want to open.
+ * @param read_which_entities Which OSM entities (nodes, ways, relations, and/or changesets)
+ * should be read from the input file. It can speed the read up
+ * significantly if objects that are not needed anyway are not
+ * parsed.
+ */
+ explicit Reader(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities = osmium::osm_entity_bits::all) :
+ m_file(file),
+ m_read_which_entities(read_which_entities),
+ m_input_done(false),
+ m_childpid(0),
+ m_input_queue(20, "raw_input"), // XXX
+ m_decompressor(m_file.buffer() ?
+ osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), m_file.buffer(), m_file.buffer_size()) :
+ osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), open_input_file_or_url(m_file.filename(), &m_childpid))),
+ m_read_future(std::async(std::launch::async, detail::ReadThread(m_input_queue, m_decompressor.get(), m_input_done))),
+ m_input(osmium::io::detail::InputFormatFactory::instance().create_input(m_file, m_read_which_entities, m_input_queue)) {
+ }
+
+ explicit Reader(const std::string& filename, osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) :
+ Reader(osmium::io::File(filename), read_types) {
+ }
+
+ explicit Reader(const char* filename, osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) :
+ Reader(osmium::io::File(filename), read_types) {
+ }
+
+ Reader(const Reader&) = delete;
+ Reader& operator=(const Reader&) = delete;
+
+ ~Reader() {
+ try {
+ close();
+ }
+ catch (...) {
+ }
+ }
+
+ /**
+ * Close down the Reader. A call to this is optional, because the
+ * destructor of Reader will also call this. But if you don't call
+ * this function first, the destructor might throw an exception
+ * which is not good.
+ *
+ * @throws Some form of std::runtime_error when there is a problem.
+ */
+ void close() {
+ // Signal to input child process that it should wrap up.
+ m_input_done = true;
+
+ m_input->close();
+
+#ifndef _WIN32
+ if (m_childpid) {
+ int status;
+ 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) {
+ throw std::system_error(errno, std::system_category(), "subprocess returned error");
+ }
+#pragma GCC diagnostic pop
+ m_childpid = 0;
+ }
+#endif
+
+ osmium::thread::wait_until_done(m_read_future);
+ }
+
+ /**
+ * Get the header data from the file.
+ */
+ osmium::io::Header header() const {
+ return m_input->header();
+ }
+
+ /**
+ * Reads the next buffer from the input. An invalid buffer signals
+ * end-of-file. After end-of-file all read() calls will return an
+ * invalid buffer. An invalid buffer is also always returned if
+ * osmium::osm_entity_bits::nothing was set when the Reader was
+ * constructed.
+ *
+ * @returns Buffer.
+ * @throws Some form of std::runtime_error if there is an error.
+ */
+ osmium::memory::Buffer read() {
+ // If an exception happened in the input thread, re-throw
+ // it in this (the main) thread.
+ osmium::thread::check_for_exception(m_read_future);
+
+ if (m_read_which_entities == osmium::osm_entity_bits::nothing || m_input_done) {
+ // If the caller didn't want anything but the header, it will
+ // always get an empty buffer here.
+ return osmium::memory::Buffer();
+ }
+
+ // m_input->read() can return an invalid buffer to signal EOF,
+ // or a valid buffer with or without data. A valid buffer
+ // without data is not an error, it just means we have to get
+ // keep getting the next buffer until there is one with data.
+ while (true) {
+ osmium::memory::Buffer buffer = m_input->read();
+ if (!buffer) {
+ m_input_done = true;
+ return buffer;
+ }
+ if (buffer.committed() > 0) {
+ return buffer;
+ }
+ }
+ }
+
+ /**
+ * Has the end of file been reached? This is set after the last
+ * data has been read. It is also set by calling close().
+ */
+ bool eof() const {
+ return m_input_done;
+ }
+
+ }; // class Reader
+
+ /**
+ * Read contents of the given file into a buffer in one go. Takes
+ * the same arguments as any of the Reader constructors.
+ *
+ * The buffer can take up quite a lot of memory, so don't do this
+ * unless you are working with small OSM files and/or have lots of
+ * RAM.
+ */
+ template <class... TArgs>
+ osmium::memory::Buffer read_file(TArgs&&... args) {
+ osmium::memory::Buffer buffer(1024*1024, osmium::memory::Buffer::auto_grow::yes);
+
+ Reader reader(std::forward<TArgs>(args)...);
+ while (osmium::memory::Buffer read_buffer = reader.read()) {
+ buffer.add_buffer(read_buffer);
+ buffer.commit();
+ }
+
+ return buffer;
+ }
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_READER_HPP
diff --git a/include/osmium/io/reader_iterator.hpp b/include/osmium/io/reader_iterator.hpp
new file mode 100644
index 0000000..8620789
--- /dev/null
+++ b/include/osmium/io/reader_iterator.hpp
@@ -0,0 +1,51 @@
+#ifndef OSMIUM_IO_READER_ITERATOR_HPP
+#define OSMIUM_IO_READER_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/io/reader.hpp>
+#include <osmium/io/input_iterator.hpp>
+
+namespace std {
+
+ inline osmium::io::InputIterator<osmium::io::Reader> begin(osmium::io::Reader& reader) {
+ return osmium::io::InputIterator<osmium::io::Reader>(reader);
+ }
+
+ inline osmium::io::InputIterator<osmium::io::Reader> end(osmium::io::Reader&) {
+ return osmium::io::InputIterator<osmium::io::Reader>();
+ }
+
+} // namespace std
+
+#endif // OSMIUM_IO_READER_ITERATOR_HPP
diff --git a/include/osmium/io/writer.hpp b/include/osmium/io/writer.hpp
new file mode 100644
index 0000000..64afe20
--- /dev/null
+++ b/include/osmium/io/writer.hpp
@@ -0,0 +1,145 @@
+#ifndef OSMIUM_IO_WRITER_HPP
+#define OSMIUM_IO_WRITER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <memory>
+#include <string>
+#include <utility>
+
+#include <osmium/io/compression.hpp>
+#include <osmium/io/detail/output_format.hpp>
+#include <osmium/io/detail/read_write.hpp>
+#include <osmium/io/detail/write_thread.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/io/overwrite.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/thread/util.hpp>
+
+namespace osmium {
+
+ namespace io {
+
+ /**
+ * This is the user-facing interface for writing OSM files. Instantiate
+ * an object of this class with a file name or osmium::io::File object
+ * and optionally the data for the header and then call operator() on it
+ * to write Buffers. Call close() to finish up.
+ */
+ class Writer {
+
+ osmium::io::File m_file;
+
+ osmium::io::detail::data_queue_type m_output_queue;
+
+ std::unique_ptr<osmium::io::detail::OutputFormat> m_output;
+
+ std::unique_ptr<osmium::io::Compressor> m_compressor;
+
+ std::future<bool> m_write_future;
+
+ public:
+
+ /**
+ * The constructor of the Writer object opens a file and writes the
+ * header to it.
+ *
+ * @param file File (contains name and format info) to open.
+ * @param header Optional header data. If this is not given sensible
+ * defaults will be used. See the default constructor
+ * of osmium::io::Header for details.
+ * @param allow_overwrite Allow overwriting of existing file? Can be
+ * osmium::io::overwrite::allow or osmium::io::overwrite::no
+ * (default).
+ *
+ * @throws std::runtime_error If the file could not be opened.
+ * @throws std::system_error If the file could not be opened.
+ */
+ explicit Writer(const osmium::io::File& file, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) :
+ m_file(file),
+ m_output_queue(20, "raw_output"), // XXX
+ m_output(osmium::io::detail::OutputFormatFactory::instance().create_output(m_file, m_output_queue)),
+ m_compressor(osmium::io::CompressionFactory::instance().create_compressor(file.compression(), osmium::io::detail::open_for_writing(m_file.filename(), allow_overwrite))),
+ m_write_future(std::async(std::launch::async, detail::WriteThread(m_output_queue, m_compressor.get()))) {
+ assert(!m_file.buffer());
+ m_output->write_header(header);
+ }
+
+ explicit Writer(const std::string& filename, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) :
+ Writer(osmium::io::File(filename), header, allow_overwrite) {
+ }
+
+ explicit Writer(const char* filename, const osmium::io::Header& header = osmium::io::Header(), overwrite allow_overwrite = overwrite::no) :
+ Writer(osmium::io::File(filename), header, allow_overwrite) {
+ }
+
+ Writer(const Writer&) = delete;
+ Writer& operator=(const Writer&) = delete;
+
+ ~Writer() {
+ close();
+ }
+
+ /**
+ * Write contents of a buffer to the output file.
+ *
+ * @throws Some form of std::runtime_error when there is a problem.
+ */
+ void operator()(osmium::memory::Buffer&& buffer) {
+ osmium::thread::check_for_exception(m_write_future);
+ if (buffer.committed() > 0) {
+ m_output->write_buffer(std::move(buffer));
+ }
+ }
+
+ /**
+ * Flush writes to output file and closes it. If you do not
+ * call this, the destructor of Writer will also do the same
+ * thing. But because this call might thrown an exception,
+ * it is better to call close() explicitly.
+ *
+ * @throws Some form of std::runtime_error when there is a problem.
+ */
+ void close() {
+ m_output->close();
+ osmium::thread::wait_until_done(m_write_future);
+ }
+
+ }; // class Writer
+
+ } // namespace io
+
+} // namespace osmium
+
+#endif // OSMIUM_IO_WRITER_HPP
diff --git a/include/osmium/io/xml_input.hpp b/include/osmium/io/xml_input.hpp
new file mode 100644
index 0000000..dfcd0a9
--- /dev/null
+++ b/include/osmium/io/xml_input.hpp
@@ -0,0 +1,48 @@
+#ifndef OSMIUM_IO_XML_INPUT_HPP
+#define OSMIUM_IO_XML_INPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 XML files.
+ *
+ * @attention If you include this file, you'll need to link with
+ * `libexpat`, and enable multithreading.
+ */
+
+#include <osmium/io/reader.hpp> // IWYU pragma: export
+#include <osmium/io/detail/xml_input_format.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_XML_INPUT_HPP
diff --git a/include/osmium/io/xml_output.hpp b/include/osmium/io/xml_output.hpp
new file mode 100644
index 0000000..18a1386
--- /dev/null
+++ b/include/osmium/io/xml_output.hpp
@@ -0,0 +1,47 @@
+#ifndef OSMIUM_IO_XML_OUTPUT_HPP
+#define OSMIUM_IO_XML_OUTPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 write OSM XML files.
+ *
+ * @attention If you include this file, you'll need to enable multithreading.
+ */
+
+#include <osmium/io/writer.hpp> // IWYU pragma: export
+#include <osmium/io/detail/xml_output_format.hpp> // IWYU pragma: export
+
+#endif // OSMIUM_IO_XML_OUTPUT_HPP
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
new file mode 100644
index 0000000..85a3a46
--- /dev/null
+++ b/include/osmium/memory/buffer.hpp
@@ -0,0 +1,535 @@
+#ifndef OSMIUM_MEMORY_BUFFER_HPP
+#define OSMIUM_MEMORY_BUFFER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <exception>
+#include <functional>
+#include <iterator>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+#include <osmium/memory/item.hpp>
+#include <osmium/memory/item_iterator.hpp>
+#include <osmium/osm/entity.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception thrown by the osmium::memory::Buffer class when somebody tries
+ * to write data into a buffer and it doesn't fit. Buffers with internal
+ * memory management will not throw this exception, but increase their size.
+ */
+ struct buffer_is_full : public std::runtime_error {
+
+ buffer_is_full() :
+ std::runtime_error("Osmium buffer is full") {
+ }
+
+ }; // struct buffer_is_full
+
+ /**
+ * @brief Memory management of items in buffers and iterators over this data.
+ */
+ namespace memory {
+
+ /**
+ * A memory area for storing OSM objects and other items. Each item stored
+ * has a type and a length. See the Item class for details.
+ *
+ * Data can be added to a buffer piece by piece using reserve_space() and
+ * add_item(). After all data that together forms an item is added, it must
+ * be committed using the commit() call. Usually this is done through the
+ * Builder class and its derived classes.
+ *
+ * You can iterate over all items in a buffer using the iterators returned
+ * by begin(), end(), cbegin(), and cend().
+ *
+ * Buffers exist in two flavours, those with external memory management and
+ * those with internal memory management. If you already have some memory
+ * with data in it (for instance read from disk), you create a Buffer with
+ * external memory managment. It is your job then to free the memory once
+ * the buffer isn't used any more. If you don't have memory already, you can
+ * create a Buffer object and have it manage the memory internally. It will
+ * dynamically allocate memory and free it again after use.
+ *
+ * By default, if a buffer gets full it will throw a buffer_is_full exception.
+ * You can use the set_full_callback() method to set a callback functor
+ * which will be called instead of throwing an exception.
+ */
+ class Buffer {
+
+ public:
+
+ enum class auto_grow : bool {
+ yes = true,
+ no = false
+ }; // enum class auto_grow
+
+ private:
+
+ std::vector<unsigned char> m_memory;
+ unsigned char* m_data;
+ size_t m_capacity;
+ size_t m_written;
+ size_t m_committed;
+ auto_grow m_auto_grow {auto_grow::no};
+ std::function<void(Buffer&)> m_full;
+
+ public:
+
+ typedef Item value_type;
+
+ /**
+ * The constructor without any parameters creates a non-initialized
+ * buffer, ie an empty hull of a buffer that has no actual memory
+ * associated with it. It can be used to signify end-of-input.
+ */
+ Buffer() noexcept :
+ m_memory(),
+ m_data(nullptr),
+ m_capacity(0),
+ m_written(0),
+ m_committed(0) {
+ }
+
+ /**
+ * Constructs an externally memory-managed buffer using the given
+ * memory and size.
+ *
+ * @param data A pointer to some already initialized data.
+ * @param size The size of the initialized data.
+ * @throws std::invalid_argument When the size isn't a multiple of the alignment.
+ */
+ explicit Buffer(unsigned char* data, size_t size) :
+ m_memory(),
+ m_data(data),
+ m_capacity(size),
+ m_written(size),
+ m_committed(size) {
+ if (size % align_bytes != 0) {
+ throw std::invalid_argument("buffer size needs to be multiple of alignment");
+ }
+ }
+
+ /**
+ * Constructs an externally memory-managed buffer with the given
+ * capacity that already contains 'committed' bytes of data.
+ *
+ * @param data A pointer to some (possibly initialized) data.
+ * @param capacity The size of the memory for this buffer.
+ * @param committed The size of the initialized data. If this is 0, the buffer startes out empty.
+ * @throws std::invalid_argument When the capacity or committed isn't a multiple of the alignment.
+ */
+ explicit Buffer(unsigned char* data, size_t capacity, size_t committed) :
+ m_memory(),
+ m_data(data),
+ m_capacity(capacity),
+ m_written(committed),
+ m_committed(committed) {
+ if (capacity % align_bytes != 0) {
+ throw std::invalid_argument("buffer capacity needs to be multiple of alignment");
+ }
+ if (committed % align_bytes != 0) {
+ throw std::invalid_argument("buffer parameter 'committed' needs to be multiple of alignment");
+ }
+ }
+
+ /**
+ * Create an internally memory-managed buffer with the given capacity.
+ * different in that it internally gets dynamic memory of the
+ * required size. The dynamic memory will be automatically
+ * freed when the Buffer is destroyed.
+ */
+ explicit Buffer(size_t capacity, auto_grow auto_grow = auto_grow::yes) :
+ m_memory(capacity),
+ m_data(m_memory.data()),
+ m_capacity(capacity),
+ m_written(0),
+ m_committed(0),
+ m_auto_grow(auto_grow) {
+ if (capacity % align_bytes != 0) {
+ throw std::invalid_argument("buffer capacity needs to be multiple of alignment");
+ }
+ }
+
+ // buffers can not be copied
+ Buffer(const Buffer&) = delete;
+ Buffer& operator=(const Buffer&) = delete;
+
+ // buffers can be moved
+ Buffer(Buffer&&) = default;
+ Buffer& operator=(Buffer&&) = default;
+
+ ~Buffer() = default;
+
+ /**
+ * Return a pointer to data inside the buffer.
+ */
+ unsigned char* data() const noexcept {
+ return m_data;
+ }
+
+ /**
+ * Returns the capacity of the buffer, ie how many bytes it can contain.
+ */
+ size_t capacity() const noexcept {
+ return m_capacity;
+ }
+
+ /**
+ * Returns the number of bytes already filled in this buffer.
+ */
+ size_t committed() const noexcept {
+ return m_committed;
+ }
+
+ /**
+ * Returns the number of bytes currently filled in this buffer that
+ * are not yet committed.
+ */
+ size_t written() const noexcept {
+ return m_written;
+ }
+
+ /**
+ * This tests if the current state of the buffer is aligned
+ * properly. Can be used for asserts.
+ */
+ bool is_aligned() const noexcept {
+ return (m_written % align_bytes == 0) && (m_committed % align_bytes == 0);
+ }
+
+ /**
+ * Set functor to be called whenever the buffer is full
+ * instead of throwing buffer_is_full.
+ */
+ void set_full_callback(std::function<void(Buffer&)> full) {
+ m_full = full;
+ }
+
+ /**
+ * Grow capacity of this buffer to the given size.
+ * This works only with internally memory-managed buffers.
+ * If the given size is not larger than the current capacity, nothing is done.
+ * Already written but not committed data is discarded.
+ *
+ * @param size New capacity.
+ */
+ void grow(size_t size) {
+ if (m_memory.empty()) {
+ throw std::logic_error("Can't grow Buffer if it doesn't use internal memory management.");
+ }
+ if (m_capacity < size) {
+ if (size % align_bytes != 0) {
+ throw std::invalid_argument("buffer capacity needs to be multiple of alignment");
+ }
+ m_memory.resize(size);
+ m_data = m_memory.data();
+ m_capacity = size;
+ }
+ }
+
+ /**
+ * Mark currently written bytes in the buffer as committed.
+ *
+ * @returns Last number of committed bytes before this commit.
+ */
+ size_t commit() {
+ assert(is_aligned());
+
+ const size_t offset = m_committed;
+ m_committed = m_written;
+ return offset;
+ }
+
+ /**
+ * Roll back changes in buffer to last committed state.
+ */
+ void rollback() {
+ m_written = m_committed;
+ }
+
+ /**
+ * Clear the buffer.
+ *
+ * @returns Number of bytes in the buffer before it was cleared.
+ */
+ size_t clear() {
+ const size_t committed = m_committed;
+ m_written = 0;
+ m_committed = 0;
+ return committed;
+ }
+
+ /**
+ * Get the data in the buffer at the given offset.
+ *
+ * @tparam T Type we want to the data to be interpreted as.
+ * @returns Reference of given type pointing to the data in the buffer.
+ */
+ template <class T>
+ T& get(const size_t offset) const {
+ return *reinterpret_cast<T*>(&m_data[offset]);
+ }
+
+ /**
+ * Reserve space of given size in buffer and return pointer to it.
+ * This is the only way of adding data to the buffer. You reserve
+ * the space and then fill it.
+ *
+ * Note that you have to eventually call commit() to actually
+ * commit this data.
+ *
+ * If there isn't enough space in the buffer, one of three things
+ * can happen:
+ *
+ * * If you have set a callback with set_full_callback(), it is
+ * called. After the call returns, you must have either grown
+ * the buffer or cleared it by calling buffer.clear().
+ * * If no callback is defined and this buffer uses internal
+ * memory management, the buffers capacity is grown, so that
+ * the new data will fit.
+ * * Else the buffer_is_full exception is thrown.
+ *
+ * @param size Number of bytes to reserve.
+ * @returns Pointer to reserved space. Note that this pointer is
+ * only guaranteed to be valid until the next call to
+ * reserve_space().
+ * @throws osmium::buffer_is_full Might be thrown if the buffer is full.
+ */
+ unsigned char* reserve_space(const size_t size) {
+ if (m_written + size > m_capacity) {
+ if (m_full) {
+ m_full(*this);
+ } else if (!m_memory.empty() && (m_auto_grow == auto_grow::yes)) {
+ // double buffer size until there is enough space
+ size_t new_capacity = m_capacity * 2;
+ while (m_written + size > new_capacity) {
+ new_capacity *= 2;
+ }
+ grow(new_capacity);
+ } else {
+ throw osmium::buffer_is_full();
+ }
+ }
+ unsigned char* data = &m_data[m_written];
+ m_written += size;
+ return data;
+ }
+
+ /**
+ * Add an item to the buffer. The size of the item is stored inside
+ * the item, so we know how much memory to copy.
+ *
+ * Note that you have to eventually call commit() to actually
+ * commit this data.
+ *
+ * @tparam T Class of the item to be copied.
+ * @param item Reference to the item to be copied.
+ * @returns Reference to newly copied data in the buffer.
+ */
+ template <class T>
+ T& add_item(const T& item) {
+ unsigned char* target = reserve_space(item.padded_size());
+ std::copy_n(reinterpret_cast<const unsigned char*>(&item), item.padded_size(), target);
+ return *reinterpret_cast<T*>(target);
+ }
+
+ /**
+ * Add committed contents of the given buffer to this buffer.
+ *
+ * Note that you have to eventually call commit() to actually
+ * commit this data.
+ */
+ void add_buffer(const Buffer& buffer) {
+ unsigned char* target = reserve_space(buffer.committed());
+ std::copy_n(reinterpret_cast<const unsigned char*>(buffer.data()), buffer.committed(), target);
+ }
+
+ /**
+ * Add an item to the buffer. This function is provided so that
+ * you can use std::back_inserter.
+ */
+ void push_back(const osmium::memory::Item& item) {
+ add_item(item);
+ commit();
+ }
+
+ /**
+ * These iterators can be used to iterate over all items in
+ * a buffer.
+ */
+ template <class T>
+ using t_iterator = osmium::memory::ItemIterator<T>;
+
+ template <class T>
+ using t_const_iterator = osmium::memory::ItemIterator<const T>;
+
+ typedef t_iterator<osmium::OSMEntity> iterator;
+ typedef t_const_iterator<osmium::OSMEntity> const_iterator;
+
+ template <class T>
+ t_iterator<T> begin() {
+ return t_iterator<T>(m_data, m_data + m_committed);
+ }
+
+ iterator begin() {
+ return iterator(m_data, m_data + m_committed);
+ }
+
+ template <class T>
+ t_iterator<T> end() {
+ return t_iterator<T>(m_data + m_committed, m_data + m_committed);
+ }
+
+ iterator end() {
+ return iterator(m_data + m_committed, m_data + m_committed);
+ }
+
+ template <class T>
+ t_const_iterator<T> cbegin() const {
+ return t_const_iterator<T>(m_data, m_data + m_committed);
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator(m_data, m_data + m_committed);
+ }
+
+ template <class T>
+ t_const_iterator<T> cend() const {
+ return t_const_iterator<T>(m_data + m_committed, m_data + m_committed);
+ }
+
+ const_iterator cend() const {
+ return const_iterator(m_data + m_committed, m_data + m_committed);
+ }
+
+ template <class T>
+ t_const_iterator<T> begin() const {
+ return cbegin<T>();
+ }
+
+ const_iterator begin() const {
+ return cbegin();
+ }
+
+ template <class T>
+ t_const_iterator<T> end() const {
+ return cend<T>();
+ }
+
+ const_iterator end() const {
+ return cend();
+ }
+
+ /**
+ * In a bool context any initialized buffer is true.
+ */
+ explicit operator bool() const {
+ return m_data != nullptr;
+ }
+
+ friend void swap(Buffer& lhs, Buffer& rhs) {
+ using std::swap;
+
+ swap(lhs.m_memory, rhs.m_memory);
+ swap(lhs.m_data, rhs.m_data);
+ swap(lhs.m_capacity, rhs.m_capacity);
+ swap(lhs.m_written, rhs.m_written);
+ swap(lhs.m_committed, rhs.m_committed);
+ }
+
+ /**
+ * Purge removed items from the buffer. This is done by moving all
+ * non-removed items forward in the buffer overwriting removed
+ * items and then correcting the m_written and m_committed numbers.
+ *
+ * Note that calling this function invalidates all iterators on this
+ * buffer and all offsets in this buffer.
+ *
+ * For every non-removed item that moves its position, the function
+ * 'moving_in_buffer' is called on the given callback object with
+ * the old and new offsets in the buffer where the object used to
+ * be and is now, respectively. This call can be used to update any
+ * indexes.
+ */
+ template <class TCallbackClass>
+ void purge_removed(TCallbackClass* callback) {
+ if (begin() == end()) {
+ return;
+ }
+
+ iterator it_write = begin();
+
+ iterator next;
+ for (iterator it_read = begin(); it_read != end(); it_read = next) {
+ next = std::next(it_read);
+ if (!it_read->removed()) {
+ if (it_read != it_write) {
+ assert(it_read.data() >= data());
+ assert(it_write.data() >= data());
+ size_t old_offset = static_cast<size_t>(it_read.data() - data());
+ size_t new_offset = static_cast<size_t>(it_write.data() - data());
+ callback->moving_in_buffer(old_offset, new_offset);
+ std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
+ }
+ it_write.advance_once();
+ }
+ }
+
+ assert(it_write.data() >= data());
+ m_written = static_cast<size_t>(it_write.data() - data());
+ m_committed = m_written;
+ }
+
+ }; // class Buffer
+
+ inline bool operator==(const Buffer& lhs, const Buffer& rhs) noexcept {
+ return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
+ }
+
+ inline bool operator!=(const Buffer& lhs, const Buffer& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ } // namespace memory
+
+} // namespace osmium
+
+#endif // OSMIUM_MEMORY_BUFFER_HPP
diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp
new file mode 100644
index 0000000..7deb88b
--- /dev/null
+++ b/include/osmium/memory/collection.hpp
@@ -0,0 +1,154 @@
+#ifndef OSMIUM_MEMORY_COLLECTION_HPP
+#define OSMIUM_MEMORY_COLLECTION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iterator>
+#include <iosfwd>
+#include <type_traits>
+
+#include <osmium/memory/item.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ namespace memory {
+
+ template <class TMember>
+ class CollectionIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+
+ // 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<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+
+ data_type m_data;
+
+ public:
+
+ CollectionIterator() noexcept :
+ m_data(nullptr) {
+ }
+
+ CollectionIterator(data_type data) noexcept :
+ m_data(data) {
+ }
+
+ CollectionIterator<TMember>& operator++() {
+ m_data = reinterpret_cast<TMember*>(m_data)->next();
+ return *static_cast<CollectionIterator<TMember>*>(this);
+ }
+
+ CollectionIterator<TMember> operator++(int) {
+ CollectionIterator<TMember> tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const CollectionIterator<TMember>& rhs) const noexcept {
+ return m_data == rhs.m_data;
+ }
+
+ bool operator!=(const CollectionIterator<TMember>& rhs) const noexcept {
+ return m_data != rhs.m_data;
+ }
+
+ unsigned char* data() const noexcept {
+ return m_data;
+ }
+
+ TMember& operator*() const {
+ return *reinterpret_cast<TMember*>(m_data);
+ }
+
+ TMember* operator->() const {
+ return reinterpret_cast<TMember*>(m_data);
+ }
+
+ template <typename TChar, typename TTraits>
+ friend std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const CollectionIterator<TMember>& iter) {
+ return out << static_cast<const void*>(iter.m_data);
+ }
+
+ }; // class CollectionIterator
+
+ template <class TMember, osmium::item_type TCollectionItemType>
+ class Collection : public Item {
+
+ public:
+
+ typedef CollectionIterator<TMember> iterator;
+ typedef CollectionIterator<const TMember> const_iterator;
+ typedef TMember value_type;
+
+ static constexpr osmium::item_type itemtype = TCollectionItemType;
+
+ Collection() :
+ Item(sizeof(Collection<TMember, TCollectionItemType>), TCollectionItemType) {
+ }
+
+ bool empty() const {
+ return sizeof(Collection<TMember, TCollectionItemType>) == byte_size();
+ }
+
+ iterator begin() {
+ return iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
+ }
+
+ iterator end() {
+ return iterator(data() + byte_size());
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
+ }
+
+ const_iterator cend() const {
+ return const_iterator(data() + byte_size());
+ }
+
+ const_iterator begin() const {
+ return cbegin();
+ }
+
+ const_iterator end() const {
+ return cend();
+ }
+
+ }; // class Collection
+
+ } // namespace memory
+
+} // namespace osmium
+
+#endif // OSMIUM_MEMORY_COLLECTION_HPP
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
new file mode 100644
index 0000000..df15d01
--- /dev/null
+++ b/include/osmium/memory/item.hpp
@@ -0,0 +1,179 @@
+#ifndef OSMIUM_MEMORY_ITEM_HPP
+#define OSMIUM_MEMORY_ITEM_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace osmium {
+
+ // forward declaration, see osmium/osm/item_type.hpp for declaration
+ enum class item_type : uint16_t;
+
+ namespace builder {
+ class Builder;
+ }
+
+ namespace memory {
+
+ typedef uint32_t item_size_type;
+
+ // align datastructures to this many bytes
+ constexpr item_size_type align_bytes = 8;
+
+ template <typename T>
+ inline T padded_length(T length) noexcept {
+ static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
+ "Template parameter must be unsigned integral type");
+ return (length + align_bytes - 1) & ~(align_bytes - 1);
+ }
+
+ /**
+ * @brief Namespace for Osmium internal use
+ */
+ namespace detail {
+
+ /**
+ * This class contains only a helper method used in several
+ * other classes.
+ */
+ class ItemHelper {
+
+ protected:
+
+ ItemHelper() = default;
+
+ ~ItemHelper() = default;
+
+ ItemHelper(const ItemHelper&) = default;
+ ItemHelper(ItemHelper&&) = default;
+
+ ItemHelper& operator=(const ItemHelper&) = default;
+ ItemHelper& operator=(ItemHelper&&) = default;
+
+ public:
+
+ unsigned char* data() noexcept {
+ return reinterpret_cast<unsigned char*>(this);
+ }
+
+ const unsigned char* data() const noexcept {
+ return reinterpret_cast<const unsigned char*>(this);
+ }
+
+ }; // class ItemHelper
+
+ } // namespace detail
+
+ class Item : public osmium::memory::detail::ItemHelper {
+
+ item_size_type m_size;
+ item_type m_type;
+ uint16_t m_removed : 1;
+ uint16_t m_padding : 15;
+
+ template <class TMember>
+ friend class CollectionIterator;
+
+ template <class TMember>
+ friend class ItemIterator;
+
+ friend class osmium::builder::Builder;
+
+ Item& add_size(const item_size_type size) noexcept {
+ m_size += size;
+ return *this;
+ }
+
+ protected:
+
+ explicit Item(item_size_type size=0, item_type type=item_type()) noexcept :
+ m_size(size),
+ m_type(type),
+ m_removed(false),
+ m_padding(0) {
+ }
+
+ Item(const Item&) = delete;
+ Item(Item&&) = delete;
+
+ Item& operator=(const Item&) = delete;
+ Item& operator=(Item&&) = delete;
+
+ Item& set_type(const item_type item_type) noexcept {
+ m_type = item_type;
+ return *this;
+ }
+
+ public:
+
+ unsigned char* next() noexcept {
+ return data() + padded_size();
+ }
+
+ const unsigned char* next() const noexcept {
+ return data() + padded_size();
+ }
+
+ item_size_type byte_size() const noexcept {
+ return m_size;
+ }
+
+ item_size_type padded_size() const {
+ return padded_length(m_size);
+ }
+
+ item_type type() const noexcept {
+ return m_type;
+ }
+
+ bool removed() const noexcept {
+ return m_removed;
+ }
+
+ void set_removed(bool removed) noexcept {
+ m_removed = removed;
+ }
+
+ }; // class Item
+
+ static_assert(sizeof(Item) == 8, "Class osmium::Item has wrong size!");
+ static_assert(sizeof(Item) % align_bytes == 0, "Class osmium::Item has wrong size to be aligned properly!");
+
+ } // namespace memory
+
+} // namespace osmium
+
+#endif // OSMIUM_MEMORY_ITEM_HPP
diff --git a/include/osmium/memory/item_iterator.hpp b/include/osmium/memory/item_iterator.hpp
new file mode 100644
index 0000000..3e5b5fa
--- /dev/null
+++ b/include/osmium/memory/item_iterator.hpp
@@ -0,0 +1,234 @@
+#ifndef OSMIUM_ITEM_ITERATOR_HPP
+#define OSMIUM_ITEM_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <iterator>
+#include <iosfwd>
+#include <type_traits>
+
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+ class Node;
+ class Way;
+ class Relation;
+ class Area;
+ class Changeset;
+ class OSMObject;
+ class OSMEntity;
+ class TagList;
+ class WayNodeList;
+ class RelationMemberList;
+ class InnerRing;
+ class OuterRing;
+
+ namespace memory {
+
+ namespace detail {
+
+ template <class T>
+ inline bool type_is_compatible(osmium::item_type) noexcept {
+ return true;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::Node>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::node;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::Way>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::way;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::Relation>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::relation;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::Area>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::area;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::Changeset>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::changeset;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::OSMObject>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::node || t == osmium::item_type::way || t == osmium::item_type::relation || t == osmium::item_type::area;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::OSMEntity>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::node || t == osmium::item_type::way || t == osmium::item_type::relation || t == osmium::item_type::area || t == osmium::item_type::changeset;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::TagList>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::tag_list;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::WayNodeList>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::way_node_list;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::RelationMemberList>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::relation_member_list || t == osmium::item_type::relation_member_list_with_full_members;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::OuterRing>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::outer_ring;
+ }
+
+ template <>
+ inline bool type_is_compatible<osmium::InnerRing>(osmium::item_type t) noexcept {
+ return t == osmium::item_type::inner_ring;
+ }
+
+ } // namespace detail
+
+ template <class TMember>
+ class ItemIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+
+ static_assert(std::is_base_of<osmium::memory::Item, TMember>::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<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+
+ data_type m_data;
+ data_type m_end;
+
+ void advance_to_next_item_of_right_type() {
+ while (m_data != m_end &&
+ !detail::type_is_compatible<typename std::remove_const<TMember>::type>(reinterpret_cast<const osmium::memory::Item*>(m_data)->type())) {
+ m_data = reinterpret_cast<TMember*>(m_data)->next();
+ }
+ }
+
+ public:
+
+ ItemIterator() noexcept :
+ m_data(nullptr),
+ m_end(nullptr) {
+ }
+
+ ItemIterator(data_type data, data_type end) :
+ m_data(data),
+ m_end(end) {
+ advance_to_next_item_of_right_type();
+ }
+
+ template <class T>
+ ItemIterator<T> cast() const {
+ return ItemIterator<T>(m_data, m_end);
+ }
+
+ ItemIterator<TMember>& operator++() {
+ assert(m_data);
+ assert(m_data != m_end);
+ m_data = reinterpret_cast<TMember*>(m_data)->next();
+ advance_to_next_item_of_right_type();
+ return *static_cast<ItemIterator<TMember>*>(this);
+ }
+
+ /**
+ * Like operator++() but will NOT skip items of unwanted
+ * types. Do not use this unless you know what you are
+ * doing.
+ */
+ ItemIterator<TMember>& advance_once() {
+ assert(m_data);
+ assert(m_data != m_end);
+ m_data = reinterpret_cast<TMember*>(m_data)->next();
+ return *static_cast<ItemIterator<TMember>*>(this);
+ }
+
+ ItemIterator<TMember> operator++(int) {
+ ItemIterator<TMember> tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const ItemIterator<TMember>& rhs) const {
+ return m_data == rhs.m_data && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const ItemIterator<TMember>& rhs) const {
+ return !(*this == rhs);
+ }
+
+ unsigned char* data() const {
+ assert(m_data);
+ return m_data;
+ }
+
+ TMember& operator*() const {
+ assert(m_data);
+ assert(m_data != m_end);
+ return *reinterpret_cast<TMember*>(m_data);
+ }
+
+ TMember* operator->() const {
+ assert(m_data);
+ assert(m_data != m_end);
+ return reinterpret_cast<TMember*>(m_data);
+ }
+
+ explicit operator bool() const {
+ return m_data != nullptr;
+ }
+
+ template <typename TChar, typename TTraits>
+ friend std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ItemIterator<TMember>& iter) {
+ return out << static_cast<void*>(iter.m_data);
+ }
+
+ }; // class ItemIterator
+
+ } // namespace memory
+
+} // namespace osmium
+
+#endif // OSMIUM_ITEM_ITERATOR_HPP
diff --git a/include/osmium/object_pointer_collection.hpp b/include/osmium/object_pointer_collection.hpp
new file mode 100644
index 0000000..7524703
--- /dev/null
+++ b/include/osmium/object_pointer_collection.hpp
@@ -0,0 +1,112 @@
+#ifndef OSMIUM_OBJECT_POINTER_COLLECTION_HPP
+#define OSMIUM_OBJECT_POINTER_COLLECTION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <utility>
+#include <vector>
+
+#include <boost/iterator/indirect_iterator.hpp>
+
+#include <osmium/handler.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/object.hpp>
+
+// IWYU pragma: no_forward_declare osmium::OSMObject
+// IWYU pragma: no_forward_declare osmium::memory::Item
+
+namespace osmium {
+
+ /**
+ * A collection of pointers to OSM objects. The pointers can be easily
+ * and quickly sorted or otherwise manipulated, while the objects
+ * themselves or the buffers they are in, do not have to be changed.
+ *
+ * An iterator is provided that can iterate over the pointers but looks
+ * like it is iterating over the underlying OSM objects.
+ *
+ * This class implements the visitor pattern which makes it easy to
+ * populate the collection from a buffer of OSM objects:
+ *
+ * osmium::ObjectPointerCollection objects;
+ * osmium::memory::Buffer buffer = reader.read();
+ * osmium::apply(buffer, objects);
+ *
+ */
+ class ObjectPointerCollection : public osmium::handler::Handler {
+
+ std::vector<osmium::OSMObject*> m_objects;
+
+ public:
+
+ typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::iterator, osmium::OSMObject> iterator;
+ typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::const_iterator, const osmium::OSMObject> const_iterator;
+
+ ObjectPointerCollection() noexcept :
+ m_objects() {
+ }
+
+ void osm_object(osmium::OSMObject& object) {
+ m_objects.push_back(&object);
+ }
+
+ /**
+ * Sort objects according to the given order functor.
+ */
+ template <class TCompare>
+ void sort(TCompare&& compare) {
+ std::sort(m_objects.begin(), m_objects.end(), std::forward<TCompare>(compare));
+ }
+
+ iterator begin() {
+ return iterator { m_objects.begin() };
+ }
+
+ iterator end() {
+ return iterator { m_objects.end() };
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator { m_objects.cbegin() };
+ }
+
+ const_iterator cend() const {
+ return const_iterator { m_objects.cend() };
+ }
+
+ }; // class ObjectPointerCollection
+
+} // namespace osmium
+
+#endif // OSMIUM_OBJECT_POINTER_COLLECTION_HPP
diff --git a/include/osmium/osm.hpp b/include/osmium/osm.hpp
new file mode 100644
index 0000000..e92d9b8
--- /dev/null
+++ b/include/osmium/osm.hpp
@@ -0,0 +1,48 @@
+#ifndef OSMIUM_OSM_HPP
+#define OSMIUM_OSM_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/node.hpp> // IWYU pragma: export
+#include <osmium/osm/way.hpp> // IWYU pragma: export
+#include <osmium/osm/relation.hpp> // IWYU pragma: export
+#include <osmium/osm/area.hpp> // IWYU pragma: export
+#include <osmium/osm/changeset.hpp> // IWYU pragma: export
+
+/**
+ * @brief Namespace for everything in the Osmium library.
+ */
+namespace osmium {
+} // namespace osmium
+
+#endif // OSMIUM_OSM_HPP
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
new file mode 100644
index 0000000..afd127e
--- /dev/null
+++ b/include/osmium/osm/area.hpp
@@ -0,0 +1,193 @@
+#ifndef OSMIUM_OSM_AREA_HPP
+#define OSMIUM_OSM_AREA_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstdlib>
+#include <utility>
+
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/node_ref_list.hpp>
+
+namespace osmium {
+
+ namespace builder {
+ template <class T> class ObjectBuilder;
+ }
+
+ /**
+ * An outer ring of an Area.
+ */
+ class OuterRing : public NodeRefList<osmium::item_type::outer_ring> {
+
+ public:
+
+ OuterRing():
+ NodeRefList<osmium::item_type::outer_ring>() {
+ }
+
+ }; // class OuterRing
+
+ static_assert(sizeof(OuterRing) % osmium::memory::align_bytes == 0, "Class osmium::OuterRing has wrong size to be aligned properly!");
+
+ /**
+ * An inner ring of an Area.
+ */
+ class InnerRing : public NodeRefList<osmium::item_type::inner_ring> {
+
+ public:
+
+ InnerRing():
+ NodeRefList<osmium::item_type::inner_ring>() {
+ }
+
+ }; // class InnerRing
+
+ static_assert(sizeof(InnerRing) % osmium::memory::align_bytes == 0, "Class osmium::InnerRing has wrong size to be aligned properly!");
+
+ /**
+ * Convert way or (multipolygon) relation id into unique area id.
+ *
+ * @param id Id of a way or relation
+ * @param type Type of object (way or relation)
+ * @returns Area id
+ */
+ inline osmium::object_id_type object_id_to_area_id(osmium::object_id_type id, osmium::item_type type) {
+ osmium::object_id_type area_id = std::abs(id) * 2;
+ if (type == osmium::item_type::relation) {
+ ++area_id;
+ }
+ return id < 0 ? -area_id : area_id;
+ }
+
+ /**
+ * Convert area id into id of the way or relation it was created from.
+ *
+ * @param id Area id
+ * @returns Way or Relation id.
+ */
+ inline osmium::object_id_type area_id_to_object_id(osmium::object_id_type id) {
+ return id / 2;
+ }
+
+ /**
+ * An OSM area created out of a closed way or a multipolygon relation.
+ */
+ class Area : public OSMObject {
+
+ friend class osmium::builder::ObjectBuilder<osmium::Area>;
+
+ Area() :
+ OSMObject(sizeof(Area), osmium::item_type::area) {
+ }
+
+ public:
+
+ static constexpr osmium::item_type itemtype = osmium::item_type::area;
+
+ /**
+ * Was this area created from a way? (In contrast to areas
+ * created from a relation and their members.)
+ */
+ bool from_way() const noexcept {
+ return (positive_id() & 0x1) == 0;
+ }
+
+ /**
+ * Return the Id of the way or relation this area was created from.
+ */
+ osmium::object_id_type orig_id() const {
+ return osmium::area_id_to_object_id(id());
+ }
+
+ /**
+ * Count the number of outer and inner rings of this area.
+ */
+ std::pair<int, int> num_rings() const {
+ std::pair<int, int> counter;
+
+ for (auto it = cbegin(); it != cend(); ++it) {
+ switch (it->type()) {
+ case osmium::item_type::outer_ring:
+ ++counter.first;
+ break;
+ case osmium::item_type::inner_ring:
+ ++counter.second;
+ break;
+ case osmium::item_type::tag_list:
+ // ignore tags
+ break;
+ case osmium::item_type::undefined:
+ case osmium::item_type::node:
+ case osmium::item_type::way:
+ case osmium::item_type::relation:
+ case osmium::item_type::area:
+ case osmium::item_type::changeset:
+ case osmium::item_type::way_node_list:
+ case osmium::item_type::relation_member_list:
+ case osmium::item_type::relation_member_list_with_full_members:
+ assert(false && "Children of Area can only be outer/inner_ring and tag_list.");
+ break;
+ }
+ }
+
+ return counter;
+ }
+
+ /**
+ * Is this area a multipolygon, ie. has it more than one outer ring?
+ */
+ bool is_multipolygon() const {
+ return num_rings().first > 1;
+ }
+
+ osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cbegin(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
+ return it.cast<const osmium::InnerRing>();
+ }
+
+ osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cend(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
+ return std::next(it).cast<const osmium::InnerRing>();
+ }
+
+ }; // class Area
+
+ static_assert(sizeof(Area) % osmium::memory::align_bytes == 0, "Class osmium::Area has wrong size to be aligned properly!");
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_AREA_HPP
diff --git a/include/osmium/osm/box.hpp b/include/osmium/osm/box.hpp
new file mode 100644
index 0000000..37baf7e
--- /dev/null
+++ b/include/osmium/osm/box.hpp
@@ -0,0 +1,207 @@
+#ifndef OSMIUM_OSM_BOX_HPP
+#define OSMIUM_OSM_BOX_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iosfwd>
+
+#include <osmium/util/compatibility.hpp>
+#include <osmium/osm/location.hpp>
+
+namespace osmium {
+
+ /**
+ * Bounding box.
+ */
+ class Box {
+
+ osmium::Location m_bottom_left;
+ osmium::Location m_top_right;
+
+ public:
+
+ /**
+ * Create undefined Box. Use the extend() function
+ * to add actual bounds.
+ */
+ constexpr Box() noexcept :
+ m_bottom_left(),
+ m_top_right() {
+ }
+
+ Box(double minx, double miny, double maxx, double maxy) :
+ m_bottom_left(minx, miny),
+ m_top_right(maxx, maxy) {
+ }
+
+ Box(const osmium::Location& bottom_left, const osmium::Location& top_right) :
+ m_bottom_left(bottom_left),
+ m_top_right(top_right) {
+ }
+
+ Box(const Box&) = default;
+ Box(Box&&) = default;
+ Box& operator=(const Box&) = default;
+ Box& operator=(Box&&) = default;
+ ~Box() = default;
+
+ /**
+ * Extend this bounding box by the given location. If the
+ * location is undefined, the bounding box is unchanged.
+ */
+ Box& extend(const Location& location) noexcept {
+ if (location) {
+ if (m_bottom_left) {
+ if (location.x() < m_bottom_left.x()) {
+ m_bottom_left.set_x(location.x());
+ }
+ if (location.x() > m_top_right.x()) {
+ m_top_right.set_x(location.x());
+ }
+ if (location.y() < m_bottom_left.y()) {
+ m_bottom_left.set_y(location.y());
+ }
+ if (location.y() > m_top_right.y()) {
+ m_top_right.set_y(location.y());
+ }
+ } else {
+ m_bottom_left = location;
+ m_top_right = location;
+ }
+ }
+ return *this;
+ }
+
+ /**
+ * Extend this bounding box by the given box. If the
+ * box is undefined, the bounding box is unchanged.
+ */
+ Box& extend(const Box& box) noexcept {
+ extend(box.bottom_left());
+ extend(box.top_right());
+ return *this;
+ }
+
+ /**
+ * Box are defined, ie. contains defined coordinates.
+ */
+ explicit constexpr operator bool() const noexcept {
+ return static_cast<bool>(m_bottom_left);
+ }
+
+ /**
+ * Box are valid, ie. defined and inside usual bounds
+ * (-180<=lon<=180, -90<=lat<=90).
+ */
+ OSMIUM_CONSTEXPR bool valid() const noexcept {
+ return bottom_left().valid() && top_right().valid();
+ }
+
+ /**
+ * Bottom-left location.
+ */
+ OSMIUM_CONSTEXPR Location bottom_left() const noexcept {
+ return m_bottom_left;
+ }
+
+ /**
+ * Bottom-left location.
+ */
+ Location& bottom_left() noexcept {
+ return m_bottom_left;
+ }
+
+ /**
+ * Top-right location.
+ */
+ OSMIUM_CONSTEXPR Location top_right() const noexcept {
+ return m_top_right;
+ }
+
+ /**
+ * Top-right location.
+ */
+ Location& top_right() noexcept {
+ return m_top_right;
+ }
+
+ /**
+ * Is the location inside the box?
+ */
+ bool contains(const osmium::Location& location) const {
+ return location.x() >= bottom_left().x() && location.y() >= bottom_left().y() &&
+ location.x() <= top_right().x() && location.y() <= top_right().y();
+ }
+
+ /**
+ * Calculate size of the box in square degrees.
+ *
+ * @throws osmium::invalid_location unless all coordinates are valid
+ */
+ double size() const {
+ return (m_top_right.lon() - m_bottom_left.lon()) *
+ (m_top_right.lat() - m_bottom_left.lat());
+ }
+
+ }; // class Box
+
+ /**
+ * Boxes are equal if both locations are equal.
+ */
+ inline OSMIUM_CONSTEXPR bool operator==(const Box& lhs, const Box& rhs) noexcept {
+ return lhs.bottom_left() == rhs.bottom_left() && lhs.top_right() == rhs.top_right();
+ }
+
+ /**
+ * Output a box to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Box& box) {
+ if (box) {
+ out << '('
+ << box.bottom_left().lon()
+ << ','
+ << box.bottom_left().lat()
+ << ','
+ << box.top_right().lon()
+ << ','
+ << box.top_right().lat()
+ << ')';
+ } else {
+ out << "(undefined)";
+ }
+ return out;
+ }
+} // namespace osmium
+
+#endif // OSMIUM_OSM_BOX_HPP
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
new file mode 100644
index 0000000..cc1c52d
--- /dev/null
+++ b/include/osmium/osm/changeset.hpp
@@ -0,0 +1,336 @@
+#ifndef OSMIUM_OSM_CHANGESET_HPP
+#define OSMIUM_OSM_CHANGESET_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdint>
+#include <cstring>
+
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/osm/entity.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace builder {
+ template <class T> class ObjectBuilder;
+ }
+
+ /**
+ * \brief An OSM Changeset, a group of changes made by a single user over
+ * a short period of time.
+ *
+ * You can not create Changeset objects directly. Use the ChangesetBuilder
+ * class to create Changesets in a Buffer.
+ */
+ class Changeset : public osmium::OSMEntity {
+
+ friend class osmium::builder::ObjectBuilder<osmium::Changeset>;
+
+ osmium::Timestamp m_created_at;
+ osmium::Timestamp m_closed_at;
+ osmium::Box m_bounds;
+ changeset_id_type m_id {0};
+ num_changes_type m_num_changes {0};
+ user_id_type m_uid {0};
+ string_size_type m_user_size;
+
+ Changeset() :
+ OSMEntity(sizeof(Changeset), osmium::item_type::changeset) {
+ }
+
+ void set_user_size(string_size_type size) {
+ m_user_size = size;
+ }
+
+ unsigned char* subitems_position() {
+ return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size);
+ }
+
+ const unsigned char* subitems_position() const {
+ return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size);
+ }
+
+ public:
+
+ /// Get ID of this changeset
+ changeset_id_type id() const noexcept {
+ return m_id;
+ }
+
+ /**
+ * Set ID of this changeset
+ *
+ * @param id The id.
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_id(changeset_id_type id) noexcept {
+ m_id = id;
+ return *this;
+ }
+
+ /**
+ * Set ID of this changeset.
+ *
+ * @param id The id.
+ * @returns Reference to object to make calls chainable.
+ */
+ Changeset& set_id(const char* id) {
+ return set_id(osmium::string_to_changeset_id(id));
+ }
+
+ /// Get user id.
+ user_id_type uid() const noexcept {
+ return m_uid;
+ }
+
+ /**
+ * Set user id.
+ *
+ * @param uid The user id.
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_uid(user_id_type uid) noexcept {
+ m_uid = uid;
+ return *this;
+ }
+
+ /**
+ * Set user id to given uid or to 0 (anonymous user) if the given
+ * uid is smaller than 0.
+ *
+ * @param uid The user id.
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_uid_from_signed(signed_user_id_type uid) noexcept {
+ m_uid = uid < 0 ? 0 : static_cast<user_id_type>(uid);
+ return *this;
+ }
+
+ /**
+ * Set user id to given uid or to 0 (anonymous user) if the given
+ * uid is smaller than 0.
+ *
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_uid(const char* uid) {
+ return set_uid_from_signed(string_to_user_id(uid));
+ }
+
+ /// Is this user anonymous?
+ bool user_is_anonymous() const noexcept {
+ return m_uid == 0;
+ }
+
+ /// Get timestamp when this changeset was created.
+ osmium::Timestamp created_at() const noexcept {
+ return m_created_at;
+ }
+
+ /**
+ * Get timestamp when this changeset was closed.
+ *
+ * @returns Timestamp. Will return the empty Timestamp when the
+ * changeset is not yet closed.
+ */
+ osmium::Timestamp closed_at() const noexcept {
+ return m_closed_at;
+ }
+
+ /// Is this changeset open?
+ bool open() const noexcept {
+ return m_closed_at == osmium::Timestamp();
+ }
+
+ /// Is this changeset closed?
+ bool closed() const noexcept {
+ return !open();
+ }
+
+ /**
+ * Set the timestamp when this changeset was created.
+ *
+ * @param timestamp Timestamp
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_created_at(const osmium::Timestamp timestamp) {
+ m_created_at = timestamp;
+ return *this;
+ }
+
+ /**
+ * Set the timestamp when this changeset was closed.
+ *
+ * @param timestamp Timestamp
+ * @returns Reference to changeset to make calls chainable.
+ */
+ Changeset& set_closed_at(const osmium::Timestamp timestamp) {
+ m_closed_at = timestamp;
+ return *this;
+ }
+
+ /// Get the number of changes in this changeset
+ num_changes_type num_changes() const noexcept {
+ return m_num_changes;
+ }
+
+ /// Set the number of changes in this changeset
+ Changeset& set_num_changes(num_changes_type num_changes) noexcept {
+ m_num_changes = num_changes;
+ return *this;
+ }
+
+ /// Set the number of changes in this changeset
+ Changeset& set_num_changes(const char* num_changes) noexcept {
+ return set_num_changes(osmium::string_to_num_changes(num_changes));
+ }
+
+ /**
+ * Get the bounding box of this changeset.
+ *
+ * @returns Bounding box. Can be empty.
+ */
+ osmium::Box& bounds() noexcept {
+ return m_bounds;
+ }
+
+ /**
+ * Get the bounding box of this changeset.
+ *
+ * @returns Bounding box. Can be empty.
+ */
+ const osmium::Box& bounds() const noexcept {
+ return m_bounds;
+ }
+
+ /// Get user name.
+ const char* user() const {
+ return reinterpret_cast<const char*>(data() + sizeof(Changeset));
+ }
+
+ /// Get the list of tags.
+ const TagList& tags() const {
+ return osmium::detail::subitem_of_type<const TagList>(cbegin(), cend());
+ }
+
+ /**
+ * Set named attribute.
+ *
+ * @param attr Name of the attribute (must be one of "id", "version",
+ * "changeset", "timestamp", "uid", "visible")
+ * @param value Value of the attribute
+ */
+ void set_attribute(const char* attr, const char* value) {
+ if (!strcmp(attr, "id")) {
+ set_id(value);
+ } else if (!strcmp(attr, "num_changes")) {
+ set_num_changes(value);
+ } else if (!strcmp(attr, "created_at")) {
+ set_created_at(osmium::Timestamp(value));
+ } else if (!strcmp(attr, "closed_at")) {
+ set_closed_at(osmium::Timestamp(value));
+ } else if (!strcmp(attr, "uid")) {
+ set_uid(value);
+ }
+ }
+
+ typedef osmium::memory::CollectionIterator<Item> iterator;
+ typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+
+ iterator begin() {
+ return iterator(subitems_position());
+ }
+
+ iterator end() {
+ return iterator(data() + padded_size());
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator(subitems_position());
+ }
+
+ const_iterator cend() const {
+ return const_iterator(data() + padded_size());
+ }
+
+ const_iterator begin() const {
+ return cbegin();
+ }
+
+ const_iterator end() const {
+ return cend();
+ }
+
+ }; // class Changeset
+
+ static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0,
+ "Class osmium::Changeset has wrong size to be aligned properly!");
+
+ /**
+ * Changesets are equal if their IDs are equal.
+ */
+ inline bool operator==(const Changeset& lhs, const Changeset& rhs) {
+ return lhs.id() == rhs.id();
+ }
+
+ inline bool operator!=(const Changeset& lhs, const Changeset& rhs) {
+ return ! (lhs == rhs);
+ }
+
+ /**
+ * Changesets can be ordered by id.
+ */
+ inline bool operator<(const Changeset& lhs, const Changeset& rhs) {
+ return lhs.id() < rhs.id();
+ }
+
+ inline bool operator>(const Changeset& lhs, const Changeset& rhs) {
+ return rhs < lhs;
+ }
+
+ inline bool operator<=(const Changeset& lhs, const Changeset& rhs) {
+ return ! (rhs < lhs);
+ }
+
+ inline bool operator>=(const Changeset& lhs, const Changeset& rhs) {
+ return ! (lhs < rhs);
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_CHANGESET_HPP
diff --git a/include/osmium/osm/diff_object.hpp b/include/osmium/osm/diff_object.hpp
new file mode 100644
index 0000000..55a5cef
--- /dev/null
+++ b/include/osmium/osm/diff_object.hpp
@@ -0,0 +1,156 @@
+#ifndef OSMIUM_OSM_DIFF_OBJECT_HPP
+#define OSMIUM_OSM_DIFF_OBJECT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/item_type.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ class Node;
+ class Way;
+ class Relation;
+
+ class DiffObject {
+
+ protected:
+
+ osmium::OSMObject* m_prev;
+ osmium::OSMObject* m_curr;
+ osmium::OSMObject* m_next;
+
+ public:
+
+ DiffObject() noexcept :
+ m_prev(nullptr),
+ m_curr(nullptr),
+ m_next(nullptr) {
+ }
+
+ explicit DiffObject(osmium::OSMObject& prev, osmium::OSMObject& curr, osmium::OSMObject& next) noexcept :
+ m_prev(&prev),
+ m_curr(&curr),
+ m_next(&next) {
+ }
+
+ DiffObject(const DiffObject&) = default;
+ DiffObject& operator=(const DiffObject&) = default;
+
+ DiffObject(DiffObject&&) = default;
+ DiffObject& operator=(DiffObject&&) = default;
+
+ const osmium::OSMObject& prev() const noexcept {
+ return *m_prev;
+ }
+
+ const osmium::OSMObject& curr() const noexcept {
+ return *m_curr;
+ }
+
+ const osmium::OSMObject& next() const noexcept {
+ return *m_next;
+ }
+
+ bool first() const noexcept {
+ return m_prev == m_curr;
+ }
+
+ bool last() const noexcept {
+ return m_curr == m_next;
+ }
+
+ osmium::item_type type() const noexcept {
+ return m_curr->type();
+ }
+
+ osmium::object_id_type id() const noexcept {
+ return m_curr->id();
+ }
+
+ osmium::object_version_type version() const noexcept {
+ return m_curr->version();
+ }
+
+ osmium::changeset_id_type changeset() const noexcept {
+ return m_curr->changeset();
+ }
+
+ const osmium::Timestamp start_time() const noexcept {
+ return m_curr->timestamp();
+ }
+
+ const osmium::Timestamp end_time() const noexcept {
+ return last() ? osmium::Timestamp() : m_next->timestamp();
+ }
+
+ }; // class DiffObject
+
+ template <class T>
+ class DiffObjectDerived : public DiffObject {
+
+ public:
+
+ DiffObjectDerived(T& prev, T& curr, T& next) noexcept :
+ DiffObject(prev, curr, next) {
+ }
+
+ DiffObjectDerived(const DiffObjectDerived&) = default;
+ DiffObjectDerived& operator=(const DiffObjectDerived&) = default;
+
+ DiffObjectDerived(DiffObjectDerived&&) = default;
+ DiffObjectDerived& operator=(DiffObjectDerived&&) = default;
+
+ const T& prev() const noexcept {
+ return *static_cast<const T*>(m_prev);
+ }
+
+ const T& curr() const noexcept {
+ return *static_cast<const T*>(m_curr);
+ }
+
+ const T& next() const noexcept {
+ return *static_cast<const T*>(m_next);
+ }
+
+ }; // class DiffObjectDerived
+
+ typedef DiffObjectDerived<osmium::Node> DiffNode;
+ typedef DiffObjectDerived<osmium::Way> DiffWay;
+ typedef DiffObjectDerived<osmium::Relation> DiffRelation;
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_DIFF_OBJECT_HPP
diff --git a/include/osmium/osm/entity.hpp b/include/osmium/osm/entity.hpp
new file mode 100644
index 0000000..14861a2
--- /dev/null
+++ b/include/osmium/osm/entity.hpp
@@ -0,0 +1,79 @@
+#ifndef OSMIUM_OSM_ENTITY_HPP
+#define OSMIUM_OSM_ENTITY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/memory/item.hpp>
+#include <osmium/osm/entity_bits.hpp>
+
+namespace osmium {
+
+ namespace detail {
+
+ template <class TSubitem, class TIter>
+ inline TSubitem& subitem_of_type(TIter it, TIter end) {
+ for (; it != end; ++it) {
+ if (it->type() == TSubitem::itemtype) {
+ return reinterpret_cast<TSubitem&>(*it);
+ }
+ }
+
+ // If no subitem of the TSubitem type was found,
+ // return a default constructed one.
+ static TSubitem subitem;
+ return subitem;
+ }
+
+ } // namespace detail
+
+ /**
+ * \brief OSMEntity is the abstract base class for the OSMObject and
+ * Changeset classes.
+ */
+ class OSMEntity : public osmium::memory::Item {
+
+ public:
+
+ explicit OSMEntity(osmium::memory::item_size_type size, osmium::item_type type) :
+ Item(size, type) {
+ }
+
+ bool type_is_in(osmium::osm_entity_bits::type entity_bits) const {
+ return (osm_entity_bits::from_item_type(type()) & entity_bits) != 0;
+ }
+
+ }; // class OSMEntity
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_ENTITY_HPP
diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp
new file mode 100644
index 0000000..1c1cb80
--- /dev/null
+++ b/include/osmium/osm/entity_bits.hpp
@@ -0,0 +1,105 @@
+#ifndef OSMIUM_OSM_ENTITY_BITS_HPP
+#define OSMIUM_OSM_ENTITY_BITS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Bitfield for OSM entity types.
+ */
+ namespace osm_entity_bits {
+
+ /**
+ * Describes zero or more OSM entities.
+ *
+ * Usage:
+ *
+ * @code{.cpp}
+ * osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way;
+ *
+ * entities |= osmium::osm_entity_bits::relation;
+ *
+ * assert(entities & osmium::osm_entity_bits::object);
+ *
+ * assert(! (entities & osmium::osm_entity_bits::changeset));
+ * @endcode
+ */
+ enum type : unsigned char {
+
+ nothing = 0x00,
+ node = 0x01,
+ way = 0x02,
+ relation = 0x04,
+ nwr = 0x07, ///< node, way, or relation object
+ area = 0x08,
+ nwra = 0x0f, ///< node, way, relation, or area object
+ object = 0x0f, ///< node, way, relation, or area object
+ changeset = 0x10,
+ all = 0x1f ///< object or changeset
+
+ }; // enum type
+
+ inline type operator|(const type lhs, const type rhs) noexcept {
+ return static_cast<type>(static_cast<int>(lhs) | static_cast<int> (rhs));
+ }
+
+ inline type& operator|=(type& lhs, const type rhs) noexcept {
+ lhs = lhs | rhs;
+ return lhs;
+ }
+
+ inline type operator&(const type lhs, const type rhs) noexcept {
+ return static_cast<type>(static_cast<int>(lhs) & static_cast<int> (rhs));
+ }
+
+ inline type operator~(const type value) noexcept {
+ return static_cast<type>(~static_cast<int>(value));
+ }
+
+ inline type operator&=(type& lhs, const type rhs) noexcept {
+ lhs = lhs & rhs;
+ return lhs;
+ }
+
+ inline type from_item_type(osmium::item_type item_type) noexcept {
+ return static_cast<osmium::osm_entity_bits::type>(0x1 << (static_cast<uint16_t>(item_type) - 1));
+ }
+
+ } // namespace osm_entity_bits
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_ENTITY_BITS_HPP
diff --git a/include/osmium/osm/item_type.hpp b/include/osmium/osm/item_type.hpp
new file mode 100644
index 0000000..c2187a3
--- /dev/null
+++ b/include/osmium/osm/item_type.hpp
@@ -0,0 +1,173 @@
+#ifndef OSMIUM_OSM_ITEM_TYPE_HPP
+#define OSMIUM_OSM_ITEM_TYPE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdint> // IWYU pragma: keep
+#include <iosfwd>
+#include <stdexcept>
+
+namespace osmium {
+
+ enum class item_type : uint16_t {
+
+ undefined = 0x00,
+ node = 0x01,
+ way = 0x02,
+ relation = 0x03,
+ area = 0x04,
+ changeset = 0x05,
+ tag_list = 0x11,
+ way_node_list = 0x12,
+ relation_member_list = 0x13,
+ relation_member_list_with_full_members = 0x23,
+ outer_ring = 0x40,
+ inner_ring = 0x41
+
+ }; // enum class item_type
+
+ inline item_type char_to_item_type(const char c) noexcept {
+ switch (c) {
+ case 'X':
+ return item_type::undefined;
+ case 'n':
+ return item_type::node;
+ case 'w':
+ return item_type::way;
+ case 'r':
+ return item_type::relation;
+ case 'a':
+ return item_type::area;
+ case 'c':
+ return item_type::changeset;
+ case 'T':
+ return item_type::tag_list;
+ case 'N':
+ return item_type::way_node_list;
+ case 'M':
+ return item_type::relation_member_list;
+ case 'F':
+ return item_type::relation_member_list_with_full_members;
+ case 'O':
+ return item_type::outer_ring;
+ case 'I':
+ return item_type::inner_ring;
+ default:
+ return item_type::undefined;
+ }
+ }
+
+// avoid g++ false positive
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreturn-type"
+ inline char item_type_to_char(const item_type type) noexcept {
+ switch (type) {
+ case item_type::undefined:
+ return 'X';
+ case item_type::node:
+ return 'n';
+ case item_type::way:
+ return 'w';
+ case item_type::relation:
+ return 'r';
+ case item_type::area:
+ return 'a';
+ case item_type::changeset:
+ return 'c';
+ case item_type::tag_list:
+ return 'T';
+ case item_type::way_node_list:
+ return 'N';
+ case item_type::relation_member_list:
+ return 'M';
+ case item_type::relation_member_list_with_full_members:
+ return 'F';
+ case item_type::outer_ring:
+ return 'O';
+ case item_type::inner_ring:
+ return 'I';
+ }
+ }
+
+ inline const char* item_type_to_name(const item_type type) noexcept {
+ switch (type) {
+ case item_type::undefined:
+ return "undefined";
+ case item_type::node:
+ return "node";
+ case item_type::way:
+ return "way";
+ case item_type::relation:
+ return "relation";
+ case item_type::area:
+ return "area";
+ case item_type::changeset:
+ return "changeset";
+ case item_type::tag_list:
+ return "tag_list";
+ case item_type::way_node_list:
+ return "way_node_list";
+ case item_type::relation_member_list:
+ return "relation_member_list";
+ case item_type::relation_member_list_with_full_members:
+ return "relation_member_list_with_full_members";
+ case item_type::outer_ring:
+ return "outer_ring";
+ case item_type::inner_ring:
+ return "inner_ring";
+ }
+ }
+#pragma GCC diagnostic pop
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const item_type item_type) {
+ return out << item_type_to_char(item_type);
+ }
+
+ /**
+ * This exception is thrown when a visitor encounters an unknown item type.
+ * Under usual circumstance this should not happen. If it does happen, it
+ * probably means the buffer contains different kinds of objects than were
+ * expected or that there is some kind of data corruption.
+ */
+ struct unknown_type : public std::runtime_error {
+
+ unknown_type() :
+ std::runtime_error("unknown item type") {
+ }
+
+ }; // struct unknown_type
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_ITEM_TYPE_HPP
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
new file mode 100644
index 0000000..0d4fdc1
--- /dev/null
+++ b/include/osmium/osm/location.hpp
@@ -0,0 +1,285 @@
+#ifndef OSMIUM_OSM_LOCATION_HPP
+#define OSMIUM_OSM_LOCATION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cmath>
+#include <cstdint>
+#include <iosfwd>
+#include <stdexcept>
+#include <string>
+
+#include <iostream>
+
+#include <osmium/util/compatibility.hpp>
+#include <osmium/util/double.hpp>
+
+namespace osmium {
+
+ /**
+ * Exception signaling an invalid location, ie a location
+ * outside the -180 to 180 and -90 to 90 degree range.
+ */
+ struct invalid_location : public std::range_error {
+
+ invalid_location(const std::string& what) :
+ std::range_error(what) {
+ }
+
+ invalid_location(const char* what) :
+ std::range_error(what) {
+ }
+
+ }; // struct invalid_location
+
+ /**
+ * Locations define a place on earth.
+ *
+ * Locations are stored in 32 bit integers for the x and y
+ * coordinates, respectively. This gives you an accuracy of a few
+ * centimeters, good enough for OSM use. (The main OSM database
+ * uses the same scheme.)
+ *
+ * An undefined Location can be created by calling the constructor
+ * without parameters.
+ *
+ * Coordinates are never checked on whether they are inside bounds.
+ * Call valid() to check this.
+ */
+ class Location {
+
+ int32_t m_x;
+ int32_t m_y;
+
+ public:
+
+ // this value is used for a coordinate to mark it as undefined
+ // MSVC doesn't declare std::numeric_limits<int32_t>::max() as
+ // constexpr, so we hard code this for the time being.
+ // static constexpr int32_t undefined_coordinate = std::numeric_limits<int32_t>::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<int32_t>(std::round(c * coordinate_precision));
+ }
+
+ static OSMIUM_CONSTEXPR double fix_to_double(const int32_t c) noexcept {
+ return static_cast<double>(c) / coordinate_precision;
+ }
+
+ /**
+ * Create undefined Location.
+ */
+ explicit constexpr Location() noexcept :
+ m_x(undefined_coordinate),
+ m_y(undefined_coordinate) {
+ }
+
+ /**
+ * Create Location with given x and y coordinates.
+ * Note that these coordinates are coordinate_precision
+ * times larger than the real coordinates.
+ */
+ constexpr Location(const int32_t x, const int32_t y) noexcept :
+ m_x(x),
+ m_y(y) {
+ }
+
+ /**
+ * Create Location with given x and y coordinates.
+ * Note that these coordinates are coordinate_precision
+ * times larger than the real coordinates.
+ */
+ constexpr Location(const int64_t x, const int64_t y) noexcept :
+ m_x(static_cast<int32_t>(x)),
+ m_y(static_cast<int32_t>(y)) {
+ }
+
+ /**
+ * Create Location with given longitude and latitude.
+ */
+ Location(const double lon, const double lat) :
+ m_x(double_to_fix(lon)),
+ m_y(double_to_fix(lat)) {
+ }
+
+ Location(const Location&) = default;
+ Location(Location&&) = default;
+ Location& operator=(const Location&) = default;
+ Location& operator=(Location&&) = default;
+ ~Location() = default;
+
+ /**
+ * Check whether the coordinates of this location
+ * are defined.
+ */
+ explicit constexpr operator bool() const noexcept {
+ return m_x != undefined_coordinate && m_y != undefined_coordinate;
+ }
+
+ /**
+ * Check whether the coordinates are inside the
+ * 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;
+ }
+
+ constexpr int32_t x() const noexcept {
+ return m_x;
+ }
+
+ constexpr int32_t y() const noexcept {
+ return m_y;
+ }
+
+ Location& set_x(const int32_t x) noexcept {
+ m_x = x;
+ return *this;
+ }
+
+ Location& set_y(const int32_t y) noexcept {
+ m_y = y;
+ return *this;
+ }
+
+ /**
+ * Get longitude.
+ *
+ * @throws invalid_location if the location is invalid
+ */
+ double lon() const {
+ if (!valid()) {
+ throw osmium::invalid_location("invalid location");
+ }
+ return fix_to_double(m_x);
+ }
+
+ /**
+ * Get longitude without checking the validity.
+ */
+ double lon_without_check() const {
+ return fix_to_double(m_x);
+ }
+
+ /**
+ * Get latitude.
+ *
+ * @throws invalid_location if the location is invalid
+ */
+ double lat() const {
+ if (!valid()) {
+ throw osmium::invalid_location("invalid location");
+ }
+ return fix_to_double(m_y);
+ }
+
+ /**
+ * Get latitude without checking the validity.
+ */
+ double lat_without_check() const {
+ return fix_to_double(m_y);
+ }
+
+ Location& set_lon(double lon) noexcept {
+ m_x = double_to_fix(lon);
+ return *this;
+ }
+
+ Location& set_lat(double lat) noexcept {
+ m_y = double_to_fix(lat);
+ return *this;
+ }
+
+ template <typename T>
+ T as_string(T iterator, const char separator) const {
+ iterator = osmium::util::double2string(iterator, lon(), 7);
+ *iterator++ = separator;
+ return osmium::util::double2string(iterator, lat(), 7);
+ }
+
+ }; // class Location
+
+ /**
+ * Locations are equal if both coordinates are equal.
+ */
+ inline OSMIUM_CONSTEXPR bool operator==(const Location& lhs, const Location& rhs) noexcept {
+ return lhs.x() == rhs.x() && lhs.y() == rhs.y();
+ }
+
+ inline OSMIUM_CONSTEXPR bool operator!=(const Location& lhs, const Location& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ /**
+ * Compare two locations by comparing first the x and then
+ * the y coordinate. If either of the locations is
+ * undefined the result is undefined.
+ */
+ inline OSMIUM_CONSTEXPR bool operator<(const Location& lhs, const Location& rhs) noexcept {
+ return (lhs.x() == rhs.x() && lhs.y() < rhs.y()) || lhs.x() < rhs.x();
+ }
+
+ inline OSMIUM_CONSTEXPR bool operator>(const Location& lhs, const Location& rhs) noexcept {
+ return rhs < lhs;
+ }
+
+ inline OSMIUM_CONSTEXPR bool operator<=(const Location& lhs, const Location& rhs) noexcept {
+ return ! (rhs < lhs);
+ }
+
+ inline OSMIUM_CONSTEXPR bool operator>=(const Location& lhs, const Location& rhs) noexcept {
+ return ! (lhs < rhs);
+ }
+
+ /**
+ * Output a location to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
+ if (location) {
+ out << '(' << location.lon() << ',' << location.lat() << ')';
+ } else {
+ out << "(undefined,undefined)";
+ }
+ return out;
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_LOCATION_HPP
diff --git a/include/osmium/osm/node.hpp b/include/osmium/osm/node.hpp
new file mode 100644
index 0000000..123bfc4
--- /dev/null
+++ b/include/osmium/osm/node.hpp
@@ -0,0 +1,76 @@
+#ifndef OSMIUM_OSM_NODE_HPP
+#define OSMIUM_OSM_NODE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/object.hpp>
+
+namespace osmium {
+
+ namespace builder {
+ template <class T> class ObjectBuilder;
+ }
+
+ class Node : public OSMObject {
+
+ friend class osmium::builder::ObjectBuilder<osmium::Node>;
+
+ osmium::Location m_location;
+
+ Node() :
+ OSMObject(sizeof(Node), osmium::item_type::node) {
+ }
+
+ public:
+
+ static constexpr osmium::item_type itemtype = osmium::item_type::node;
+
+ osmium::Location location() const noexcept {
+ return m_location;
+ }
+
+ Node& set_location(const osmium::Location& location) {
+ m_location = location;
+ return *this;
+ }
+
+ }; // class Node
+
+ static_assert(sizeof(Node) % osmium::memory::align_bytes == 0, "Class osmium::Node has wrong size to be aligned properly!");
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_NODE_HPP
diff --git a/include/osmium/osm/node_ref.hpp b/include/osmium/osm/node_ref.hpp
new file mode 100644
index 0000000..1a66607
--- /dev/null
+++ b/include/osmium/osm/node_ref.hpp
@@ -0,0 +1,173 @@
+#ifndef OSMIUM_OSM_NODE_REF_HPP
+#define OSMIUM_OSM_NODE_REF_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdlib>
+#include <iosfwd>
+
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ /**
+ * This reference to a node contains a node ID and a (possibly empty)
+ * location.
+ */
+ class NodeRef : public osmium::memory::detail::ItemHelper {
+
+ osmium::object_id_type m_ref;
+ osmium::Location m_location;
+
+ public:
+
+ NodeRef(const osmium::object_id_type ref=0, const osmium::Location& location=Location()) noexcept :
+ m_ref(ref),
+ m_location(location) {
+ }
+
+ osmium::object_id_type ref() const noexcept {
+ return m_ref;
+ }
+
+ osmium::unsigned_object_id_type positive_ref() const noexcept {
+ return static_cast<osmium::unsigned_object_id_type>(std::abs(m_ref));
+ }
+
+ /**
+ * Get reference to location in this NodeRef. Can be used to update it.
+ */
+ osmium::Location& location() noexcept {
+ return m_location;
+ }
+
+ osmium::Location location() const noexcept {
+ return m_location;
+ }
+
+ double lon() const {
+ return m_location.lon();
+ }
+
+ double lat() const {
+ return m_location.lat();
+ }
+
+ int32_t x() const noexcept {
+ return m_location.x();
+ }
+
+ int32_t y() const noexcept {
+ return m_location.y();
+ }
+
+ NodeRef& set_ref(const osmium::object_id_type ref) noexcept {
+ m_ref = ref;
+ return *this;
+ }
+
+ NodeRef& set_location(const osmium::Location& location) noexcept {
+ m_location = location;
+ return *this;
+ }
+
+ }; // class NodeRef
+
+ inline bool operator==(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return lhs.ref() == rhs.ref();
+ }
+
+ inline bool operator!=(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ inline bool operator<(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return lhs.ref() < rhs.ref();
+ }
+
+ inline bool operator>(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return rhs < lhs;
+ }
+
+ inline bool operator<=(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return ! (rhs < lhs);
+ }
+
+ inline bool operator>=(const NodeRef& lhs, const NodeRef& rhs) noexcept {
+ return ! (lhs < rhs);
+ }
+
+ /**
+ * Output a NodeRef to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::NodeRef& nr) {
+ return out << "<" << nr.ref() << " " << nr.location() << ">";
+ }
+
+ /**
+ * Functor to compare NodeRefs by Location instead of id.
+ */
+ struct location_equal {
+
+ bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept {
+ return lhs.location() == rhs.location();
+ }
+
+ typedef NodeRef first_argument_type;
+ typedef NodeRef second_argument_type;
+ typedef bool result_type;
+
+ }; // struct location_equal
+
+ /**
+ * Functor to compare NodeRefs by Location instead of id.
+ */
+ struct location_less {
+
+ bool operator()(const NodeRef& lhs, const NodeRef& rhs) const noexcept {
+ return lhs.location() < rhs.location();
+ }
+
+ typedef NodeRef first_argument_type;
+ typedef NodeRef second_argument_type;
+ typedef bool result_type;
+
+ }; // struct location_less
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_NODE_REF_HPP
diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp
new file mode 100644
index 0000000..f45bf6b
--- /dev/null
+++ b/include/osmium/osm/node_ref_list.hpp
@@ -0,0 +1,135 @@
+#ifndef OSMIUM_OSM_NODE_REF_LIST_HPP
+#define OSMIUM_OSM_NODE_REF_LIST_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstddef>
+#include <iterator>
+
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/node_ref.hpp>
+
+namespace osmium {
+
+ /**
+ * A vector of NodeRef objects. Usually this is not instatiated directly,
+ * but one of its subclasses are used.
+ */
+ template <osmium::item_type TItemType>
+ class NodeRefList : public osmium::memory::Item {
+
+ public:
+
+ static constexpr osmium::item_type itemtype = TItemType;
+
+ NodeRefList() noexcept :
+ osmium::memory::Item(sizeof(NodeRefList), TItemType) {
+ }
+
+ bool empty() const noexcept {
+ return sizeof(NodeRefList) == byte_size();
+ }
+
+ size_t size() const noexcept {
+ assert((osmium::memory::Item::byte_size() - sizeof(NodeRefList)) % sizeof(NodeRef) == 0);
+ return (osmium::memory::Item::byte_size() - sizeof(NodeRefList)) / sizeof(NodeRef);
+ }
+
+ const NodeRef& operator[](size_t n) const {
+ const NodeRef* node_ref = &*(cbegin());
+ return node_ref[n];
+ }
+
+ const NodeRef& front() const {
+ return operator[](0);
+ }
+
+ const NodeRef& back() const {
+ return operator[](size()-1);
+ }
+
+ bool is_closed() const {
+ return front().ref() == back().ref();
+ }
+
+ bool ends_have_same_id() const {
+ return front().ref() == back().ref();
+ }
+
+ bool ends_have_same_location() const {
+ return front().location() == back().location();
+ }
+
+ typedef NodeRef* iterator;
+ typedef const NodeRef* const_iterator;
+ typedef std::reverse_iterator<const NodeRef*> const_reverse_iterator;
+
+ iterator begin() {
+ return iterator(data() + sizeof(NodeRefList));
+ }
+
+ iterator end() {
+ return iterator(data() + byte_size());
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator(data() + sizeof(NodeRefList));
+ }
+
+ const_iterator cend() const {
+ return const_iterator(data() + byte_size());
+ }
+
+ const_iterator begin() const {
+ return cbegin();
+ }
+
+ const_iterator end() const {
+ return cend();
+ }
+
+ const_reverse_iterator crbegin() const {
+ return const_reverse_iterator(cend());
+ }
+
+ const_reverse_iterator crend() const {
+ return const_reverse_iterator(cbegin());
+ }
+
+ }; // class NodeRefList
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_NODE_REF_LIST_HPP
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
new file mode 100644
index 0000000..d5ae48a
--- /dev/null
+++ b/include/osmium/osm/object.hpp
@@ -0,0 +1,437 @@
+#ifndef OSMIUM_OSM_OBJECT_HPP
+#define OSMIUM_OSM_OBJECT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/memory/item_iterator.hpp>
+#include <osmium/osm/entity.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/osm/timestamp.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ /**
+ * OSMObject (Node, Way, Relation, or Area).
+ */
+ class OSMObject : public osmium::OSMEntity {
+
+ object_id_type m_id;
+ bool m_deleted : 1;
+ object_version_type m_version : 31;
+ osmium::Timestamp m_timestamp;
+ user_id_type m_uid;
+ changeset_id_type m_changeset;
+
+ size_t sizeof_object() const noexcept {
+ return sizeof(OSMObject) + (type() == item_type::node ? sizeof(osmium::Location) : 0) + sizeof(string_size_type);
+ }
+
+ unsigned char* user_position() noexcept {
+ return data() + sizeof_object() - sizeof(string_size_type);
+ }
+
+ const unsigned char* user_position() const noexcept {
+ return data() + sizeof_object() - sizeof(string_size_type);
+ }
+
+ string_size_type user_size() const noexcept {
+ return *reinterpret_cast<const string_size_type*>(user_position());
+ }
+
+ unsigned char* subitems_position() {
+ return data() + osmium::memory::padded_length(sizeof_object() + user_size());
+ }
+
+ const unsigned char* subitems_position() const {
+ return data() + osmium::memory::padded_length(sizeof_object() + user_size());
+ }
+
+ protected:
+
+ OSMObject(osmium::memory::item_size_type size, osmium::item_type type) :
+ OSMEntity(size, type),
+ m_id(0),
+ m_deleted(false),
+ m_version(0),
+ m_timestamp(),
+ m_uid(0),
+ m_changeset(0) {
+ }
+
+ void set_user_size(string_size_type size) {
+ *reinterpret_cast<string_size_type*>(user_position()) = size;
+ }
+
+ public:
+
+ /// Get ID of this object.
+ object_id_type id() const noexcept {
+ return m_id;
+ }
+
+ /// Get absolute value of the ID of this object.
+ unsigned_object_id_type positive_id() const noexcept {
+ return static_cast<unsigned_object_id_type>(std::abs(m_id));
+ }
+
+ /**
+ * Set ID of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_id(object_id_type id) noexcept {
+ m_id = id;
+ return *this;
+ }
+
+ /**
+ * Set ID of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_id(const char* id) {
+ return set_id(osmium::string_to_object_id(id));
+ }
+
+ /// Is this object marked as deleted?
+ bool deleted() const noexcept {
+ return m_deleted;
+ }
+
+ /// Is this object marked visible (ie not deleted)?
+ bool visible() const noexcept {
+ return !deleted();
+ }
+
+ /**
+ * Mark this object as deleted (or not).
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_deleted(bool deleted) noexcept {
+ m_deleted = deleted;
+ return *this;
+ }
+
+ /**
+ * Mark this object as visible (ie not deleted) (or not).
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_visible(bool visible) noexcept {
+ m_deleted = !visible;
+ return *this;
+ }
+
+ /**
+ * Mark this object as visible (ie not deleted) or deleted.
+ *
+ * @param visible Either "true" or "false"
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_visible(const char* visible) {
+ if (!strcmp("true", visible)) {
+ set_visible(true);
+ } else if (!strcmp("false", visible)) {
+ set_visible(false);
+ } else {
+ throw std::invalid_argument("Unknown value for visible attribute (allowed is 'true' or 'false')");
+ }
+ return *this;
+ }
+
+ /// Get version of this object.
+ object_version_type version() const noexcept {
+ return m_version;
+ }
+
+ /**
+ * Set object version.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_version(object_version_type version) noexcept {
+ m_version = version;
+ return *this;
+ }
+
+ /**
+ * Set object version.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_version(const char* version) {
+ return set_version(string_to_object_version(version));
+ }
+
+ /// Get changeset id of this object.
+ changeset_id_type changeset() const noexcept {
+ return m_changeset;
+ }
+
+ /**
+ * Set changeset id of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_changeset(changeset_id_type changeset) noexcept {
+ m_changeset = changeset;
+ return *this;
+ }
+
+ /**
+ * Set changeset id of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_changeset(const char* changeset) {
+ return set_changeset(string_to_changeset_id(changeset));
+ }
+
+ /// Get user id of this object.
+ user_id_type uid() const noexcept {
+ return m_uid;
+ }
+
+ /**
+ * Set user id of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_uid(user_id_type uid) noexcept {
+ m_uid = uid;
+ return *this;
+ }
+
+ /**
+ * Set user id of this object.
+ * Sets uid to 0 (anonymous) if the given uid is smaller than 0.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_uid_from_signed(signed_user_id_type uid) noexcept {
+ m_uid = uid < 0 ? 0 : static_cast<user_id_type>(uid);
+ return *this;
+ }
+
+ /**
+ * Set user id of this object.
+ *
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_uid(const char* uid) {
+ return set_uid_from_signed(string_to_user_id(uid));
+ }
+
+ /// Is this user anonymous?
+ bool user_is_anonymous() const noexcept {
+ return m_uid == 0;
+ }
+
+ /// Get timestamp when this object last changed.
+ osmium::Timestamp timestamp() const noexcept {
+ return m_timestamp;
+ }
+
+ /**
+ * Set the timestamp when this object last changed.
+ *
+ * @param timestamp Timestamp
+ * @returns Reference to object to make calls chainable.
+ */
+ OSMObject& set_timestamp(const osmium::Timestamp timestamp) noexcept {
+ m_timestamp = timestamp;
+ return *this;
+ }
+
+ /// Get user name for this object.
+ const char* user() const noexcept {
+ return reinterpret_cast<const char*>(data() + sizeof_object());
+ }
+
+ /// Get the list of tags for this object.
+ const TagList& tags() const {
+ return osmium::detail::subitem_of_type<const TagList>(cbegin(), cend());
+ }
+
+ /**
+ * Get tag value by key.
+ *
+ * Convenience function that will forward to same function on TagList
+ * object.
+ */
+ const char* get_value_by_key(const char* key, const char* default_value = nullptr) const noexcept {
+ return tags().get_value_by_key(key, default_value);
+ }
+
+ /**
+ * Set named attribute.
+ *
+ * @param attr Name of the attribute (must be one of "id", "version", "changeset", "timestamp", "uid", "visible")
+ * @param value Value of the attribute
+ */
+ void set_attribute(const char* attr, const char* value) {
+ if (!strcmp(attr, "id")) {
+ set_id(value);
+ } else if (!strcmp(attr, "version")) {
+ set_version(value);
+ } else if (!strcmp(attr, "changeset")) {
+ set_changeset(value);
+ } else if (!strcmp(attr, "timestamp")) {
+ set_timestamp(osmium::Timestamp(value));
+ } else if (!strcmp(attr, "uid")) {
+ set_uid(value);
+ } else if (!strcmp(attr, "visible")) {
+ set_visible(value);
+ }
+ }
+
+ typedef osmium::memory::CollectionIterator<Item> iterator;
+ typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+
+ iterator begin() {
+ return iterator(subitems_position());
+ }
+
+ iterator end() {
+ return iterator(next());
+ }
+
+ const_iterator cbegin() const {
+ return const_iterator(subitems_position());
+ }
+
+ const_iterator cend() const {
+ return const_iterator(next());
+ }
+
+ const_iterator begin() const {
+ return cbegin();
+ }
+
+ const_iterator end() const {
+ return cend();
+ }
+
+ template <class T>
+ using t_iterator = osmium::memory::ItemIterator<T>;
+
+ template <class T>
+ using t_const_iterator = osmium::memory::ItemIterator<const T>;
+
+ template <class T>
+ t_iterator<T> begin() {
+ return t_iterator<T>(subitems_position(), next());
+ }
+
+ template <class T>
+ t_iterator<T> end() {
+ return t_iterator<T>(next(), next());
+ }
+
+ template <class T>
+ t_const_iterator<T> cbegin() const {
+ return t_const_iterator<T>(subitems_position(), next());
+ }
+
+ template <class T>
+ t_const_iterator<T> cend() const {
+ return t_const_iterator<T>(next(), next());
+ }
+
+ template <class T>
+ t_const_iterator<T> begin() const {
+ return cbegin<T>();
+ }
+
+ template <class T>
+ t_const_iterator<T> end() const {
+ return cend<T>();
+ }
+
+ }; // class OSMObject
+
+ static_assert(sizeof(OSMObject) % osmium::memory::align_bytes == 0, "Class osmium::OSMObject has wrong size to be aligned properly!");
+
+ /**
+ * 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() &&
+ lhs.version() == rhs.version();
+ }
+
+ inline bool operator!=(const OSMObject& lhs, const OSMObject& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ /**
+ * 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.
+ */
+ 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();
+ }
+
+ inline bool operator>(const OSMObject& lhs, const OSMObject& rhs) noexcept {
+ return rhs < lhs;
+ }
+
+ inline bool operator<=(const OSMObject& lhs, const OSMObject& rhs) noexcept {
+ return ! (rhs < lhs);
+ }
+
+ inline bool operator>=(const OSMObject& lhs, const OSMObject& rhs) noexcept {
+ return ! (lhs < rhs);
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_OBJECT_HPP
diff --git a/include/osmium/osm/object_comparisons.hpp b/include/osmium/osm/object_comparisons.hpp
new file mode 100644
index 0000000..bdf99e1
--- /dev/null
+++ b/include/osmium/osm/object_comparisons.hpp
@@ -0,0 +1,110 @@
+#ifndef OSMIUM_OSM_OBJECT_COMPARISONS_HPP
+#define OSMIUM_OSM_OBJECT_COMPARISONS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/osm/object.hpp>
+
+namespace osmium {
+
+ /**
+ * Function object class for comparing OSM objects for equality by type, id, and version.
+ */
+ struct object_equal_type_id_version {
+
+ bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
+ return *lhs == *rhs;
+ }
+
+ }; // struct object_equal_type_id_version
+
+ /**
+ * Function object class for comparing OSM objects for equality by type and id,
+ * ignoring the version.
+ */
+ struct object_equal_type_id {
+
+ bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept {
+ return lhs.type() == rhs.type() &&
+ lhs.id() == rhs.id();
+ }
+
+ bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
+ return operator()(*lhs, *rhs);
+ }
+
+ }; // struct object_equal_type_id
+
+ /**
+ * Function object class for ordering OSM objects by type, id, and version.
+ */
+ struct object_order_type_id_version {
+
+ bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept {
+ return lhs < rhs;
+ }
+
+ bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
+ return *lhs < *rhs;
+ }
+
+ }; // 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.
+ */
+ 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();
+ }
+
+ bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
+ return operator()(*lhs, *rhs);
+ }
+
+ }; // struct object_order_type_id_reverse_version
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_OBJECT_COMPARISONS_HPP
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
new file mode 100644
index 0000000..6c03815
--- /dev/null
+++ b/include/osmium/osm/relation.hpp
@@ -0,0 +1,189 @@
+#ifndef OSMIUM_OSM_RELATION_HPP
+#define OSMIUM_OSM_RELATION_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iterator>
+
+#include <osmium/memory/collection.hpp> // IWYU pragma: keep
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace builder {
+ template <class> class ObjectBuilder;
+ class RelationMemberListBuilder;
+ }
+
+ class RelationMember : public osmium::memory::detail::ItemHelper {
+
+ friend class osmium::builder::RelationMemberListBuilder;
+
+ object_id_type m_ref;
+ item_type m_type;
+ uint16_t m_flags;
+ string_size_type m_role_size {0};
+
+ RelationMember(const RelationMember&) = delete;
+ RelationMember(RelationMember&&) = delete;
+
+ RelationMember& operator=(const RelationMember&) = delete;
+ RelationMember& operator=(RelationMember&&) = delete;
+
+ unsigned char* endpos() {
+ return data() + osmium::memory::padded_length(sizeof(RelationMember) + m_role_size);
+ }
+
+ const unsigned char* endpos() const {
+ return data() + osmium::memory::padded_length(sizeof(RelationMember) + m_role_size);
+ }
+
+ template <class TMember>
+ friend class osmium::memory::CollectionIterator;
+
+ unsigned char* next() {
+ if (full_member()) {
+ return endpos() + reinterpret_cast<osmium::memory::Item*>(endpos())->byte_size();
+ } else {
+ return endpos();
+ }
+ }
+
+ unsigned const char* next() const {
+ if (full_member()) {
+ return endpos() + reinterpret_cast<const osmium::memory::Item*>(endpos())->byte_size();
+ } else {
+ return endpos();
+ }
+ }
+
+ void set_role_size(string_size_type size) noexcept {
+ m_role_size = size;
+ }
+
+ public:
+
+ static constexpr item_type collection_type = item_type::relation_member_list;
+
+ RelationMember(const object_id_type ref=0, const item_type type=item_type(), const bool full=false) noexcept :
+ m_ref(ref),
+ m_type(type),
+ m_flags(full ? 1 : 0) {
+ }
+
+ object_id_type ref() const noexcept {
+ return m_ref;
+ }
+
+ RelationMember& ref(object_id_type ref) noexcept {
+ m_ref = ref;
+ return *this;
+ }
+
+ unsigned_object_id_type positive_ref() const noexcept {
+ return static_cast<unsigned_object_id_type>(std::abs(m_ref));
+ }
+
+ item_type type() const noexcept {
+ return m_type;
+ }
+
+ bool full_member() const noexcept {
+ return m_flags == 1;
+ }
+
+ const char* role() const noexcept {
+ return reinterpret_cast<const char*>(data() + sizeof(RelationMember));
+ }
+
+ OSMObject& get_object() {
+ return *reinterpret_cast<OSMObject*>(endpos());
+ }
+
+ const OSMObject& get_object() const {
+ return *reinterpret_cast<const OSMObject*>(endpos());
+ }
+
+ }; // class RelationMember
+
+ class RelationMemberList : public osmium::memory::Collection<RelationMember, osmium::item_type::relation_member_list> {
+
+ public:
+
+ typedef size_t size_type;
+
+ RelationMemberList() :
+ osmium::memory::Collection<RelationMember, osmium::item_type::relation_member_list>() {
+ }
+
+ size_type size() const noexcept {
+ return static_cast<size_type>(std::distance(begin(), end()));
+ }
+
+ }; // class RelationMemberList
+
+ static_assert(sizeof(RelationMemberList) % osmium::memory::align_bytes == 0, "Class osmium::RelationMemberList has wrong size to be aligned properly!");
+
+ class Relation : public OSMObject {
+
+ friend class osmium::builder::ObjectBuilder<osmium::Relation>;
+
+ Relation() noexcept :
+ OSMObject(sizeof(Relation), osmium::item_type::relation) {
+ }
+
+ public:
+
+ static constexpr osmium::item_type itemtype = osmium::item_type::relation;
+
+ RelationMemberList& members() {
+ return osmium::detail::subitem_of_type<RelationMemberList>(begin(), end());
+ }
+
+ const RelationMemberList& members() const {
+ return osmium::detail::subitem_of_type<const RelationMemberList>(cbegin(), cend());
+ }
+
+ }; // class Relation
+
+ static_assert(sizeof(Relation) % osmium::memory::align_bytes == 0, "Class osmium::Relation has wrong size to be aligned properly!");
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_RELATION_HPP
diff --git a/include/osmium/osm/segment.hpp b/include/osmium/osm/segment.hpp
new file mode 100644
index 0000000..f3a82c9
--- /dev/null
+++ b/include/osmium/osm/segment.hpp
@@ -0,0 +1,105 @@
+#ifndef OSMIUM_OSM_SEGMENT_HPP
+#define OSMIUM_OSM_SEGMENT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iosfwd>
+#include <utility>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * A Segment is the directed connection between two Locations.
+ */
+ class Segment {
+
+ osmium::Location m_first;
+ osmium::Location m_second;
+
+ public:
+
+ explicit constexpr Segment(const osmium::Location& location1, const osmium::Location& location2) noexcept :
+ m_first(location1),
+ m_second(location2) {
+ }
+
+ constexpr Segment(const Segment&) = default;
+ constexpr Segment(Segment&&) = default;
+
+ Segment& operator=(const Segment&) = default;
+ Segment& operator=(Segment&&) = default;
+
+ ~Segment() = default;
+
+ /// Return first Location of Segment.
+ OSMIUM_CONSTEXPR osmium::Location first() const noexcept {
+ return m_first;
+ }
+
+ /// Return second Location of Segment.
+ OSMIUM_CONSTEXPR osmium::Location second() const noexcept {
+ return m_second;
+ }
+
+ protected:
+
+ void swap_locations() {
+ using std::swap;
+ swap(m_first, m_second);
+ }
+
+ }; // class Segment
+
+ /// Segments are equal if both their locations are equal
+ inline OSMIUM_CONSTEXPR bool operator==(const Segment& lhs, const Segment& rhs) noexcept {
+ return lhs.first() == rhs.first() && lhs.second() == rhs.second();
+ }
+
+ inline OSMIUM_CONSTEXPR bool operator!=(const Segment& lhs, const Segment& rhs) noexcept {
+ return ! (lhs == rhs);
+ }
+
+ /**
+ * Output Segment to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Segment& segment) {
+ return out << segment.first() << "->" << segment.second();
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_SEGMENT_HPP
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
new file mode 100644
index 0000000..2e93ede
--- /dev/null
+++ b/include/osmium/osm/tag.hpp
@@ -0,0 +1,140 @@
+#ifndef OSMIUM_OSM_TAG_HPP
+#define OSMIUM_OSM_TAG_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <cstring>
+#include <iosfwd>
+#include <iterator>
+
+#include <osmium/memory/collection.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+ class Tag : public osmium::memory::detail::ItemHelper {
+
+ Tag(const Tag&) = delete;
+ Tag(Tag&&) = delete;
+
+ Tag& operator=(const Tag&) = delete;
+ Tag& operator=(Tag&&) = delete;
+
+ template <class TMember>
+ friend class osmium::memory::CollectionIterator;
+
+ static unsigned char* after_null(unsigned char* ptr) {
+ return reinterpret_cast<unsigned char*>(std::strchr(reinterpret_cast<char*>(ptr), 0) + 1);
+ }
+
+ static const unsigned char* after_null(const unsigned char* ptr) {
+ return reinterpret_cast<const unsigned char*>(std::strchr(reinterpret_cast<const char*>(ptr), 0) + 1);
+ }
+
+ unsigned char* next() {
+ return after_null(after_null(data()));
+ }
+
+ const unsigned char* next() const {
+ return after_null(after_null(data()));
+ }
+
+ public:
+
+ static constexpr item_type collection_type = item_type::tag_list;
+
+ const char* key() const noexcept {
+ return reinterpret_cast<const char*>(data());
+ }
+
+ const char* value() const {
+ return reinterpret_cast<const char*>(after_null(data()));
+ }
+
+ }; // class Tag
+
+ inline bool operator==(const Tag& a, const Tag& b) {
+ return !std::strcmp(a.key(), b.key()) && !strcmp(a.value(), b.value());
+ }
+
+ inline bool operator<(const Tag& a, const Tag& b) {
+ return (!std::strcmp(a.key(), b.key()) && (std::strcmp(a.value(), b.value()) < 0)) || (std::strcmp(a.key(), b.key()) < 0);
+ }
+
+ /**
+ * Output a Tag to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const Tag& tag) {
+ return out << tag.key() << '=' << tag.value();
+ }
+
+ class TagList : public osmium::memory::Collection<Tag, osmium::item_type::tag_list> {
+
+ public:
+
+ typedef size_t size_type;
+
+ TagList() :
+ osmium::memory::Collection<Tag, osmium::item_type::tag_list>() {
+ }
+
+ size_type size() const noexcept {
+ return static_cast<size_type>(std::distance(begin(), end()));
+ }
+
+ 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;
+ } else {
+ return result->value();
+ }
+ }
+
+ const char* operator[](const char* key) const noexcept {
+ return get_value_by_key(key);
+ }
+
+ }; // class TagList
+
+ static_assert(sizeof(TagList) % osmium::memory::align_bytes == 0, "Class osmium::TagList has wrong size to be aligned properly!");
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_TAG_HPP
diff --git a/include/osmium/osm/timestamp.hpp b/include/osmium/osm/timestamp.hpp
new file mode 100644
index 0000000..f36ea36
--- /dev/null
+++ b/include/osmium/osm/timestamp.hpp
@@ -0,0 +1,171 @@
+#ifndef OSMIUM_OSM_TIMESTAMP_HPP
+#define OSMIUM_OSM_TIMESTAMP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdint>
+#include <ctime>
+#include <iosfwd>
+#include <limits>
+#include <stdexcept>
+#include <string>
+#include <time.h>
+
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ /**
+ * A timestamp. Internal representation is an unsigned 32bit integer
+ * holding seconds since epoch, so this will overflow in 2038.
+ */
+ class Timestamp {
+
+ // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0
+ static constexpr int timestamp_length = 20 + 1;
+
+ /**
+ * The timestamp format for OSM timestamps in strftime(3) format.
+ * This is the ISO-Format yyyy-mm-ddThh:mm:ssZ
+ */
+ static const char* timestamp_format() {
+ static const char f[timestamp_length] = "%Y-%m-%dT%H:%M:%SZ";
+ return f;
+ }
+
+ uint32_t m_timestamp;
+
+ public:
+
+ constexpr Timestamp() noexcept :
+ m_timestamp(0) {
+ }
+
+ // Not "explicit" so that conversions from time_t work
+ // like in node.timestamp(123);
+ constexpr Timestamp(time_t timestamp) noexcept :
+ m_timestamp(static_cast<uint32_t>(timestamp)) {
+ }
+
+ /**
+ * Construct timestamp from ISO date/time string.
+ * 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<uint32_t>(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<uint32_t>(_mkgmtime(&tm));
+#endif
+ }
+
+ constexpr time_t seconds_since_epoch() const noexcept {
+ return static_cast<time_t>(m_timestamp);
+ }
+
+ constexpr operator time_t() const noexcept {
+ return static_cast<time_t>(m_timestamp);
+ }
+
+ template <typename T>
+ void operator+=(T time_difference) noexcept {
+ m_timestamp += time_difference;
+ }
+
+ template <typename T>
+ void operator-=(T time_difference) noexcept {
+ m_timestamp -= time_difference;
+ }
+
+ /**
+ * Return UTC Unix time as string in ISO date/time format.
+ */
+ std::string to_iso() const {
+ std::string s;
+
+ if (m_timestamp != 0) {
+ struct tm tm;
+ time_t sse = seconds_since_epoch();
+#ifndef _MSC_VER
+ gmtime_r(&sse, &tm);
+#else
+ gmtime_s(&tm, &sse);
+#endif
+
+ s.resize(timestamp_length);
+ /* This const_cast is ok, because we know we have enough space
+ in the string for the format we are using (well at least until
+ the year will have 5 digits). And by setting the size
+ afterwards from the result of strftime we make sure thats set
+ right, too. */
+ s.resize(strftime(const_cast<char*>(s.c_str()), timestamp_length, timestamp_format(), &tm));
+ }
+
+ return s;
+ }
+
+ }; // class Timestamp
+
+ inline OSMIUM_CONSTEXPR Timestamp start_of_time() noexcept {
+ return Timestamp(1);
+ }
+
+ inline OSMIUM_CONSTEXPR Timestamp end_of_time() noexcept {
+ return Timestamp(std::numeric_limits<time_t>::max());
+ }
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, Timestamp timestamp) {
+ out << timestamp.to_iso();
+ return out;
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_TIMESTAMP_HPP
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
new file mode 100644
index 0000000..aea61bd
--- /dev/null
+++ b/include/osmium/osm/types.hpp
@@ -0,0 +1,83 @@
+#ifndef OSMIUM_OSM_TYPES_HPP
+#define OSMIUM_OSM_TYPES_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdint>
+#include <cstdlib>
+
+namespace osmium {
+
+ /*
+ * The following typedefs 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.
+
+ /**
+ * 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;
+
+ inline object_id_type string_to_object_id(const char* string) {
+ return std::atoll(string);
+ }
+
+ inline object_version_type string_to_object_version(const char* string) {
+ return static_cast<object_version_type>(std::atol(string));
+ }
+
+ inline changeset_id_type string_to_changeset_id(const char* string) {
+ return static_cast<changeset_id_type>(std::atol(string));
+ }
+
+ inline signed_user_id_type string_to_user_id(const char* string) {
+ return static_cast<signed_user_id_type>(std::atol(string));
+ }
+
+ inline num_changes_type string_to_num_changes(const char* string) {
+ return static_cast<num_changes_type>(std::atol(string));
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_TYPES_HPP
diff --git a/include/osmium/osm/undirected_segment.hpp b/include/osmium/osm/undirected_segment.hpp
new file mode 100644
index 0000000..654ef7d
--- /dev/null
+++ b/include/osmium/osm/undirected_segment.hpp
@@ -0,0 +1,100 @@
+#ifndef OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
+#define OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <iosfwd>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/segment.hpp>
+
+namespace osmium {
+
+ /**
+ * Undirected connection between two Locations. The first Location is
+ * always equal or "smaller" than the second Location, ie to the left
+ * and down.
+ */
+ class UndirectedSegment : public Segment {
+
+ public:
+
+ explicit UndirectedSegment(const osmium::Location& location1, const osmium::Location& location2) :
+ Segment(location1, location2) {
+ if (location2 < location1) {
+ swap_locations();
+ }
+ }
+
+ UndirectedSegment(const UndirectedSegment&) = default;
+ UndirectedSegment(UndirectedSegment&&) = default;
+
+ UndirectedSegment& operator=(const UndirectedSegment&) = default;
+ UndirectedSegment& operator=(UndirectedSegment&&) = default;
+
+ ~UndirectedSegment() = default;
+
+ }; // class UndirectedSegment
+
+ /**
+ * UndirectedSegments 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.
+ */
+ inline bool operator<(const UndirectedSegment& lhs, const UndirectedSegment& rhs) noexcept {
+ return (lhs.first() == rhs.first() && lhs.second() < rhs.second()) || lhs.first() < rhs.first();
+ }
+
+ inline bool operator>(const UndirectedSegment& lhs, const UndirectedSegment& rhs) noexcept {
+ return rhs < lhs;
+ }
+
+ inline bool operator<=(const UndirectedSegment& lhs, const UndirectedSegment& rhs) noexcept {
+ return ! (rhs < lhs);
+ }
+
+ inline bool operator>=(const UndirectedSegment& lhs, const UndirectedSegment& rhs) noexcept {
+ return ! (lhs < rhs);
+ }
+
+ /**
+ * Output UndirectedSegment to a stream.
+ */
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::UndirectedSegment& segment) {
+ return out << segment.first() << "--" << segment.second();
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_UNDIRECTED_SEGMENT_HPP
diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp
new file mode 100644
index 0000000..6d3e2de
--- /dev/null
+++ b/include/osmium/osm/way.hpp
@@ -0,0 +1,115 @@
+#ifndef OSMIUM_OSM_WAY_HPP
+#define OSMIUM_OSM_WAY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <osmium/memory/item.hpp>
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/node_ref_list.hpp>
+
+namespace osmium {
+
+ namespace builder {
+ template <class T> class ObjectBuilder;
+ }
+
+ /**
+ * List of node references (id and location) in a way.
+ */
+ class WayNodeList : public NodeRefList<osmium::item_type::way_node_list> {
+
+ public:
+
+ WayNodeList():
+ NodeRefList<osmium::item_type::way_node_list>() {
+ }
+
+ }; // class WayNodeList
+
+ static_assert(sizeof(WayNodeList) % osmium::memory::align_bytes == 0, "Class osmium::WayNodeList has wrong size to be aligned properly!");
+
+ class Way : public OSMObject {
+
+ friend class osmium::builder::ObjectBuilder<osmium::Way>;
+
+ Way() noexcept :
+ OSMObject(sizeof(Way), osmium::item_type::way) {
+ }
+
+ public:
+
+ WayNodeList& nodes() {
+ return osmium::detail::subitem_of_type<WayNodeList>(begin(), end());
+ }
+
+ const WayNodeList& nodes() const {
+ return osmium::detail::subitem_of_type<const WayNodeList>(cbegin(), cend());
+ }
+
+ /**
+ * Update all nodes in a way with the ID of the given NodeRef with the
+ * location of the given NodeRef.
+ */
+ void update_node_location(const NodeRef& new_node_ref) {
+ for (auto& node_ref : nodes()) {
+ if (node_ref.ref() == new_node_ref.ref()) {
+ node_ref.set_location(new_node_ref.location());
+ }
+ }
+ }
+
+ /**
+ * Do the nodes in this way form a closed ring?
+ */
+ bool is_closed() const {
+ return nodes().is_closed();
+ }
+
+ bool ends_have_same_id() const {
+ return nodes().ends_have_same_id();
+ }
+
+ bool ends_have_same_location() const {
+ return nodes().ends_have_same_location();
+ }
+
+ }; // class Way
+
+ static_assert(sizeof(Way) % osmium::memory::align_bytes == 0, "Class osmium::Way has wrong size to be aligned properly!");
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_WAY_HPP
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
new file mode 100644
index 0000000..d4af916
--- /dev/null
+++ b/include/osmium/relations/collector.hpp
@@ -0,0 +1,544 @@
+#ifndef OSMIUM_RELATIONS_COLLECTOR_HPP
+#define OSMIUM_RELATIONS_COLLECTOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include <osmium/osm/item_type.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp> // IWYU pragma: keep
+#include <osmium/osm/types.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/visitor.hpp>
+
+#include <osmium/relations/detail/relation_meta.hpp>
+#include <osmium/relations/detail/member_meta.hpp>
+
+namespace osmium {
+
+ class Node;
+ class Way;
+
+ /**
+ * @brief Code related to the assembly of OSM relations
+ */
+ namespace relations {
+
+ /**
+ * The Collector class collects members of a relation. This is a generic
+ * base class that can be used to assemble all kinds of relations. It has numerous
+ * hooks you can implement in derived classes to customize its behaviour.
+ *
+ * The collector provides two handlers (HandlerPass1 and HandlerPass2) for a first
+ * and second pass through an input file, respectively. In the first pass all
+ * relations we are interested in are stored in RelationMeta objects in the
+ * m_relations vector. All members we are interested in are stored in MemberMeta
+ * objects in the m_member_meta vectors.
+ * The MemberMeta objects also store the information where the relations containing
+ * those members are to be found.
+ *
+ * Later the m_member_meta vectors are sorted according to the
+ * member ids so that a binary search (with std::equal_range) can be used in the second
+ * pass to find the parent relations for each node, way, or relation coming along.
+ * The member objects are stored together with their relation and once a relation
+ * is complete the complete_relation() method is called which you must overwrite in
+ * a derived class of Collector.
+ *
+ * @tparam TCollector Derived class of this class.
+ *
+ * @tparam TNodes Are we interested in member nodes?
+ *
+ * @tparam TWays Are we interested in member ways?
+ *
+ * @tparam TRelations Are we interested in member relations?
+ */
+ template <class TCollector, bool TNodes, bool TWays, bool TRelations>
+ class Collector {
+
+ /**
+ * This is the handler class for the first pass of the Collector.
+ */
+ class HandlerPass1 : public osmium::handler::Handler {
+
+ TCollector& m_collector;
+
+ public:
+
+ HandlerPass1(TCollector& collector) noexcept :
+ m_collector(collector) {
+ }
+
+ void relation(const osmium::Relation& relation) {
+ if (m_collector.keep_relation(relation)) {
+ m_collector.add_relation(relation);
+ }
+ }
+
+ }; // class HandlerPass1
+
+ public:
+
+ /**
+ * This is the handler class for the second pass of the Collector.
+ */
+ class HandlerPass2 : public osmium::handler::Handler {
+
+ TCollector& m_collector;
+
+ /**
+ * This variable is initialized with the number of different
+ * kinds of OSM objects we are interested in. If we only need
+ * way members (for instance for the multipolygon collector)
+ * it is intialized with 1 for instance. If node and way
+ * members are needed, it is initialized with 2.
+ *
+ * In the after_* methods of this handler, it is decremented
+ * and once it reaches 0, we know we have all members available
+ * that we are ever going to get.
+ */
+ int m_want_types;
+
+ /**
+ * Find this object in the member vectors and add it to all
+ * relations that need it.
+ *
+ * @returns true if the member was added to at least one
+ * relation and false otherwise
+ */
+ bool find_and_add_object(const osmium::OSMObject& object) {
+ auto& mmv = m_collector.member_meta(object.type());
+ auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id()));
+
+ if (osmium::relations::count_not_removed(range.first, range.second) == 0) {
+ // nothing found
+ return false;
+ }
+
+ {
+ m_collector.members_buffer().add_item(object);
+ const size_t member_offset = m_collector.members_buffer().commit();
+
+ for (auto it = range.first; it != range.second; ++it) {
+ it->set_buffer_offset(member_offset);
+ }
+ }
+
+ for (auto it = range.first; it != range.second; ++it) {
+ MemberMeta& member_meta = *it;
+ if (member_meta.removed()) {
+ break;
+ }
+ assert(member_meta.member_id() == object.id());
+ assert(member_meta.relation_pos() < m_collector.m_relations.size());
+ RelationMeta& relation_meta = m_collector.m_relations[member_meta.relation_pos()];
+// std::cerr << " => " << member_meta.member_pos() << " < " << m_collector.get_relation(relation_meta).members().size() << " (id=" << m_collector.get_relation(relation_meta).id() << ")\n";
+ assert(member_meta.member_pos() < m_collector.get_relation(relation_meta).members().size());
+// std::cerr << " add way " << member_meta.member_id() << " to rel " << m_collector.get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n";
+ relation_meta.got_one_member();
+ if (relation_meta.has_all_members()) {
+ const size_t relation_offset = member_meta.relation_pos();
+ m_collector.complete_relation(relation_meta);
+ m_collector.m_relations[relation_offset] = RelationMeta();
+ m_collector.possibly_purge_removed_members();
+ }
+ }
+
+ // Remove MemberMetas that were marked as removed.
+ mmv.erase(std::remove_if(mmv.begin(), mmv.end(), [](MemberMeta& mm) {
+ return mm.removed();
+ }), mmv.end());
+
+ return true;
+ }
+
+ public:
+
+ HandlerPass2(TCollector& collector) noexcept :
+ m_collector(collector),
+ m_want_types((TNodes?1:0) + (TWays?1:0) + (TRelations?1:0)) {
+ }
+
+ void node(const osmium::Node& node) {
+ if (TNodes) {
+ if (! find_and_add_object(node)) {
+ m_collector.node_not_in_any_relation(node);
+ }
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ if (TWays) {
+ if (! find_and_add_object(way)) {
+ m_collector.way_not_in_any_relation(way);
+ }
+ }
+ }
+
+ void relation(const osmium::Relation& relation) {
+ if (TRelations) {
+ if (! find_and_add_object(relation)) {
+ m_collector.relation_not_in_any_relation(relation);
+ }
+ }
+ }
+
+ void flush() {
+ m_collector.flush();
+ }
+
+ }; // class HandlerPass2
+
+ HandlerPass2 m_handler_pass2;
+
+ // All relations we are interested in will be kept in this buffer
+ osmium::memory::Buffer m_relations_buffer;
+
+ // All members we are interested in will be kept in this buffer
+ osmium::memory::Buffer m_members_buffer;
+
+ /// Vector with all relations we are interested in
+ std::vector<RelationMeta> m_relations;
+
+ /**
+ * One vector each for nodes, ways, and relations containing all
+ * mappings from member ids to their relations.
+ */
+ std::vector<MemberMeta> m_member_meta[3];
+
+ int m_count_complete = 0;
+
+ typedef std::function<void(osmium::memory::Buffer&&)> callback_func_type;
+ callback_func_type m_callback;
+
+ static constexpr size_t initial_buffer_size = 1024 * 1024;
+
+ public:
+
+ /**
+ * Create an Collector.
+ */
+ Collector() :
+ m_handler_pass2(*static_cast<TCollector*>(this)),
+ m_relations_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes),
+ m_members_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes),
+ m_relations(),
+ m_member_meta() {
+ }
+
+ protected:
+
+ std::vector<MemberMeta>& member_meta(const item_type type) {
+ return m_member_meta[static_cast<uint16_t>(type) - 1];
+ }
+
+ callback_func_type callback() {
+ return m_callback;
+ }
+
+ const std::vector<RelationMeta>& relations() const {
+ return m_relations;
+ }
+
+ /**
+ * This method is called from the first pass handler for every
+ * relation in the input, to check whether it should be kept.
+ *
+ * Overwrite this method in a child class to only add relations
+ * you are interested in, for instance depending on the type tag.
+ * Storing relations takes a lot of memory, so it makes sense to
+ * filter this as much as possible.
+ */
+ bool keep_relation(const osmium::Relation& /*relation*/) const {
+ return true;
+ }
+
+ /**
+ * This method is called for every member of every relation that
+ * should be kept. It should decide if the member is interesting or
+ * not and return true or false to signal that. Only interesting
+ * members are later added to the relation.
+ *
+ * Overwrite this method in a child class. In the MultiPolygonCollector
+ * this is for instance used to only keep members of type way and
+ * ignore all others.
+ */
+ bool keep_member(const osmium::relations::RelationMeta& /*relation_meta*/, const osmium::RelationMember& /*member*/) const {
+ return true;
+ }
+
+ /**
+ * This method is called for all nodes that are not a member of
+ * any relation.
+ *
+ * Overwrite this method in a child class if you are interested
+ * in this.
+ */
+ void node_not_in_any_relation(const osmium::Node& /*node*/) {
+ }
+
+ /**
+ * This method is called for all ways that are not a member of
+ * any relation.
+ *
+ * Overwrite this method in a child class if you are interested
+ * in this.
+ */
+ void way_not_in_any_relation(const osmium::Way& /*way*/) {
+ }
+
+ /**
+ * This method is called for all relations that are not a member of
+ * any relation.
+ *
+ * Overwrite this method in a child class if you are interested
+ * in this.
+ */
+ void relation_not_in_any_relation(const osmium::Relation& /*relation*/) {
+ }
+
+ /**
+ * This method is called from the 2nd pass handler when all objects
+ * of types we are interested in have been seen.
+ *
+ * Overwrite this method in a child class if you are interested
+ * in this.
+ *
+ * Note that even after this call members might be missing if they
+ * were not in the input file! The derived class has to handle this
+ * case.
+ */
+ void flush() {
+ }
+
+ /**
+ * This removes all relations that have already been assembled
+ * from the m_relations vector.
+ */
+ void clean_assembled_relations() {
+ m_relations.erase(
+ std::remove_if(m_relations.begin(), m_relations.end(), has_all_members()),
+ m_relations.end()
+ );
+ }
+
+ const osmium::Relation& get_relation(size_t offset) const {
+ return m_relations_buffer.get<osmium::Relation>(offset);
+ }
+
+ /**
+ * Get the relation from a relation_meta.
+ */
+ const osmium::Relation& get_relation(const RelationMeta& relation_meta) const {
+ return get_relation(relation_meta.relation_offset());
+ }
+
+ osmium::OSMObject& get_member(size_t offset) const {
+ return m_members_buffer.get<osmium::OSMObject>(offset);
+ }
+
+ /**
+ * Tell the Collector that you are interested in this relation
+ * and want it kept until all members have been assembled and
+ * it is handed back to you.
+ *
+ * The relation is copied and stored in a buffer inside the
+ * collector.
+ */
+ void add_relation(const osmium::Relation& relation) {
+ const size_t offset = m_relations_buffer.committed();
+ m_relations_buffer.add_item(relation);
+
+ RelationMeta relation_meta(offset);
+
+ int n=0;
+ for (auto& member : m_relations_buffer.get<osmium::Relation>(offset).members()) {
+ if (static_cast<TCollector*>(this)->keep_member(relation_meta, member)) {
+ member_meta(member.type()).emplace_back(member.ref(), m_relations.size(), n);
+ relation_meta.increment_need_members();
+ } else {
+ member.ref(0); // set member id to zero to indicate we are not interested
+ }
+ ++n;
+ }
+
+ assert(offset == m_relations_buffer.committed());
+ if (relation_meta.has_all_members()) {
+ m_relations_buffer.rollback();
+ } else {
+ m_relations_buffer.commit();
+ m_relations.push_back(std::move(relation_meta));
+// std::cerr << "added relation id=" << relation.id() << "\n";
+ }
+ }
+
+ /**
+ * Sort the vectors with the member infos so that we can do binary
+ * 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());
+ }
+
+ public:
+
+ uint64_t used_memory() const {
+ const uint64_t nmembers = m_member_meta[0].capacity() + m_member_meta[1].capacity() + m_member_meta[2].capacity();
+ const uint64_t members = nmembers * sizeof(MemberMeta);
+ const uint64_t relations = m_relations.capacity() * sizeof(RelationMeta);
+ const uint64_t relations_buffer_capacity = m_relations_buffer.capacity();
+ const uint64_t members_buffer_capacity = m_members_buffer.capacity();
+
+ std::cout << " nR = m_relations.capacity() ........... = " << std::setw(12) << m_relations.capacity() << "\n";
+ std::cout << " nMN = m_member_meta[NODE].capacity() ... = " << std::setw(12) << m_member_meta[0].capacity() << "\n";
+ std::cout << " nMW = m_member_meta[WAY].capacity() .... = " << std::setw(12) << m_member_meta[1].capacity() << "\n";
+ std::cout << " nMR = m_member_meta[RELATION].capacity() = " << std::setw(12) << m_member_meta[2].capacity() << "\n";
+ std::cout << " nM = m_member_meta[*].capacity() ...... = " << std::setw(12) << nmembers << "\n";
+
+ std::cout << " sRM = sizeof(RelationMeta) ............. = " << std::setw(12) << sizeof(RelationMeta) << "\n";
+ std::cout << " sMM = sizeof(MemberMeta) ............... = " << std::setw(12) << sizeof(MemberMeta) << "\n\n";
+
+ std::cout << " nR * sRM ............................... = " << std::setw(12) << relations << "\n";
+ std::cout << " nM * sMM ............................... = " << std::setw(12) << members << "\n";
+ std::cout << " relations_buffer_capacity .............. = " << std::setw(12) << relations_buffer_capacity << "\n";
+ std::cout << " members_buffer_capacity ................ = " << std::setw(12) << members_buffer_capacity << "\n";
+
+ const uint64_t total = relations + members + relations_buffer_capacity + members_buffer_capacity;
+
+ std::cout << " total .................................. = " << std::setw(12) << total << "\n";
+ std::cout << " =======================================================\n";
+
+ return relations_buffer_capacity + members_buffer_capacity + relations + members;
+ }
+
+ /**
+ * Return reference to second pass handler.
+ */
+ HandlerPass2& handler(const callback_func_type& callback = nullptr) {
+ m_callback = callback;
+ return m_handler_pass2;
+ }
+
+ osmium::memory::Buffer& members_buffer() {
+ return m_members_buffer;
+ }
+
+ size_t get_offset(osmium::item_type type, osmium::object_id_type id) {
+ const auto& mmv = member_meta(type);
+ const auto range = std::equal_range(mmv.cbegin(), mmv.cend(), MemberMeta(id));
+ assert(range.first != range.second);
+ return range.first->buffer_offset();
+ }
+
+ template <class TIter>
+ void read_relations(TIter begin, TIter end) {
+ HandlerPass1 handler(*static_cast<TCollector*>(this));
+ osmium::apply(begin, end, handler);
+ sort_member_meta();
+ }
+
+ template <class TSource>
+ void read_relations(TSource& source) {
+ read_relations(std::begin(source), std::end(source));
+ source.close();
+ }
+
+ void moving_in_buffer(size_t old_offset, size_t new_offset) {
+ const osmium::OSMObject& object = m_members_buffer.get<osmium::OSMObject>(old_offset);
+ auto& mmv = member_meta(object.type());
+ auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(object.id()));
+ for (auto it = range.first; it != range.second; ++it) {
+ assert(it->buffer_offset() == old_offset);
+ it->set_buffer_offset(new_offset);
+ }
+ }
+
+ /**
+ * Decide whether to purge removed members and then do it.
+ *
+ * Currently the purging is done every thousand calls.
+ * This could probably be improved upon.
+ */
+ void possibly_purge_removed_members() {
+ ++m_count_complete;
+ if (m_count_complete > 10000) { // XXX
+ const size_t size_before = m_members_buffer.committed();
+ m_members_buffer.purge_removed(this);
+ const size_t size_after = m_members_buffer.committed();
+ double percent = static_cast<double>(size_before - size_after);
+ percent /= size_before;
+ percent *= 100;
+ std::cerr << "PURGE (size before=" << size_before << " after=" << size_after << " purged=" << (size_before - size_after) << " / " << static_cast<int>(percent) << "%)\n";
+ m_count_complete = 0;
+ }
+ }
+
+ /**
+ * Get a vector with pointers to all Relations that could not
+ * be completed, because members were missing in the input
+ * data.
+ *
+ * Note that these pointers point into memory allocated and
+ * owned by the Collector object.
+ */
+ std::vector<const osmium::Relation*> get_incomplete_relations() const {
+ std::vector<const osmium::Relation*> relations;
+ for (const auto& relation_meta : m_relations) {
+ if (!relation_meta.has_all_members()) {
+ relations.push_back(&get_relation(relation_meta));
+ }
+ }
+ return relations;
+ }
+
+ }; // class Collector
+
+ } // namespace relations
+
+} // namespace osmium
+
+#endif // OSMIUM_RELATIONS_COLLECTOR_HPP
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
new file mode 100644
index 0000000..a45088e
--- /dev/null
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -0,0 +1,158 @@
+#ifndef OSMIUM_RELATIONS_DETAIL_MEMBER_META_HPP
+#define OSMIUM_RELATIONS_DETAIL_MEMBER_META_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cstddef>
+#include <iosfwd>
+#include <iterator>
+
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+ namespace relations {
+
+ /**
+ * Helper class for the Collector class.
+ *
+ * Stores an object ID and information where the object should be
+ * stored.
+ */
+ class MemberMeta {
+
+ /**
+ * Object ID of this relation member. Can be a node, way, or relation ID.
+ * It depends on the vector in which this object is stored which kind of
+ * object is referenced here.
+ */
+ osmium::object_id_type m_member_id;
+
+ /**
+ * Position of the relation this member is a part of in the
+ * m_relations vector.
+ */
+ size_t m_relation_pos;
+
+ /**
+ * Position of this member in the list of members of the
+ * relation this member is a part of.
+ */
+ size_t m_member_pos;
+
+ /**
+ * Offset in the buffer where the object is stored.
+ */
+ size_t m_buffer_offset { 0 };
+
+ bool m_removed = false;
+
+ public:
+
+ /**
+ * Create new MemberMeta. The variant with zeros for relation_pos and
+ * member_pos is used to create dummy MemberMeta that can be compared
+ * to the MemberMeta in the vectors using the equal_range algorithm.
+ */
+ explicit MemberMeta(osmium::object_id_type member_id, size_t relation_pos=0, size_t member_pos=0) noexcept :
+ m_member_id(member_id),
+ m_relation_pos(relation_pos),
+ m_member_pos(member_pos) {
+ }
+
+ osmium::object_id_type member_id() const noexcept {
+ return m_member_id;
+ }
+
+ size_t relation_pos() const noexcept {
+ return m_relation_pos;
+ }
+
+ size_t member_pos() const noexcept {
+ return m_member_pos;
+ }
+
+ size_t buffer_offset() const noexcept {
+ return m_buffer_offset;
+ }
+
+ void set_buffer_offset(size_t offset) noexcept {
+ m_buffer_offset = offset;
+ }
+
+ bool removed() const noexcept {
+ return m_removed;
+ }
+
+ void remove() noexcept {
+ m_removed = true;
+ }
+
+ }; // class MemberMeta
+
+ /**
+ * Compares two MemberMeta objects by only looking at the member id.
+ * Used to sort a vector of MemberMeta objects and to later find
+ * them using binary search.
+ */
+ inline bool operator<(const MemberMeta& a, const MemberMeta& b) noexcept {
+ return a.member_id() < b.member_id();
+ }
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& 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() << ")";
+ return out;
+ }
+
+ /**
+ * Count the number of MemberMeta objects in the iterator range
+ * that are not marked as removed.
+ *
+ * @tparam TIter Iterator that dereferences to a MemberMeta
+ * @param begin Begin of iterator range
+ * @param end End of iterator range
+ */
+ template <class TIter>
+ inline typename std::iterator_traits<TIter>::difference_type count_not_removed(TIter begin, TIter end) {
+ return std::count_if(begin, end, [](MemberMeta& mm) {
+ return !mm.removed();
+ });
+ }
+
+ } // namespace relations
+
+} // namespace osmium
+
+#endif // OSMIUM_RELATIONS_DETAIL_MEMBER_META_HPP
diff --git a/include/osmium/relations/detail/relation_meta.hpp b/include/osmium/relations/detail/relation_meta.hpp
new file mode 100644
index 0000000..a48c50a
--- /dev/null
+++ b/include/osmium/relations/detail/relation_meta.hpp
@@ -0,0 +1,136 @@
+#ifndef OSMIUM_RELATIONS_DETAIL_RELATION_META_HPP
+#define OSMIUM_RELATIONS_DETAIL_RELATION_META_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cassert>
+#include <cstddef>
+#include <iosfwd>
+
+namespace osmium {
+
+ namespace relations {
+
+ /**
+ * Helper class for the Collector class.
+ *
+ * Stores information needed to collect all members of a relation. This
+ * includes the offset of the relation in a buffer plus the information
+ * needed to add members to this relation.
+ */
+ class RelationMeta {
+
+ /// The relation we are assembling.
+ size_t m_relation_offset;
+
+ /**
+ * The number of members still needed before the relation is
+ * complete. This will be set to the number of members we are
+ * interested in and then count down for every member we find.
+ * When it is 0, the relation is complete.
+ */
+ int m_need_members = 0;
+
+ public:
+
+ /**
+ * Initialize an empty RelationMeta. This is needed to zero out
+ * relations that have been completed.
+ */
+ RelationMeta() noexcept :
+ m_relation_offset(0) {
+ }
+
+ explicit RelationMeta(size_t relation_offset) noexcept :
+ m_relation_offset(relation_offset) {
+ }
+
+ /**
+ * Get offset of relation in buffer.
+ */
+ size_t relation_offset() const noexcept {
+ return m_relation_offset;
+ }
+
+ /**
+ * Increment the m_need_members counter.
+ */
+ void increment_need_members() noexcept {
+ ++m_need_members;
+ }
+
+ /**
+ * This decrements the "members needed" counter.
+ */
+ void got_one_member() {
+ assert(m_need_members > 0);
+ --m_need_members;
+ }
+
+ /**
+ * Returns true if all members for this relation are available.
+ */
+ bool has_all_members() const noexcept {
+ return m_need_members == 0;
+ }
+
+ }; // class RelationMeta
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const RelationMeta& rm) {
+ out << "RelationMeta(relation_offset=" << rm.relation_offset() << " has_all_members=" << rm.has_all_members() << ")";
+ return out;
+ }
+
+ /**
+ * Function object to check if a relation is complete.
+ */
+ struct has_all_members {
+
+ typedef RelationMeta& argument_type;
+ typedef bool result_type;
+
+ /**
+ * @returns true if this relation is complete, false otherwise.
+ */
+ bool operator()(RelationMeta& relation_info) const {
+ return relation_info.has_all_members();
+ }
+
+ }; // struct has_all_members
+
+ } // namespace relations
+
+} // namespace osmium
+
+#endif // OSMIUM_RELATIONS_DETAIL_RELATION_META_HPP
diff --git a/include/osmium/tags/filter.hpp b/include/osmium/tags/filter.hpp
new file mode 100644
index 0000000..3c1946c
--- /dev/null
+++ b/include/osmium/tags/filter.hpp
@@ -0,0 +1,162 @@
+#ifndef OSMIUM_TAGS_FILTER_HPP
+#define OSMIUM_TAGS_FILTER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <string>
+#include <type_traits>
+#include <vector>
+
+#include <boost/iterator/filter_iterator.hpp>
+
+#include <osmium/memory/collection.hpp>
+#include <osmium/osm/tag.hpp>
+
+namespace osmium {
+
+ namespace tags {
+
+ template <class TKey>
+ struct match_key {
+ bool operator()(const TKey& rule_key, const char* tag_key) {
+ return rule_key == tag_key;
+ }
+ }; // struct match_key
+
+ struct match_key_prefix {
+ bool operator()(const std::string& rule_key, const char* tag_key) {
+ return rule_key.compare(0, std::string::npos, tag_key, 0, rule_key.size()) == 0;
+ }
+ }; // struct match_key_prefix
+
+ template <class TValue>
+ struct match_value {
+ bool operator()(const TValue& rule_value, const char* tag_value) {
+ return rule_value == tag_value;
+ }
+ }; // struct match_value
+
+ template <>
+ struct match_value<void> {
+ bool operator()(const bool, const char*) {
+ return true;
+ }
+ }; // struct match_value<void>
+
+ template <class TKey, class TValue=void, class TKeyComp=match_key<TKey>, class TValueComp=match_value<TValue>>
+ class Filter {
+
+ typedef TKey key_type;
+ typedef typename std::conditional<std::is_void<TValue>::value, bool, TValue>::type value_type;
+
+ struct Rule {
+ key_type key;
+ value_type value;
+ bool ignore_value;
+ bool result;
+
+ explicit Rule(bool r, bool ignore, const key_type& k, const value_type& v) :
+ key(k),
+ value(v),
+ ignore_value(ignore),
+ result(r) {
+ }
+
+ explicit Rule(bool r, bool ignore, const key_type& k) :
+ key(k),
+ value(),
+ ignore_value(ignore),
+ result(r) {
+ }
+
+ }; // struct Rule
+
+ std::vector<Rule> m_rules;
+ bool m_default_result;
+
+ public:
+
+ typedef Filter<TKey, TValue, TKeyComp, TValueComp> filter_type;
+ typedef const osmium::Tag& argument_type;
+ typedef bool result_type;
+ typedef boost::filter_iterator<filter_type, osmium::TagList::const_iterator> iterator;
+
+ explicit Filter(bool default_result = false) :
+ m_default_result(default_result) {
+ }
+
+ template <class V=TValue, typename std::enable_if<!std::is_void<V>::value, int>::type = 0>
+ Filter& add(bool result, const key_type& key, const value_type& value) {
+ m_rules.emplace_back(result, false, key, value);
+ return *this;
+ }
+
+ Filter& add(bool result, const key_type& key) {
+ m_rules.emplace_back(result, true, key);
+ return *this;
+ }
+
+ bool operator()(const osmium::Tag& tag) const {
+ for (const Rule& rule : m_rules) {
+ if (TKeyComp()(rule.key, tag.key()) && (rule.ignore_value || TValueComp()(rule.value, tag.value()))) {
+ return rule.result;
+ }
+ }
+ return m_default_result;
+ }
+
+ /**
+ * Return the number of rules in this filter.
+ */
+ size_t count() const {
+ return m_rules.count();
+ }
+
+ /**
+ * Is this filter empty, ie are there no rules defined?
+ */
+ bool empty() const {
+ return m_rules.empty();
+ }
+
+ }; // class Filter
+
+ typedef Filter<std::string, std::string> KeyValueFilter;
+ typedef Filter<std::string> KeyFilter;
+ typedef Filter<std::string, void, match_key_prefix> KeyPrefixFilter;
+
+ } // namespace tags
+
+} // namespace osmium
+
+#endif // OSMIUM_TAGS_FILTER_HPP
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp
new file mode 100644
index 0000000..725c423
--- /dev/null
+++ b/include/osmium/tags/regex_filter.hpp
@@ -0,0 +1,58 @@
+#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
+#define OSMIUM_TAGS_REGEX_FILTER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <regex>
+#include <string>
+
+#include <osmium/tags/filter.hpp>
+
+namespace osmium {
+
+ namespace tags {
+
+ template <>
+ struct match_value<std::regex> {
+ bool operator()(const std::regex& rule_value, const char* tag_value) {
+ return std::regex_match(tag_value, rule_value);
+ }
+ }; // struct match_value<std::regex>
+
+ typedef Filter<std::string, std::regex> RegexFilter;
+
+ } // namespace tags
+
+} // namespace osmium
+
+#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
diff --git a/include/osmium/tags/taglist.hpp b/include/osmium/tags/taglist.hpp
new file mode 100644
index 0000000..d7c78dc
--- /dev/null
+++ b/include/osmium/tags/taglist.hpp
@@ -0,0 +1,67 @@
+#ifndef OSMIUM_TAGS_TAGLIST_HPP
+#define OSMIUM_TAGS_TAGLIST_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <utility>
+
+#include <osmium/osm/tag.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Code related to working with OSM tags
+ */
+ namespace tags {
+
+ template <class TFilter>
+ inline bool match_any_of(const osmium::TagList& tag_list, TFilter&& filter) {
+ return std::any_of(tag_list.cbegin(), tag_list.cend(), std::forward<TFilter>(filter));
+ }
+
+ template <class TFilter>
+ inline bool match_all_of(const osmium::TagList& tag_list, TFilter&& filter) {
+ return std::all_of(tag_list.cbegin(), tag_list.cend(), std::forward<TFilter>(filter));
+ }
+
+ template <class TFilter>
+ inline bool match_none_of(const osmium::TagList& tag_list, TFilter&& filter) {
+ return std::none_of(tag_list.cbegin(), tag_list.cend(), std::forward<TFilter>(filter));
+ }
+
+ } // namespace tags
+
+} // namespace osmium
+
+#endif // OSMIUM_TAGS_TAGLIST_HPP
diff --git a/include/osmium/thread/function_wrapper.hpp b/include/osmium/thread/function_wrapper.hpp
new file mode 100644
index 0000000..fe0a492
--- /dev/null
+++ b/include/osmium/thread/function_wrapper.hpp
@@ -0,0 +1,111 @@
+#ifndef OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
+#define OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <memory>
+
+namespace osmium {
+
+ namespace thread {
+
+ /**
+ * This function wrapper can collect move-only functions unlike
+ * std::function which needs copyable functions.
+ * Taken from the book "C++ Concurrency in Action".
+ */
+ class function_wrapper {
+
+ struct impl_base {
+
+ virtual ~impl_base() = default;
+ virtual void call() = 0;
+
+ }; // struct impl_base
+
+ std::unique_ptr<impl_base> impl;
+
+ template <typename F>
+ struct impl_type : impl_base {
+ F m_functor;
+
+ impl_type(F&& functor) :
+ m_functor(std::move(functor)) {
+ }
+
+ void call() override {
+ m_functor();
+ }
+ }; // struct impl_type
+
+ public:
+
+ // Constructor must not be "explicit" for wrapper
+ // to work seemlessly.
+ template <typename F>
+ function_wrapper(F&& f) :
+ impl(new impl_type<F>(std::move(f))) {
+ }
+
+ void operator()() {
+ impl->call();
+ }
+
+ function_wrapper() = default;
+
+ function_wrapper(function_wrapper&& other) :
+ impl(std::move(other.impl)) {
+ }
+
+ function_wrapper& operator=(function_wrapper&& other) {
+ impl = std::move(other.impl);
+ return *this;
+ }
+
+ function_wrapper(const function_wrapper&) = delete;
+ function_wrapper& operator=(const function_wrapper&) = delete;
+
+ ~function_wrapper() = default;
+
+ explicit operator bool() const {
+ return static_cast<bool>(impl);
+ }
+
+ }; // class function_wrapper
+
+ } // namespace thread
+
+} // namespace osmium
+
+#endif // OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
new file mode 100644
index 0000000..bc1e9a8
--- /dev/null
+++ b/include/osmium/thread/pool.hpp
@@ -0,0 +1,180 @@
+#ifndef OSMIUM_THREAD_POOL_HPP
+#define OSMIUM_THREAD_POOL_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstdlib>
+#include <future>
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include <osmium/thread/function_wrapper.hpp>
+#include <osmium/thread/queue.hpp>
+#include <osmium/thread/util.hpp>
+#include <osmium/util/config.hpp>
+
+namespace osmium {
+
+ /**
+ * @brief Threading-related low-level code
+ */
+ namespace thread {
+
+ /**
+ * Thread pool.
+ */
+ class Pool {
+
+ /**
+ * This class makes sure all pool threads will be joined when
+ * the pool is destructed.
+ */
+ class thread_joiner {
+
+ std::vector<std::thread>& m_threads;
+
+ public:
+
+ explicit thread_joiner(std::vector<std::thread>& threads) :
+ m_threads(threads) {
+ }
+
+ ~thread_joiner() {
+ for (auto& thread : m_threads) {
+ if (thread.joinable()) {
+ thread.join();
+ }
+ }
+ }
+
+ }; // class thread_joiner
+
+ std::atomic<bool> m_done;
+ osmium::thread::Queue<function_wrapper> m_work_queue;
+ std::vector<std::thread> m_threads;
+ thread_joiner m_joiner;
+ int m_num_threads;
+
+ void worker_thread() {
+ osmium::thread::set_thread_name("_osmium_worker");
+ while (!m_done) {
+ function_wrapper task;
+ m_work_queue.wait_and_pop_with_timeout(task);
+ if (task) {
+ task();
+ }
+ }
+ }
+
+ /**
+ * Create thread pool with the given number of threads. If
+ * num_threads is 0, the number of threads is read from
+ * the environment variable OSMIUM_POOL_THREADS. The default
+ * value in that case is -2.
+ *
+ * If the number of threads is a negative number, it will be
+ * set to the actual number of cores on the system plus the
+ * given number, ie it will leave a number of cores unused.
+ *
+ * In all cases the minimum number of threads in the pool is 1.
+ */
+ explicit Pool(int num_threads, size_t max_queue_size) :
+ m_done(false),
+ m_work_queue(max_queue_size, "work"),
+ m_threads(),
+ m_joiner(m_threads),
+ m_num_threads(num_threads) {
+
+ if (m_num_threads == 0) {
+ m_num_threads = osmium::config::get_pool_threads();
+ }
+
+ if (m_num_threads <= 0) {
+ m_num_threads = std::max(1, static_cast<int>(std::thread::hardware_concurrency()) + m_num_threads);
+ }
+
+ try {
+ for (int i=0; i < m_num_threads; ++i) {
+ m_threads.push_back(std::thread(&Pool::worker_thread, this));
+ }
+ } catch (...) {
+ m_done = true;
+ throw;
+ }
+ }
+
+ 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);
+ return pool;
+ }
+
+ ~Pool() {
+ m_done = true;
+ }
+
+ size_t queue_size() const {
+ return m_work_queue.size();
+ }
+
+ bool queue_empty() const {
+ return m_work_queue.empty();
+ }
+
+ template <typename TFunction>
+ std::future<typename std::result_of<TFunction()>::type> submit(TFunction&& func) {
+
+ typedef typename std::result_of<TFunction()>::type result_type;
+
+ std::packaged_task<result_type()> task(std::forward<TFunction>(func));
+ std::future<result_type> future_result(task.get_future());
+ m_work_queue.push(std::move(task));
+
+ return future_result;
+ }
+
+ }; // class Pool
+
+ } // namespace thread
+
+} // namespace osmium
+
+#endif // OSMIUM_THREAD_POOL_HPP
diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp
new file mode 100644
index 0000000..12e5932
--- /dev/null
+++ b/include/osmium/thread/queue.hpp
@@ -0,0 +1,181 @@
+#ifndef OSMIUM_THREAD_QUEUE_HPP
+#define OSMIUM_THREAD_QUEUE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <cstddef>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <osmium/util/compatibility.hpp>
+
+namespace osmium {
+
+ namespace thread {
+
+ OSMIUM_CONSTEXPR std::chrono::milliseconds full_queue_sleep_duration { 10 }; // XXX
+
+ /**
+ * A thread-safe queue.
+ */
+ template <typename T>
+ class Queue {
+
+ /// Maximum size of this queue. If the queue is full pushing to
+ /// the queue will block.
+ const size_t m_max_size;
+
+ /// Name of this queue (for debugging only).
+ const std::string m_name;
+
+ mutable std::mutex m_mutex;
+
+ std::queue<T> m_queue;
+
+ /// Used to signal readers when data is available in the queue.
+ std::condition_variable m_data_available;
+
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ /// The largest size the queue has been so far.
+ size_t m_largest_size;
+
+ /// The number of times the queue was full and a thread pushing
+ /// to the queue was blocked.
+ std::atomic<int> m_full_counter;
+#endif
+
+ public:
+
+ /**
+ * Construct a multithreaded queue.
+ *
+ * @param max_size Maximum number of elements in the queue. Set to
+ * 0 for an unlimited size.
+ * @param name Optional name for this queue. (Used for debugging.)
+ */
+ Queue(size_t max_size = 0, const std::string& name = "") :
+ m_max_size(max_size),
+ m_name(name),
+ m_mutex(),
+ m_queue(),
+ m_data_available()
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ ,
+ m_largest_size(0),
+ m_full_counter(0)
+#endif
+ {
+ }
+
+ ~Queue() {
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times\n";
+#endif
+ }
+
+ /**
+ * Push an element onto the queue. If the queue has a max size, this
+ * call will block if the queue is full.
+ */
+ void push(T value) {
+ if (m_max_size) {
+ while (size() >= m_max_size) {
+ std::this_thread::sleep_for(full_queue_sleep_duration);
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ ++m_full_counter;
+#endif
+ }
+ }
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_queue.push(std::move(value));
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ if (m_largest_size < m_queue.size()) {
+ m_largest_size = m_queue.size();
+ }
+#endif
+ m_data_available.notify_one();
+ }
+
+ void wait_and_pop(T& value) {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_data_available.wait(lock, [this] {
+ return !m_queue.empty();
+ });
+ value=std::move(m_queue.front());
+ m_queue.pop();
+ }
+
+ void wait_and_pop_with_timeout(T& value) {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if (!m_data_available.wait_for(lock, std::chrono::seconds(1), [this] {
+ return !m_queue.empty();
+ })) {
+ return;
+ }
+ value=std::move(m_queue.front());
+ m_queue.pop();
+ }
+
+ bool try_pop(T& value) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (m_queue.empty()) {
+ return false;
+ }
+ value=std::move(m_queue.front());
+ m_queue.pop();
+ return true;
+ }
+
+ bool empty() const {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return m_queue.empty();
+ }
+
+ size_t size() const {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return m_queue.size();
+ }
+
+ }; // class Queue
+
+ } // namespace thread
+
+} // namespace osmium
+
+#endif // OSMIUM_THREAD_QUEUE_HPP
diff --git a/include/osmium/thread/sorted_queue.hpp b/include/osmium/thread/sorted_queue.hpp
new file mode 100644
index 0000000..e0181eb
--- /dev/null
+++ b/include/osmium/thread/sorted_queue.hpp
@@ -0,0 +1,159 @@
+#ifndef OSMIUM_THREAD_SORTED_QUEUE_HPP
+#define OSMIUM_THREAD_SORTED_QUEUE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <condition_variable>
+#include <cstddef>
+#include <deque>
+#include <mutex>
+
+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 <typename T>
+ class SortedQueue {
+
+ typedef typename std::deque<T>::size_type size_type;
+
+ mutable std::mutex m_mutex;
+ std::deque<T> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(m_mutex);
+ return m_queue.size();
+ }
+
+ }; // class SortedQueue
+
+ } // namespace thread
+
+} // namespace osmium
+
+#endif // OSMIUM_THREAD_SORTED_QUEUE_HPP
diff --git a/include/osmium/thread/util.hpp b/include/osmium/thread/util.hpp
new file mode 100644
index 0000000..62bb82a
--- /dev/null
+++ b/include/osmium/thread/util.hpp
@@ -0,0 +1,87 @@
+#ifndef OSMIUM_THREAD_UTIL_HPP
+#define OSMIUM_THREAD_UTIL_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <chrono>
+#include <future>
+
+#ifdef __linux__
+# include <sys/prctl.h>
+#endif
+
+namespace osmium {
+
+ namespace thread {
+
+ /**
+ * Check if the future resulted in an exception. This will re-throw
+ * the exception stored in the future if there was one. Otherwise it
+ * will just return.
+ */
+ template <class T>
+ inline void check_for_exception(std::future<T>& future) {
+ if (future.valid() && future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+ future.get();
+ }
+ }
+
+ /**
+ * Wait until the given future becomes ready. Will block if the future
+ * is not ready. Can be called more than once unless future.get().
+ */
+ template <class T>
+ inline void wait_until_done(std::future<T>& future) {
+ if (future.valid()) {
+ future.get();
+ }
+ }
+
+ /**
+ * Set name of current thread for debugging. This only works on Linux.
+ */
+#ifdef __linux__
+ inline void set_thread_name(const char* name) {
+ prctl(PR_SET_NAME, name, 0, 0, 0);
+ }
+#else
+ inline void set_thread_name(const char*) {
+ // intentionally left blank
+ }
+#endif
+
+ } // namespace thread
+
+} // namespace osmium
+
+#endif // OSMIUM_THREAD_UTIL_HPP
diff --git a/include/osmium/util/cast.hpp b/include/osmium/util/cast.hpp
new file mode 100644
index 0000000..80adadf
--- /dev/null
+++ b/include/osmium/util/cast.hpp
@@ -0,0 +1,102 @@
+#ifndef OSMIUM_UTIL_CAST_HPP
+#define OSMIUM_UTIL_CAST_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+#ifndef assert
+# include <cassert>
+#endif
+
+#include <limits>
+#include <type_traits>
+
+namespace osmium {
+
+ // These functions are wrappers around static_cast<>() that call assert()
+ // to check that there is no integer overflow happening before doing the
+ // cast. There are several versions of this templated function here
+ // depending on the types of the input and output. In any case, both input
+ // and output have to be integral types. If the cast can't overflow, no
+ // check is done.
+
+ template <typename A, typename B>
+ struct are_real_integers :
+ std::integral_constant<bool,
+ std::is_integral<A>::value &&
+ std::is_integral<B>::value &&
+ !std::is_same<A, bool>::value &&
+ !std::is_same<B, bool>::value> {
+ };
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && std::is_same<T, F>::value, int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ return value;
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) > sizeof(F)), int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ return static_cast<T>(value);
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && std::is_signed<T>::value == std::is_signed<F>::value && (sizeof(T) == sizeof(F)), int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ return static_cast<T>(value);
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) < sizeof(F)) && std::is_signed<T>::value && std::is_signed<F>::value, int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ assert(value >= std::numeric_limits<T>::min() && value <= std::numeric_limits<T>::max());
+ return static_cast<T>(value);
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) <= sizeof(F)) && std::is_unsigned<T>::value && std::is_signed<F>::value, int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ assert(value >= 0 && static_cast<typename std::make_unsigned<F>::type>(value) <= std::numeric_limits<T>::max());
+ return static_cast<T>(value);
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) < sizeof(F)) && std::is_unsigned<T>::value && std::is_unsigned<F>::value, int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ assert(value <= std::numeric_limits<T>::max());
+ return static_cast<T>(value);
+ }
+
+ template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) <= sizeof(F)) && std::is_signed<T>::value && std::is_unsigned<F>::value, int>::type = 0>
+ inline T static_cast_with_assert(const F value) {
+ assert(value <= std::numeric_limits<T>::max());
+ return static_cast<T>(value);
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_CAST_HPP
diff --git a/include/osmium/util/compatibility.hpp b/include/osmium/util/compatibility.hpp
new file mode 100644
index 0000000..90d85c5
--- /dev/null
+++ b/include/osmium/util/compatibility.hpp
@@ -0,0 +1,47 @@
+#ifndef OSMIUM_UTIL_COMPATIBILITY_HPP
+#define OSMIUM_UTIL_COMPATIBILITY_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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.
+
+*/
+
+// Workarounds for MSVC which doesn't support
+// * constexpr in all cases yet
+// * [[noreturn]]
+#ifdef _MSC_VER
+# define OSMIUM_CONSTEXPR
+# define OSMIUM_NORETURN __declspec(noreturn)
+#else
+# define OSMIUM_CONSTEXPR constexpr
+# define OSMIUM_NORETURN [[noreturn]]
+#endif
+
+#endif // OSMIUM_UTIL_COMPATIBILITY_HPP
diff --git a/include/osmium/util/config.hpp b/include/osmium/util/config.hpp
new file mode 100644
index 0000000..3285eed
--- /dev/null
+++ b/include/osmium/util/config.hpp
@@ -0,0 +1,72 @@
+#ifndef OSMIUM_UTIL_CONFIG_HPP
+#define OSMIUM_UTIL_CONFIG_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <cstdlib>
+#include <cstring>
+
+#ifdef _MSC_VER
+# define strcasecmp _stricmp
+#endif
+
+namespace osmium {
+
+ namespace config {
+
+ inline int get_pool_threads() {
+ const char* env = getenv("OSMIUM_POOL_THREADS");
+ if (env) {
+ return std::atoi(env);
+ }
+ return -2;
+ }
+
+ inline bool use_pool_threads_for_pbf_parsing() {
+ const char* env = getenv("OSMIUM_USE_POOL_THREADS_FOR_PBF_PARSING");
+ if (env) {
+ if (!strcasecmp(env, "off") ||
+ !strcasecmp(env, "false") ||
+ !strcasecmp(env, "no") ||
+ !strcasecmp(env, "0")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ } // namespace config
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_CONFIG_HPP
diff --git a/include/osmium/util/double.hpp b/include/osmium/util/double.hpp
new file mode 100644
index 0000000..85a2508
--- /dev/null
+++ b/include/osmium/util/double.hpp
@@ -0,0 +1,93 @@
+#ifndef OSMIUM_UTIL_DOUBLE_HPP
+#define OSMIUM_UTIL_DOUBLE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <iterator>
+#include <string>
+
+namespace osmium {
+
+ namespace util {
+
+ constexpr int max_double_length = 20; // should fit any double
+
+ /**
+ * Write double to iterator, removing superfluous '0' characters at
+ * the end. The decimal dot will also be removed if necessary.
+ *
+ * @tparam T iterator type
+ * @param iterator output iterator
+ * @param value the value that should be written
+ * @param precision max number of digits after the decimal point (must be <= 17)
+ */
+ template <typename T>
+ inline T double2string(T iterator, double value, int precision) {
+ assert(precision <= 17);
+
+ char buffer[max_double_length];
+
+#ifndef _MSC_VER
+ int len = snprintf(buffer, max_double_length, "%.*f", precision, value);
+#else
+ int len = _snprintf(buffer, max_double_length, "%.*f", precision, value);
+#endif
+ assert(len > 0 && len < max_double_length);
+
+ while (buffer[len-1] == '0') --len;
+ if (buffer[len-1] == '.') --len;
+
+ return std::copy_n(buffer, len, iterator);
+ }
+
+ /**
+ * Write double to string, removing superfluous '0' characters at
+ * the end. The decimal dot will also be removed if necessary.
+ *
+ * @param out string
+ * @param value the value that should be written
+ * @param precision max number of digits after the decimal point
+ */
+ inline void double2string(std::string& out, double value, int precision) {
+ double2string(std::back_inserter(out), value, precision);
+ }
+
+ } // namespace util
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_DOUBLE_HPP
diff --git a/include/osmium/util/options.hpp b/include/osmium/util/options.hpp
new file mode 100644
index 0000000..fea0752
--- /dev/null
+++ b/include/osmium/util/options.hpp
@@ -0,0 +1,155 @@
+#ifndef OSMIUM_UTIL_OPTIONS_HPP
+#define OSMIUM_UTIL_OPTIONS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <initializer_list>
+#include <map>
+#include <string>
+#include <utility>
+
+namespace osmium {
+
+ namespace util {
+
+ /**
+ * Stores key=value type options. This class can be used stand-alone or
+ * as a base class. Options are stored and retrieved by key using the
+ * different set() and get() methods.
+ *
+ * You can iterate over all set options. Dereferencing an iterator
+ * yields a std::pair of the key and value strings.
+ */
+ class Options {
+
+ typedef std::map<std::string, std::string> option_map;
+ option_map m_options;
+
+ public:
+
+ typedef option_map::iterator iterator;
+ typedef option_map::const_iterator const_iterator;
+ typedef option_map::value_type value_type;
+
+ Options() = default;
+
+ explicit Options(const std::initializer_list<value_type>& values) :
+ m_options(values) {
+ }
+
+ Options(const Options&) = default;
+ Options& operator=(const Options&) = default;
+
+ Options(Options&&) = default;
+ Options& operator=(Options&&) = default;
+
+ ~Options() = default;
+
+ void set(const std::string& key, const std::string& value) {
+ m_options[key] = value;
+ }
+
+ void set(const std::string& key, const char* value) {
+ m_options[key] = value;
+ }
+
+ void set(const std::string& key, bool value) {
+ m_options[key] = value ? "true" : "false";
+ }
+
+ void set(std::string data) {
+ size_t pos = data.find_first_of('=');
+ if (pos == std::string::npos) {
+ m_options[data] = "true";
+ } else {
+ std::string value = data.substr(pos+1);
+ data.erase(pos);
+ set(data, value);
+ }
+ }
+
+ /**
+ * Get value of "key" option. If not set the default_value (or
+ * empty string) is returned.
+ */
+ std::string get(const std::string& key, const std::string& default_value="") const noexcept {
+ auto it = m_options.find(key);
+ if (it == m_options.end()) {
+ return default_value;
+ }
+ return it->second;
+ }
+
+ /**
+ * Is this option set to a true value ("true" or "yes")?
+ */
+ bool is_true(const std::string& key) const noexcept {
+ std::string value = get(key);
+ return (value == "true" || value == "yes");
+ }
+
+ size_t size() const noexcept {
+ return m_options.size();
+ }
+
+ iterator begin() noexcept {
+ return m_options.begin();
+ }
+
+ iterator end() noexcept {
+ return m_options.end();
+ }
+
+ const_iterator begin() const noexcept {
+ return m_options.cbegin();
+ }
+
+ const_iterator end() const noexcept {
+ return m_options.cend();
+ }
+
+ const_iterator cbegin() const noexcept {
+ return m_options.cbegin();
+ }
+
+ const_iterator cend() const noexcept {
+ return m_options.cend();
+ }
+
+ }; // class Options
+
+ } // namespace util
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_OPTIONS_HPP
diff --git a/include/osmium/util/string.hpp b/include/osmium/util/string.hpp
new file mode 100644
index 0000000..54eb361
--- /dev/null
+++ b/include/osmium/util/string.hpp
@@ -0,0 +1,68 @@
+#ifndef OSMIUM_UTIL_STRING_HPP
+#define OSMIUM_UTIL_STRING_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <string>
+#include <vector>
+#include <iostream>
+
+namespace osmium {
+
+ /**
+ * Split string on the separator character.
+ *
+ * @param str The string to be split.
+ * @param sep The separastor character.
+ * @returns Vector with the parts of the string split up.
+ */
+ inline std::vector<std::string> split_string(const std::string& str, const char sep) {
+ std::vector<std::string> tokens;
+
+ if (!str.empty()) {
+ size_t pos = 0;
+ size_t nextpos = str.find_first_of(sep);
+ while (nextpos != std::string::npos) {
+ tokens.push_back(str.substr(pos, nextpos-pos));
+ pos = nextpos + 1;
+ nextpos = str.find_first_of(sep, pos);
+ }
+ tokens.push_back(str.substr(pos));
+ }
+
+ return tokens;
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_STRING_HPP
diff --git a/include/osmium/util/verbose_output.hpp b/include/osmium/util/verbose_output.hpp
new file mode 100644
index 0000000..178781e
--- /dev/null
+++ b/include/osmium/util/verbose_output.hpp
@@ -0,0 +1,139 @@
+#ifndef OSMIUM_UTIL_VERBOSE_OUTPUT_HPP
+#define OSMIUM_UTIL_VERBOSE_OUTPUT_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <time.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+namespace osmium {
+
+ /**
+ * @brief Helpful utility classes and functions not strictly OSM related
+ */
+ namespace util {
+
+ /**
+ * Osmium programs often run for a long time because of the amount of
+ * OSM data processed. This class helps with keeping the user up to
+ * date by offering an easy way for programs to optionally output
+ * verbose information about what's going on.
+ *
+ * Use an object of this class instead of std::cerr as an output
+ * stream. Nothing is actually written if the object is not set to
+ * verbose mode. If it is set to verbose mode, each line is prepended
+ * with the running time, ie the time since the VerboseOutput object
+ * was created.
+ */
+ class VerboseOutput {
+
+ /// all time output will be relative to this start time
+ time_t m_start;
+
+ /// is verbose mode enabled?
+ bool m_verbose;
+
+ /// a newline was written, start next output with runtime
+ bool m_newline;
+
+ /**
+ * If we remember that a newline was written as the last thing
+ * write out the time elapsed and reset the newline flag.
+ */
+ void start_line() {
+ if (m_newline) {
+ time_t elapsed = runtime();
+
+ 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);
+
+ m_newline = false;
+ }
+ }
+
+ public:
+
+ explicit VerboseOutput(bool verbose=false) noexcept :
+ m_start(time(NULL)),
+ m_verbose(verbose),
+ m_newline(true) {
+ }
+
+ ~VerboseOutput() = default;
+
+ VerboseOutput(const VerboseOutput&) = default;
+ VerboseOutput& operator=(const VerboseOutput&) = default;
+ VerboseOutput(VerboseOutput&&) = default;
+ VerboseOutput& operator=(VerboseOutput&&) = default;
+
+ time_t runtime() const noexcept {
+ return time(NULL) - m_start;
+ }
+
+ /// Get "verbose" setting.
+ bool verbose() const noexcept {
+ return m_verbose;
+ }
+
+ /// Set "verbose" setting.
+ void verbose(bool verbose) noexcept {
+ m_verbose = verbose;
+ }
+
+ template<typename T>
+ friend VerboseOutput& operator<<(VerboseOutput& verbose_output, const T& value) {
+ if (verbose_output.m_verbose) {
+ verbose_output.start_line();
+ std::cerr << value;
+
+ // check if there was a newline a the end and remember that
+ std::ostringstream output_buffer;
+ output_buffer << value;
+ if (!output_buffer.str().empty() && output_buffer.str().back() == '\n') {
+ verbose_output.m_newline = true;
+ }
+ }
+ return verbose_output;
+ }
+
+ }; // class VerboseOutput
+
+ } // namespace util
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_VERBOSE_OUTPUT_HPP
diff --git a/include/osmium/visitor.hpp b/include/osmium/visitor.hpp
new file mode 100644
index 0000000..0250f11
--- /dev/null
+++ b/include/osmium/visitor.hpp
@@ -0,0 +1,255 @@
+#ifndef OSMIUM_VISITOR_HPP
+#define OSMIUM_VISITOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> 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 <type_traits>
+
+#include <osmium/io/reader_iterator.hpp> // IWYU pragma: keep
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/entity.hpp>
+#include <osmium/osm/item_type.hpp>
+
+namespace osmium {
+
+ class TagList;
+ class WayNodeList;
+ class RelationMemberList;
+ class OuterRing;
+ class InnerRing;
+
+ namespace memory {
+ class Item;
+ }
+
+ namespace detail {
+
+ template <typename T, typename U>
+ using ConstIfConst = typename std::conditional<std::is_const<T>::value, typename std::add_const<U>::type, U>::type;
+
+ template <class THandler, class TItem>
+ inline void apply_item_recurse(TItem& item, THandler& handler) {
+ switch (item.type()) {
+ case osmium::item_type::undefined:
+ break;
+ case osmium::item_type::node:
+ handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+ handler.node(static_cast<ConstIfConst<TItem, osmium::Node>&>(item));
+ break;
+ case osmium::item_type::way:
+ handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+ handler.way(static_cast<ConstIfConst<TItem, osmium::Way>&>(item));
+ break;
+ case osmium::item_type::relation:
+ handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+ handler.relation(static_cast<ConstIfConst<TItem, osmium::Relation>&>(item));
+ break;
+ case osmium::item_type::area:
+ handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+ handler.area(static_cast<ConstIfConst<TItem, osmium::Area>&>(item));
+ break;
+ case osmium::item_type::changeset:
+ handler.changeset(static_cast<ConstIfConst<TItem, osmium::Changeset>&>(item));
+ break;
+ case osmium::item_type::tag_list:
+ handler.tag_list(static_cast<ConstIfConst<TItem, osmium::TagList>&>(item));
+ break;
+ case osmium::item_type::way_node_list:
+ handler.way_node_list(static_cast<ConstIfConst<TItem, osmium::WayNodeList>&>(item));
+ break;
+ case osmium::item_type::relation_member_list:
+ case osmium::item_type::relation_member_list_with_full_members:
+ handler.relation_member_list(static_cast<ConstIfConst<TItem, osmium::RelationMemberList>&>(item));
+ break;
+ case osmium::item_type::outer_ring:
+ handler.outer_ring(static_cast<ConstIfConst<TItem, osmium::OuterRing>&>(item));
+ break;
+ case osmium::item_type::inner_ring:
+ handler.inner_ring(static_cast<ConstIfConst<TItem, osmium::InnerRing>&>(item));
+ break;
+ }
+ }
+
+ template <class THandler>
+ inline void apply_item_recurse(const osmium::OSMEntity& item, THandler& handler) {
+ switch (item.type()) {
+ case osmium::item_type::node:
+ handler.osm_object(static_cast<const osmium::OSMObject&>(item));
+ handler.node(static_cast<const osmium::Node&>(item));
+ break;
+ case osmium::item_type::way:
+ handler.osm_object(static_cast<const osmium::OSMObject&>(item));
+ handler.way(static_cast<const osmium::Way&>(item));
+ break;
+ case osmium::item_type::relation:
+ handler.osm_object(static_cast<const osmium::OSMObject&>(item));
+ handler.relation(static_cast<const osmium::Relation&>(item));
+ break;
+ case osmium::item_type::area:
+ handler.osm_object(static_cast<const osmium::OSMObject&>(item));
+ handler.area(static_cast<const osmium::Area&>(item));
+ break;
+ case osmium::item_type::changeset:
+ handler.changeset(static_cast<const osmium::Changeset&>(item));
+ break;
+ default:
+ throw osmium::unknown_type();
+ }
+ }
+
+ template <class THandler>
+ inline void apply_item_recurse(osmium::OSMEntity& item, THandler& handler) {
+ switch (item.type()) {
+ case osmium::item_type::node:
+ handler.osm_object(static_cast<osmium::OSMObject&>(item));
+ handler.node(static_cast<osmium::Node&>(item));
+ break;
+ case osmium::item_type::way:
+ handler.osm_object(static_cast<osmium::OSMObject&>(item));
+ handler.way(static_cast<osmium::Way&>(item));
+ break;
+ case osmium::item_type::relation:
+ handler.osm_object(static_cast<osmium::OSMObject&>(item));
+ handler.relation(static_cast<osmium::Relation&>(item));
+ break;
+ case osmium::item_type::area:
+ handler.osm_object(static_cast<osmium::OSMObject&>(item));
+ handler.area(static_cast<osmium::Area&>(item));
+ break;
+ case osmium::item_type::changeset:
+ handler.changeset(static_cast<osmium::Changeset&>(item));
+ break;
+ default:
+ throw osmium::unknown_type();
+ }
+ }
+
+ template <class THandler>
+ inline void apply_item_recurse(const osmium::OSMObject& item, THandler& handler) {
+ switch (item.type()) {
+ case osmium::item_type::node:
+ handler.osm_object(item);
+ handler.node(static_cast<const osmium::Node&>(item));
+ break;
+ case osmium::item_type::way:
+ handler.osm_object(item);
+ handler.way(static_cast<const osmium::Way&>(item));
+ break;
+ case osmium::item_type::relation:
+ handler.osm_object(item);
+ handler.relation(static_cast<const osmium::Relation&>(item));
+ break;
+ case osmium::item_type::area:
+ handler.osm_object(item);
+ handler.area(static_cast<const osmium::Area&>(item));
+ break;
+ default:
+ throw osmium::unknown_type();
+ }
+ }
+
+ template <class THandler>
+ inline void apply_item_recurse(osmium::OSMObject& item, THandler& handler) {
+ switch (item.type()) {
+ case osmium::item_type::node:
+ handler.osm_object(item);
+ handler.node(static_cast<osmium::Node&>(item));
+ break;
+ case osmium::item_type::way:
+ handler.osm_object(item);
+ handler.way(static_cast<osmium::Way&>(item));
+ break;
+ case osmium::item_type::relation:
+ handler.osm_object(item);
+ handler.relation(static_cast<osmium::Relation&>(item));
+ break;
+ case osmium::item_type::area:
+ handler.osm_object(item);
+ handler.area(static_cast<osmium::Area&>(item));
+ break;
+ default:
+ throw osmium::unknown_type();
+ }
+ }
+
+ template <class THandler, class TItem, class ...TRest>
+ inline void apply_item_recurse(TItem& item, THandler& handler, TRest&... more) {
+ apply_item_recurse(item, handler);
+ apply_item_recurse(item, more...);
+ }
+
+ template <class THandler>
+ inline void flush_recurse(THandler& handler) {
+ handler.flush();
+ }
+
+ template <class THandler, class ...TRest>
+ inline void flush_recurse(THandler& handler, TRest&... more) {
+ flush_recurse(handler);
+ flush_recurse(more...);
+ }
+
+ } // namespace detail
+
+ template <class ...THandlers>
+ inline void apply_item(const osmium::memory::Item& item, THandlers&... handlers) {
+ detail::apply_item_recurse(item, handlers...);
+ }
+
+ template <class ...THandlers>
+ inline void apply_item(osmium::memory::Item& item, THandlers&... handlers) {
+ detail::apply_item_recurse(item, handlers...);
+ }
+
+ template <class TIterator, class ...THandlers>
+ inline void apply(TIterator it, TIterator end, THandlers&... handlers) {
+ for (; it != end; ++it) {
+ detail::apply_item_recurse(*it, handlers...);
+ }
+ detail::flush_recurse(handlers...);
+ }
+
+ template <class TContainer, class ...THandlers>
+ inline void apply(TContainer& c, THandlers&... handlers) {
+ apply(std::begin(c), std::end(c), handlers...);
+ }
+
+ template <class ...THandlers>
+ inline void apply(const osmium::memory::Buffer& buffer, THandlers&... handlers) {
+ apply(buffer.cbegin(), buffer.cend(), handlers...);
+ }
+
+} // namespace osmium
+
+#endif // OSMIUM_VISITOR_HPP
diff --git a/osmium.imp b/osmium.imp
new file mode 100644
index 0000000..c45794d
--- /dev/null
+++ b/osmium.imp
@@ -0,0 +1,11 @@
+#-----------------------------------------------------------------------------
+#
+# Configuration for Include-What-You-Use tool
+#
+# https://code.google.com/p/include-what-you-use/
+#
+#-----------------------------------------------------------------------------
+[
+ { "include": ["<bits/fcntl-linux.h>", "private", "<fcntl.h>", "public"] },
+ { "include": ["<sys/types.h>", "public", "<cstdint>", "public"] }
+]
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..334fe31
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,141 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium unit tests
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring unit tests")
+
+include_directories(include)
+
+add_library(testlib STATIC test_main.cpp)
+
+set(ALL_TESTS "")
+
+
+#-----------------------------------------------------------------------------
+#
+# Define function for adding tests
+#
+# Call with parameters:
+# TGROUP - test group (directory)
+# TNAME - name of test
+# ARGV2 - flag to enable test (optional)
+# ARGV3 - libraries to add (optional)
+#
+#-----------------------------------------------------------------------------
+function(add_unit_test TGROUP TNAME)
+ set(ALL_TESTS "${ALL_TESTS};${TGROUP}/${TNAME}" PARENT_SCOPE)
+ if((${ARGC} EQUAL 2) OR (${ARGV2}))
+ if(Osmium_DEBUG)
+ message("Adding test ${TGROUP}/${TNAME}")
+ endif()
+ set(TESTNAME "${TGROUP}_${TNAME}")
+ add_executable(${TESTNAME} t/${TGROUP}/${TNAME}.cpp)
+ target_link_libraries(${TESTNAME} testlib)
+ if((${ARGV2}) AND (DEFINED ARGV3))
+ if(Osmium_DEBUG)
+ message(" Adding libs ${ARGV3}")
+ endif()
+ target_link_libraries(${TESTNAME} ${ARGV3})
+ endif()
+ add_test(NAME ${TESTNAME}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND ${TESTNAME}
+ )
+ set_tests_properties(${TESTNAME} PROPERTIES
+ LABELS "unit;fast;${TGROUP}"
+ ENVIRONMENT "OSMIUM_TEST_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR}"
+ )
+ else()
+ message("Skipped test ${TGROUP}/${TNAME} because a dependency was not found")
+ set(OSMIUM_SKIPPED_TESTS
+ "${OSMIUM_SKIPPED_TESTS} ${TGROUP}/${TNAME}"
+ CACHE STRING "Tests that were skipped because of missing dependecies")
+ endif()
+endfunction()
+
+#-----------------------------------------------------------------------------
+#
+# Add all tests.
+#
+#-----------------------------------------------------------------------------
+add_unit_test(area test_area_id)
+add_unit_test(area test_node_ref_segment)
+
+add_unit_test(basic test_box)
+add_unit_test(basic test_changeset)
+add_unit_test(basic test_entity_bits)
+add_unit_test(basic test_location)
+add_unit_test(basic test_node)
+add_unit_test(basic test_node_ref)
+add_unit_test(basic test_object_comparisons)
+add_unit_test(basic test_relation)
+add_unit_test(basic test_timestamp)
+add_unit_test(basic test_way)
+
+add_unit_test(buffer test_buffer_node)
+add_unit_test(buffer test_buffer_purge)
+
+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
+ ${GEOS_AND_PROJ_FOUND}
+ "${GEOS_LIBRARY};${PROJ_LIBRARY}")
+
+add_unit_test(geom test_geojson)
+add_unit_test(geom test_geos ${GEOS_FOUND} ${GEOS_LIBRARY})
+add_unit_test(geom test_geos_wkb ${GEOS_FOUND} ${GEOS_LIBRARY})
+add_unit_test(geom test_mercator)
+add_unit_test(geom test_ogr ${GDAL_FOUND} ${GDAL_LIBRARY})
+add_unit_test(geom test_projection ${PROJ_FOUND} ${PROJ_LIBRARY})
+add_unit_test(geom test_wkb)
+add_unit_test(geom test_wkt)
+
+add_unit_test(index test_id_to_location ${SPARSEHASH_FOUND})
+add_unit_test(index test_typed_mmap)
+
+add_unit_test(io test_bzip2 ${BZIP2_FOUND} ${BZIP2_LIBRARIES})
+add_unit_test(io test_file_formats)
+add_unit_test(io test_reader TRUE "${OSMIUM_XML_LIBRARIES}")
+add_unit_test(io test_output_iterator ${Threads_FOUND} ${CMAKE_THREAD_LIBS_INIT})
+
+add_unit_test(tags test_filter)
+add_unit_test(tags test_operators)
+add_unit_test(tags test_tag_list)
+
+add_unit_test(thread test_pool ${Threads_FOUND} ${CMAKE_THREAD_LIBS_INIT})
+
+add_unit_test(util test_cast_with_assert)
+add_unit_test(util test_double)
+add_unit_test(util test_options)
+add_unit_test(util test_string)
+
+
+#-----------------------------------------------------------------------------
+#
+# Check that all tests available in test/t/*/test_*.cpp are run.
+#
+#-----------------------------------------------------------------------------
+file(GLOB TESTS_IN_DIR RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/t" t/*/test_*.cpp)
+
+foreach(file ${TESTS_IN_DIR})
+ string(REPLACE ".cpp" "" out1 ${file})
+ string(REPLACE "//" "/" tname ${out1})
+ list(FIND ALL_TESTS ${tname} found)
+ if(${found} EQUAL -1)
+ message(WARNING "Test '${tname}' not found in cmake config. It will not be run!")
+ endif()
+endforeach()
+
+
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring unit tests - done")
+
+
+#-----------------------------------------------------------------------------
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..8195824
--- /dev/null
+++ b/test/README
@@ -0,0 +1,13 @@
+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/test/data-tests/.gitignore b/test/data-tests/.gitignore
new file mode 100644
index 0000000..98df22e
--- /dev/null
+++ b/test/data-tests/.gitignore
@@ -0,0 +1 @@
+multipolygon.qgs~
diff --git a/test/data-tests/CMakeLists.txt b/test/data-tests/CMakeLists.txt
new file mode 100644
index 0000000..89aead9
--- /dev/null
+++ b/test/data-tests/CMakeLists.txt
@@ -0,0 +1,118 @@
+#-----------------------------------------------------------------------------
+#
+# CMake Config
+#
+# Libosmium data tests
+#
+#-----------------------------------------------------------------------------
+
+message(STATUS "Configuring data tests")
+
+if(NOT GDAL_FOUND OR NOT EXPAT_FOUND)
+ message(STATUS "Sorry, building data tests needs GDAL and Expat")
+ message(STATUS "Configuring data tests - failed")
+ return()
+endif()
+
+message(STATUS "Looking for osm-testdata")
+find_path(OSM_TESTDATA grid/data/all.osm HINT ../../../osm-testdata)
+if(OSM_TESTDATA STREQUAL "OSM_TESTDATA-NOTFOUND")
+ message(STATUS "Looking for osm-testdata - not found (data tests disabled)")
+ message(STATUS "Configuring data tests - failed")
+ return()
+endif()
+message(STATUS "Looking for osm-testdata - found")
+
+
+#-----------------------------------------------------------------------------
+
+include_directories("include")
+include_directories("../include")
+
+
+#-----------------------------------------------------------------------------
+#
+# testcases
+#
+#-----------------------------------------------------------------------------
+file(GLOB TESTCASE_CPPS testcases/*.cpp)
+add_executable(testdata-testcases testdata-testcases.cpp ${TESTCASE_CPPS})
+target_link_libraries(testdata-testcases
+ ${OSMIUM_XML_LIBRARIES}
+)
+add_test(NAME testdata-testcases
+ COMMAND testdata-testcases
+)
+set_tests_properties(testdata-testcases PROPERTIES
+ ENVIRONMENT "TESTCASES_DIR=${OSM_TESTDATA}/grid/data"
+ LABELS "data;fast")
+
+
+#-----------------------------------------------------------------------------
+#
+# xml
+#
+#-----------------------------------------------------------------------------
+add_executable(testdata-xml testdata-xml.cpp)
+target_link_libraries(testdata-xml
+ ${OSMIUM_XML_LIBRARIES}
+)
+add_test(NAME testdata-xml
+ COMMAND testdata-xml
+)
+set_tests_properties(testdata-xml PROPERTIES
+ ENVIRONMENT "TESTDIR=${OSM_TESTDATA}/xml/data"
+ LABELS "data;fast")
+
+
+#-----------------------------------------------------------------------------
+#
+# overview
+#
+#-----------------------------------------------------------------------------
+add_executable(testdata-overview testdata-overview.cpp)
+target_link_libraries(testdata-overview
+ ${OSMIUM_XML_LIBRARIES}
+ ${GDAL_LIBRARIES}
+)
+add_test(NAME testdata-overview
+ COMMAND testdata-overview ${OSM_TESTDATA}/grid/data/all.osm
+)
+set_tests_properties(testdata-overview PROPERTIES
+ LABELS "data;slow")
+
+
+#-----------------------------------------------------------------------------
+#
+# multipolygon
+#
+#-----------------------------------------------------------------------------
+
+find_package(Ruby 1.9)
+find_package(Gem COMPONENTS json)
+find_program(SPATIALITE spatialite)
+
+if(RUBY_FOUND AND GEM_json_FOUND AND SPATIALITE)
+ add_executable(testdata-multipolygon testdata-multipolygon.cpp)
+ target_link_libraries(testdata-multipolygon
+ ${OSMIUM_XML_LIBRARIES}
+ ${GDAL_LIBRARIES}
+ )
+
+ add_test(NAME testdata-multipolygon
+ COMMAND ${CMAKE_COMMAND}
+ -D OSM_TESTDATA=${OSM_TESTDATA}
+ -D RUBY=${RUBY_EXECUTABLE}
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/run-testdata-multipolygon.cmake)
+
+ set_tests_properties(testdata-multipolygon PROPERTIES LABELS "data;slow")
+else()
+ message(WARNING "Disabled testdata-multipolygon test because 'ruby' and/or 'json' ruby gem and/or 'spatialite' was not found")
+endif()
+
+
+#-----------------------------------------------------------------------------
+message(STATUS "Configuring data tests - done")
+
+
+#-----------------------------------------------------------------------------
diff --git a/test/data-tests/README.md b/test/data-tests/README.md
new file mode 100644
index 0000000..5138bf8
--- /dev/null
+++ b/test/data-tests/README.md
@@ -0,0 +1,10 @@
+# OSM Testdata
+
+This directory contains software that can be used with the osm-testdata
+repository at https://github.com/osmcode/osm-testdata . To use it, clone
+the `osm-testdata` repository in the same directory where you cloned the
+`libosmium` repository.
+
+Tests will be built if the CMake option `BUILD_DATA_TESTS` is set and run as
+part of the `ctest` run.
+
diff --git a/test/data-tests/include/check_basics_handler.hpp b/test/data-tests/include/check_basics_handler.hpp
new file mode 100644
index 0000000..757ab4d
--- /dev/null
+++ b/test/data-tests/include/check_basics_handler.hpp
@@ -0,0 +1,92 @@
+#ifndef CHECK_BASICS_HANDLER_HPP
+#define CHECK_BASICS_HANDLER_HPP
+
+#include <iostream>
+#include <unordered_set>
+
+#include <osmium/handler.hpp>
+#include <osmium/osm.hpp>
+
+/**
+ * Check some basics of the input data:
+ *
+ * 1. Correct number of nodes, ways, and relations
+ * 2. Correct ID space used by nodes, ways, and relations
+ * 3. No ID used more than once
+ */
+class CheckBasicsHandler : public osmium::handler::Handler {
+
+ // Lower bound for the id range allowed in this test.
+ int m_id_range;
+
+ // In the beginning these contains the number of nodes, ways, and relations
+ // supposedly in the data.osm file. They will be decremented on each object
+ // and have to be 0 at the end.
+ int m_num_nodes;
+ int m_num_ways;
+ int m_num_relations;
+
+ // All IDs encountered in the data.osm file will be stored in this set and
+ // checked for duplicates.
+ std::unordered_set<osmium::object_id_type> m_ids;
+
+ // Check id is in range [min, max] and that it isn't more than once in input.
+ void id_check(osmium::object_id_type id, osmium::object_id_type min, osmium::object_id_type max) {
+ if (id < m_id_range + min || id > m_id_range + max) {
+ std::cerr << " id " << id << " out of range for this test case\n";
+ exit(1);
+ }
+
+ auto r = m_ids.insert(id);
+ if (!r.second) {
+ std::cerr << " id " << id << " contained twice in data.osm\n";
+ exit(1);
+ }
+ }
+
+public:
+
+ static const int ids_per_testcase = 1000;
+
+ CheckBasicsHandler(int testcase, int nodes, int ways, int relations) :
+ osmium::handler::Handler(),
+ m_id_range(testcase * ids_per_testcase),
+ m_num_nodes(nodes),
+ m_num_ways(ways),
+ m_num_relations(relations) {
+ }
+
+ ~CheckBasicsHandler() {
+ if (m_num_nodes != 0) {
+ std::cerr << " wrong number of nodes in data.osm\n";
+ exit(1);
+ }
+ if (m_num_ways != 0) {
+ std::cerr << " wrong number of ways in data.osm\n";
+ exit(1);
+ }
+ if (m_num_relations != 0) {
+ std::cerr << " wrong number of relations in data.osm\n";
+ exit(1);
+ }
+ }
+
+ void node(const osmium::Node& node) {
+ id_check(node.id(), 0, 799);
+ --m_num_nodes;
+ }
+
+ void way(const osmium::Way& way) {
+ id_check(way.id(), 800, 899);
+ --m_num_ways;
+ }
+
+ void relations(const osmium::Relation& relation) {
+ id_check(relation.id(), 900, 999);
+ --m_num_relations;
+ }
+
+}; // CheckBasicsHandler
+
+
+#endif // CHECK_BASICS_HANDLER_HPP
diff --git a/test/data-tests/include/check_wkt_handler.hpp b/test/data-tests/include/check_wkt_handler.hpp
new file mode 100644
index 0000000..fe0199e
--- /dev/null
+++ b/test/data-tests/include/check_wkt_handler.hpp
@@ -0,0 +1,86 @@
+#ifndef CHECK_WKT_HANDLER_HPP
+#define CHECK_WKT_HANDLER_HPP
+
+#include <cassert>
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <osmium/handler.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/types.hpp>
+
+class CheckWKTHandler : public osmium::handler::Handler {
+
+ std::map<osmium::object_id_type, std::string> m_geometries;
+ osmium::geom::WKTFactory<> m_factory;
+
+ void read_wkt_file(const std::string& filename) {
+ std::ifstream in(filename, std::ifstream::in);
+ if (in) {
+ osmium::object_id_type id;
+ std::string line;
+ while (std::getline(in, line)) {
+ size_t pos = line.find_first_of(' ');
+
+ if (pos == std::string::npos) {
+ std::cerr << filename << " not formatted correctly\n";
+ exit(1);
+ }
+
+ std::string id_str = line.substr(0, pos);
+ std::istringstream iss(id_str);
+ iss >> id;
+
+ if (m_geometries.find(id) != m_geometries.end()) {
+ std::cerr << filename + " contains id " << id << "twice\n";
+ exit(1);
+ }
+
+ m_geometries[id] = line.substr(pos+1);
+ }
+ }
+ }
+
+public:
+
+ CheckWKTHandler(const std::string& dirname, int test_id) :
+ osmium::handler::Handler() {
+
+ std::string filename = dirname + "/" + std::to_string(test_id / 100) + "/" + std::to_string(test_id) + "/";
+ read_wkt_file(filename + "nodes.wkt");
+ read_wkt_file(filename + "ways.wkt");
+ }
+
+ ~CheckWKTHandler() {
+ if (!m_geometries.empty()) {
+ for (const auto& geom : m_geometries) {
+ std::cerr << "geometry id " << geom.first << " not in data.osm.\n";
+ }
+ exit(1);
+ }
+ }
+
+ void node(const osmium::Node& node) {
+ const std::string wkt = m_geometries[node.id()];
+ assert(wkt != "" && "Missing geometry for node in nodes.wkt");
+
+ std::string this_wkt = m_factory.create_point(node.location());
+ assert(wkt == this_wkt && "wkt geometries don't match");
+ m_geometries.erase(node.id());
+ }
+
+ void way(const osmium::Way& way) {
+ const std::string wkt = m_geometries[way.id()];
+ assert(wkt != "" && "Missing geometry for way in ways.wkt");
+
+ std::string this_wkt = m_factory.create_linestring(way);
+ assert(wkt == this_wkt && "wkt geometries don't match");
+ m_geometries.erase(way.id());
+ }
+
+}; // CheckWKTHandler
+
+
+#endif // CHECK_WKT_HANDLER_HPP
diff --git a/test/data-tests/include/common.hpp b/test/data-tests/include/common.hpp
new file mode 100644
index 0000000..a6fd3df
--- /dev/null
+++ b/test/data-tests/include/common.hpp
@@ -0,0 +1,22 @@
+#ifndef COMMON_HPP
+#define COMMON_HPP
+
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+#include <osmium/geom/wkt.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/io/xml_input.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_neg_type;
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_pos_type;
+typedef osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type> location_handler_type;
+
+#include "check_basics_handler.hpp"
+#include "check_wkt_handler.hpp"
+
+#include "testdata-testcases.hpp"
+
+#endif // COMMON_HPP
diff --git a/test/data-tests/include/testdata-testcases.hpp b/test/data-tests/include/testdata-testcases.hpp
new file mode 100644
index 0000000..d7d0c01
--- /dev/null
+++ b/test/data-tests/include/testdata-testcases.hpp
@@ -0,0 +1,10 @@
+#ifndef TESTDATA_TESTCASES_HPP
+#define TESTDATA_TESTCASES_HPP
+
+#include <catch.hpp>
+
+#include <string>
+
+extern std::string dirname;
+
+#endif // TESTDATA_TESTCASES_HPP
diff --git a/test/data-tests/multipolygon.qgs b/test/data-tests/multipolygon.qgs
new file mode 100644
index 0000000..5553670
--- /dev/null
+++ b/test/data-tests/multipolygon.qgs
@@ -0,0 +1,880 @@
+<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
+<qgis projectname="" version="2.2.0-Valmiera">
+ <title></title>
+ <relations/>
+ <mapcanvas>
+ <units>degrees</units>
+ <extent>
+ <xmin>0.77500024999999972</xmin>
+ <ymin>-0.84791712574962541</ymin>
+ <xmax>10.22498975000000065</xmax>
+ <ymax>3.94791712574962572</ymax>
+ </extent>
+ <projections>0</projections>
+ <destinationsrs>
+ <spatialrefsys>
+ <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+ <srsid>3452</srsid>
+ <srid>4326</srid>
+ <authid>EPSG:4326</authid>
+ <description>WGS 84</description>
+ <projectionacronym>longlat</projectionacronym>
+ <ellipsoidacronym>WGS84</ellipsoidacronym>
+ <geographicflag>true</geographicflag>
+ </spatialrefsys>
+ </destinationsrs>
+ <layer_coordinate_transform_info/>
+ </mapcanvas>
+ <legend updateDrawingOrder="true">
+ <legendlayer drawingOrder="-1" open="true" checked="Qt::Checked" name="Error Points" showFeatureCount="0">
+ <filegroup open="true" hidden="false">
+ <legendlayerfile isInOverview="0" layerid="perrors20140228163658956" visible="1"/>
+ </filegroup>
+ </legendlayer>
+ <legendlayer drawingOrder="-1" open="true" checked="Qt::Checked" name="Error Lines" showFeatureCount="0">
+ <filegroup open="true" hidden="false">
+ <legendlayerfile isInOverview="0" layerid="lerrors20140228172357933" visible="1"/>
+ </filegroup>
+ </legendlayer>
+ <legendlayer drawingOrder="-1" open="true" checked="Qt::Checked" name="multipolygons" showFeatureCount="0">
+ <filegroup open="true" hidden="false">
+ <legendlayerfile isInOverview="0" layerid="multipolygons20140221151811742" visible="1"/>
+ </filegroup>
+ </legendlayer>
+ <legendgroup embedded="1" drawingOrder="-1" open="true" checked="Qt::Checked" name="Overview" project="../../../osm-testdata/grid/tests.qgs"/>
+ <legendgroup embedded="1" drawingOrder="-1" open="true" checked="Qt::Checked" name="Test Framework" project="../../../osm-testdata/grid/tests.qgs"/>
+ </legend>
+ <projectlayers layercount="9">
+ <maplayer minimumScale="-4.65661e-10" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="0" maxLabelScale="1e+08" simplifyDrawingTol="1" geometry="Line" simplifyMaxScale="1" type="vector" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
+ <id>lerrors20140228172357933</id>
+ <datasource>dbname='./multipolygon.db' table="lerrors" (GEOMETRY) sql=</datasource>
+ <title></title>
+ <abstract></abstract>
+ <keywordList>
+ <value></value>
+ </keywordList>
+ <layername>Error Lines</layername>
+ <srs>
+ <spatialrefsys>
+ <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+ <srsid>3452</srsid>
+ <srid>4326</srid>
+ <authid>EPSG:4326</authid>
+ <description>WGS 84</description>
+ <projectionacronym>longlat</projectionacronym>
+ <ellipsoidacronym>WGS84</ellipsoidacronym>
+ <geographicflag>true</geographicflag>
+ </spatialrefsys>
+ </srs>
+ <provider encoding="System">spatialite</provider>
+ <previewExpression>COALESCE( "OGC_FID", '<NULL>' )</previewExpression>
+ <vectorjoins/>
+ <renderer-v2 attr="problem_type" symbollevels="0" type="categorizedSymbol">
+ <categories>
+ <category symbol="0" value="intersection" label="intersection"/>
+ <category symbol="1" value="role_should_be_outer" label="role_should_be_outer"/>
+ <category symbol="2" value="role_should_be_inner" label="role_should_be_inner"/>
+ <category symbol="3" value="" label=""/>
+ </categories>
+ <symbols>
+ <symbol alpha="1" type="line" name="0">
+ <layer pass="0" class="SimpleLine" locked="0">
+ <prop k="capstyle" v="square"/>
+ <prop k="color" v="255,0,0,255"/>
+ <prop k="customdash" v="5;2"/>
+ <prop k="customdash_unit" v="MM"/>
+ <prop k="draw_inside_polygon" v="0"/>
+ <prop k="joinstyle" v="bevel"/>
+ <prop k="offset" v="0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="penstyle" v="solid"/>
+ <prop k="use_custom_dash" v="0"/>
+ <prop k="width" v="0.5"/>
+ <prop k="width_unit" v="MM"/>
+ </layer>
+ </symbol>
+ <symbol alpha="1" type="line" name="1">
+ <layer pass="0" class="SimpleLine" locked="0">
+ <prop k="capstyle" v="square"/>
+ <prop k="color" v="255,122,33,255"/>
+ <prop k="customdash" v="5;2"/>
+ <prop k="customdash_unit" v="MM"/>
+ <prop k="draw_inside_polygon" v="0"/>
+ <prop k="joinstyle" v="bevel"/>
+ <prop k="offset" v="0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="penstyle" v="solid"/>
+ <prop k="use_custom_dash" v="0"/>
+ <prop k="width" v="0.5"/>
+ <prop k="width_unit" v="MM"/>
+ </layer>
+ </symbol>
+ <symbol alpha="1" type="line" name="2">
+ <layer pass="0" class="SimpleLine" locked="0">
+ <prop k="capstyle" v="square"/>
+ <prop k="color" v="255,122,33,255"/>
+ <prop k="customdash" v="5;2"/>
+ <prop k="customdash_unit" v="MM"/>
+ <prop k="draw_inside_polygon" v="0"/>
+ <prop k="joinstyle" v="bevel"/>
+ <prop k="offset" v="0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="penstyle" v="dash"/>
+ <prop k="use_custom_dash" v="0"/>
+ <prop k="width" v="0.5"/>
+ <prop k="width_unit" v="MM"/>
+ </layer>
+ </symbol>
+ <symbol alpha="1" type="line" name="3">
+ <layer pass="0" class="SimpleLine" locked="0">
+ <prop k="capstyle" v="square"/>
+ <prop k="color" v="255,0,0,255"/>
+ <prop k="customdash" v="5;2"/>
+ <prop k="customdash_unit" v="MM"/>
+ <prop k="draw_inside_polygon" v="0"/>
+ <prop k="joinstyle" v="bevel"/>
+ <prop k="offset" v="0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="penstyle" v="solid"/>
+ <prop k="use_custom_dash" v="0"/>
+ <prop k="width" v="0.5"/>
+ <prop k="width_unit" v="MM"/>
+ </layer>
+ </symbol>
+ </symbols>
+ <source-symbol>
+ <symbol alpha="1" type="line" name="0">
+ <layer pass="0" class="SimpleLine" locked="0">
+ <prop k="capstyle" v="square"/>
+ <prop k="color" v="77,243,51,255"/>
+ <prop k="customdash" v="5;2"/>
+ <prop k="customdash_unit" v="MM"/>
+ <prop k="draw_inside_polygon" v="0"/>
+ <prop k="joinstyle" v="bevel"/>
+ <prop k="offset" v="0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="penstyle" v="solid"/>
+ <prop k="use_custom_dash" v="0"/>
+ <prop k="width" v="0.26"/>
+ <prop k="width_unit" v="MM"/>
+ </layer>
+ </symbol>
+ </source-symbol>
+ <rotation/>
+ <sizescale scalemethod="area"/>
+ </renderer-v2>
+ <customproperties>
+ <property key="labeling" value="pal"/>
+ <property key="labeling/addDirectionSymbol" value="false"/>
+ <property key="labeling/angleOffset" value="0"/>
+ <property key="labeling/blendMode" value="0"/>
+ <property key="labeling/bufferBlendMode" value="0"/>
+ <property key="labeling/bufferColorA" value="255"/>
+ <property key="labeling/bufferColorB" value="255"/>
+ <property key="labeling/bufferColorG" value="255"/>
+ <property key="labeling/bufferColorR" value="255"/>
+ <property key="labeling/bufferDraw" value="false"/>
+ <property key="labeling/bufferJoinStyle" value="64"/>
+ <property key="labeling/bufferNoFill" value="false"/>
+ <property key="labeling/bufferSize" value="1"/>
+ <property key="labeling/bufferSizeInMapUnits" value="false"/>
+ <property key="labeling/bufferTransp" value="0"/>
+ <property key="labeling/centroidWhole" value="false"/>
+ <property key="labeling/decimals" value="3"/>
+ <property key="labeling/displayAll" value="false"/>
+ <property key="labeling/dist" value="0"/>
+ <property key="labeling/distInMapUnits" value="false"/>
+ <property key="labeling/enabled" value="false"/>
+ <property key="labeling/fieldName" value=""/>
+ <property key="labeling/fontBold" value="false"/>
+ <property key="labeling/fontCapitals" value="0"/>
+ <property key="labeling/fontFamily" value="Sans"/>
+ <property key="labeling/fontItalic" value="false"/>
+ <property key="labeling/fontLetterSpacing" value="0"/>
+ <property key="labeling/fontLimitPixelSize" value="false"/>
+ <property key="labeling/fontMaxPixelSize" value="10000"/>
+ <property key="labeling/fontMinPixelSize" value="3"/>
+ <property key="labeling/fontSize" value="10"/>
+ <property key="labeling/fontSizeInMapUnits" value="false"/>
+ <property key="labeling/fontStrikeout" value="false"/>
+ <property key="labeling/fontUnderline" value="false"/>
+ <property key="labeling/fontWeight" value="50"/>
+ <property key="labeling/fontWordSpacing" value="0"/>
+ <property key="labeling/formatNumbers" value="false"/>
+ <property key="labeling/isExpression" value="false"/>
+ <property key="labeling/labelOffsetInMapUnits" value="true"/>
+ <property key="labeling/labelPerPart" value="false"/>
+ <property key="labeling/leftDirectionSymbol" value="<"/>
+ <property key="labeling/limitNumLabels" value="false"/>
+ <property key="labeling/maxCurvedCharAngleIn" value="20"/>
+ <property key="labeling/maxCurvedCharAngleOut" value="-20"/>
+ <property key="labeling/maxNumLabels" value="2000"/>
+ <property key="labeling/mergeLines" value="false"/>
+ <property key="labeling/minFeatureSize" value="0"/>
+ <property key="labeling/multilineAlign" value="0"/>
+ <property key="labeling/multilineHeight" value="1"/>
+ <property key="labeling/namedStyle" value=""/>
+ <property key="labeling/obstacle" value="true"/>
+ <property key="labeling/placeDirectionSymbol" value="0"/>
+ <property key="labeling/placement" value="2"/>
+ <property key="labeling/placementFlags" value="10"/>
+ <property key="labeling/plussign" value="false"/>
+ <property key="labeling/preserveRotation" value="true"/>
+ <property key="labeling/previewBkgrdColor" value="#ffffff"/>
+ <property key="labeling/priority" value="5"/>
+ <property key="labeling/quadOffset" value="4"/>
+ <property key="labeling/reverseDirectionSymbol" value="false"/>
+ <property key="labeling/rightDirectionSymbol" value=">"/>
+ <property key="labeling/scaleMax" value="10000000"/>
+ <property key="labeling/scaleMin" value="1"/>
+ <property key="labeling/scaleVisibility" value="false"/>
+ <property key="labeling/shadowBlendMode" value="6"/>
+ <property key="labeling/shadowColorB" value="0"/>
+ <property key="labeling/shadowColorG" value="0"/>
+ <property key="labeling/shadowColorR" value="0"/>
+ <property key="labeling/shadowDraw" value="false"/>
+ <property key="labeling/shadowOffsetAngle" value="135"/>
+ <property key="labeling/shadowOffsetDist" value="1"/>
+ <property key="labeling/shadowOffsetGlobal" value="true"/>
+ <property key="labeling/shadowOffsetUnits" value="1"/>
+ <property key="labeling/shadowRadius" value="1.5"/>
+ <property key="labeling/shadowRadiusAlphaOnly" value="false"/>
+ <property key="labeling/shadowRadiusUnits" value="1"/>
+ <property key="labeling/shadowScale" value="100"/>
+ <property key="labeling/shadowTransparency" value="30"/>
+ <property key="labeling/shadowUnder" value="0"/>
+ <property key="labeling/shapeBlendMode" value="0"/>
+ <property key="labeling/shapeBorderColorA" value="255"/>
+ <property key="labeling/shapeBorderColorB" value="128"/>
+ <property key="labeling/shapeBorderColorG" value="128"/>
+ <property key="labeling/shapeBorderColorR" value="128"/>
+ <property key="labeling/shapeBorderWidth" value="0"/>
+ <property key="labeling/shapeBorderWidthUnits" value="1"/>
+ <property key="labeling/shapeDraw" value="false"/>
+ <property key="labeling/shapeFillColorA" value="255"/>
+ <property key="labeling/shapeFillColorB" value="255"/>
+ <property key="labeling/shapeFillColorG" value="255"/>
+ <property key="labeling/shapeFillColorR" value="255"/>
+ <property key="labeling/shapeJoinStyle" value="64"/>
+ <property key="labeling/shapeOffsetUnits" value="1"/>
+ <property key="labeling/shapeOffsetX" value="0"/>
+ <property key="labeling/shapeOffsetY" value="0"/>
+ <property key="labeling/shapeRadiiUnits" value="1"/>
+ <property key="labeling/shapeRadiiX" value="0"/>
+ <property key="labeling/shapeRadiiY" value="0"/>
+ <property key="labeling/shapeRotation" value="0"/>
+ <property key="labeling/shapeRotationType" value="0"/>
+ <property key="labeling/shapeSVGFile" value=""/>
+ <property key="labeling/shapeSizeType" value="0"/>
+ <property key="labeling/shapeSizeUnits" value="1"/>
+ <property key="labeling/shapeSizeX" value="0"/>
+ <property key="labeling/shapeSizeY" value="0"/>
+ <property key="labeling/shapeTransparency" value="0"/>
+ <property key="labeling/shapeType" value="0"/>
+ <property key="labeling/textColorA" value="255"/>
+ <property key="labeling/textColorB" value="0"/>
+ <property key="labeling/textColorG" value="0"/>
+ <property key="labeling/textColorR" value="0"/>
+ <property key="labeling/textTransp" value="0"/>
+ <property key="labeling/upsidedownLabels" value="0"/>
+ <property key="labeling/wrapChar" value=""/>
+ <property key="labeling/xOffset" value="0"/>
+ <property key="labeling/yOffset" value="0"/>
+ </customproperties>
+ <blendMode>0</blendMode>
+ <featureBlendMode>0</featureBlendMode>
+ <layerTransparency>0</layerTransparency>
+ <displayfield>OGC_FID</displayfield>
+ <label>0</label>
+ <labelattributes>
+ <label fieldname="" text="Label"/>
+ <family fieldname="" name="Sans"/>
+ <size fieldname="" units="pt" value="12"/>
+ <bold fieldname="" on="0"/>
+ <italic fieldname="" on="0"/>
+ <underline fieldname="" on="0"/>
+ <strikeout fieldname="" on="0"/>
+ <color fieldname="" red="0" blue="0" green="0"/>
+ <x fieldname=""/>
+ <y fieldname=""/>
+ <offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
+ <angle fieldname="" value="0" auto="0"/>
+ <alignment fieldname="" value="center"/>
+ <buffercolor fieldname="" red="255" blue="255" green="255"/>
+ <buffersize fieldname="" units="pt" value="1"/>
+ <bufferenabled fieldname="" on=""/>
+ <multilineenabled fieldname="" on=""/>
+ <selectedonly on=""/>
+ </labelattributes>
+ <edittypes>
+ <edittype labelontop="0" editable="1" type="0" name="OGC_FID"/>
+ <edittype labelontop="0" editable="1" type="0" name="id"/>
+ <edittype labelontop="0" editable="1" type="0" name="object_id"/>
+ <edittype labelontop="0" editable="1" type="0" name="problem_type"/>
+ <edittype labelontop="0" editable="1" type="0" name="type"/>
+ <edittype labelontop="0" editable="1" type="0" name="way_id"/>
+ </edittypes>
+ <editform>.</editform>
+ <editforminit></editforminit>
+ <featformsuppress>0</featformsuppress>
+ <annotationform>.</annotationform>
+ <editorlayout>generatedlayout</editorlayout>
+ <excludeAttributesWMS/>
+ <excludeAttributesWFS/>
+ <attributeactions/>
+ </maplayer>
+ <maplayer minimumScale="-4.65661e-10" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="0" maxLabelScale="1e+08" simplifyDrawingTol="1" geometry="Polygon" simplifyMaxScale="1" type="vector" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
+ <id>multipolygons20140221151811742</id>
+ <datasource>dbname='./multipolygon.db' table="multipolygons" (GEOMETRY) sql=</datasource>
+ <title></title>
+ <abstract></abstract>
+ <keywordList>
+ <value></value>
+ </keywordList>
+ <layername>multipolygons</layername>
+ <srs>
+ <spatialrefsys>
+ <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+ <srsid>3452</srsid>
+ <srid>4326</srid>
+ <authid>EPSG:4326</authid>
+ <description>WGS 84</description>
+ <projectionacronym>longlat</projectionacronym>
+ <ellipsoidacronym>WGS84</ellipsoidacronym>
+ <geographicflag>true</geographicflag>
+ </spatialrefsys>
+ </srs>
+ <provider encoding="System">spatialite</provider>
+ <previewExpression></previewExpression>
+ <vectorjoins/>
+ <renderer-v2 symbollevels="0" type="singleSymbol">
+ <symbols>
+ <symbol alpha="0.494118" type="fill" name="0">
+ <layer pass="0" class="SimpleFill" locked="0">
+ <prop k="border_width_unit" v="MM"/>
+ <prop k="color" v="0,170,255,255"/>
+ <prop k="color_border" v="0,0,0,255"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="style" v="solid"/>
+ <prop k="style_border" v="solid"/>
+ <prop k="width_border" v="0.26"/>
+ </layer>
+ </symbol>
+ </symbols>
+ <rotation/>
+ <sizescale scalemethod="area"/>
+ </renderer-v2>
+ <customproperties>
+ <property key="labeling" value="pal"/>
+ <property key="labeling/addDirectionSymbol" value="false"/>
+ <property key="labeling/angleOffset" value="0"/>
+ <property key="labeling/blendMode" value="0"/>
+ <property key="labeling/bufferBlendMode" value="0"/>
+ <property key="labeling/bufferColorA" value="255"/>
+ <property key="labeling/bufferColorB" value="255"/>
+ <property key="labeling/bufferColorG" value="255"/>
+ <property key="labeling/bufferColorR" value="255"/>
+ <property key="labeling/bufferDraw" value="false"/>
+ <property key="labeling/bufferJoinStyle" value="64"/>
+ <property key="labeling/bufferNoFill" value="false"/>
+ <property key="labeling/bufferSize" value="1"/>
+ <property key="labeling/bufferSizeInMapUnits" value="false"/>
+ <property key="labeling/bufferTransp" value="0"/>
+ <property key="labeling/centroidWhole" value="false"/>
+ <property key="labeling/decimals" value="3"/>
+ <property key="labeling/displayAll" value="false"/>
+ <property key="labeling/dist" value="0"/>
+ <property key="labeling/distInMapUnits" value="false"/>
+ <property key="labeling/enabled" value="false"/>
+ <property key="labeling/fieldName" value=""/>
+ <property key="labeling/fontBold" value="false"/>
+ <property key="labeling/fontCapitals" value="0"/>
+ <property key="labeling/fontFamily" value="Sans"/>
+ <property key="labeling/fontItalic" value="false"/>
+ <property key="labeling/fontLetterSpacing" value="0"/>
+ <property key="labeling/fontLimitPixelSize" value="false"/>
+ <property key="labeling/fontMaxPixelSize" value="10000"/>
+ <property key="labeling/fontMinPixelSize" value="3"/>
+ <property key="labeling/fontSize" value="10"/>
+ <property key="labeling/fontSizeInMapUnits" value="false"/>
+ <property key="labeling/fontStrikeout" value="false"/>
+ <property key="labeling/fontUnderline" value="false"/>
+ <property key="labeling/fontWeight" value="50"/>
+ <property key="labeling/fontWordSpacing" value="0"/>
+ <property key="labeling/formatNumbers" value="false"/>
+ <property key="labeling/isExpression" value="false"/>
+ <property key="labeling/labelOffsetInMapUnits" value="true"/>
+ <property key="labeling/labelPerPart" value="false"/>
+ <property key="labeling/leftDirectionSymbol" value="<"/>
+ <property key="labeling/limitNumLabels" value="false"/>
+ <property key="labeling/maxCurvedCharAngleIn" value="20"/>
+ <property key="labeling/maxCurvedCharAngleOut" value="-20"/>
+ <property key="labeling/maxNumLabels" value="2000"/>
+ <property key="labeling/mergeLines" value="false"/>
+ <property key="labeling/minFeatureSize" value="0"/>
+ <property key="labeling/multilineAlign" value="0"/>
+ <property key="labeling/multilineHeight" value="1"/>
+ <property key="labeling/namedStyle" value=""/>
+ <property key="labeling/obstacle" value="true"/>
+ <property key="labeling/placeDirectionSymbol" value="0"/>
+ <property key="labeling/placement" value="0"/>
+ <property key="labeling/placementFlags" value="0"/>
+ <property key="labeling/plussign" value="false"/>
+ <property key="labeling/preserveRotation" value="true"/>
+ <property key="labeling/previewBkgrdColor" value="#ffffff"/>
+ <property key="labeling/priority" value="5"/>
+ <property key="labeling/quadOffset" value="4"/>
+ <property key="labeling/reverseDirectionSymbol" value="false"/>
+ <property key="labeling/rightDirectionSymbol" value=">"/>
+ <property key="labeling/scaleMax" value="10000000"/>
+ <property key="labeling/scaleMin" value="1"/>
+ <property key="labeling/scaleVisibility" value="false"/>
+ <property key="labeling/shadowBlendMode" value="6"/>
+ <property key="labeling/shadowColorB" value="0"/>
+ <property key="labeling/shadowColorG" value="0"/>
+ <property key="labeling/shadowColorR" value="0"/>
+ <property key="labeling/shadowDraw" value="false"/>
+ <property key="labeling/shadowOffsetAngle" value="135"/>
+ <property key="labeling/shadowOffsetDist" value="1"/>
+ <property key="labeling/shadowOffsetGlobal" value="true"/>
+ <property key="labeling/shadowOffsetUnits" value="1"/>
+ <property key="labeling/shadowRadius" value="1.5"/>
+ <property key="labeling/shadowRadiusAlphaOnly" value="false"/>
+ <property key="labeling/shadowRadiusUnits" value="1"/>
+ <property key="labeling/shadowScale" value="100"/>
+ <property key="labeling/shadowTransparency" value="30"/>
+ <property key="labeling/shadowUnder" value="0"/>
+ <property key="labeling/shapeBlendMode" value="0"/>
+ <property key="labeling/shapeBorderColorA" value="255"/>
+ <property key="labeling/shapeBorderColorB" value="128"/>
+ <property key="labeling/shapeBorderColorG" value="128"/>
+ <property key="labeling/shapeBorderColorR" value="128"/>
+ <property key="labeling/shapeBorderWidth" value="0"/>
+ <property key="labeling/shapeBorderWidthUnits" value="1"/>
+ <property key="labeling/shapeDraw" value="false"/>
+ <property key="labeling/shapeFillColorA" value="255"/>
+ <property key="labeling/shapeFillColorB" value="255"/>
+ <property key="labeling/shapeFillColorG" value="255"/>
+ <property key="labeling/shapeFillColorR" value="255"/>
+ <property key="labeling/shapeJoinStyle" value="64"/>
+ <property key="labeling/shapeOffsetUnits" value="1"/>
+ <property key="labeling/shapeOffsetX" value="0"/>
+ <property key="labeling/shapeOffsetY" value="0"/>
+ <property key="labeling/shapeRadiiUnits" value="1"/>
+ <property key="labeling/shapeRadiiX" value="0"/>
+ <property key="labeling/shapeRadiiY" value="0"/>
+ <property key="labeling/shapeRotation" value="0"/>
+ <property key="labeling/shapeRotationType" value="0"/>
+ <property key="labeling/shapeSVGFile" value=""/>
+ <property key="labeling/shapeSizeType" value="0"/>
+ <property key="labeling/shapeSizeUnits" value="1"/>
+ <property key="labeling/shapeSizeX" value="0"/>
+ <property key="labeling/shapeSizeY" value="0"/>
+ <property key="labeling/shapeTransparency" value="0"/>
+ <property key="labeling/shapeType" value="0"/>
+ <property key="labeling/textColorA" value="255"/>
+ <property key="labeling/textColorB" value="0"/>
+ <property key="labeling/textColorG" value="0"/>
+ <property key="labeling/textColorR" value="0"/>
+ <property key="labeling/textTransp" value="0"/>
+ <property key="labeling/upsidedownLabels" value="0"/>
+ <property key="labeling/wrapChar" value=""/>
+ <property key="labeling/xOffset" value="0"/>
+ <property key="labeling/yOffset" value="0"/>
+ </customproperties>
+ <blendMode>0</blendMode>
+ <featureBlendMode>0</featureBlendMode>
+ <layerTransparency>0</layerTransparency>
+ <displayfield>OGC_FID</displayfield>
+ <label>0</label>
+ <labelattributes>
+ <label fieldname="" text="Label"/>
+ <family fieldname="" name="Sans"/>
+ <size fieldname="" units="pt" value="12"/>
+ <bold fieldname="" on="0"/>
+ <italic fieldname="" on="0"/>
+ <underline fieldname="" on="0"/>
+ <strikeout fieldname="" on="0"/>
+ <color fieldname="" red="0" blue="0" green="0"/>
+ <x fieldname=""/>
+ <y fieldname=""/>
+ <offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
+ <angle fieldname="" value="0" auto="0"/>
+ <alignment fieldname="" value="center"/>
+ <buffercolor fieldname="" red="255" blue="255" green="255"/>
+ <buffersize fieldname="" units="pt" value="1"/>
+ <bufferenabled fieldname="" on=""/>
+ <multilineenabled fieldname="" on=""/>
+ <selectedonly on=""/>
+ </labelattributes>
+ <edittypes>
+ <edittype labelontop="0" editable="1" type="0" name="OGC_FID"/>
+ <edittype labelontop="0" editable="1" type="0" name="id"/>
+ <edittype labelontop="0" editable="1" type="0" name="type"/>
+ </edittypes>
+ <editform>.</editform>
+ <editforminit></editforminit>
+ <featformsuppress>0</featformsuppress>
+ <annotationform>.</annotationform>
+ <editorlayout>generatedlayout</editorlayout>
+ <excludeAttributesWMS/>
+ <excludeAttributesWFS/>
+ <attributeactions/>
+ </maplayer>
+ <maplayer minimumScale="0" maximumScale="1e+08" simplifyDrawingHints="0" minLabelScale="0" maxLabelScale="1e+08" simplifyDrawingTol="1" geometry="Point" simplifyMaxScale="1" type="vector" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
+ <id>perrors20140228163658956</id>
+ <datasource>dbname='./multipolygon.db' table="perrors" (GEOMETRY) sql=</datasource>
+ <title></title>
+ <abstract></abstract>
+ <keywordList>
+ <value></value>
+ </keywordList>
+ <layername>Error Points</layername>
+ <srs>
+ <spatialrefsys>
+ <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+ <srsid>3452</srsid>
+ <srid>4326</srid>
+ <authid>EPSG:4326</authid>
+ <description>WGS 84</description>
+ <projectionacronym>longlat</projectionacronym>
+ <ellipsoidacronym>WGS84</ellipsoidacronym>
+ <geographicflag>true</geographicflag>
+ </spatialrefsys>
+ </srs>
+ <provider encoding="System">spatialite</provider>
+ <previewExpression>COALESCE( "OGC_FID", '<NULL>' )</previewExpression>
+ <vectorjoins/>
+ <renderer-v2 attr="problem_type" symbollevels="0" type="categorizedSymbol">
+ <categories>
+ <category symbol="0" value="intersection" label="intersection"/>
+ <category symbol="1" value="ring_not_closed" label="ring_not_closed"/>
+ <category symbol="2" value="duplicate_node" label="duplicate_node"/>
+ </categories>
+ <symbols>
+ <symbol alpha="1" type="marker" name="0">
+ <layer pass="0" class="SimpleMarker" locked="0">
+ <prop k="angle" v="0"/>
+ <prop k="color" v="255,0,0,255"/>
+ <prop k="color_border" v="255,255,255,255"/>
+ <prop k="horizontal_anchor_point" v="1"/>
+ <prop k="name" v="diamond"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="outline_style" v="solid"/>
+ <prop k="outline_width" v="0.4"/>
+ <prop k="outline_width_unit" v="MM"/>
+ <prop k="scale_method" v="area"/>
+ <prop k="size" v="2.8"/>
+ <prop k="size_unit" v="MM"/>
+ <prop k="vertical_anchor_point" v="1"/>
+ </layer>
+ </symbol>
+ <symbol alpha="1" type="marker" name="1">
+ <layer pass="0" class="SimpleMarker" locked="0">
+ <prop k="angle" v="0"/>
+ <prop k="color" v="255,0,0,255"/>
+ <prop k="color_border" v="255,255,255,255"/>
+ <prop k="horizontal_anchor_point" v="1"/>
+ <prop k="name" v="triangle"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="outline_style" v="solid"/>
+ <prop k="outline_width" v="0.4"/>
+ <prop k="outline_width_unit" v="MM"/>
+ <prop k="scale_method" v="area"/>
+ <prop k="size" v="2.8"/>
+ <prop k="size_unit" v="MM"/>
+ <prop k="vertical_anchor_point" v="1"/>
+ </layer>
+ </symbol>
+ <symbol alpha="1" type="marker" name="2">
+ <layer pass="0" class="SimpleMarker" locked="0">
+ <prop k="angle" v="0"/>
+ <prop k="color" v="255,255,255,255"/>
+ <prop k="color_border" v="255,0,0,255"/>
+ <prop k="horizontal_anchor_point" v="1"/>
+ <prop k="name" v="circle"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="outline_style" v="solid"/>
+ <prop k="outline_width" v="0.4"/>
+ <prop k="outline_width_unit" v="MM"/>
+ <prop k="scale_method" v="area"/>
+ <prop k="size" v="2.4"/>
+ <prop k="size_unit" v="MM"/>
+ <prop k="vertical_anchor_point" v="1"/>
+ </layer>
+ <layer pass="0" class="SimpleMarker" locked="0">
+ <prop k="angle" v="0"/>
+ <prop k="color" v="255,0,0,255"/>
+ <prop k="color_border" v="255,0,0,255"/>
+ <prop k="horizontal_anchor_point" v="1"/>
+ <prop k="name" v="circle"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="outline_style" v="solid"/>
+ <prop k="outline_width" v="0.8"/>
+ <prop k="outline_width_unit" v="MM"/>
+ <prop k="scale_method" v="area"/>
+ <prop k="size" v="0.5"/>
+ <prop k="size_unit" v="MM"/>
+ <prop k="vertical_anchor_point" v="1"/>
+ </layer>
+ </symbol>
+ </symbols>
+ <source-symbol>
+ <symbol alpha="1" type="marker" name="0">
+ <layer pass="0" class="SimpleMarker" locked="0">
+ <prop k="angle" v="0"/>
+ <prop k="color" v="139,168,110,255"/>
+ <prop k="color_border" v="0,0,0,255"/>
+ <prop k="horizontal_anchor_point" v="1"/>
+ <prop k="name" v="circle"/>
+ <prop k="offset" v="0,0"/>
+ <prop k="offset_unit" v="MM"/>
+ <prop k="outline_style" v="solid"/>
+ <prop k="outline_width" v="0"/>
+ <prop k="outline_width_unit" v="MM"/>
+ <prop k="scale_method" v="area"/>
+ <prop k="size" v="2"/>
+ <prop k="size_unit" v="MM"/>
+ <prop k="vertical_anchor_point" v="1"/>
+ </layer>
+ </symbol>
+ </source-symbol>
+ <rotation/>
+ <sizescale scalemethod="area"/>
+ </renderer-v2>
+ <customproperties>
+ <property key="labeling" value="pal"/>
+ <property key="labeling/addDirectionSymbol" value="false"/>
+ <property key="labeling/angleOffset" value="0"/>
+ <property key="labeling/blendMode" value="0"/>
+ <property key="labeling/bufferBlendMode" value="0"/>
+ <property key="labeling/bufferColorA" value="255"/>
+ <property key="labeling/bufferColorB" value="255"/>
+ <property key="labeling/bufferColorG" value="255"/>
+ <property key="labeling/bufferColorR" value="255"/>
+ <property key="labeling/bufferDraw" value="false"/>
+ <property key="labeling/bufferJoinStyle" value="64"/>
+ <property key="labeling/bufferNoFill" value="false"/>
+ <property key="labeling/bufferSize" value="1"/>
+ <property key="labeling/bufferSizeInMapUnits" value="false"/>
+ <property key="labeling/bufferTransp" value="0"/>
+ <property key="labeling/centroidWhole" value="false"/>
+ <property key="labeling/decimals" value="3"/>
+ <property key="labeling/displayAll" value="false"/>
+ <property key="labeling/dist" value="0"/>
+ <property key="labeling/distInMapUnits" value="false"/>
+ <property key="labeling/enabled" value="false"/>
+ <property key="labeling/fieldName" value=""/>
+ <property key="labeling/fontBold" value="false"/>
+ <property key="labeling/fontCapitals" value="0"/>
+ <property key="labeling/fontFamily" value="Sans"/>
+ <property key="labeling/fontItalic" value="false"/>
+ <property key="labeling/fontLetterSpacing" value="0"/>
+ <property key="labeling/fontLimitPixelSize" value="false"/>
+ <property key="labeling/fontMaxPixelSize" value="10000"/>
+ <property key="labeling/fontMinPixelSize" value="3"/>
+ <property key="labeling/fontSize" value="10"/>
+ <property key="labeling/fontSizeInMapUnits" value="false"/>
+ <property key="labeling/fontStrikeout" value="false"/>
+ <property key="labeling/fontUnderline" value="false"/>
+ <property key="labeling/fontWeight" value="50"/>
+ <property key="labeling/fontWordSpacing" value="0"/>
+ <property key="labeling/formatNumbers" value="false"/>
+ <property key="labeling/isExpression" value="false"/>
+ <property key="labeling/labelOffsetInMapUnits" value="true"/>
+ <property key="labeling/labelPerPart" value="false"/>
+ <property key="labeling/leftDirectionSymbol" value="<"/>
+ <property key="labeling/limitNumLabels" value="false"/>
+ <property key="labeling/maxCurvedCharAngleIn" value="20"/>
+ <property key="labeling/maxCurvedCharAngleOut" value="-20"/>
+ <property key="labeling/maxNumLabels" value="2000"/>
+ <property key="labeling/mergeLines" value="false"/>
+ <property key="labeling/minFeatureSize" value="0"/>
+ <property key="labeling/multilineAlign" value="0"/>
+ <property key="labeling/multilineHeight" value="1"/>
+ <property key="labeling/namedStyle" value=""/>
+ <property key="labeling/obstacle" value="true"/>
+ <property key="labeling/placeDirectionSymbol" value="0"/>
+ <property key="labeling/placement" value="0"/>
+ <property key="labeling/placementFlags" value="0"/>
+ <property key="labeling/plussign" value="false"/>
+ <property key="labeling/preserveRotation" value="true"/>
+ <property key="labeling/previewBkgrdColor" value="#ffffff"/>
+ <property key="labeling/priority" value="5"/>
+ <property key="labeling/quadOffset" value="4"/>
+ <property key="labeling/reverseDirectionSymbol" value="false"/>
+ <property key="labeling/rightDirectionSymbol" value=">"/>
+ <property key="labeling/scaleMax" value="10000000"/>
+ <property key="labeling/scaleMin" value="1"/>
+ <property key="labeling/scaleVisibility" value="false"/>
+ <property key="labeling/shadowBlendMode" value="6"/>
+ <property key="labeling/shadowColorB" value="0"/>
+ <property key="labeling/shadowColorG" value="0"/>
+ <property key="labeling/shadowColorR" value="0"/>
+ <property key="labeling/shadowDraw" value="false"/>
+ <property key="labeling/shadowOffsetAngle" value="135"/>
+ <property key="labeling/shadowOffsetDist" value="1"/>
+ <property key="labeling/shadowOffsetGlobal" value="true"/>
+ <property key="labeling/shadowOffsetUnits" value="1"/>
+ <property key="labeling/shadowRadius" value="1.5"/>
+ <property key="labeling/shadowRadiusAlphaOnly" value="false"/>
+ <property key="labeling/shadowRadiusUnits" value="1"/>
+ <property key="labeling/shadowScale" value="100"/>
+ <property key="labeling/shadowTransparency" value="30"/>
+ <property key="labeling/shadowUnder" value="0"/>
+ <property key="labeling/shapeBlendMode" value="0"/>
+ <property key="labeling/shapeBorderColorA" value="255"/>
+ <property key="labeling/shapeBorderColorB" value="128"/>
+ <property key="labeling/shapeBorderColorG" value="128"/>
+ <property key="labeling/shapeBorderColorR" value="128"/>
+ <property key="labeling/shapeBorderWidth" value="0"/>
+ <property key="labeling/shapeBorderWidthUnits" value="1"/>
+ <property key="labeling/shapeDraw" value="false"/>
+ <property key="labeling/shapeFillColorA" value="255"/>
+ <property key="labeling/shapeFillColorB" value="255"/>
+ <property key="labeling/shapeFillColorG" value="255"/>
+ <property key="labeling/shapeFillColorR" value="255"/>
+ <property key="labeling/shapeJoinStyle" value="64"/>
+ <property key="labeling/shapeOffsetUnits" value="1"/>
+ <property key="labeling/shapeOffsetX" value="0"/>
+ <property key="labeling/shapeOffsetY" value="0"/>
+ <property key="labeling/shapeRadiiUnits" value="1"/>
+ <property key="labeling/shapeRadiiX" value="0"/>
+ <property key="labeling/shapeRadiiY" value="0"/>
+ <property key="labeling/shapeRotation" value="0"/>
+ <property key="labeling/shapeRotationType" value="0"/>
+ <property key="labeling/shapeSVGFile" value=""/>
+ <property key="labeling/shapeSizeType" value="0"/>
+ <property key="labeling/shapeSizeUnits" value="1"/>
+ <property key="labeling/shapeSizeX" value="0"/>
+ <property key="labeling/shapeSizeY" value="0"/>
+ <property key="labeling/shapeTransparency" value="0"/>
+ <property key="labeling/shapeType" value="0"/>
+ <property key="labeling/textColorA" value="255"/>
+ <property key="labeling/textColorB" value="0"/>
+ <property key="labeling/textColorG" value="0"/>
+ <property key="labeling/textColorR" value="0"/>
+ <property key="labeling/textTransp" value="0"/>
+ <property key="labeling/upsidedownLabels" value="0"/>
+ <property key="labeling/wrapChar" value=""/>
+ <property key="labeling/xOffset" value="0"/>
+ <property key="labeling/yOffset" value="0"/>
+ </customproperties>
+ <blendMode>0</blendMode>
+ <featureBlendMode>0</featureBlendMode>
+ <layerTransparency>0</layerTransparency>
+ <displayfield>OGC_FID</displayfield>
+ <label>0</label>
+ <labelattributes>
+ <label fieldname="" text="Label"/>
+ <family fieldname="" name="Sans"/>
+ <size fieldname="" units="pt" value="12"/>
+ <bold fieldname="" on="0"/>
+ <italic fieldname="" on="0"/>
+ <underline fieldname="" on="0"/>
+ <strikeout fieldname="" on="0"/>
+ <color fieldname="" red="0" blue="0" green="0"/>
+ <x fieldname=""/>
+ <y fieldname=""/>
+ <offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
+ <angle fieldname="" value="0" auto="0"/>
+ <alignment fieldname="" value="center"/>
+ <buffercolor fieldname="" red="255" blue="255" green="255"/>
+ <buffersize fieldname="" units="pt" value="1"/>
+ <bufferenabled fieldname="" on=""/>
+ <multilineenabled fieldname="" on=""/>
+ <selectedonly on=""/>
+ </labelattributes>
+ <edittypes>
+ <edittype labelontop="0" editable="1" type="0" name="OGC_FID"/>
+ <edittype labelontop="0" editable="1" type="0" name="id"/>
+ <edittype labelontop="0" editable="1" type="0" name="node_id"/>
+ <edittype labelontop="0" editable="1" type="0" name="object_id"/>
+ <edittype labelontop="0" editable="1" type="0" name="problem_type"/>
+ <edittype labelontop="0" editable="1" type="0" name="type"/>
+ </edittypes>
+ <editform>.</editform>
+ <editforminit></editforminit>
+ <featformsuppress>0</featformsuppress>
+ <annotationform>.</annotationform>
+ <editorlayout>generatedlayout</editorlayout>
+ <excludeAttributesWMS/>
+ <excludeAttributesWFS/>
+ <attributeactions/>
+ </maplayer>
+ </projectlayers>
+ <properties>
+ <WMSContactPerson type="QString"></WMSContactPerson>
+ <WMSOnlineResource type="QString"></WMSOnlineResource>
+ <WMSContactOrganization type="QString"></WMSContactOrganization>
+ <WMSExtent type="QStringList">
+ <value>0.82500024999999999</value>
+ <value>-0.35415386986094277</value>
+ <value>8.17498974999999994</value>
+ <value>3.45415386986094308</value>
+ </WMSExtent>
+ <WMSKeywordList type="QStringList">
+ <value></value>
+ </WMSKeywordList>
+ <WFSUrl type="QString"></WFSUrl>
+ <Paths>
+ <Absolute type="bool">false</Absolute>
+ </Paths>
+ <WMSServiceTitle type="QString">mp test</WMSServiceTitle>
+ <WFSLayers type="QStringList"/>
+ <WMSContactMail type="QString"></WMSContactMail>
+ <PositionPrecision>
+ <DecimalPlaces type="int">2</DecimalPlaces>
+ <Automatic type="bool">true</Automatic>
+ <DegreeFormat type="QString">D</DegreeFormat>
+ </PositionPrecision>
+ <WCSUrl type="QString"></WCSUrl>
+ <WMSContactPhone type="QString"></WMSContactPhone>
+ <WMSServiceCapabilities type="bool">true</WMSServiceCapabilities>
+ <WMSServiceAbstract type="QString"></WMSServiceAbstract>
+ <WMSAddWktGeometry type="bool">false</WMSAddWktGeometry>
+ <Measure>
+ <Ellipsoid type="QString">NONE</Ellipsoid>
+ </Measure>
+ <WFSTLayers>
+ <Insert type="QStringList"/>
+ <Update type="QStringList"/>
+ <Delete type="QStringList"/>
+ </WFSTLayers>
+ <Gui>
+ <SelectionColorBluePart type="int">0</SelectionColorBluePart>
+ <CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
+ <CanvasColorRedPart type="int">255</CanvasColorRedPart>
+ <SelectionColorRedPart type="int">255</SelectionColorRedPart>
+ <SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
+ <SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
+ <CanvasColorBluePart type="int">255</CanvasColorBluePart>
+ </Gui>
+ <Identify>
+ <disabledLayers type="QStringList"/>
+ </Identify>
+ <Macros>
+ <pythonCode type="QString"></pythonCode>
+ </Macros>
+ <WMSAccessConstraints type="QString"></WMSAccessConstraints>
+ <WCSLayers type="QStringList"/>
+ <SpatialRefSys>
+ <ProjectCrs type="QString">EPSG:4326</ProjectCrs>
+ </SpatialRefSys>
+ <DefaultStyles>
+ <Fill type="QString"></Fill>
+ <Line type="QString"></Line>
+ <Marker type="QString"></Marker>
+ <RandomColors type="bool">true</RandomColors>
+ <AlphaInt type="int">255</AlphaInt>
+ <ColorRamp type="QString"></ColorRamp>
+ </DefaultStyles>
+ <WMSFees type="QString"></WMSFees>
+ <WMSUrl type="QString"></WMSUrl>
+ </properties>
+</qgis>
diff --git a/test/data-tests/run-testdata-multipolygon.cmake b/test/data-tests/run-testdata-multipolygon.cmake
new file mode 100644
index 0000000..0d08f5a
--- /dev/null
+++ b/test/data-tests/run-testdata-multipolygon.cmake
@@ -0,0 +1,46 @@
+#-----------------------------------------------------------------------------
+#
+# Helper script that runs the 'multipolygon' test.
+#
+#-----------------------------------------------------------------------------
+
+# Remove files that might be left over from previous run
+file(REMOVE multipolygon.db multipolygon-tests.json)
+
+
+#-----------------------------------------------------------------------------
+#
+# Create multipolygons from test data.
+#
+#-----------------------------------------------------------------------------
+execute_process(
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/testdata-multipolygon
+ ${OSM_TESTDATA}/grid/data/all.osm
+ RESULT_VARIABLE _result
+ OUTPUT_FILE multipolygon.log
+ ERROR_FILE multipolygon.log
+)
+
+if(_result)
+ message(FATAL_ERROR "Error running testdata-multipolygon command")
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
+# Compare created multipolygons with reference data.
+#
+#-----------------------------------------------------------------------------
+execute_process(
+ COMMAND ${RUBY} ${OSM_TESTDATA}/bin/compare-areas.rb
+ ${OSM_TESTDATA}/grid/data/tests.json
+ multipolygon-tests.json
+ RESULT_VARIABLE _result
+)
+
+if(_result)
+ message(FATAL_ERROR "Error running compare-areas command")
+endif()
+
+
+#-----------------------------------------------------------------------------
diff --git a/test/data-tests/testcases/test-100.cpp b/test/data-tests/testcases/test-100.cpp
new file mode 100644
index 0000000..feafe77
--- /dev/null
+++ b/test/data-tests/testcases/test-100.cpp
@@ -0,0 +1,41 @@
+
+#include "common.hpp"
+
+class TestHandler100 : public osmium::handler::Handler {
+
+public:
+
+ TestHandler100() :
+ osmium::handler::Handler() {
+ }
+
+ void node(osmium::Node& node) {
+ if (node.id() == 100000) {
+ REQUIRE(node.version() == 1);
+ REQUIRE(node.timestamp() == osmium::Timestamp("2014-01-01T00:00:00Z"));
+ REQUIRE(node.uid() == 1);
+ REQUIRE(!strcmp(node.user(), "test"));
+ REQUIRE(node.changeset() == 1);
+ REQUIRE(node.location().lon() == 1.02);
+ REQUIRE(node.location().lat() == 1.02);
+ } else {
+ throw std::runtime_error("Unknown ID");
+ }
+ }
+
+}; // class TestHandler100
+
+TEST_CASE("100") {
+
+ SECTION("test 100") {
+ osmium::io::Reader reader(dirname + "/1/100/data.osm");
+
+ CheckBasicsHandler check_basics_handler(100, 1, 0, 0);
+ CheckWKTHandler check_wkt_handler(dirname, 100);
+ TestHandler100 test_handler;
+
+ osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler);
+ }
+
+}
+
diff --git a/test/data-tests/testcases/test-101.cpp b/test/data-tests/testcases/test-101.cpp
new file mode 100644
index 0000000..de2a5fd
--- /dev/null
+++ b/test/data-tests/testcases/test-101.cpp
@@ -0,0 +1,43 @@
+
+#include "common.hpp"
+
+class TestHandler101 : public osmium::handler::Handler {
+
+public:
+
+ TestHandler101() :
+ osmium::handler::Handler() {
+ }
+
+ void node(osmium::Node& node) {
+ if (node.id() == 101000) {
+ REQUIRE(node.version() == 1);
+ REQUIRE(node.location().lon() == 1.12);
+ REQUIRE(node.location().lat() == 1.02);
+ } else if (node.id() == 101001) {
+ REQUIRE(node.version() == 1);
+ REQUIRE(node.location().lon() == 1.12);
+ REQUIRE(node.location().lat() == 1.03);
+ } else if (node.id() == 101002) {
+ } else if (node.id() == 101003) {
+ } else {
+ throw std::runtime_error("Unknown ID");
+ }
+ }
+
+}; // class TestHandler101
+
+TEST_CASE("101") {
+
+ SECTION("test 101") {
+ osmium::io::Reader reader(dirname + "/1/101/data.osm");
+
+ CheckBasicsHandler check_basics_handler(101, 4, 0, 0);
+ CheckWKTHandler check_wkt_handler(dirname, 101);
+ TestHandler101 test_handler;
+
+ osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler);
+ }
+
+}
+
diff --git a/test/data-tests/testcases/test-110.cpp b/test/data-tests/testcases/test-110.cpp
new file mode 100644
index 0000000..16b039b
--- /dev/null
+++ b/test/data-tests/testcases/test-110.cpp
@@ -0,0 +1,58 @@
+
+#include "common.hpp"
+
+class TestHandler110 : public osmium::handler::Handler {
+
+public:
+
+ TestHandler110() :
+ osmium::handler::Handler() {
+ }
+
+ void node(const osmium::Node& node) {
+ if (node.id() == 110000) {
+ REQUIRE(node.location().lon() == 1.02);
+ REQUIRE(node.location().lat() == 1.12);
+ } else if (node.id() == 110001) {
+ REQUIRE(node.location().lon() == 1.07);
+ REQUIRE(node.location().lat() == 1.13);
+ } else {
+ throw std::runtime_error("Unknown ID");
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ if (way.id() == 110800) {
+ REQUIRE(way.version() == 1);
+ REQUIRE(way.nodes().size() == 2);
+ REQUIRE(!way.is_closed());
+
+ const char *test_id = way.tags().get_value_by_key("test:id");
+ REQUIRE(test_id);
+ REQUIRE(!strcmp(test_id, "110"));
+ } else {
+ throw std::runtime_error("Unknown ID");
+ }
+ }
+
+}; // class TestHandler110
+
+TEST_CASE("110") {
+
+ SECTION("test 110") {
+ osmium::io::Reader reader(dirname + "/1/110/data.osm");
+
+ index_pos_type index_pos;
+ index_neg_type index_neg;
+ location_handler_type location_handler(index_pos, index_neg);
+ location_handler.ignore_errors();
+
+ CheckBasicsHandler check_basics_handler(110, 2, 1, 0);
+ CheckWKTHandler check_wkt_handler(dirname, 110);
+ TestHandler110 test_handler;
+
+ osmium::apply(reader, location_handler, check_basics_handler, check_wkt_handler, test_handler);
+ }
+
+}
+
diff --git a/test/data-tests/testdata-multipolygon.cpp b/test/data-tests/testdata-multipolygon.cpp
new file mode 100644
index 0000000..0fd0d98
--- /dev/null
+++ b/test/data-tests/testdata-multipolygon.cpp
@@ -0,0 +1,291 @@
+
+#include <iostream>
+#include <fstream>
+#include <map>
+
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+#include <osmium/area/assembler.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/problem_reporter_ogr.hpp>
+#include <osmium/geom/ogr.hpp>
+#include <osmium/geom/wkt.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/io/xml_input.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+typedef osmium::handler::NodeLocationsForWays<index_type> location_handler_type;
+
+struct less_charptr {
+
+ bool operator()(const char* a, const char* b) const {
+ return std::strcmp(a, b) < 0;
+ }
+
+}; // less_charptr
+
+typedef std::map<const char*, const char*, less_charptr> tagmap_type;
+
+inline tagmap_type create_map(const osmium::TagList& taglist) {
+ tagmap_type map;
+
+ for (auto& tag : taglist) {
+ map[tag.key()] = tag.value();
+ }
+
+ return map;
+}
+
+class TestHandler : public osmium::handler::Handler {
+
+ OGRDataSource* m_data_source;
+ OGRLayer* m_layer_point;
+ OGRLayer* m_layer_linestring;
+ OGRLayer* m_layer_polygon;
+
+ osmium::geom::OGRFactory<> m_ogr_factory;
+ osmium::geom::WKTFactory<> m_wkt_factory;
+
+ std::ofstream m_out;
+
+ bool m_first_out {true};
+
+public:
+
+ TestHandler(OGRDataSource* data_source) :
+ m_data_source(data_source),
+ m_out("multipolygon-tests.json") {
+
+ OGRSpatialReference sparef;
+ sparef.SetWellKnownGeogCS("WGS84");
+
+ /**************/
+
+ m_layer_point = m_data_source->CreateLayer("points", &sparef, wkbPoint, nullptr);
+ if (!m_layer_point) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_id("id", OFTReal);
+ layer_point_field_id.SetWidth(10);
+
+ if (m_layer_point->CreateField(&layer_point_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_point_field_type("type", OFTString);
+ layer_point_field_type.SetWidth(30);
+
+ if (m_layer_point->CreateField(&layer_point_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ /**************/
+
+ m_layer_linestring = m_data_source->CreateLayer("lines", &sparef, wkbLineString, nullptr);
+ if (!m_layer_linestring) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_id("id", OFTReal);
+ layer_linestring_field_id.SetWidth(10);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_linestring_field_type("type", OFTString);
+ layer_linestring_field_type.SetWidth(30);
+
+ if (m_layer_linestring->CreateField(&layer_linestring_field_type) != OGRERR_NONE) {
+ std::cerr << "Creating type field failed.\n";
+ exit(1);
+ }
+
+ /**************/
+
+ m_layer_polygon = m_data_source->CreateLayer("multipolygons", &sparef, wkbMultiPolygon, nullptr);
+ if (!m_layer_polygon) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_id("id", OFTInteger);
+ layer_polygon_field_id.SetWidth(10);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_polygon_field_from_type("from_type", OFTString);
+ layer_polygon_field_from_type.SetWidth(1);
+
+ if (m_layer_polygon->CreateField(&layer_polygon_field_from_type) != OGRERR_NONE) {
+ std::cerr << "Creating from_type field failed.\n";
+ exit(1);
+ }
+ }
+
+ ~TestHandler() {
+ m_out << "\n]\n";
+ }
+
+ void node(const osmium::Node& node) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_point->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_ogr_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+ feature->SetField("type", node.tags().get_value_by_key("type"));
+
+ if (m_layer_point->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ }
+
+ void way(const osmium::Way& way) {
+ try {
+ std::unique_ptr<OGRLineString> ogr_linestring = m_ogr_factory.create_linestring(way);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_linestring->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id", static_cast<double>(way.id()));
+ feature->SetField("type", way.tags().get_value_by_key("type"));
+
+ if (m_layer_linestring->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
+ }
+ }
+
+ void area(const osmium::Area& area) {
+ if (m_first_out) {
+ m_out << "[\n";
+ m_first_out = false;
+ } else {
+ m_out << ",\n";
+ }
+ 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);
+ m_out << wkt << "\",\n \"tags\": {";
+
+ auto tagmap = create_map(area.tags());
+ bool first = true;
+ for (auto& tag : tagmap) {
+ if (first) {
+ first = false;
+ } else {
+ m_out << ", ";
+ }
+ m_out << '"' << tag.first << "\": \"" << tag.second << '"';
+ }
+ m_out << "}\n}";
+ } catch (osmium::geometry_error&) {
+ m_out << "INVALID\"\n}";
+ }
+ try {
+ std::unique_ptr<OGRMultiPolygon> ogr_polygon = m_ogr_factory.create_multipolygon(area);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_polygon->GetLayerDefn());
+ feature->SetGeometry(ogr_polygon.get());
+ feature->SetField("id", static_cast<int>(area.orig_id()));
+
+ std::string from_type;
+ if (area.from_way()) {
+ from_type = "w";
+ } else {
+ from_type = "r";
+ }
+ feature->SetField("from_type", from_type.c_str());
+
+ if (m_layer_polygon->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n";
+ }
+ }
+
+}; // class TestHandler
+
+/* ================================================== */
+
+OGRDataSource* initialize_database(const std::string& output_format, const std::string& output_filename) {
+ OGRRegisterAll();
+
+ OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(output_format.c_str());
+ if (!driver) {
+ std::cerr << output_format << " driver not available.\n";
+ exit(1);
+ }
+
+ CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
+ const char* options[] = { "SPATIALITE=TRUE", nullptr };
+ OGRDataSource* data_source = driver->CreateDataSource(output_filename.c_str(), const_cast<char**>(options));
+ if (!data_source) {
+ std::cerr << "Creation of output file failed.\n";
+ exit(1);
+ }
+
+ return data_source;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " INFILE\n";
+ exit(1);
+ }
+
+ std::string output_format("SQLite");
+ std::string input_filename(argv[1]);
+ std::string output_filename("multipolygon.db");
+
+ OGRDataSource* data_source = initialize_database(output_format, output_filename);
+
+ osmium::area::ProblemReporterOGR problem_reporter(data_source);
+ osmium::area::Assembler::config_type assembler_config(&problem_reporter);
+ assembler_config.enable_debug_output();
+ osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
+
+ std::cerr << "Pass 1...\n";
+ osmium::io::Reader reader1(input_filename);
+ collector.read_relations(reader1);
+ reader1.close();
+ std::cerr << "Pass 1 done\n";
+
+ index_type index;
+ location_handler_type location_handler(index);
+ location_handler.ignore_errors();
+
+ TestHandler test_handler(data_source);
+
+ std::cerr << "Pass 2...\n";
+ 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);
+ }));
+ reader2.close();
+ std::cerr << "Pass 2 done\n";
+
+ OGRDataSource::DestroyDataSource(data_source);
+ OGRCleanupAll();
+}
+
diff --git a/test/data-tests/testdata-overview.cpp b/test/data-tests/testdata-overview.cpp
new file mode 100644
index 0000000..2d63dc6
--- /dev/null
+++ b/test/data-tests/testdata-overview.cpp
@@ -0,0 +1,197 @@
+/* The code in this file is released into the Public Domain. */
+
+#include <iostream>
+
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+#include <osmium/geom/ogr.hpp>
+#include <osmium/handler.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/io/xml_input.hpp>
+#include <osmium/visitor.hpp>
+
+typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+typedef osmium::handler::NodeLocationsForWays<index_type> location_handler_type;
+
+class TestOverviewHandler : public osmium::handler::Handler {
+
+ OGRDataSource* m_data_source;
+
+ OGRLayer* m_layer_nodes;
+ OGRLayer* m_layer_labels;
+ OGRLayer* m_layer_ways;
+
+ osmium::geom::OGRFactory<> m_factory;
+
+public:
+
+ TestOverviewHandler(const std::string& driver_name, const std::string& filename) {
+
+ OGRRegisterAll();
+
+ OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str());
+ if (!driver) {
+ std::cerr << driver_name << " driver not available.\n";
+ exit(1);
+ }
+
+ CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
+ const char* options[] = { "SPATIALITE=TRUE", nullptr };
+ m_data_source = driver->CreateDataSource(filename.c_str(), const_cast<char**>(options));
+ if (!m_data_source) {
+ std::cerr << "Creation of output file failed.\n";
+ exit(1);
+ }
+
+ OGRSpatialReference sparef;
+ sparef.SetWellKnownGeogCS("WGS84");
+
+ // nodes layer
+
+ m_layer_nodes = m_data_source->CreateLayer("nodes", &sparef, wkbPoint, nullptr);
+ if (!m_layer_nodes) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_nodes_field_id("id", OFTReal);
+ layer_nodes_field_id.SetWidth(10);
+
+ if (m_layer_nodes->CreateField(&layer_nodes_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ // labels layer
+
+ m_layer_labels = m_data_source->CreateLayer("labels", &sparef, wkbPoint, nullptr);
+ if (!m_layer_labels) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_labels_field_id("id", OFTReal);
+ layer_labels_field_id.SetWidth(10);
+
+ if (m_layer_labels->CreateField(&layer_labels_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_labels_field_label("label", OFTString);
+ layer_labels_field_label.SetWidth(30);
+
+ if (m_layer_labels->CreateField(&layer_labels_field_label) != OGRERR_NONE) {
+ std::cerr << "Creating label field failed.\n";
+ exit(1);
+ }
+
+ // ways layer
+
+ m_layer_ways = m_data_source->CreateLayer("ways", &sparef, wkbLineString, nullptr);
+ if (!m_layer_ways) {
+ std::cerr << "Layer creation failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_way_field_id("id", OFTReal);
+ layer_way_field_id.SetWidth(10);
+
+ if (m_layer_ways->CreateField(&layer_way_field_id) != OGRERR_NONE) {
+ std::cerr << "Creating id field failed.\n";
+ exit(1);
+ }
+
+ OGRFieldDefn layer_way_field_test("test", OFTInteger);
+ layer_way_field_test.SetWidth(3);
+
+ if (m_layer_ways->CreateField(&layer_way_field_test) != OGRERR_NONE) {
+ std::cerr << "Creating test field failed.\n";
+ exit(1);
+ }
+ }
+
+ ~TestOverviewHandler() {
+ OGRDataSource::DestroyDataSource(m_data_source);
+ OGRCleanupAll();
+ }
+
+ void node(const osmium::Node& node) {
+ const char* label = node.tags().get_value_by_key("label");
+ if (label) {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_labels->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+ feature->SetField("label", label);
+
+ if (m_layer_labels->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } else {
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_nodes->GetLayerDefn());
+ std::unique_ptr<OGRPoint> ogr_point = m_factory.create_point(node);
+ feature->SetGeometry(ogr_point.get());
+ feature->SetField("id", static_cast<double>(node.id()));
+
+ if (m_layer_nodes->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+ OGRFeature::DestroyFeature(feature);
+ }
+ }
+
+ void way(const osmium::Way& way) {
+ try {
+ std::unique_ptr<OGRLineString> ogr_linestring = m_factory.create_linestring(way);
+ OGRFeature* feature = OGRFeature::CreateFeature(m_layer_ways->GetLayerDefn());
+ feature->SetGeometry(ogr_linestring.get());
+ feature->SetField("id", static_cast<double>(way.id()));
+
+ const char* test = way.tags().get_value_by_key("test");
+ if (test) {
+ feature->SetField("test", test);
+ }
+
+ if (m_layer_ways->CreateFeature(feature) != OGRERR_NONE) {
+ std::cerr << "Failed to create feature.\n";
+ exit(1);
+ }
+
+ OGRFeature::DestroyFeature(feature);
+ } catch (osmium::geometry_error&) {
+ std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
+ }
+ }
+
+};
+
+/* ================================================== */
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " INFILE\n";
+ exit(1);
+ }
+
+ std::string output_format("SQLite");
+ std::string input_filename(argv[1]);
+ std::string output_filename("testdata-overview.db");
+ ::unlink(output_filename.c_str());
+
+ osmium::io::Reader reader(input_filename);
+
+ index_type index;
+ location_handler_type location_handler(index);
+ location_handler.ignore_errors();
+
+ TestOverviewHandler handler(output_format, output_filename);
+
+ osmium::apply(reader, location_handler, handler);
+ reader.close();
+}
+
diff --git a/test/data-tests/testdata-testcases.cpp b/test/data-tests/testdata-testcases.cpp
new file mode 100644
index 0000000..6ed6ae9
--- /dev/null
+++ b/test/data-tests/testdata-testcases.cpp
@@ -0,0 +1,27 @@
+
+#include <iostream>
+#include <string>
+
+#define CATCH_CONFIG_RUNNER
+
+#include "testdata-testcases.hpp"
+
+#include <osmpbf/osmpbf.h>
+
+std::string dirname;
+
+int main(int argc, char* argv[]) {
+ const char* testcases_dir = getenv("TESTCASES_DIR");
+ if (testcases_dir) {
+ dirname = testcases_dir;
+ std::cerr << "Running tests from '" << dirname << "' (from TESTCASES_DIR environment variable)\n";
+ } else {
+ std::cerr << "Please set TESTCASES_DIR environment variable.\n";
+ exit(1);
+ }
+
+ int result = Catch::Session().run(argc, argv);
+
+ return result;
+}
+
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
new file mode 100644
index 0000000..94af6f9
--- /dev/null
+++ b/test/data-tests/testdata-xml.cpp
@@ -0,0 +1,368 @@
+/* The code in this file is released into the Public Domain. */
+
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+#include <osmium/io/xml_input.hpp>
+#include <osmium/io/gzip_compression.hpp>
+#include <osmium/visitor.hpp>
+
+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::string f;
+ f += testdir;
+ f += "/";
+ f += test_id;
+ f += "/data.";
+ f += suffix;
+ return f;
+}
+
+struct header_buffer_type {
+ osmium::io::Header header;
+ osmium::memory::Buffer buffer;
+};
+
+// =============================================
+
+// The following helper functions are used to call different parts of the
+// Osmium internals used to read and parse XML files. This way those parts
+// can be tested individually. These function can not be used in normal
+// operations, because they make certain assumptions, for instance that
+// 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));
+ assert(fd >= 0);
+
+ std::string input(10000, '\0');
+ auto n = ::read(fd, reinterpret_cast<unsigned char*>(const_cast<char*>(input.data())), 10000);
+ assert(n >= 0);
+ input.resize(static_cast<std::string::size_type>(n));
+
+ close(fd);
+
+ return input;
+}
+
+std::string read_gz_file(const char* test_id, const char* suffix) {
+ int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix));
+ assert(fd >= 0);
+
+ osmium::io::GzipDecompressor gzip_decompressor(fd);
+ std::string input = gzip_decompressor.read();
+ gzip_decompressor.close();
+
+ return input;
+}
+
+
+header_buffer_type parse_xml(std::string input) {
+ osmium::thread::Queue<std::string> input_queue;
+ osmium::thread::Queue<osmium::memory::Buffer> output_queue;
+ std::promise<osmium::io::Header> header_promise;
+ std::atomic<bool> done {false};
+ input_queue.push(input);
+ input_queue.push(std::string()); // EOF marker
+
+ osmium::io::detail::XMLParser parser(input_queue, output_queue, header_promise, osmium::osm_entity_bits::all, done);
+ parser();
+
+ header_buffer_type result;
+ result.header = header_promise.get_future().get();
+ output_queue.wait_and_pop(result.buffer);
+
+ if (result.buffer) {
+ osmium::memory::Buffer buffer;
+ output_queue.wait_and_pop(buffer);
+ assert(!buffer);
+ }
+
+ return result;
+}
+
+header_buffer_type read_xml(const char* test_id) {
+ std::string input = read_file(test_id);
+ return parse_xml(input);
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 100") {
+
+ SECTION("Direct") {
+ header_buffer_type r = read_xml("100-correct_but_no_data");
+
+ REQUIRE(r.header.get("generator") == "testdata");
+ REQUIRE(0 == r.buffer.committed());
+ REQUIRE(! r.buffer);
+ }
+
+ SECTION("Using Reader") {
+ osmium::io::Reader reader(filename("100-correct_but_no_data"));
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(0 == buffer.committed());
+ REQUIRE(! buffer);
+ reader.close();
+ }
+
+ SECTION("Using Reader asking for header only") {
+ osmium::io::Reader reader(filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing);
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+ reader.close();
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 101") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error);
+ try {
+ read_xml("101-missing_version");
+ } catch (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::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::format_version_error);
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 102") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error);
+ try {
+ read_xml("102-wrong_version");
+ } catch (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::Header header = reader.header();
+ osmium::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::format_version_error);
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 103") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error);
+ try {
+ read_xml("103-old_version");
+ } catch (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::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::format_version_error);
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 104") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error);
+ try {
+ read_xml("104-empty_file");
+ } catch (osmium::xml_error& e) {
+ REQUIRE(e.line == 1);
+ REQUIRE(e.column == 0);
+ }
+ }
+
+ SECTION("Using Reader") {
+ REQUIRE_THROWS_AS({
+ osmium::io::Reader reader(filename("104-empty_file"));
+ osmium::io::Header header = reader.header();
+ osmium::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::xml_error);
+ }
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 105") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS(read_xml("105-incomplete_xml_file"), osmium::xml_error);
+ }
+
+ SECTION("Using Reader") {
+ REQUIRE_THROWS_AS({
+ osmium::io::Reader reader(filename("105-incomplete_xml_file"));
+ osmium::io::Header header = reader.header();
+ osmium::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::xml_error);
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 120") {
+
+ SECTION("Direct") {
+ std::string data = read_gz_file("120-correct_gzip_file_without_data", "osm.gz");
+
+ REQUIRE(data.size() == 102);
+
+ header_buffer_type r = parse_xml(data);
+ REQUIRE(r.header.get("generator") == "testdata");
+ REQUIRE(0 == r.buffer.committed());
+ REQUIRE(! r.buffer);
+ }
+
+ SECTION("Using Reader") {
+ osmium::io::Reader reader(filename("120-correct_gzip_file_without_data", "osm.gz"));
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(0 == buffer.committed());
+ REQUIRE(! buffer);
+ reader.close();
+ }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 121") {
+
+ SECTION("Direct") {
+ REQUIRE_THROWS_AS( {
+ read_gz_file("121-truncated_gzip_file", "osm.gz");
+ }, osmium::gzip_error);
+ }
+
+#if 0
+ SECTION("Using Reader") {
+ REQUIRE_THROWS_AS({
+ osmium::io::Reader reader(filename("121-truncated_gzip_file", "osm.gz"));
+ osmium::io::Header header = reader.header();
+ osmium::memory::Buffer buffer = reader.read();
+ reader.close();
+ }, osmium::gzip_error);
+ }
+#endif
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 200") {
+
+ SECTION("Direct") {
+ header_buffer_type r = read_xml("200-nodes");
+
+ REQUIRE(r.header.get("generator") == "testdata");
+ REQUIRE(r.buffer.committed() > 0);
+ REQUIRE(r.buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
+ REQUIRE(r.buffer.get<osmium::Node>(0).id() == 36966060);
+ REQUIRE(std::distance(r.buffer.begin(), r.buffer.end()) == 3);
+ }
+
+ SECTION("Using Reader") {
+ osmium::io::Reader reader(filename("200-nodes"));
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(buffer.committed() > 0);
+ REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
+ REQUIRE(buffer.get<osmium::Node>(0).id() == 36966060);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
+ reader.close();
+ }
+
+ SECTION("Using Reader asking for nodes") {
+ osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::node);
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(buffer.committed() > 0);
+ REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
+ REQUIRE(buffer.get<osmium::Node>(0).id() == 36966060);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
+ reader.close();
+ }
+
+ SECTION("Using Reader asking for header only") {
+ osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::nothing);
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(0 == buffer.committed());
+ REQUIRE(! buffer);
+ reader.close();
+ }
+
+ SECTION("Using Reader asking for ways") {
+ osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::way);
+
+ osmium::io::Header header = reader.header();
+ REQUIRE(header.get("generator") == "testdata");
+
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(0 == buffer.committed());
+ REQUIRE(! buffer);
+ reader.close();
+ }
+
+}
+
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
new file mode 100644
index 0000000..bb87af2
--- /dev/null
+++ b/test/include/catch.hpp
@@ -0,0 +1,9003 @@
+
+// This is needed for Windows
+#define CATCH_CONFIG_CPP11_NULLPTR
+
+/*
+ * CATCH v1.0 build 53 (master branch)
+ * Generated: 2014-08-20 08:08:19.533804
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wvariadic-macros"
+#pragma clang diagnostic ignored "-Wc99-extensions"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wc++98-compat"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#elif defined __GNUC__
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpadded"
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wsign-promo"
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+# define CATCH_CONFIG_RUNNER
+#endif
+
+#ifdef CATCH_CONFIG_RUNNER
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Much of the following code is based on Boost (1.53)
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#if (__BORLANDC__ > 0x582 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#if (__EDG_VERSION__ > 238 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#if (__DMC__ > 0x840 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+
+#if (__GNUC_MINOR__ >= 96 )
+//#define CATCH_CONFIG_SFINAE
+#endif
+
+#elif __GNUC__ >= 3
+
+// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
+
+#endif // __GNUC__ < 3
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // _MSC_VER
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
+#define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// detect language version:
+#if (__cplusplus == 201103L)
+# define CATCH_CPP11
+# define CATCH_CPP11_OR_GREATER
+#elif (__cplusplus >= 201103L)
+# define CATCH_CPP11_OR_GREATER
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+namespace Catch {
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& );
+ void operator = ( NonCopyable const& );
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template<typename ContainerT>
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template<typename AssociativeContainerT>
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+ SourceLineInfo( SourceLineInfo const& other );
+# ifdef CATCH_CPP11_OR_GREATER
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+
+ std::string file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include <ostream>
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template<typename T>
+ class Ptr {
+ public:
+ Ptr() : m_p( NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() { return m_p; }
+ const T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template<typename T = IShared>
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <stdlib.h>
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr<IConfig const> getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
+
+ };
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+struct AutoReg {
+
+ AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template<typename C>
+ AutoReg( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+ registerTestCase( new MethodTestCase<C>( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ void registerTestCase( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x00,
+
+ ContinueOnFailure = 0x01, // Failures fail test, but execution continues
+ FalseTest = 0x02, // Prefix expression with !
+ SuppressFail = 0x04 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ std::string capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+ std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CPP11_OR_GREATER
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str("");
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition );
+
+ template<typename T>
+ ExpressionLhs<T const&> operator->* ( T const& operand );
+ ExpressionLhs<bool> operator->* ( bool value );
+
+ template<typename T>
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream.oss << value;
+ return *this;
+ }
+
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+ ResultBuilder& setLhs( std::string const& lhs );
+ ResultBuilder& setRhs( std::string const& rhs );
+ ResultBuilder& setOp( std::string const& op );
+
+ void endExpression();
+
+ std::string reconstructExpression() const;
+ AssertionResult build() const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+ struct ExprComponents {
+ ExprComponents() : testFalse( false ) {}
+ bool testFalse;
+ std::string lhs, rhs, op;
+ } m_exprComponents;
+ CopyableStream m_stream;
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+ template<typename T>
+ inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template<typename T1, typename T2, Operator Op>
+ class Evaluator{};
+
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return opCast( lhs ) == opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) != opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) < opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) > opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) >= opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) <= opCast( rhs );
+ }
+ };
+
+ template<Operator Op, typename T1, typename T2>
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template<Operator Op, typename T1, typename T2>
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+
+ // unsigned X to long
+ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+
+ // int to unsigned X
+ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+// #included from: catch_sfinae.hpp
+#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
+
+// Try to detect if the current compiler supports SFINAE
+
+namespace Catch {
+
+ struct TrueType {
+ static const bool value = true;
+ typedef void Enable;
+ char sizer[1];
+ };
+ struct FalseType {
+ static const bool value = false;
+ typedef void Disable;
+ char sizer[2];
+ };
+
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<bool> struct NotABooleanExpression;
+
+ template<bool c> struct If : NotABooleanExpression<c> {};
+ template<> struct If<true> : TrueType {};
+ template<> struct If<false> : FalseType {};
+
+ template<int size> struct SizedIf;
+ template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
+ template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
+
+#endif // CATCH_CONFIG_SFINAE
+
+} // end namespace Catch
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+namespace Catch {
+namespace Detail {
+
+// SFINAE is currently disabled by default for all compilers.
+// If the non SFINAE version of IsStreamInsertable is ambiguous for you
+// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<typename T>
+ class IsStreamInsertableHelper {
+ template<int N> struct TrueIfSizeable : TrueType {};
+
+ template<typename T2>
+ static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
+ static FalseType dummy(...);
+
+ public:
+ typedef SizedIf<sizeof(dummy((T*)0))> type;
+ };
+
+ template<typename T>
+ struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
+
+#else
+
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template<typename T>
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+
+#endif
+
+ template<bool C>
+ struct StringMakerBase {
+ template<typename T>
+ static std::string convert( T const& ) { return "{?}"; }
+ };
+
+ template<>
+ struct StringMakerBase<true> {
+ template<typename T>
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template<typename T>
+std::string toString( T const& value );
+
+template<typename T>
+struct StringMaker :
+ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+ template<typename U>
+ static std::string convert( U* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+template<typename T, typename Allocator>
+struct StringMaker<std::vector<T, Allocator> > {
+ static std::string convert( std::vector<T,Allocator> const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+ }
+};
+
+namespace Detail {
+ template<typename T>
+ std::string makeString( T const& value ) {
+ return StringMaker<T>::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+ return StringMaker<T>::convert( value );
+}
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+ namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << toString( *first );
+ for( ++first ; first != last ; ++first ) {
+ oss << ", " << toString( *first );
+ }
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template<typename T>
+class ExpressionLhs {
+ ExpressionLhs& operator = ( ExpressionLhs const& );
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+# endif
+
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs( ExpressionLhs const& ) = default;
+ ExpressionLhs( ExpressionLhs && ) = default;
+# endif
+
+ template<typename RhsT>
+ ResultBuilder& operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ bool value = m_lhs ? true : false;
+ m_rb
+ .setLhs( Catch::toString( value ) )
+ .setResultType( value )
+ .endExpression();
+ }
+
+ // Only simple binary expressions are allowed on the LHS.
+ // If more complex compositions are required then place the sub expression in parentheses
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ ResultBuilder& captureExpression( RhsT const& rhs ) {
+ return m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
+ .setLhs( Catch::toString( m_lhs ) )
+ .setRhs( Catch::toString( rhs ) )
+ .setOp( Internal::OperatorTraits<Op>::getName() );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template<typename T>
+ inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
+ return ExpressionLhs<T const&>( *this, operand );
+ }
+
+ inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
+ return ExpressionLhs<bool>( *this, value );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_IPHONE
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include <string>
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #ifdef DEBUG
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_BREAK_INTO_DEBUGGER() \
+ if( Catch::isDebuggerActive() ) { \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" ); \
+ }
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+ #endif
+ #endif
+
+#elif defined(_MSC_VER)
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ ( __catchResult->*expr ).endExpression(); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+ try { \
+ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+ __catchResult \
+ .setLhs( Catch::toString( arg ) ) \
+ .setRhs( matcherAsString == "{?}" ? #matcher : matcherAsString ) \
+ .setOp( "matches" ) \
+ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+ __catchResult.captureExpression(); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedNanoseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ uint64_t m_ticks;
+ };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+ class Section {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+#ifdef CATCH_CPP11_OR_GREATER
+ Section( Section const& ) = delete;
+ Section( Section && ) = delete;
+ Section& operator = ( Section const& ) = delete;
+ Section& operator = ( Section && ) = delete;
+#else
+ Section( Section const& info );
+ Section& operator = ( Section const& );
+#endif
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast<int>( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast<std::size_t>( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator<T>* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator<T>* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector<const IGenerator<T>*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template<typename T>
+ CompositeGenerator<T> between( T from, T to ) {
+ CompositeGenerator<T> generators;
+ generators.add( new BetweenGenerator<T>( from, to ) );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3 ){
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ valuesGen->add( val4 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate() const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ virtual std::string translate() const {
+ try {
+ throw;
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+ }
+
+ friend bool operator == ( Approx const& lhs, double rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ friend bool operator != ( double lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ friend bool operator != ( Approx const& lhs, double rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+ };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+ return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ExpressionT>
+ struct Matcher : SharedImpl<IShared>
+ {
+ typedef ExpressionT ExpressionType;
+
+ virtual ~Matcher() {}
+ virtual Ptr<Matcher> clone() const = 0;
+ virtual bool match( ExpressionT const& expr ) const = 0;
+ virtual std::string toString() const = 0;
+ };
+
+ template<typename DerivedT, typename ExpressionT>
+ struct MatcherImpl : Matcher<ExpressionT> {
+
+ virtual Ptr<Matcher<ExpressionT> > clone() const {
+ return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
+ }
+ };
+
+ namespace Generic {
+
+ template<typename ExpressionT>
+ class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AllOf() {}
+ AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AllOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( !m_matchers[i]->match( expr ) )
+ return false;
+ return true;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " and ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ template<typename ExpressionT>
+ class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AnyOf() {}
+ AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( m_matchers[i]->match( expr ) )
+ return true;
+ return false;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " or ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ }
+
+ namespace StdString {
+
+ inline std::string makeString( std::string const& str ) { return str; }
+ inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+ struct Equals : MatcherImpl<Equals, std::string> {
+ Equals( std::string const& str ) : m_str( str ){}
+ Equals( Equals const& other ) : m_str( other.m_str ){}
+
+ virtual ~Equals();
+
+ virtual bool match( std::string const& expr ) const {
+ return m_str == expr;
+ }
+ virtual std::string toString() const {
+ return "equals: \"" + m_str + "\"";
+ }
+
+ std::string m_str;
+ };
+
+ struct Contains : MatcherImpl<Contains, std::string> {
+ Contains( std::string const& substr ) : m_substr( substr ){}
+ Contains( Contains const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~Contains();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) != std::string::npos;
+ }
+ virtual std::string toString() const {
+ return "contains: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct StartsWith : MatcherImpl<StartsWith, std::string> {
+ StartsWith( std::string const& substr ) : m_substr( substr ){}
+ StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~StartsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == 0;
+ }
+ virtual std::string toString() const {
+ return "starts with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct EndsWith : MatcherImpl<EndsWith, std::string> {
+ EndsWith( std::string const& substr ) : m_substr( substr ){}
+ EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~EndsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == expr.size() - m_substr.size();
+ }
+ virtual std::string toString() const {
+ return "ends with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+ } // namespace StdString
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+
+ inline Impl::StdString::Equals Equals( std::string const& str ) {
+ return Impl::StdString::Equals( str );
+ }
+ inline Impl::StdString::Equals Equals( const char* str ) {
+ return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
+ }
+ inline Impl::StdString::Contains Contains( std::string const& substr ) {
+ return Impl::StdString::Contains( substr );
+ }
+ inline Impl::StdString::Contains Contains( const char* substr ) {
+ return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
+ return Impl::StdString::StartsWith( substr );
+ }
+ inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
+ return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
+ return Impl::StdString::EndsWith( substr );
+ }
+ inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
+ return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = NULL;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != NULL; }
+ bool none() const { return nullableValue == NULL; }
+
+ bool operator !() const { return nullableValue == NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T* nullableValue;
+ char storage[sizeof(T)];
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestCase;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ TestCaseInfo( TestCaseInfo const& other );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::set<std::string> tags;
+ std::set<std::string> lcaseTags;
+ std::string tagsAsString;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestCase* testCase, TestCaseInfo const& info );
+ TestCase( TestCase const& other );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ void swap( TestCase& other );
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+ TestCase& operator = ( TestCase const& other );
+
+ private:
+ Ptr<ITestCase> test;
+ };
+
+ TestCase makeTestCase( ITestCase* testCase,
+ std::string const& className,
+ std::string const& name,
+ std::string const& description,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+ at protocol OcFixture
+
+ at optional
+
+-(void) setUp;
+-(void) tearDown;
+
+ at end
+
+namespace Catch {
+
+ class OcMethod : public SharedImpl<ITestCase> {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline size_t registerTestMethods() {
+ size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( NULL, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ template<typename MatcherT>
+ struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder<Equals> {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string toString() const {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder<Contains> {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string toString() const {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder<StartsWith> {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string toString() const {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder<EndsWith> {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string toString() const {
+ return "ends with: " + Catch::toString( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_CONFIG_RUNNER
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: catch_runner.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec {
+ struct Pattern : SharedImpl<> {
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ };
+ class NamePattern : public Pattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+ NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
+ if( startsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 0, m_name.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_name == toLower( testCase.name );
+ case WildcardAtStart:
+ return endsWith( toLower( testCase.name ), m_name );
+ case WildcardAtEnd:
+ return startsWith( toLower( testCase.name ), m_name );
+ case WildcardAtBothEnds:
+ return contains( toLower( testCase.name ), m_name );
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+ throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ }
+ private:
+ std::string m_name;
+ WildcardPosition m_wildcard;
+ };
+ class TagPattern : public Pattern {
+ public:
+ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ virtual ~TagPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+ }
+ private:
+ std::string m_tag;
+ };
+ class ExcludedPattern : public Pattern {
+ public:
+ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+ virtual ~ExcludedPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ private:
+ Ptr<Pattern> m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<Ptr<Pattern> > m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const {
+ // All patterns in a filter must match for the filter to be a match
+ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
+ if( !(*it)->matches( testCase ) )
+ return false;
+ return true;
+ }
+ };
+
+ public:
+ bool hasFilters() const {
+ return !m_filters.empty();
+ }
+ bool matches( TestCaseInfo const& testCase ) const {
+ // A TestSpec matches if any filter matches
+ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+ if( it->matches( testCase ) )
+ return true;
+ return false;
+ }
+
+ private:
+ std::vector<Filter> m_filters;
+
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ visitChar( m_arg[m_pos] );
+ if( m_mode == Name )
+ addPattern<TestSpec::NamePattern>();
+ return *this;
+ }
+ TestSpec testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ private:
+ void visitChar( char c ) {
+ if( m_mode == None ) {
+ switch( c ) {
+ case ' ': return;
+ case '~': m_exclusion = true; return;
+ case '[': return startNewMode( Tag, ++m_pos );
+ case '"': return startNewMode( QuotedName, ++m_pos );
+ default: startNewMode( Name, m_pos ); break;
+ }
+ }
+ if( m_mode == Name ) {
+ if( c == ',' ) {
+ addPattern<TestSpec::NamePattern>();
+ addFilter();
+ }
+ else if( c == '[' ) {
+ if( subString() == "exclude:" )
+ m_exclusion = true;
+ else
+ addPattern<TestSpec::NamePattern>();
+ startNewMode( Tag, ++m_pos );
+ }
+ }
+ else if( m_mode == QuotedName && c == '"' )
+ addPattern<TestSpec::NamePattern>();
+ else if( m_mode == Tag && c == ']' )
+ addPattern<TestSpec::TagPattern>();
+ }
+ void startNewMode( Mode mode, std::size_t start ) {
+ m_mode = mode;
+ m_start = start;
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ if( startsWith( token, "exclude:" ) ) {
+ m_exclusion = true;
+ token = token.substr( 8 );
+ }
+ if( !token.empty() ) {
+ Ptr<TestSpec::Pattern> pattern = new T( token );
+ if( m_exclusion )
+ pattern = new TestSpec::ExcludedPattern( pattern );
+ m_currentFilter.m_patterns.push_back( pattern );
+ }
+ m_exclusion = false;
+ m_mode = None;
+ }
+ void addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+ };
+ inline TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct Verbosity { enum Level {
+ NoOutput = 0,
+ Quiet,
+ Normal
+ }; };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : IShared {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+#include <streambuf>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ class Stream {
+ public:
+ Stream();
+ Stream( std::streambuf* _streamBuf, bool _isOwned );
+ void release();
+
+ std::streambuf* streamBuf;
+
+ private:
+ bool isOwned;
+ };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct ConfigData {
+
+ ConfigData()
+ : listTests( false ),
+ listTags( false ),
+ listReporters( false ),
+ listTestNamesOnly( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ abortAfter( -1 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+
+ int abortAfter;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+
+ std::string reporterName;
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> testsOrTags;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ : m_os( std::cout.rdbuf() )
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_os( std::cout.rdbuf() )
+ {
+ if( !data.testsOrTags.empty() ) {
+ TestSpecParser parser( ITagAliasRegistry::get() );
+ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+ parser.parse( data.testsOrTags[i] );
+ m_testSpec = parser.testSpec();
+ }
+ }
+
+ virtual ~Config() {
+ m_os.rdbuf( std::cout.rdbuf() );
+ m_stream.release();
+ }
+
+ void setFilename( std::string const& filename ) {
+ m_data.outputFilename = filename;
+ }
+
+ std::string const& getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool listTests() const { return m_data.listTests; }
+ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool listTags() const { return m_data.listTags; }
+ bool listReporters() const { return m_data.listReporters; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+
+ void setStreamBuf( std::streambuf* buf ) {
+ m_os.rdbuf( buf ? buf : std::cout.rdbuf() );
+ }
+
+ void useStream( std::string const& streamName ) {
+ Stream stream = createStream( streamName );
+ setStreamBuf( stream.streamBuf );
+ m_stream.release();
+ m_stream = stream;
+ }
+
+ std::string getReporterName() const { return m_data.reporterName; }
+
+ int abortAfter() const { return m_data.abortAfter; }
+
+ TestSpec const& testSpec() const { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+ bool showInvisibles() const { return m_data.showInvisibles; }
+
+ // IConfig interface
+ virtual bool allowThrows() const { return !m_data.noThrow; }
+ virtual std::ostream& stream() const { return m_os; }
+ virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
+
+ private:
+ ConfigData m_data;
+
+ Stream m_stream;
+ mutable std::ostream m_os;
+ TestSpec m_testSpec;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <memory>
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+ struct UnpositionalTag {};
+
+ extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+ UnpositionalTag _;
+#endif
+
+ namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ using namespace Tbc;
+
+ inline bool startsWith( std::string const& str, std::string const& prefix ) {
+ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+ }
+
+ template<typename T> struct RemoveConstRef{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+ template<typename T> struct IsBool { static const bool value = false; };
+ template<> struct IsBool<bool> { static const bool value = true; };
+
+ template<typename T>
+ void convertInto( std::string const& _source, T& _dest ) {
+ std::stringstream ss;
+ ss << _source;
+ ss >> _dest;
+ if( ss.fail() )
+ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+ }
+ inline void convertInto( std::string const& _source, std::string& _dest ) {
+ _dest = _source;
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+ _dest = true;
+ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+ _dest = false;
+ else
+ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
+ }
+ inline void convertInto( bool _source, bool& _dest ) {
+ _dest = _source;
+ }
+ template<typename T>
+ inline void convertInto( bool, T& ) {
+ throw std::runtime_error( "Invalid conversion" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+# ifdef CATCH_CPP11_OR_GREATER
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+# endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual void setFlag( ConfigT& config ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ void setFlag( ConfigT& config ) const {
+ functionObj->setFlag( config );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) const {}
+ virtual void setFlag( C& ) const {}
+ virtual bool takesArg() const { return true; }
+ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+ };
+
+ template<typename C, typename M>
+ struct BoundDataMember : IArgFunction<C>{
+ BoundDataMember( M C::* _member ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ convertInto( stringValue, p.*member );
+ }
+ virtual void setFlag( C& p ) const {
+ convertInto( true, p.*member );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+ M C::* member;
+ };
+ template<typename C, typename M>
+ struct BoundUnaryMethod : IArgFunction<C>{
+ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( stringValue, value );
+ (p.*member)( value );
+ }
+ virtual void setFlag( C& p ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( true, value );
+ (p.*member)( value );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+ void (C::*member)( M );
+ };
+ template<typename C>
+ struct BoundNullaryMethod : IArgFunction<C>{
+ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ (p.*member)();
+ }
+ virtual void setFlag( C& p ) const {
+ (p.*member)();
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+ void (C::*member)();
+ };
+
+ template<typename C>
+ struct BoundUnaryFunction : IArgFunction<C>{
+ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ function( obj );
+ }
+ virtual void setFlag( C& p ) const {
+ function( p );
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+ void (*function)( C& );
+ };
+
+ template<typename C, typename T>
+ struct BoundBinaryFunction : IArgFunction<C>{
+ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( stringValue, value );
+ function( obj, value );
+ }
+ virtual void setFlag( C& obj ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( true, value );
+ function( obj, value );
+ }
+ virtual bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ struct Parser {
+ Parser() : separators( " \t=:" ) {}
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+ const std::string doubleDash = "--";
+ for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
+ parseIntoTokens( argv[i] , tokens);
+ }
+ void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
+ while( !arg.empty() ) {
+ Parser::Token token( Parser::Token::Positional, arg );
+ arg = "";
+ if( token.data[0] == '-' ) {
+ if( token.data.size() > 1 && token.data[1] == '-' ) {
+ token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
+ }
+ else {
+ token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
+ if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
+ arg = "-" + token.data.substr( 1 );
+ token.data = token.data.substr( 0, 1 );
+ }
+ }
+ }
+ if( token.type != Parser::Token::Positional ) {
+ std::size_t pos = token.data.find_first_of( separators );
+ if( pos != std::string::npos ) {
+ arg = token.data.substr( pos+1 );
+ token.data = token.data.substr( 0, pos );
+ }
+ }
+ tokens.push_back( token );
+ }
+ }
+ std::string separators;
+ };
+
+ template<typename ConfigT>
+ struct CommonArgProperties {
+ CommonArgProperties() {}
+ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+ Detail::BoundArgFunction<ConfigT> boundField;
+ std::string description;
+ std::string detail;
+ std::string placeholder; // Only value if boundField takes an arg
+
+ bool takesArg() const {
+ return !placeholder.empty();
+ }
+ void validate() const {
+ if( !boundField.isSet() )
+ throw std::logic_error( "option not bound" );
+ }
+ };
+ struct OptionArgProperties {
+ std::vector<std::string> shortNames;
+ std::string longName;
+
+ bool hasShortName( std::string const& shortName ) const {
+ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+ }
+ bool hasLongName( std::string const& _longName ) const {
+ return _longName == longName;
+ }
+ };
+ struct PositionalArgProperties {
+ PositionalArgProperties() : position( -1 ) {}
+ int position; // -1 means non-positional (floating)
+
+ bool isFixedPositional() const {
+ return position != -1;
+ }
+ };
+
+ template<typename ConfigT>
+ class CommandLine {
+
+ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+ Arg() {}
+ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+ using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+ std::string dbgName() const {
+ if( !longName.empty() )
+ return "--" + longName;
+ if( !shortNames.empty() )
+ return "-" + shortNames[0];
+ return "positional args";
+ }
+ std::string commands() const {
+ std::ostringstream oss;
+ bool first = true;
+ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+ for(; it != itEnd; ++it ) {
+ if( first )
+ first = false;
+ else
+ oss << ", ";
+ oss << "-" << *it;
+ }
+ if( !longName.empty() ) {
+ if( !first )
+ oss << ", ";
+ oss << "--" << longName;
+ }
+ if( !placeholder.empty() )
+ oss << " <" << placeholder << ">";
+ return oss.str();
+ }
+ };
+
+ // NOTE: std::auto_ptr is deprecated in c++11/c++0x
+#if defined(__cplusplus) && __cplusplus > 199711L
+ typedef std::unique_ptr<Arg> ArgAutoPtr;
+#else
+ typedef std::auto_ptr<Arg> ArgAutoPtr;
+#endif
+
+ friend void addOptName( Arg& arg, std::string const& optName )
+ {
+ if( optName.empty() )
+ return;
+ if( Detail::startsWith( optName, "--" ) ) {
+ if( !arg.longName.empty() )
+ throw std::logic_error( "Only one long opt may be specified. '"
+ + arg.longName
+ + "' already specified, now attempting to add '"
+ + optName + "'" );
+ arg.longName = optName.substr( 2 );
+ }
+ else if( Detail::startsWith( optName, "-" ) )
+ arg.shortNames.push_back( optName.substr( 1 ) );
+ else
+ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+ }
+ friend void setPositionalArg( Arg& arg, int position )
+ {
+ arg.position = position;
+ }
+
+ class ArgBuilder {
+ public:
+ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+ // Bind a non-boolean data member (requires placeholder string)
+ template<typename C, typename M>
+ void bind( M C::* field, std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+ m_arg->placeholder = placeholder;
+ }
+ // Bind a boolean data member (no placeholder required)
+ template<typename C>
+ void bind( bool C::* field ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+ }
+
+ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+ template<typename C, typename M>
+ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+ m_arg->placeholder = placeholder;
+ }
+
+ // Bind a method taking a single, boolean argument (no placeholder string required)
+ template<typename C>
+ void bind( void (C::* unaryMethod)( bool ) ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+ }
+
+ // Bind a method that takes no arguments (will be called if opt is present)
+ template<typename C>
+ void bind( void (C::* nullaryMethod)() ) {
+ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+ template<typename C>
+ void bind( void (* unaryFunction)( C& ) ) {
+ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+ template<typename C, typename T>
+ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+ m_arg->placeholder = placeholder;
+ }
+
+ ArgBuilder& describe( std::string const& description ) {
+ m_arg->description = description;
+ return *this;
+ }
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
+ return *this;
+ }
+
+ protected:
+ Arg* m_arg;
+ };
+
+ class OptBuilder : public ArgBuilder {
+ public:
+ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+ OptBuilder& operator[]( std::string const& optName ) {
+ addOptName( *ArgBuilder::m_arg, optName );
+ return *this;
+ }
+ };
+
+ public:
+
+ CommandLine()
+ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+ m_highestSpecifiedArgPosition( 0 ),
+ m_throwOnUnrecognisedTokens( false )
+ {}
+ CommandLine( CommandLine const& other )
+ : m_boundProcessName( other.m_boundProcessName ),
+ m_options ( other.m_options ),
+ m_positionalArgs( other.m_positionalArgs ),
+ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+ {
+ if( other.m_floatingArg.get() )
+ m_floatingArg = ArgAutoPtr( new Arg( *other.m_floatingArg ) );
+ }
+
+ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+ m_throwOnUnrecognisedTokens = shouldThrow;
+ return *this;
+ }
+
+ OptBuilder operator[]( std::string const& optName ) {
+ m_options.push_back( Arg() );
+ addOptName( m_options.back(), optName );
+ OptBuilder builder( &m_options.back() );
+ return builder;
+ }
+
+ ArgBuilder operator[]( int position ) {
+ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+ if( position > m_highestSpecifiedArgPosition )
+ m_highestSpecifiedArgPosition = position;
+ setPositionalArg( m_positionalArgs[position], position );
+ ArgBuilder builder( &m_positionalArgs[position] );
+ return builder;
+ }
+
+ // Invoke this with the _ instance
+ ArgBuilder operator[]( UnpositionalTag ) {
+ if( m_floatingArg.get() )
+ throw std::logic_error( "Only one unpositional argument can be added" );
+ m_floatingArg = ArgAutoPtr( new Arg() );
+ ArgBuilder builder( m_floatingArg.get() );
+ return builder;
+ }
+
+ template<typename C, typename M>
+ void bindProcessName( M C::* field ) {
+ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+ }
+ template<typename C, typename M>
+ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+ }
+
+ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+ std::size_t maxWidth = 0;
+ for( it = itBegin; it != itEnd; ++it )
+ maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+ for( it = itBegin; it != itEnd; ++it ) {
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
+ .setWidth( maxWidth+indent )
+ .setIndent( indent ) );
+ Detail::Text desc( it->description, Detail::TextAttributes()
+ .setWidth( width - maxWidth - 3 ) );
+
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
+ os << usageCol;
+
+ if( i < desc.size() && !desc[i].empty() )
+ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+ << desc[i];
+ os << "\n";
+ }
+ }
+ }
+ std::string optUsage() const {
+ std::ostringstream oss;
+ optUsage( oss );
+ return oss.str();
+ }
+
+ void argSynopsis( std::ostream& os ) const {
+ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+ if( i > 1 )
+ os << " ";
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+ if( it != m_positionalArgs.end() )
+ os << "<" << it->second.placeholder << ">";
+ else if( m_floatingArg.get() )
+ os << "<" << m_floatingArg->placeholder << ">";
+ else
+ throw std::logic_error( "non consecutive positional arguments with no floating args" );
+ }
+ // !TBD No indication of mandatory args
+ if( m_floatingArg.get() ) {
+ if( m_highestSpecifiedArgPosition > 1 )
+ os << " ";
+ os << "[<" << m_floatingArg->placeholder << "> ...]";
+ }
+ }
+ std::string argSynopsis() const {
+ std::ostringstream oss;
+ argSynopsis( oss );
+ return oss.str();
+ }
+
+ void usage( std::ostream& os, std::string const& procName ) const {
+ validate();
+ os << "usage:\n " << procName << " ";
+ argSynopsis( os );
+ if( !m_options.empty() ) {
+ os << " [options]\n\nwhere options are: \n";
+ optUsage( os, 2 );
+ }
+ os << "\n";
+ }
+ std::string usage( std::string const& procName ) const {
+ std::ostringstream oss;
+ usage( oss, procName );
+ return oss.str();
+ }
+
+ ConfigT parse( int argc, char const * const * argv ) const {
+ ConfigT config;
+ parseInto( argc, argv, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+ std::string processName = argv[0];
+ std::size_t lastSlash = processName.find_last_of( "/\\" );
+ if( lastSlash != std::string::npos )
+ processName = processName.substr( lastSlash+1 );
+ m_boundProcessName.set( config, processName );
+ std::vector<Parser::Token> tokens;
+ Parser parser;
+ parser.parseIntoTokens( argc, argv, tokens );
+ return populate( tokens, config );
+ }
+
+ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ validate();
+ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+ unusedTokens = populateFixedArgs( unusedTokens, config );
+ unusedTokens = populateFloatingArgs( unusedTokens, config );
+ return unusedTokens;
+ }
+
+ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ std::vector<std::string> errors;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+ for(; it != itEnd; ++it ) {
+ Arg const& arg = *it;
+
+ try {
+ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+ if( arg.takesArg() ) {
+ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+ errors.push_back( "Expected argument to option: " + token.data );
+ else
+ arg.boundField.set( config, tokens[++i].data );
+ }
+ else {
+ arg.boundField.setFlag( config );
+ }
+ break;
+ }
+ }
+ catch( std::exception& ex ) {
+ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+ }
+ }
+ if( it == itEnd ) {
+ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+ unusedTokens.push_back( token );
+ else if( m_throwOnUnrecognisedTokens )
+ errors.push_back( "unrecognised option: " + token.data );
+ }
+ }
+ if( !errors.empty() ) {
+ std::ostringstream oss;
+ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+ it != itEnd;
+ ++it ) {
+ if( it != errors.begin() )
+ oss << "\n";
+ oss << *it;
+ }
+ throw std::runtime_error( oss.str() );
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ int position = 1;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+ if( it != m_positionalArgs.end() )
+ it->second.boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ if( token.type == Parser::Token::Positional )
+ position++;
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ if( !m_floatingArg.get() )
+ return tokens;
+ std::vector<Parser::Token> unusedTokens;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ if( token.type == Parser::Token::Positional )
+ m_floatingArg->boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ }
+ return unusedTokens;
+ }
+
+ void validate() const
+ {
+ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+ throw std::logic_error( "No options or arguments specified" );
+
+ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
+ itEnd = m_options.end();
+ it != itEnd; ++it )
+ it->validate();
+ }
+
+ private:
+ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+ std::vector<Arg> m_options;
+ std::map<int, Arg> m_positionalArgs;
+ ArgAutoPtr m_floatingArg;
+ int m_highestSpecifiedArgPosition;
+ bool m_throwOnUnrecognisedTokens;
+ };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+
+namespace Catch {
+
+ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+ inline void abortAfterX( ConfigData& config, int x ) {
+ if( x < 1 )
+ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+ config.abortAfter = x;
+ }
+ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+
+ inline void addWarning( ConfigData& config, std::string const& _warning ) {
+ if( _warning == "NoAssertions" )
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+ else
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
+
+ }
+ inline void setVerbosity( ConfigData& config, int level ) {
+ // !TBD: accept strings?
+ config.verbosity = static_cast<Verbosity::Level>( level );
+ }
+ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+ config.showDurations = _showDurations
+ ? ShowDurations::Always
+ : ShowDurations::Never;
+ }
+ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+ std::ifstream f( _filename.c_str() );
+ if( !f.is_open() )
+ throw std::domain_error( "Unable to load input file: " + _filename );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, "#" ) )
+ addTestOrTags( config, "\"" + line + "\"," );
+ }
+ }
+
+ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+ using namespace Clara;
+ CommandLine<ConfigData> cli;
+
+ cli.bindProcessName( &ConfigData::processName );
+
+ cli["-?"]["-h"]["--help"]
+ .describe( "display usage information" )
+ .bind( &ConfigData::showHelp );
+
+ cli["-l"]["--list-tests"]
+ .describe( "list all/matching test cases" )
+ .bind( &ConfigData::listTests );
+
+ cli["-t"]["--list-tags"]
+ .describe( "list all/matching tags" )
+ .bind( &ConfigData::listTags );
+
+ cli["-s"]["--success"]
+ .describe( "include successful tests in output" )
+ .bind( &ConfigData::showSuccessfulTests );
+
+ cli["-b"]["--break"]
+ .describe( "break into debugger on failure" )
+ .bind( &ConfigData::shouldDebugBreak );
+
+ cli["-e"]["--nothrow"]
+ .describe( "skip exception tests" )
+ .bind( &ConfigData::noThrow );
+
+ cli["-i"]["--invisibles"]
+ .describe( "show invisibles (tabs, newlines)" )
+ .bind( &ConfigData::showInvisibles );
+
+ cli["-o"]["--out"]
+ .describe( "output filename" )
+ .bind( &ConfigData::outputFilename, "filename" );
+
+ cli["-r"]["--reporter"]
+// .placeholder( "name[:filename]" )
+ .describe( "reporter to use (defaults to console)" )
+ .bind( &ConfigData::reporterName, "name" );
+
+ cli["-n"]["--name"]
+ .describe( "suite name" )
+ .bind( &ConfigData::name, "name" );
+
+ cli["-a"]["--abort"]
+ .describe( "abort at first failure" )
+ .bind( &abortAfterFirst );
+
+ cli["-x"]["--abortx"]
+ .describe( "abort after x failures" )
+ .bind( &abortAfterX, "no. failures" );
+
+ cli["-w"]["--warn"]
+ .describe( "enable warnings" )
+ .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+// cli.into( &setVerbosity )
+// .describe( "level of verbosity (0=no output)" )
+// .shortOpt( "v")
+// .longOpt( "verbosity" )
+// .placeholder( "level" );
+
+ cli[_]
+ .describe( "which test or tests to use" )
+ .bind( &addTestOrTags, "test name, pattern or tags" );
+
+ cli["-d"]["--durations"]
+ .describe( "show test durations" )
+ .bind( &setShowDurations, "yes/no" );
+
+ cli["-f"]["--input-file"]
+ .describe( "load test names to run from a file" )
+ .bind( &loadTestNamesFromFile, "filename" );
+
+ // Less common commands which don't have a short form
+ cli["--list-test-names-only"]
+ .describe( "list all/matching test cases names only" )
+ .bind( &ConfigData::listTestNamesOnly );
+
+ cli["--list-reporters"]
+ .describe( "list all reporters" )
+ .bind( &ConfigData::listReporters );
+
+ return cli;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# endif
+# else
+# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+ using Tbc::Text;
+ using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+ namespace Detail {
+ struct IColourImpl;
+ }
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = Yellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = Yellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour const& other );
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ static Detail::IColourImpl* impl();
+ bool m_moved;
+ };
+
+ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+#include <assert.h>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig> m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ ReporterPreferences()
+ : shouldRedirectStdOut( false )
+ {}
+
+ bool shouldRedirectStdOut;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat() : used( false ) {}
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name ) : name( _name ) {}
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+ virtual ~AssertionStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = default;
+ AssertionStats& operator = ( AssertionStats && ) = default;
+# endif
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+ virtual ~SectionStats();
+# ifdef CATCH_CPP11_OR_GREATER
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+# endif
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+ virtual ~TestCaseStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+# endif
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+ virtual ~TestGroupStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+# endif
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ virtual ~TestRunStats();
+
+# ifndef CATCH_CPP11_OR_GREATER
+ TestRunStats( TestRunStats const& _other )
+ : runInfo( _other.runInfo ),
+ totals( _other.totals ),
+ aborting( _other.aborting )
+ {}
+# else
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+# endif
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct IStreamingReporter : IShared {
+ virtual ~IStreamingReporter();
+
+ // Implementing class must also provide the following static method:
+ // static std::string getDescription();
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+ };
+
+ struct IReporterFactory {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, IReporterFactory*> FactoryMap;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ };
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+ inline std::size_t listTests( Config const& config ) {
+
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ std::cout << "Matching test cases:\n";
+ else {
+ std::cout << "All available test cases:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::size_t matchedTests = 0;
+ TextAttributes nameAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl;
+ if( !testCaseInfo.tags.empty() )
+ std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+ }
+
+ if( !config.testSpec().hasFilters() )
+ std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
+ else
+ std::cout << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
+ return matchedTests;
+ }
+
+ inline std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( !config.testSpec().hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ std::cout << testCaseInfo.name << std::endl;
+ }
+ return matchedTests;
+ }
+
+ struct TagInfo {
+ TagInfo() : count ( 0 ) {}
+ void add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+ std::string all() const {
+ std::string out;
+ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+ it != itEnd;
+ ++it )
+ out += "[" + *it + "]";
+ return out;
+ }
+ std::set<std::string> spellings;
+ std::size_t count;
+ };
+
+ inline std::size_t listTags( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ std::cout << "Tags for matching test cases:\n";
+ else {
+ std::cout << "All available tags:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
+ tagItEnd = it->getTestCaseInfo().tags.end();
+ tagIt != tagItEnd;
+ ++tagIt ) {
+ std::string tagName = *tagIt;
+ std::string lcaseTagName = toLower( tagName );
+ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+ countItEnd = tagCounts.end();
+ countIt != countItEnd;
+ ++countIt ) {
+ std::ostringstream oss;
+ oss << " " << std::setw(2) << countIt->second.count << " ";
+ Text wrapper( countIt->second.all(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( oss.str().size() )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+ std::cout << oss.str() << wrapper << "\n";
+ }
+ std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
+ return tagCounts.size();
+ }
+
+ inline std::size_t listReporters( Config const& /*config*/ ) {
+ std::cout << "Available reports:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+ std::size_t maxNameLen = 0;
+ for(it = itBegin; it != itEnd; ++it )
+ maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+ for(it = itBegin; it != itEnd; ++it ) {
+ Text wrapper( it->second->getDescription(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( 7+maxNameLen )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+ std::cout << " "
+ << it->first
+ << ":"
+ << std::string( maxNameLen - it->first.size() + 2, ' ' )
+ << wrapper << "\n";
+ }
+ std::cout << std::endl;
+ return factories.size();
+ }
+
+ inline Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> listedCount;
+ if( config.listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( config );
+ if( config.listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+ if( config.listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( config );
+ if( config.listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters( config );
+ return listedCount;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_runner_impl.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+
+namespace Catch {
+namespace SectionTracking {
+
+ class TrackedSection {
+
+ typedef std::map<std::string, TrackedSection> TrackedSections;
+
+ public:
+ enum RunState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ Completed
+ };
+
+ TrackedSection( std::string const& name, TrackedSection* parent )
+ : m_name( name ), m_runState( NotStarted ), m_parent( parent )
+ {}
+
+ RunState runState() const { return m_runState; }
+
+ TrackedSection* findChild( std::string const& childName ) {
+ TrackedSections::iterator it = m_children.find( childName );
+ return it != m_children.end()
+ ? &it->second
+ : NULL;
+ }
+ TrackedSection* acquireChild( std::string const& childName ) {
+ if( TrackedSection* child = findChild( childName ) )
+ return child;
+ m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
+ return findChild( childName );
+ }
+ void enter() {
+ if( m_runState == NotStarted )
+ m_runState = Executing;
+ }
+ void leave() {
+ for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
+ it != itEnd;
+ ++it )
+ if( it->second.runState() != Completed ) {
+ m_runState = ExecutingChildren;
+ return;
+ }
+ m_runState = Completed;
+ }
+ TrackedSection* getParent() {
+ return m_parent;
+ }
+ bool hasChildren() const {
+ return !m_children.empty();
+ }
+
+ private:
+ std::string m_name;
+ RunState m_runState;
+ TrackedSections m_children;
+ TrackedSection* m_parent;
+
+ };
+
+ class TestCaseTracker {
+ public:
+ TestCaseTracker( std::string const& testCaseName )
+ : m_testCase( testCaseName, NULL ),
+ m_currentSection( &m_testCase ),
+ m_completedASectionThisRun( false )
+ {}
+
+ bool enterSection( std::string const& name ) {
+ TrackedSection* child = m_currentSection->acquireChild( name );
+ if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
+ return false;
+
+ m_currentSection = child;
+ m_currentSection->enter();
+ return true;
+ }
+ void leaveSection() {
+ m_currentSection->leave();
+ m_currentSection = m_currentSection->getParent();
+ assert( m_currentSection != NULL );
+ m_completedASectionThisRun = true;
+ }
+
+ bool currentSectionHasChildren() const {
+ return m_currentSection->hasChildren();
+ }
+ bool isCompleted() const {
+ return m_testCase.runState() == TrackedSection::Completed;
+ }
+
+ class Guard {
+ public:
+ Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
+ m_tracker.enterTestCase();
+ }
+ ~Guard() {
+ m_tracker.leaveTestCase();
+ }
+ private:
+ Guard( Guard const& );
+ void operator = ( Guard const& );
+ TestCaseTracker& m_tracker;
+ };
+
+ private:
+ void enterTestCase() {
+ m_currentSection = &m_testCase;
+ m_completedASectionThisRun = false;
+ m_testCase.enter();
+ }
+ void leaveTestCase() {
+ m_testCase.leave();
+ }
+
+ TrackedSection m_testCase;
+ TrackedSection* m_currentSection;
+ bool m_completedASectionThisRun;
+ };
+
+} // namespace SectionTracking
+
+using SectionTracking::TestCaseTracker;
+
+} // namespace Catch
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+ class StreamRedirect {
+
+ public:
+ StreamRedirect( std::ostream& stream, std::string& targetString )
+ : m_stream( stream ),
+ m_prevBuf( stream.rdbuf() ),
+ m_targetString( targetString )
+ {
+ stream.rdbuf( m_oss.rdbuf() );
+ }
+
+ ~StreamRedirect() {
+ m_targetString += m_oss.str();
+ m_stream.rdbuf( m_prevBuf );
+ }
+
+ private:
+ std::ostream& m_stream;
+ std::streambuf* m_prevBuf;
+ std::ostringstream m_oss;
+ std::string& m_targetString;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ RunContext( RunContext const& );
+ void operator =( RunContext const& );
+
+ public:
+
+ explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
+ : m_runInfo( config->name() ),
+ m_context( getCurrentMutableContext() ),
+ m_activeTestCase( NULL ),
+ m_config( config ),
+ m_reporter( reporter ),
+ m_prevRunner( m_context.getRunner() ),
+ m_prevResultCapture( m_context.getResultCapture() ),
+ m_prevConfig( m_context.getConfig() )
+ {
+ m_context.setRunner( this );
+ m_context.setConfig( m_config );
+ m_context.setResultCapture( this );
+ m_reporter->testRunStarting( m_runInfo );
+ }
+
+ virtual ~RunContext() {
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+ m_context.setRunner( m_prevRunner );
+ m_context.setConfig( NULL );
+ m_context.setResultCapture( m_prevResultCapture );
+ m_context.setConfig( m_prevConfig );
+ }
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+ }
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+ }
+
+ Totals runTest( TestCase const& testCase ) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting( testInfo );
+
+ m_activeTestCase = &testCase;
+ m_testCaseTracker = TestCaseTracker( testInfo.name );
+
+ do {
+ do {
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isCompleted() && !aborting() );
+ }
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = NULL;
+ m_testCaseTracker.reset();
+
+ return deltaTotals;
+ }
+
+ Ptr<IConfig const> config() const {
+ return m_config;
+ }
+
+ private: // IResultCapture
+
+ virtual void assertionEnded( AssertionResult const& result ) {
+ if( result.getResultType() == ResultWas::Ok ) {
+ m_totals.assertions.passed++;
+ }
+ else if( !result.isOk() ) {
+ m_totals.assertions.failed++;
+ }
+
+ if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+ m_messages.clear();
+
+ // Reset working state
+ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastResult = result;
+ }
+
+ virtual bool sectionStarted (
+ SectionInfo const& sectionInfo,
+ Counts& assertions
+ )
+ {
+ std::ostringstream oss;
+ oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
+
+ if( !m_testCaseTracker->enterSection( oss.str() ) )
+ return false;
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 ||
+ !m_config->warnAboutMissingAssertions() ||
+ m_testCaseTracker->currentSectionHasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
+ /*if( std::uncaught_exception() ) { // XXX Hack that makes Catch not run in loop in certain situations
+ m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
+ return;
+ }*/
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ m_testCaseTracker->leaveSection();
+
+ m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void pushScopedMessage( MessageInfo const& message ) {
+ m_messages.push_back( message );
+ }
+
+ virtual void popScopedMessage( MessageInfo const& message ) {
+ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+ }
+
+ virtual std::string getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : "";
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const {
+ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+ }
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+ m_reporter->sectionStarting( testCaseSection );
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+ TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+ Timer timer;
+ timer.start();
+ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+ StreamRedirect coutRedir( std::cout, redirectedCout );
+ StreamRedirect cerrRedir( std::cerr, redirectedCerr );
+ m_activeTestCase->invoke();
+ }
+ else {
+ m_activeTestCase->invoke();
+ }
+ duration = timer.getElapsedSeconds();
+ }
+ catch( TestFailureException& ) {
+ // This just means the test was aborted due to failure
+ }
+ catch(...) {
+ ResultBuilder exResult( m_lastAssertionInfo.macroName.c_str(),
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression.c_str(),
+ m_lastAssertionInfo.resultDisposition );
+ exResult.useActiveException();
+ }
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+ m_unfinishedSections.clear();
+ m_messages.clear();
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( testCaseInfo.okToFail() ) {
+ std::swap( assertions.failedButOk, assertions.failed );
+ m_totals.assertions.failed -= assertions.failedButOk;
+ m_totals.assertions.failedButOk += assertions.failedButOk;
+ }
+
+ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+ m_reporter->sectionEnded( testCaseSectionStats );
+ }
+
+ private:
+ struct UnfinishedSections {
+ UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
+ : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo info;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ Option<TestCaseTracker> m_testCaseTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ IRunner* m_prevRunner;
+ IResultCapture* m_prevResultCapture;
+ Ptr<IConfig const> m_prevConfig;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<UnfinishedSections> m_unfinishedSections;
+ };
+
+ IResultCapture& getResultCapture() {
+ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+ return *capture;
+ else
+ throw std::logic_error( "No result capture instance" );
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _buildNumber,
+ char const* const _branchName )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ buildNumber( _buildNumber ),
+ branchName( _branchName )
+ {}
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const buildNumber;
+ char const* const branchName;
+
+ private:
+ void operator=( Version const& );
+ };
+
+ extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ class Runner {
+
+ public:
+ Runner( Ptr<Config> const& config )
+ : m_config( config )
+ {
+ openStream();
+ makeReporter();
+ }
+
+ Totals runTests() {
+
+ RunContext context( m_config.get(), m_reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( "", 1, 1 ); // deprecated?
+
+ TestSpec testSpec = m_config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> testCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+
+ int testsRunForGroup = 0;
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it ) {
+ testsRunForGroup++;
+ if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+
+ if( context.aborting() )
+ break;
+
+ totals += context.runTest( *it );
+ m_testsAlreadyRun.insert( *it );
+ }
+ }
+ context.testGroupEnded( "", totals, 1, 1 );
+ return totals;
+ }
+
+ private:
+ void openStream() {
+ // Open output file, if specified
+ if( !m_config->getFilename().empty() ) {
+ m_ofs.open( m_config->getFilename().c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << m_config->getFilename() << "'";
+ throw std::domain_error( oss.str() );
+ }
+ m_config->setStreamBuf( m_ofs.rdbuf() );
+ }
+ }
+ void makeReporter() {
+ std::string reporterName = m_config->getReporterName().empty()
+ ? "console"
+ : m_config->getReporterName();
+
+ m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
+ if( !m_reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ private:
+ Ptr<Config> m_config;
+ std::ofstream m_ofs;
+ Ptr<IStreamingReporter> m_reporter;
+ std::set<TestCase> m_testsAlreadyRun;
+ };
+
+ class Session {
+ static bool alreadyInstantiated;
+
+ public:
+
+ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+ Session()
+ : m_cli( makeCommandLineParser() ) {
+ if( alreadyInstantiated ) {
+ std::string msg = "Only one instance of Catch::Session can ever be used";
+ std::cerr << msg << std::endl;
+ throw std::logic_error( msg );
+ }
+ alreadyInstantiated = true;
+ }
+ ~Session() {
+ Catch::cleanUp();
+ }
+
+ void showHelp( std::string const& processName ) {
+ std::cout << "\nCatch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " build "
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ std::cout << " (" << libraryVersion.branchName << " branch)";
+ std::cout << "\n";
+
+ m_cli.usage( std::cout, processName );
+ std::cout << "For more detail usage please see the project docs\n" << std::endl;
+ }
+
+ int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+ if( m_configData.showHelp )
+ showHelp( m_configData.processName );
+ m_config.reset();
+ }
+ catch( std::exception& ex ) {
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr << "\nError(s) in input:\n"
+ << Text( ex.what(), TextAttributes().setIndent(2) )
+ << "\n\n";
+ }
+ m_cli.usage( std::cout, m_configData.processName );
+ return (std::numeric_limits<int>::max)();
+ }
+ return 0;
+ }
+
+ void useConfigData( ConfigData const& _configData ) {
+ m_configData = _configData;
+ m_config.reset();
+ }
+
+ int run( int argc, char* const argv[] ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+ Runner runner( m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runner.runTests().assertions.failed );
+ }
+ catch( std::exception& ex ) {
+ std::cerr << ex.what() << std::endl;
+ return (std::numeric_limits<int>::max)();
+ }
+ }
+
+ Clara::CommandLine<ConfigData> const& cli() const {
+ return m_cli;
+ }
+ std::vector<Clara::Parser::Token> const& unusedTokens() const {
+ return m_unusedTokens;
+ }
+ ConfigData& configData() {
+ return m_configData;
+ }
+ Config& config() {
+ if( !m_config )
+ m_config = new Config( m_configData );
+ return *m_config;
+ }
+
+ private:
+ Clara::CommandLine<ConfigData> m_cli;
+ std::vector<Clara::Parser::Token> m_unusedTokens;
+ ConfigData m_configData;
+ Ptr<Config> m_config;
+ };
+
+ bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <iostream>
+
+namespace Catch {
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ TestRegistry() : m_unnamedCount( 0 ) {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name == "" ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+
+ if( m_functions.find( testCase ) == m_functions.end() ) {
+ m_functions.insert( testCase );
+ m_functionsInOrder.push_back( testCase );
+ if( !testCase.isHidden() )
+ m_nonHiddenFunctions.push_back( testCase );
+ }
+ else {
+ TestCase const& prev = *m_functions.find( testCase );
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
+ << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functionsInOrder;
+ }
+
+ virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
+ return m_nonHiddenFunctions;
+ }
+
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
+ for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
+ itEnd = m_functionsInOrder.end();
+ it != itEnd;
+ ++it ) {
+ if( testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ) )
+ matchingTestCases.push_back( *it );
+ }
+ }
+
+ private:
+
+ std::set<TestCase> m_functions;
+ std::vector<TestCase> m_functionsInOrder;
+ std::vector<TestCase> m_nonHiddenFunctions;
+ size_t m_unnamedCount;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+ public:
+
+ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+ virtual void invoke() const {
+ m_fun();
+ }
+
+ private:
+ virtual ~FreeFunctionTestCase();
+
+ TestFunction m_fun;
+ };
+
+ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+ std::string className = classOrQualifiedMethodName;
+ if( startsWith( className, "&" ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ AutoReg::~AutoReg() {}
+
+ void AutoReg::registerTestCase( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ virtual ~ReporterRegistry() {
+ deleteAllValues( m_factories );
+ }
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+
+ FactoryMap const& getFactories() const {
+ return m_factories;
+ }
+
+ private:
+ FactoryMap m_factories;
+ };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry() {
+ deleteAll( m_translators );
+ }
+
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( translator );
+ }
+
+ virtual std::string translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ throw;
+ }
+ @catch (NSException *exception) {
+ return toString( [exception description] );
+ }
+#else
+ throw;
+#endif
+ }
+ catch( TestFailureException& ) {
+ throw;
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return tryTranslators( m_translators.begin() );
+ }
+ }
+
+ std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
+ if( it == m_translators.end() )
+ return "Unknown exception";
+
+ try {
+ return (*it)->translate();
+ }
+ catch(...) {
+ return tryTranslators( it+1 );
+ }
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+ return m_exceptionTranslatorRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = NULL;
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <ostream>
+
+namespace Catch {
+
+ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+ : m_lineInfo( lineInfo ) {
+ std::ostringstream oss;
+ oss << lineInfo << ": function ";
+ oss << "not implemented";
+ m_what = oss.str();
+ }
+
+ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+ return m_what.c_str();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+ class StreamBufBase : public std::streambuf {
+ public:
+ virtual ~StreamBufBase() CATCH_NOEXCEPT;
+ };
+}
+
+#include <stdexcept>
+#include <cstdio>
+
+namespace Catch {
+
+ template<typename WriterF, size_t bufferSize=256>
+ class StreamBufImpl : public StreamBufBase {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() CATCH_NOEXCEPT {
+ sync();
+ }
+
+ private:
+ int overflow( int c ) {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ Stream::Stream()
+ : streamBuf( NULL ), isOwned( false )
+ {}
+
+ Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
+ : streamBuf( _streamBuf ), isOwned( _isOwned )
+ {}
+
+ void Stream::release() {
+ if( isOwned ) {
+ delete streamBuf;
+ streamBuf = NULL;
+ isOwned = false;
+ }
+ }
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public: // IContext
+ virtual IResultCapture* getResultCapture() {
+ return m_resultCapture;
+ }
+ virtual IRunner* getRunner() {
+ return m_runner;
+ }
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+ return getGeneratorsForCurrentTest()
+ .getGeneratorInfo( fileInfo, totalSize )
+ .getCurrentIndex();
+ }
+ virtual bool advanceGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ return generators && generators->moveNext();
+ }
+
+ virtual Ptr<IConfig const> getConfig() const {
+ return m_config;
+ }
+
+ public: // IMutableContext
+ virtual void setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
+ }
+ virtual void setRunner( IRunner* runner ) {
+ m_runner = runner;
+ }
+ virtual void setConfig( Ptr<IConfig const> const& config ) {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IGeneratorsForTest* findGeneratorsForCurrentTest() {
+ std::string testName = getResultCapture()->getCurrentTestName();
+
+ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+ m_generatorsByTestName.find( testName );
+ return it != m_generatorsByTestName.end()
+ ? it->second
+ : NULL;
+ }
+
+ IGeneratorsForTest& getGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ if( !generators ) {
+ std::string testName = getResultCapture()->getCurrentTestName();
+ generators = createGeneratorsForTest();
+ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+ }
+ return *generators;
+ }
+
+ private:
+ Ptr<IConfig const> m_config;
+ IRunner* m_runner;
+ IResultCapture* m_resultCapture;
+ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+ };
+
+ namespace {
+ Context* currentContext = NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ Stream createStream( std::string const& streamName ) {
+ if( streamName == "stdout" ) return Stream( std::cout.rdbuf(), false );
+ if( streamName == "stderr" ) return Stream( std::cerr.rdbuf(), false );
+ if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
+
+ throw std::domain_error( "Unknown stream: " + streamName );
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch { namespace Detail {
+ struct IColourImpl {
+ virtual ~IColourImpl() {}
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+}}
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public Detail::IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalAttributes = csbiInfo.wAttributes;
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+ }
+ HANDLE stdoutHandle;
+ WORD originalAttributes;
+ };
+
+ inline bool shouldUseColourForPlatform() {
+ return true;
+ }
+
+ static Detail::IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+ return &s_instance;
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public Detail::IColourImpl {
+ public:
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0:34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+ private:
+ void setColour( const char* _escapeCode ) {
+ std::cout << '\033' << _escapeCode;
+ }
+ };
+
+ inline bool shouldUseColourForPlatform() {
+ return isatty(STDOUT_FILENO);
+ }
+
+ static Detail::IColourImpl* platformColourInstance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#endif // not Windows
+
+namespace Catch {
+
+ namespace {
+ struct NoColourImpl : Detail::IColourImpl {
+ void use( Colour::Code ) {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+ static bool shouldUseColour() {
+ return shouldUseColourForPlatform() && !isDebuggerActive();
+ }
+ }
+
+ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+ void Colour::use( Code _colourCode ) {
+ impl()->use( _colourCode );
+ }
+
+ Detail::IColourImpl* Colour::impl() {
+ return shouldUseColour()
+ ? platformColourInstance()
+ : NoColourImpl::instance();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+ struct GeneratorInfo : IGeneratorInfo {
+
+ GeneratorInfo( std::size_t size )
+ : m_size( size ),
+ m_currentIndex( 0 )
+ {}
+
+ bool moveNext() {
+ if( ++m_currentIndex == m_size ) {
+ m_currentIndex = 0;
+ return false;
+ }
+ return true;
+ }
+
+ std::size_t getCurrentIndex() const {
+ return m_currentIndex;
+ }
+
+ std::size_t m_size;
+ std::size_t m_currentIndex;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class GeneratorsForTest : public IGeneratorsForTest {
+
+ public:
+ ~GeneratorsForTest() {
+ deleteAll( m_generatorsInOrder );
+ }
+
+ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+ if( it == m_generatorsByName.end() ) {
+ IGeneratorInfo* info = new GeneratorInfo( size );
+ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+ m_generatorsInOrder.push_back( info );
+ return *info;
+ }
+ return *it->second;
+ }
+
+ bool moveNext() {
+ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+ for(; it != itEnd; ++it ) {
+ if( (*it)->moveNext() )
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+ std::vector<IGeneratorInfo*> m_generatorsInOrder;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest()
+ {
+ return new GeneratorsForTest();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+ AssertionInfo::AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition )
+ {}
+
+ AssertionResult::AssertionResult() {}
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ AssertionResult::~AssertionResult() {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return "!" + m_info.capturedExpression;
+ else
+ return m_info.capturedExpression;
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName.empty() )
+ return m_info.capturedExpression;
+ else
+ return m_info.macroName + "( " + m_info.capturedExpression + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructedExpression;
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ std::string AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+namespace Catch {
+
+ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( tag == "." ||
+ tag == "hide" ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n";
+ }
+ {
+ Colour colourGuard( Colour::FileName );
+ std::cerr << _lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ TestCase makeTestCase( ITestCase* _testCase,
+ std::string const& _className,
+ std::string const& _name,
+ std::string const& _descOrTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+ // Parse out tags
+ std::set<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+ char c = _descOrTags[i];
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ enforceNotReservedTag( tag, _lineInfo );
+
+ inTag = false;
+ if( tag == "hide" || tag == "." )
+ isHidden = true;
+ else
+ tags.insert( tag );
+ tag.clear();
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ tags.insert( "hide" );
+ tags.insert( "." );
+ }
+
+ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, info );
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ tags( _tags ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ std::ostringstream oss;
+ for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
+ oss << "[" << *it << "]";
+ std::string lcaseTag = toLower( *it );
+ properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
+ lcaseTags.insert( lcaseTag );
+ }
+ tagsAsString = oss.str();
+ }
+
+ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+ : name( other.name ),
+ className( other.className ),
+ description( other.description ),
+ tags( other.tags ),
+ lcaseTags( other.lcaseTags ),
+ tagsAsString( other.tagsAsString ),
+ lineInfo( other.lineInfo ),
+ properties( other.properties )
+ {}
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+ TestCase::TestCase( TestCase const& other )
+ : TestCaseInfo( other ),
+ test( other.test )
+ {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::swap( TestCase& other ) {
+ test.swap( other.test );
+ name.swap( other.name );
+ className.swap( other.className );
+ description.swap( other.description );
+ tags.swap( other.tags );
+ lcaseTags.swap( other.lcaseTags );
+ tagsAsString.swap( other.tagsAsString );
+ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+ std::swap( lineInfo, other.lineInfo );
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+ TestCase& TestCase::operator = ( TestCase const& other ) {
+ TestCase temp( other );
+ swap( temp );
+ return *this;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+ // These numbers are maintained by a script
+ Version libraryVersion( 1, 0, 53, "master" );
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info )
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+ ScopedMessage::ScopedMessage( ScopedMessage const& other )
+ : m_info( other.m_info )
+ {}
+
+ ScopedMessage::~ScopedMessage() {
+ getResultCapture().popScopedMessage( m_info );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+ // Deprecated
+ struct IReporter : IShared {
+ virtual ~IReporter();
+
+ virtual bool shouldRedirectStdout() const = 0;
+
+ virtual void StartTesting() = 0;
+ virtual void EndTesting( Totals const& totals ) = 0;
+ virtual void StartGroup( std::string const& groupName ) = 0;
+ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+ virtual void Aborted() = 0;
+ virtual void Result( AssertionResult const& result ) = 0;
+ };
+
+ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+ {
+ public:
+ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+ virtual ~LegacyReporterAdapter();
+
+ virtual ReporterPreferences getPreferences() const;
+ virtual void noMatchingTestCases( std::string const& );
+ virtual void testRunStarting( TestRunInfo const& );
+ virtual void testGroupStarting( GroupInfo const& groupInfo );
+ virtual void testCaseStarting( TestCaseInfo const& testInfo );
+ virtual void sectionStarting( SectionInfo const& sectionInfo );
+ virtual void assertionStarting( AssertionInfo const& );
+ virtual bool assertionEnded( AssertionStats const& assertionStats );
+ virtual void sectionEnded( SectionStats const& sectionStats );
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+ virtual void testRunEnded( TestRunStats const& testRunStats );
+
+ private:
+ Ptr<IReporter> m_legacyReporter;
+ };
+}
+
+namespace Catch
+{
+ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+ : m_legacyReporter( legacyReporter )
+ {}
+ LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+ return prefs;
+ }
+
+ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+ m_legacyReporter->StartTesting();
+ }
+ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+ m_legacyReporter->StartGroup( groupInfo.name );
+ }
+ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ m_legacyReporter->StartTestCase( testInfo );
+ }
+ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+ }
+ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+ // Not on legacy interface
+ }
+
+ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+ rb << it->message;
+ rb.setResultType( ResultWas::Info );
+ AssertionResult result = rb.build();
+ m_legacyReporter->Result( result );
+ }
+ }
+ }
+ m_legacyReporter->Result( assertionStats.assertionResult );
+ return true;
+ }
+ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+ if( sectionStats.missingAssertions )
+ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+ }
+ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ m_legacyReporter->EndTestCase
+ ( testCaseStats.testInfo,
+ testCaseStats.totals,
+ testCaseStats.stdOut,
+ testCaseStats.stdErr );
+ }
+ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ if( testGroupStats.aborting )
+ m_legacyReporter->Aborted();
+ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+ }
+ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+ m_legacyReporter->EndTesting( testRunStats.totals );
+ }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ uint64_t getCurrentTicks() {
+ static uint64_t hz=0, hzo=0;
+ if (!hz) {
+ QueryPerformanceFrequency((LARGE_INTEGER*)&hz);
+ QueryPerformanceCounter((LARGE_INTEGER*)&hzo);
+ }
+ uint64_t t;
+ QueryPerformanceCounter((LARGE_INTEGER*)&t);
+ return ((t-hzo)*1000000)/hz;
+ }
+#else
+ uint64_t getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,NULL);
+ return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
+ }
+#endif
+ }
+
+ void Timer::start() {
+ m_ticks = getCurrentTicks();
+ }
+ unsigned int Timer::getElapsedNanoseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ unsigned int Timer::getElapsedMilliseconds() const {
+ return static_cast<unsigned int>((getCurrentTicks() - m_ticks)/1000);
+ }
+ double Timer::getElapsedSeconds() const {
+ return (getCurrentTicks() - m_ticks)/1000000.0;
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), ::tolower );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << " " << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << "s";
+ return os;
+ }
+
+ SourceLineInfo::SourceLineInfo() : line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
+ : file( other.file ),
+ line( other.line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file.empty();
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && file == other.file;
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << "(" << info.line << ")";
+#else
+ os << info.file << ":" << info.line;
+#endif
+ return os;
+ }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+ std::ostringstream oss;
+ oss << locationInfo << ": Internal Catch error: '" << message << "'";
+ if( alwaysTrue() )
+ throw std::logic_error( oss.str() );
+ }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description )
+ : name( _name ),
+ description( _description ),
+ lineInfo( _lineInfo )
+ {}
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+ Section::~Section() {
+ if( m_sectionIncluded )
+ getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ }
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#include <iostream>
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+
+ namespace Catch{
+
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+ std::cerr << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ } // namespace Catch
+
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ inline bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+#else
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ std::cout << text;
+ }
+ }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+ namespace {
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ union _{
+ int asInt;
+ char asChar[sizeof (int)];
+ } u;
+
+ u.asInt = 1;
+ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size )
+ {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str();
+ }
+}
+
+std::string toString( std::string const& value ) {
+ std::string s = value;
+ if( getCurrentContext().getConfig()->showInvisibles() ) {
+ for(size_t i = 0; i < s.size(); ++i ) {
+ std::string subs;
+ switch( s[i] ) {
+ case '\n': subs = "\\n"; break;
+ case '\t': subs = "\\t"; break;
+ default: break;
+ }
+ if( !subs.empty() ) {
+ s = s.substr( 0, i ) + subs + s.substr( i+1 );
+ ++i;
+ }
+ }
+ }
+ return "\"" + s + "\"";
+}
+std::string toString( std::wstring const& value ) {
+
+ std::string s;
+ s.reserve( value.size() );
+ for(size_t i = 0; i < value.size(); ++i )
+ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+ return toString( s );
+}
+
+std::string toString( const char* const value ) {
+ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+ return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+ std::ostringstream oss;
+ oss << value;
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ if( value > 8192 )
+ oss << "0x" << std::hex << value;
+ else
+ oss << value;
+ return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+ return toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ std::ostringstream oss;
+ oss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = oss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+std::string toString( const double value ) {
+ return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+ return fpToString( value, 5 ) + "f";
+}
+
+std::string toString( bool value ) {
+ return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+ return value < ' '
+ ? toString( static_cast<unsigned int>( value ) )
+ : Detail::makeString( value );
+}
+
+std::string toString( signed char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+ return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSObject* const& nsObject ) {
+ return toString( [nsObject description] );
+ }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+ ResultBuilder::ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition )
+ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false )
+ {}
+
+ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+ m_data.resultType = result;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setResultType( bool result ) {
+ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
+ m_exprComponents.lhs = lhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
+ m_exprComponents.rhs = rhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
+ m_exprComponents.op = op;
+ return *this;
+ }
+
+ void ResultBuilder::endExpression() {
+ m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
+ captureExpression();
+ }
+
+ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+ m_assertionInfo.resultDisposition = resultDisposition;
+ m_stream.oss << Catch::translateActiveException();
+ captureResult( ResultWas::ThrewException );
+ }
+
+ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+ setResultType( resultType );
+ captureExpression();
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ getResultCapture().assertionEnded( result );
+
+ if( !result.isOk() ) {
+ if( getCurrentContext().getConfig()->shouldDebugBreak() )
+ m_shouldDebugBreak = true;
+ if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal )
+ m_shouldThrow = true;
+ }
+ }
+ void ResultBuilder::react() {
+ if( m_shouldThrow )
+ throw Catch::TestFailureException();
+ }
+
+ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+ AssertionResult ResultBuilder::build() const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+
+ AssertionResultData data = m_data;
+
+ // Flip bool results if testFalse is set
+ if( m_exprComponents.testFalse ) {
+ if( data.resultType == ResultWas::Ok )
+ data.resultType = ResultWas::ExpressionFailed;
+ else if( data.resultType == ResultWas::ExpressionFailed )
+ data.resultType = ResultWas::Ok;
+ }
+
+ data.message = m_stream.oss.str();
+ data.reconstructedExpression = reconstructExpression();
+ if( m_exprComponents.testFalse ) {
+ if( m_exprComponents.op == "" )
+ data.reconstructedExpression = "!" + data.reconstructedExpression;
+ else
+ data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
+ }
+ return AssertionResult( m_assertionInfo, data );
+ }
+ std::string ResultBuilder::reconstructExpression() const {
+ if( m_exprComponents.op == "" )
+ return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+ else if( m_exprComponents.op == "matches" )
+ return m_exprComponents.lhs + " " + m_exprComponents.rhs;
+ else if( m_exprComponents.op != "!" ) {
+ if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
+ m_exprComponents.lhs.find("\n") == std::string::npos &&
+ m_exprComponents.rhs.find("\n") == std::string::npos )
+ return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
+ else
+ return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
+ }
+ else
+ return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ virtual ~TagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+ void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ static TagAliasRegistry& get();
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+#include <map>
+#include <iostream>
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return it->second;
+ else
+ return Option<TagAlias>();
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+ it != itEnd;
+ ++it ) {
+ std::size_t pos = expandedTestSpec.find( it->first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ it->second.tag +
+ expandedTestSpec.substr( pos + it->first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+ << "\tRedefined at " << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ TagAliasRegistry& TagAliasRegistry::get() {
+ static TagAliasRegistry instance;
+ return instance;
+
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+ ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ try {
+ TagAliasRegistry::get().add( alias, tag, lineInfo );
+ }
+ catch( std::exception& ex ) {
+ Colour colourGuard( Colour::Red );
+ std::cerr << ex.what() << std::endl;
+ exit(1);
+ }
+ }
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+namespace Catch {
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+
+ virtual ~StreamingReporterBase();
+
+ virtual void noMatchingTestCases( std::string const& ) {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+ currentTestCaseInfo.reset();
+ assert( m_sectionStack.empty() );
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ };
+
+ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+ template<typename T, typename ChildNodeT>
+ struct Node : SharedImpl<> {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode : SharedImpl<> {
+ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+ virtual ~SectionNode();
+
+ bool operator == ( SectionNode const& other ) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == ( Ptr<SectionNode> const& other ) const {
+ return operator==( *other );
+ }
+
+ SectionStats stats;
+ typedef std::vector<Ptr<SectionNode> > ChildSections;
+ typedef std::vector<AssertionStats> Assertions;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() ( Ptr<SectionNode> const& node ) const {
+ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+ }
+ private:
+ void operator=( BySectionInfo const& );
+ SectionInfo const& m_other;
+ };
+
+ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+ ~CumulativeReporterBase();
+
+ virtual void testRunStarting( TestRunInfo const& ) {}
+ virtual void testGroupStarting( GroupInfo const& ) {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ Ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = new SectionNode( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ SectionNode::ChildSections::const_iterator it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = new SectionNode( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = node;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+ assert( m_sectionStack.size() == 0 );
+ node->children.push_back( m_rootSection );
+ m_testCases.push_back( node );
+ m_rootSection.reset();
+
+ assert( m_deepestSection );
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) {
+ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+ node->children.swap( m_testGroups );
+ m_testRuns.push_back( node );
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+ std::vector<Ptr<TestCaseNode> > m_testCases;
+ std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+ std::vector<Ptr<TestRunNode> > m_testRuns;
+
+ Ptr<SectionNode> m_rootSection;
+ Ptr<SectionNode> m_deepestSection;
+ std::vector<Ptr<SectionNode> > m_sectionStack;
+
+ };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+ template<typename T>
+ class LegacyReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new LegacyReporterAdapter( new T( config ) );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ LegacyReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+
+ // *** Please Note ***:
+ // - If you end up here looking at a compiler error because it's trying to register
+ // your custom reporter class be aware that the native reporter interface has changed
+ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+ // However please consider updating to the new interface as the old one is now
+ // deprecated and will probably be removed quite soon!
+ // Please contact me via github if you have any questions at all about this.
+ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+ // no idea who is actually using custom reporters at all (possibly no-one!).
+ // The new interface is designed to minimise exposure to interface changes in the future.
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = NULL;
+ }
+
+ ~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ ScopedElement& writeText( std::string const& text, bool indent = true ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer;
+ };
+
+ XmlWriter()
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &std::cout )
+ {}
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &os )
+ {}
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+//# ifndef CATCH_CPP11_OR_GREATER
+// XmlWriter& operator = ( XmlWriter const& other ) {
+// XmlWriter temp( other );
+// swap( temp );
+// return *this;
+// }
+//# else
+// XmlWriter( XmlWriter const& ) = default;
+// XmlWriter( XmlWriter && ) = default;
+// XmlWriter& operator = ( XmlWriter const& ) = default;
+// XmlWriter& operator = ( XmlWriter && ) = default;
+//# endif
+//
+// void swap( XmlWriter& other ) {
+// std::swap( m_tagIsOpen, other.m_tagIsOpen );
+// std::swap( m_needsNewline, other.m_needsNewline );
+// std::swap( m_tags, other.m_tags );
+// std::swap( m_indent, other.m_indent );
+// std::swap( m_os, other.m_os );
+// }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ stream() << m_indent << "<" << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ ScopedElement scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ stream() << "/>\n";
+ m_tagIsOpen = false;
+ }
+ else {
+ stream() << m_indent << "</" << m_tags.back() << ">\n";
+ }
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() ) {
+ stream() << " " << name << "=\"";
+ writeEncodedText( attribute );
+ stream() << "\"";
+ }
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ if( !name.empty() )
+ stream() << " " << name << "=\"" << attribute << "\"";
+ return *this;
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ stream() << m_indent;
+ writeEncodedText( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ stream() << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ stream() << "\n";
+ return *this;
+ }
+
+ void setStream( std::ostream& os ) {
+ m_os = &os;
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ std::ostream& stream() {
+ return *m_os;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ stream() << ">\n";
+ m_tagIsOpen = false;
+ }
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ stream() << "\n";
+ m_needsNewline = false;
+ }
+ }
+
+ void writeEncodedText( std::string const& text ) {
+ static const char* charsToEncode = "<&\"";
+ std::string mtext = text;
+ std::string::size_type pos = mtext.find_first_of( charsToEncode );
+ while( pos != std::string::npos ) {
+ stream() << mtext.substr( 0, pos );
+
+ switch( mtext[pos] ) {
+ case '<':
+ stream() << "<";
+ break;
+ case '&':
+ stream() << "&";
+ break;
+ case '\"':
+ stream() << """;
+ break;
+ }
+ mtext = mtext.substr( pos+1 );
+ pos = mtext.find_first_of( charsToEncode );
+ }
+ stream() << mtext;
+ }
+
+ bool m_tagIsOpen;
+ bool m_needsNewline;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream* m_os;
+ };
+
+}
+namespace Catch {
+ class XmlReporter : public SharedImpl<IReporter> {
+ public:
+ XmlReporter( ReporterConfig const& config ) : m_config( config ), m_sectionDepth( 0 ) {}
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+ virtual ~XmlReporter();
+
+ private: // IReporter
+
+ virtual bool shouldRedirectStdout() const {
+ return true;
+ }
+
+ virtual void StartTesting() {
+ m_xml.setStream( m_config.stream() );
+ m_xml.startElement( "Catch" );
+ if( !m_config.fullConfig()->name().empty() )
+ m_xml.writeAttribute( "name", m_config.fullConfig()->name() );
+ }
+
+ virtual void EndTesting( const Totals& totals ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", totals.assertions.passed )
+ .writeAttribute( "failures", totals.assertions.failed )
+ .writeAttribute( "expectedFailures", totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void StartGroup( const std::string& groupName ) {
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupName );
+ }
+
+ virtual void EndGroup( const std::string&, const Totals& totals ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", totals.assertions.passed )
+ .writeAttribute( "failures", totals.assertions.failed )
+ .writeAttribute( "expectedFailures", totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void StartSection( const std::string& sectionName, const std::string& description ) {
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionName ) )
+ .writeAttribute( "description", description );
+ }
+ }
+ virtual void NoAssertionsInSection( const std::string& ) {}
+ virtual void NoAssertionsInTestCase( const std::string& ) {}
+
+ virtual void EndSection( const std::string& /*sectionName*/, const Counts& assertions ) {
+ if( --m_sectionDepth > 0 ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", assertions.passed )
+ .writeAttribute( "failures", assertions.failed )
+ .writeAttribute( "expectedFailures", assertions.failedButOk );
+ m_xml.endElement();
+ }
+ }
+
+ virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) {
+ m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
+ m_currentTestSuccess = true;
+ }
+
+ virtual void Result( const Catch::AssertionResult& assertionResult ) {
+ if( !m_config.fullConfig()->includeSuccessfulResults() && assertionResult.getResultType() == ResultWas::Ok )
+ return;
+
+ if( assertionResult.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", assertionResult.succeeded() )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( assertionResult.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( assertionResult.getExpandedExpression() );
+ m_currentTestSuccess &= assertionResult.succeeded();
+ }
+
+ switch( assertionResult.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.scopedElement( "Exception" )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line )
+ .writeText( assertionResult.getMessage() );
+ m_currentTestSuccess = false;
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::Warning:
+ m_xml.scopedElement( "Warning" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.scopedElement( "Failure" )
+ .writeText( assertionResult.getMessage() );
+ m_currentTestSuccess = false;
+ break;
+ case ResultWas::Unknown:
+ case ResultWas::Ok:
+ case ResultWas::FailureBit:
+ case ResultWas::ExpressionFailed:
+ case ResultWas::Exception:
+ case ResultWas::DidntThrowException:
+ break;
+ }
+ if( assertionResult.hasExpression() )
+ m_xml.endElement();
+ }
+
+ virtual void Aborted() {
+ // !TBD
+ }
+
+ virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) {
+ m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess );
+ m_xml.endElement();
+ }
+
+ private:
+ ReporterConfig m_config;
+ bool m_currentTestSuccess;
+ XmlWriter m_xml;
+ int m_sectionDepth;
+ };
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() )
+ {}
+
+ ~JunitReporter();
+
+ static std::string getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = true;
+ return prefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() {
+ xml.endElement();
+ }
+
+ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "timestamp", "tbd" ); // !TBD
+
+ // Write test cases
+ for( TestGroupNode::ChildNodes::const_iterator
+ it = groupNode.children.begin(), itEnd = groupNode.children.end();
+ it != itEnd;
+ ++it )
+ writeTestCase( **it );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+ }
+
+ void writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ if( rootSection.childSections.empty() )
+ className = "global";
+ }
+ writeSection( className, "", rootSection );
+ }
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode ) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + "/" + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", toString( sectionNode.stats.durationInSeconds ) );
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ }
+ for( SectionNode::ChildSections::const_iterator
+ it = sectionNode.childSections.begin(),
+ itEnd = sectionNode.childSections.end();
+ it != itEnd;
+ ++it )
+ if( className.empty() )
+ writeSection( name, "", **it );
+ else
+ writeSection( className, name, **it );
+ }
+
+ void writeAssertions( SectionNode const& sectionNode ) {
+ for( SectionNode::Assertions::const_iterator
+ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+ it != itEnd;
+ ++it )
+ writeAssertion( *it );
+ }
+ void writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ elementName = "failure";
+ break;
+ case ResultWas::ExpressionFailed:
+ elementName = "failure";
+ break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ std::ostringstream oss;
+ if( !result.getMessage().empty() )
+ oss << result.getMessage() << "\n";
+ for( std::vector<MessageInfo>::const_iterator
+ it = stats.infoMessages.begin(),
+ itEnd = stats.infoMessages.end();
+ it != itEnd;
+ ++it )
+ if( it->type == ResultWas::Info )
+ oss << it->message << "\n";
+
+ oss << "at " << result.getSourceInfo();
+ xml.writeText( oss.str(), false );
+ }
+ }
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::ostringstream stdOutForSuite;
+ std::ostringstream stdErrForSuite;
+ unsigned int unexpectedExceptions;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cstring>
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter();
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) {
+ if( _sectionStats.missingAssertions ) {
+ lazyPrint();
+ Colour colour( Colour::ResultError );
+ if( m_sectionStack.size() > 1 )
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ if( m_headerPrinted ) {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ m_headerPrinted = false;
+ }
+ else {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+ if( currentGroupInfo.used ) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals( _testGroupStats.totals );
+ stream << "\n" << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded( _testGroupStats );
+ }
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotalsDivider( _testRunStats.totals );
+ printTotals( _testRunStats.totals );
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream ),
+ stats( _stats ),
+ result( _stats.assertionResult ),
+ colour( Colour::None ),
+ message( result.getMessage() ),
+ messages( _stats.infoMessages ),
+ printInfoMessages( _printInfoMessages )
+ {
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() ) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ }
+ else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with message";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "explicitly with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if( stats.totals.assertions.total() > 0 ) {
+ if( result.isOk() )
+ stream << "\n";
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ }
+ else {
+ stream << "\n";
+ }
+ printMessage();
+ }
+
+ private:
+ void printResultType() const {
+ if( !passOrFail.empty() ) {
+ Colour colourGuard( colour );
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ Colour colourGuard( Colour::OriginalExpression );
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << "\n";
+ }
+ }
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ stream << "with expansion:\n";
+ Colour colourGuard( Colour::ReconstructedExpression );
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printMessage() const {
+ if( !messageLabel.empty() )
+ stream << messageLabel << ":" << "\n";
+ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+ it != itEnd;
+ ++it ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || it->type != ResultWas::Info )
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+ };
+
+ void lazyPrint() {
+
+ if( !currentTestRunInfo.used )
+ lazyPrintRunInfo();
+ if( !currentGroupInfo.used )
+ lazyPrintGroupInfo();
+
+ if( !m_headerPrinted ) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+ }
+ void lazyPrintRunInfo() {
+ stream << "\n" << getLineOfChars<'~'>() << "\n";
+ Colour colour( Colour::SecondaryText );
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " b"
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ stream << " (" << libraryVersion.branchName << ")";
+ stream << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ currentTestRunInfo.used = true;
+ }
+ void lazyPrintGroupInfo() {
+ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+ printClosedHeader( "Group: " + currentGroupInfo->name );
+ currentGroupInfo.used = true;
+ }
+ }
+ void printTestCaseAndSectionHeader() {
+ assert( !m_sectionStack.empty() );
+ printOpenHeader( currentTestCaseInfo->name );
+
+ if( m_sectionStack.size() > 1 ) {
+ Colour colourGuard( Colour::Headers );
+
+ std::vector<SectionInfo>::const_iterator
+ it = m_sectionStack.begin()+1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for( ; it != itEnd; ++it )
+ printHeaderString( it->name, 2 );
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+ if( !lineInfo.empty() ){
+ stream << getLineOfChars<'-'>() << "\n";
+ Colour colourGuard( Colour::FileName );
+ stream << lineInfo << "\n";
+ }
+ stream << getLineOfChars<'.'>() << "\n" << std::endl;
+ }
+
+ void printClosedHeader( std::string const& _name ) {
+ printOpenHeader( _name );
+ stream << getLineOfChars<'.'>() << "\n";
+ }
+ void printOpenHeader( std::string const& _name ) {
+ stream << getLineOfChars<'-'>() << "\n";
+ {
+ Colour colourGuard( Colour::Headers );
+ printHeaderString( _name );
+ }
+ }
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+ std::size_t i = _string.find( ": " );
+ if( i != std::string::npos )
+ i+=2;
+ else
+ i = 0;
+ stream << Text( _string, TextAttributes()
+ .setIndent( indent+i)
+ .setInitialIndent( indent ) ) << "\n";
+ }
+
+ struct SummaryColumn {
+
+ SummaryColumn( std::string const& _label, Colour::Code _colour )
+ : label( _label ),
+ colour( _colour )
+ {}
+ SummaryColumn addRow( std::size_t count ) {
+ std::ostringstream oss;
+ oss << count;
+ std::string row = oss.str();
+ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+ while( it->size() < row.size() )
+ *it = " " + *it;
+ while( it->size() > row.size() )
+ row = " " + row;
+ }
+ rows.push_back( row );
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+ };
+
+ void printTotals( Totals const& totals ) {
+ if( totals.testCases.total() == 0 ) {
+ stream << Colour( Colour::Warning ) << "No tests ran\n";
+ }
+ else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+ stream << " ("
+ << pluralise( totals.assertions.passed, "assertion" ) << " in "
+ << pluralise( totals.testCases.passed, "test case" ) << ")"
+ << "\n";
+ }
+ else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back( SummaryColumn( "", Colour::None )
+ .addRow( totals.testCases.total() )
+ .addRow( totals.assertions.total() ) );
+ columns.push_back( SummaryColumn( "passed", Colour::Success )
+ .addRow( totals.testCases.passed )
+ .addRow( totals.assertions.passed ) );
+ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+ .addRow( totals.testCases.failed )
+ .addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+ .addRow( totals.testCases.failedButOk )
+ .addRow( totals.assertions.failedButOk ) );
+
+ printSummaryRow( "test cases", columns, 0 );
+ printSummaryRow( "assertions", columns, 1 );
+ }
+ }
+ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+ std::string value = it->rows[row];
+ if( it->label.empty() ) {
+ stream << label << ": ";
+ if( value != "0" )
+ stream << value;
+ else
+ stream << Colour( Colour::Warning ) << "- none -";
+ }
+ else if( value != "0" ) {
+ stream << Colour( Colour::LightGrey ) << " | ";
+ stream << Colour( it->colour )
+ << value << " " << it->label;
+ }
+ }
+ stream << "\n";
+ }
+
+ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+ }
+ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+ if( i > j && i > k )
+ return i;
+ else if( j > k )
+ return j;
+ else
+ return k;
+ }
+
+ void printTotalsDivider( Totals const& totals ) {
+ if( totals.testCases.total() > 0 ) {
+ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )++;
+ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+ if( totals.testCases.allPassed() )
+ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+ else
+ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+ }
+ else {
+ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+ }
+ stream << "\n";
+ }
+ void printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << "\n";
+ }
+ template<char C>
+ static char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ private:
+ bool m_headerPrinted;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase {
+
+ CompactReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual ~CompactReporter();
+
+ static std::string getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( _testRunStats.totals );
+ stream << "\n" << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream )
+ , stats( _stats )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( _printInfoMessages )
+ {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( Colour::ResultSuccess, passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() )
+ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+ else
+ printResultType( Colour::Error, failedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( Colour::None, "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( Colour::None, "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "explicitly" );
+ printRemainingMessages( Colour::None );
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType( Colour::Error, "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ // Colour::LightGrey
+
+ static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+ static const char* failedString() { return "FAILED"; }
+ static const char* passedString() { return "PASSED"; }
+#else
+ static const char* failedString() { return "failed"; }
+ static const char* passedString() { return "passed"; }
+#endif
+
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ":";
+ }
+
+ void printResultType( Colour::Code colour, std::string passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << " " << passOrFail;
+ }
+ stream << ":";
+ }
+ }
+
+ void printIssue( std::string issue ) const {
+ stream << " " << issue;
+ }
+
+ void printExpressionWas() {
+ if( result.hasExpression() ) {
+ stream << ";";
+ {
+ Colour colour( dimColour() );
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ stream << " " << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ {
+ Colour colour( dimColour() );
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if ( itMessage != messages.end() ) {
+ stream << " '" << itMessage->message << "'";
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages( Colour::Code colour = dimColour() ) {
+ if ( itMessage == messages.end() )
+ return;
+
+ // using messages.end() directly yields compilation error:
+ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+ {
+ Colour colourGuard( colour );
+ stream << " with " << pluralise( N, "message" ) << ":";
+ }
+
+ for(; itMessage != itEnd; ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+ stream << " '" << itMessage->message << "'";
+ if ( ++itMessage != itEnd ) {
+ Colour colourGuard( dimColour() );
+ stream << " and";
+ }
+ }
+ }
+ }
+
+ private:
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+ };
+
+ // Colour, message variants:
+ // - white: No tests ran.
+ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+ // - white: Passed [both/all] N test cases (no assertions).
+ // - red: Failed N tests cases, failed M assertions.
+ // - green: Passed [both/all] N tests cases with M assertions.
+
+ std::string bothOrAll( std::size_t count ) const {
+ return count == 1 ? "" : count == 2 ? "both " : "all " ;
+ }
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "No tests ran.";
+ }
+ else if( totals.testCases.failed == totals.testCases.total() ) {
+ Colour colour( Colour::ResultError );
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll( totals.assertions.failed ) : "";
+ stream <<
+ "Failed " << bothOrAll( totals.testCases.failed )
+ << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else if( totals.assertions.total() == 0 ) {
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.total() )
+ << pluralise( totals.testCases.total(), "test case" )
+ << " (no assertions).";
+ }
+ else if( totals.assertions.failed ) {
+ Colour colour( Colour::ResultError );
+ stream <<
+ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else {
+ Colour colour( Colour::ResultSuccess );
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.passed )
+ << pluralise( totals.testCases.passed, "test case" ) <<
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
+ }
+ }
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+ IContext::~IContext() {}
+ IResultCapture::~IResultCapture() {}
+ ITestCase::~ITestCase() {}
+ ITestCaseRegistry::~ITestCaseRegistry() {}
+ IRegistryHub::~IRegistryHub() {}
+ IMutableRegistryHub::~IMutableRegistryHub() {}
+ IExceptionTranslator::~IExceptionTranslator() {}
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+ IReporter::~IReporter() {}
+ IReporterFactory::~IReporterFactory() {}
+ IReporterRegistry::~IReporterRegistry() {}
+ IStreamingReporter::~IStreamingReporter() {}
+ AssertionStats::~AssertionStats() {}
+ SectionStats::~SectionStats() {}
+ TestCaseStats::~TestCaseStats() {}
+ TestGroupStats::~TestGroupStats() {}
+ TestRunStats::~TestRunStats() {}
+ CumulativeReporterBase::SectionNode::~SectionNode() {}
+ CumulativeReporterBase::~CumulativeReporterBase() {}
+
+ StreamingReporterBase::~StreamingReporterBase() {}
+ ConsoleReporter::~ConsoleReporter() {}
+ CompactReporter::~CompactReporter() {}
+ IRunner::~IRunner() {}
+ IMutableContext::~IMutableContext() {}
+ IConfig::~IConfig() {}
+ XmlReporter::~XmlReporter() {}
+ JunitReporter::~JunitReporter() {}
+ TestRegistry::~TestRegistry() {}
+ FreeFunctionTestCase::~FreeFunctionTestCase() {}
+ IGeneratorInfo::~IGeneratorInfo() {}
+ IGeneratorsForTest::~IGeneratorsForTest() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+ Matchers::Impl::StdString::Equals::~Equals() {}
+ Matchers::Impl::StdString::Contains::~Contains() {}
+ Matchers::Impl::StdString::StartsWith::~StartsWith() {}
+ Matchers::Impl::StdString::EndsWith::~EndsWith() {}
+
+ void Config::dummy() {}
+
+ INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter )
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * const argv[]) {
+ return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return result;
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
+#else
+ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
+#else
+ #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc ) SECTION( " Given: " desc, "" )
+#define WHEN( desc ) SECTION( " When: " desc, "" )
+#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
+#define THEN( desc ) SECTION( " Then: " desc, "" )
+#define AND_THEN( desc ) SECTION( " And: " desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#elif defined __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/test/include/catch_orig.hpp b/test/include/catch_orig.hpp
new file mode 100644
index 0000000..6b8dfb5
--- /dev/null
+++ b/test/include/catch_orig.hpp
@@ -0,0 +1,8997 @@
+/*
+ * CATCH v1.0 build 53 (master branch)
+ * Generated: 2014-08-20 08:08:19.533804
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wvariadic-macros"
+#pragma clang diagnostic ignored "-Wc99-extensions"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wc++98-compat"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#elif defined __GNUC__
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+# define CATCH_CONFIG_RUNNER
+#endif
+
+#ifdef CATCH_CONFIG_RUNNER
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Much of the following code is based on Boost (1.53)
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#if (__BORLANDC__ > 0x582 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#if (__EDG_VERSION__ > 238 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#if (__DMC__ > 0x840 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+
+#if (__GNUC_MINOR__ >= 96 )
+//#define CATCH_CONFIG_SFINAE
+#endif
+
+#elif __GNUC__ >= 3
+
+// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
+
+#endif // __GNUC__ < 3
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // _MSC_VER
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
+#define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// detect language version:
+#if (__cplusplus == 201103L)
+# define CATCH_CPP11
+# define CATCH_CPP11_OR_GREATER
+#elif (__cplusplus >= 201103L)
+# define CATCH_CPP11_OR_GREATER
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+namespace Catch {
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& );
+ void operator = ( NonCopyable const& );
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template<typename ContainerT>
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template<typename AssociativeContainerT>
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+ SourceLineInfo( SourceLineInfo const& other );
+# ifdef CATCH_CPP11_OR_GREATER
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+
+ std::string file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include <ostream>
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template<typename T>
+ class Ptr {
+ public:
+ Ptr() : m_p( NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() { return m_p; }
+ const T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template<typename T = IShared>
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <stdlib.h>
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr<IConfig const> getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
+
+ };
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+struct AutoReg {
+
+ AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template<typename C>
+ AutoReg( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+ registerTestCase( new MethodTestCase<C>( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ void registerTestCase( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x00,
+
+ ContinueOnFailure = 0x01, // Failures fail test, but execution continues
+ FalseTest = 0x02, // Prefix expression with !
+ SuppressFail = 0x04 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ std::string capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+ std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CPP11_OR_GREATER
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str("");
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition );
+
+ template<typename T>
+ ExpressionLhs<T const&> operator->* ( T const& operand );
+ ExpressionLhs<bool> operator->* ( bool value );
+
+ template<typename T>
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream.oss << value;
+ return *this;
+ }
+
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+ ResultBuilder& setLhs( std::string const& lhs );
+ ResultBuilder& setRhs( std::string const& rhs );
+ ResultBuilder& setOp( std::string const& op );
+
+ void endExpression();
+
+ std::string reconstructExpression() const;
+ AssertionResult build() const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+ struct ExprComponents {
+ ExprComponents() : testFalse( false ) {}
+ bool testFalse;
+ std::string lhs, rhs, op;
+ } m_exprComponents;
+ CopyableStream m_stream;
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+ template<typename T>
+ inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template<typename T1, typename T2, Operator Op>
+ class Evaluator{};
+
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return opCast( lhs ) == opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) != opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) < opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) > opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) >= opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) <= opCast( rhs );
+ }
+ };
+
+ template<Operator Op, typename T1, typename T2>
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template<Operator Op, typename T1, typename T2>
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+
+ // unsigned X to long
+ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+
+ // int to unsigned X
+ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+// #included from: catch_sfinae.hpp
+#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
+
+// Try to detect if the current compiler supports SFINAE
+
+namespace Catch {
+
+ struct TrueType {
+ static const bool value = true;
+ typedef void Enable;
+ char sizer[1];
+ };
+ struct FalseType {
+ static const bool value = false;
+ typedef void Disable;
+ char sizer[2];
+ };
+
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<bool> struct NotABooleanExpression;
+
+ template<bool c> struct If : NotABooleanExpression<c> {};
+ template<> struct If<true> : TrueType {};
+ template<> struct If<false> : FalseType {};
+
+ template<int size> struct SizedIf;
+ template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
+ template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
+
+#endif // CATCH_CONFIG_SFINAE
+
+} // end namespace Catch
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+namespace Catch {
+namespace Detail {
+
+// SFINAE is currently disabled by default for all compilers.
+// If the non SFINAE version of IsStreamInsertable is ambiguous for you
+// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<typename T>
+ class IsStreamInsertableHelper {
+ template<int N> struct TrueIfSizeable : TrueType {};
+
+ template<typename T2>
+ static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
+ static FalseType dummy(...);
+
+ public:
+ typedef SizedIf<sizeof(dummy((T*)0))> type;
+ };
+
+ template<typename T>
+ struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
+
+#else
+
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template<typename T>
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+
+#endif
+
+ template<bool C>
+ struct StringMakerBase {
+ template<typename T>
+ static std::string convert( T const& ) { return "{?}"; }
+ };
+
+ template<>
+ struct StringMakerBase<true> {
+ template<typename T>
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template<typename T>
+std::string toString( T const& value );
+
+template<typename T>
+struct StringMaker :
+ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+ template<typename U>
+ static std::string convert( U* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+template<typename T, typename Allocator>
+struct StringMaker<std::vector<T, Allocator> > {
+ static std::string convert( std::vector<T,Allocator> const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+ }
+};
+
+namespace Detail {
+ template<typename T>
+ std::string makeString( T const& value ) {
+ return StringMaker<T>::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+ return StringMaker<T>::convert( value );
+}
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+ namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << toString( *first );
+ for( ++first ; first != last ; ++first ) {
+ oss << ", " << toString( *first );
+ }
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template<typename T>
+class ExpressionLhs {
+ ExpressionLhs& operator = ( ExpressionLhs const& );
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+# endif
+
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs( ExpressionLhs const& ) = default;
+ ExpressionLhs( ExpressionLhs && ) = default;
+# endif
+
+ template<typename RhsT>
+ ResultBuilder& operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ bool value = m_lhs ? true : false;
+ m_rb
+ .setLhs( Catch::toString( value ) )
+ .setResultType( value )
+ .endExpression();
+ }
+
+ // Only simple binary expressions are allowed on the LHS.
+ // If more complex compositions are required then place the sub expression in parentheses
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ ResultBuilder& captureExpression( RhsT const& rhs ) {
+ return m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
+ .setLhs( Catch::toString( m_lhs ) )
+ .setRhs( Catch::toString( rhs ) )
+ .setOp( Internal::OperatorTraits<Op>::getName() );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template<typename T>
+ inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
+ return ExpressionLhs<T const&>( *this, operand );
+ }
+
+ inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
+ return ExpressionLhs<bool>( *this, value );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_IPHONE
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include <string>
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #ifdef DEBUG
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_BREAK_INTO_DEBUGGER() \
+ if( Catch::isDebuggerActive() ) { \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" ); \
+ }
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+ #endif
+ #endif
+
+#elif defined(_MSC_VER)
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ ( __catchResult->*expr ).endExpression(); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+ try { \
+ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+ __catchResult \
+ .setLhs( Catch::toString( arg ) ) \
+ .setRhs( matcherAsString == "{?}" ? #matcher : matcherAsString ) \
+ .setOp( "matches" ) \
+ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+ __catchResult.captureExpression(); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedNanoseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ uint64_t m_ticks;
+ };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+ class Section {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+#ifdef CATCH_CPP11_OR_GREATER
+ Section( Section const& ) = delete;
+ Section( Section && ) = delete;
+ Section& operator = ( Section const& ) = delete;
+ Section& operator = ( Section && ) = delete;
+#else
+ Section( Section const& info );
+ Section& operator = ( Section const& );
+#endif
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast<int>( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast<std::size_t>( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator<T>* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator<T>* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector<const IGenerator<T>*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template<typename T>
+ CompositeGenerator<T> between( T from, T to ) {
+ CompositeGenerator<T> generators;
+ generators.add( new BetweenGenerator<T>( from, to ) );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3 ){
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ valuesGen->add( val4 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate() const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ virtual std::string translate() const {
+ try {
+ throw;
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+ }
+
+ friend bool operator == ( Approx const& lhs, double rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ friend bool operator != ( double lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ friend bool operator != ( Approx const& lhs, double rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+ };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+ return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ExpressionT>
+ struct Matcher : SharedImpl<IShared>
+ {
+ typedef ExpressionT ExpressionType;
+
+ virtual ~Matcher() {}
+ virtual Ptr<Matcher> clone() const = 0;
+ virtual bool match( ExpressionT const& expr ) const = 0;
+ virtual std::string toString() const = 0;
+ };
+
+ template<typename DerivedT, typename ExpressionT>
+ struct MatcherImpl : Matcher<ExpressionT> {
+
+ virtual Ptr<Matcher<ExpressionT> > clone() const {
+ return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
+ }
+ };
+
+ namespace Generic {
+
+ template<typename ExpressionT>
+ class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AllOf() {}
+ AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AllOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( !m_matchers[i]->match( expr ) )
+ return false;
+ return true;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " and ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ template<typename ExpressionT>
+ class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AnyOf() {}
+ AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( m_matchers[i]->match( expr ) )
+ return true;
+ return false;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " or ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ }
+
+ namespace StdString {
+
+ inline std::string makeString( std::string const& str ) { return str; }
+ inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+ struct Equals : MatcherImpl<Equals, std::string> {
+ Equals( std::string const& str ) : m_str( str ){}
+ Equals( Equals const& other ) : m_str( other.m_str ){}
+
+ virtual ~Equals();
+
+ virtual bool match( std::string const& expr ) const {
+ return m_str == expr;
+ }
+ virtual std::string toString() const {
+ return "equals: \"" + m_str + "\"";
+ }
+
+ std::string m_str;
+ };
+
+ struct Contains : MatcherImpl<Contains, std::string> {
+ Contains( std::string const& substr ) : m_substr( substr ){}
+ Contains( Contains const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~Contains();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) != std::string::npos;
+ }
+ virtual std::string toString() const {
+ return "contains: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct StartsWith : MatcherImpl<StartsWith, std::string> {
+ StartsWith( std::string const& substr ) : m_substr( substr ){}
+ StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~StartsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == 0;
+ }
+ virtual std::string toString() const {
+ return "starts with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct EndsWith : MatcherImpl<EndsWith, std::string> {
+ EndsWith( std::string const& substr ) : m_substr( substr ){}
+ EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~EndsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == expr.size() - m_substr.size();
+ }
+ virtual std::string toString() const {
+ return "ends with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+ } // namespace StdString
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+
+ inline Impl::StdString::Equals Equals( std::string const& str ) {
+ return Impl::StdString::Equals( str );
+ }
+ inline Impl::StdString::Equals Equals( const char* str ) {
+ return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
+ }
+ inline Impl::StdString::Contains Contains( std::string const& substr ) {
+ return Impl::StdString::Contains( substr );
+ }
+ inline Impl::StdString::Contains Contains( const char* substr ) {
+ return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
+ return Impl::StdString::StartsWith( substr );
+ }
+ inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
+ return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
+ return Impl::StdString::EndsWith( substr );
+ }
+ inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
+ return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = NULL;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != NULL; }
+ bool none() const { return nullableValue == NULL; }
+
+ bool operator !() const { return nullableValue == NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T* nullableValue;
+ char storage[sizeof(T)];
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestCase;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ TestCaseInfo( TestCaseInfo const& other );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::set<std::string> tags;
+ std::set<std::string> lcaseTags;
+ std::string tagsAsString;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestCase* testCase, TestCaseInfo const& info );
+ TestCase( TestCase const& other );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ void swap( TestCase& other );
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+ TestCase& operator = ( TestCase const& other );
+
+ private:
+ Ptr<ITestCase> test;
+ };
+
+ TestCase makeTestCase( ITestCase* testCase,
+ std::string const& className,
+ std::string const& name,
+ std::string const& description,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+ at protocol OcFixture
+
+ at optional
+
+-(void) setUp;
+-(void) tearDown;
+
+ at end
+
+namespace Catch {
+
+ class OcMethod : public SharedImpl<ITestCase> {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline size_t registerTestMethods() {
+ size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( NULL, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ template<typename MatcherT>
+ struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder<Equals> {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string toString() const {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder<Contains> {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string toString() const {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder<StartsWith> {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string toString() const {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder<EndsWith> {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string toString() const {
+ return "ends with: " + Catch::toString( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_CONFIG_RUNNER
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: catch_runner.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec {
+ struct Pattern : SharedImpl<> {
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ };
+ class NamePattern : public Pattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+ NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
+ if( startsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 0, m_name.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_name == toLower( testCase.name );
+ case WildcardAtStart:
+ return endsWith( toLower( testCase.name ), m_name );
+ case WildcardAtEnd:
+ return startsWith( toLower( testCase.name ), m_name );
+ case WildcardAtBothEnds:
+ return contains( toLower( testCase.name ), m_name );
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+ throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ }
+ private:
+ std::string m_name;
+ WildcardPosition m_wildcard;
+ };
+ class TagPattern : public Pattern {
+ public:
+ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ virtual ~TagPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+ }
+ private:
+ std::string m_tag;
+ };
+ class ExcludedPattern : public Pattern {
+ public:
+ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+ virtual ~ExcludedPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ private:
+ Ptr<Pattern> m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<Ptr<Pattern> > m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const {
+ // All patterns in a filter must match for the filter to be a match
+ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
+ if( !(*it)->matches( testCase ) )
+ return false;
+ return true;
+ }
+ };
+
+ public:
+ bool hasFilters() const {
+ return !m_filters.empty();
+ }
+ bool matches( TestCaseInfo const& testCase ) const {
+ // A TestSpec matches if any filter matches
+ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+ if( it->matches( testCase ) )
+ return true;
+ return false;
+ }
+
+ private:
+ std::vector<Filter> m_filters;
+
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ visitChar( m_arg[m_pos] );
+ if( m_mode == Name )
+ addPattern<TestSpec::NamePattern>();
+ return *this;
+ }
+ TestSpec testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ private:
+ void visitChar( char c ) {
+ if( m_mode == None ) {
+ switch( c ) {
+ case ' ': return;
+ case '~': m_exclusion = true; return;
+ case '[': return startNewMode( Tag, ++m_pos );
+ case '"': return startNewMode( QuotedName, ++m_pos );
+ default: startNewMode( Name, m_pos ); break;
+ }
+ }
+ if( m_mode == Name ) {
+ if( c == ',' ) {
+ addPattern<TestSpec::NamePattern>();
+ addFilter();
+ }
+ else if( c == '[' ) {
+ if( subString() == "exclude:" )
+ m_exclusion = true;
+ else
+ addPattern<TestSpec::NamePattern>();
+ startNewMode( Tag, ++m_pos );
+ }
+ }
+ else if( m_mode == QuotedName && c == '"' )
+ addPattern<TestSpec::NamePattern>();
+ else if( m_mode == Tag && c == ']' )
+ addPattern<TestSpec::TagPattern>();
+ }
+ void startNewMode( Mode mode, std::size_t start ) {
+ m_mode = mode;
+ m_start = start;
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ if( startsWith( token, "exclude:" ) ) {
+ m_exclusion = true;
+ token = token.substr( 8 );
+ }
+ if( !token.empty() ) {
+ Ptr<TestSpec::Pattern> pattern = new T( token );
+ if( m_exclusion )
+ pattern = new TestSpec::ExcludedPattern( pattern );
+ m_currentFilter.m_patterns.push_back( pattern );
+ }
+ m_exclusion = false;
+ m_mode = None;
+ }
+ void addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+ };
+ inline TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct Verbosity { enum Level {
+ NoOutput = 0,
+ Quiet,
+ Normal
+ }; };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : IShared {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+#include <streambuf>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ class Stream {
+ public:
+ Stream();
+ Stream( std::streambuf* _streamBuf, bool _isOwned );
+ void release();
+
+ std::streambuf* streamBuf;
+
+ private:
+ bool isOwned;
+ };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct ConfigData {
+
+ ConfigData()
+ : listTests( false ),
+ listTags( false ),
+ listReporters( false ),
+ listTestNamesOnly( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ abortAfter( -1 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+
+ int abortAfter;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+
+ std::string reporterName;
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> testsOrTags;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ : m_os( std::cout.rdbuf() )
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_os( std::cout.rdbuf() )
+ {
+ if( !data.testsOrTags.empty() ) {
+ TestSpecParser parser( ITagAliasRegistry::get() );
+ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+ parser.parse( data.testsOrTags[i] );
+ m_testSpec = parser.testSpec();
+ }
+ }
+
+ virtual ~Config() {
+ m_os.rdbuf( std::cout.rdbuf() );
+ m_stream.release();
+ }
+
+ void setFilename( std::string const& filename ) {
+ m_data.outputFilename = filename;
+ }
+
+ std::string const& getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool listTests() const { return m_data.listTests; }
+ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool listTags() const { return m_data.listTags; }
+ bool listReporters() const { return m_data.listReporters; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+
+ void setStreamBuf( std::streambuf* buf ) {
+ m_os.rdbuf( buf ? buf : std::cout.rdbuf() );
+ }
+
+ void useStream( std::string const& streamName ) {
+ Stream stream = createStream( streamName );
+ setStreamBuf( stream.streamBuf );
+ m_stream.release();
+ m_stream = stream;
+ }
+
+ std::string getReporterName() const { return m_data.reporterName; }
+
+ int abortAfter() const { return m_data.abortAfter; }
+
+ TestSpec const& testSpec() const { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+ bool showInvisibles() const { return m_data.showInvisibles; }
+
+ // IConfig interface
+ virtual bool allowThrows() const { return !m_data.noThrow; }
+ virtual std::ostream& stream() const { return m_os; }
+ virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
+
+ private:
+ ConfigData m_data;
+
+ Stream m_stream;
+ mutable std::ostream m_os;
+ TestSpec m_testSpec;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <memory>
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+ struct UnpositionalTag {};
+
+ extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+ UnpositionalTag _;
+#endif
+
+ namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ using namespace Tbc;
+
+ inline bool startsWith( std::string const& str, std::string const& prefix ) {
+ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+ }
+
+ template<typename T> struct RemoveConstRef{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+ template<typename T> struct IsBool { static const bool value = false; };
+ template<> struct IsBool<bool> { static const bool value = true; };
+
+ template<typename T>
+ void convertInto( std::string const& _source, T& _dest ) {
+ std::stringstream ss;
+ ss << _source;
+ ss >> _dest;
+ if( ss.fail() )
+ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+ }
+ inline void convertInto( std::string const& _source, std::string& _dest ) {
+ _dest = _source;
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+ _dest = true;
+ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+ _dest = false;
+ else
+ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
+ }
+ inline void convertInto( bool _source, bool& _dest ) {
+ _dest = _source;
+ }
+ template<typename T>
+ inline void convertInto( bool, T& ) {
+ throw std::runtime_error( "Invalid conversion" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+# ifdef CATCH_CPP11_OR_GREATER
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+# endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual void setFlag( ConfigT& config ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ void setFlag( ConfigT& config ) const {
+ functionObj->setFlag( config );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) const {}
+ virtual void setFlag( C& ) const {}
+ virtual bool takesArg() const { return true; }
+ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+ };
+
+ template<typename C, typename M>
+ struct BoundDataMember : IArgFunction<C>{
+ BoundDataMember( M C::* _member ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ convertInto( stringValue, p.*member );
+ }
+ virtual void setFlag( C& p ) const {
+ convertInto( true, p.*member );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+ M C::* member;
+ };
+ template<typename C, typename M>
+ struct BoundUnaryMethod : IArgFunction<C>{
+ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( stringValue, value );
+ (p.*member)( value );
+ }
+ virtual void setFlag( C& p ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( true, value );
+ (p.*member)( value );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+ void (C::*member)( M );
+ };
+ template<typename C>
+ struct BoundNullaryMethod : IArgFunction<C>{
+ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ (p.*member)();
+ }
+ virtual void setFlag( C& p ) const {
+ (p.*member)();
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+ void (C::*member)();
+ };
+
+ template<typename C>
+ struct BoundUnaryFunction : IArgFunction<C>{
+ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ function( obj );
+ }
+ virtual void setFlag( C& p ) const {
+ function( p );
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+ void (*function)( C& );
+ };
+
+ template<typename C, typename T>
+ struct BoundBinaryFunction : IArgFunction<C>{
+ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( stringValue, value );
+ function( obj, value );
+ }
+ virtual void setFlag( C& obj ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( true, value );
+ function( obj, value );
+ }
+ virtual bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ struct Parser {
+ Parser() : separators( " \t=:" ) {}
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+ const std::string doubleDash = "--";
+ for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
+ parseIntoTokens( argv[i] , tokens);
+ }
+ void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
+ while( !arg.empty() ) {
+ Parser::Token token( Parser::Token::Positional, arg );
+ arg = "";
+ if( token.data[0] == '-' ) {
+ if( token.data.size() > 1 && token.data[1] == '-' ) {
+ token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
+ }
+ else {
+ token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
+ if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
+ arg = "-" + token.data.substr( 1 );
+ token.data = token.data.substr( 0, 1 );
+ }
+ }
+ }
+ if( token.type != Parser::Token::Positional ) {
+ std::size_t pos = token.data.find_first_of( separators );
+ if( pos != std::string::npos ) {
+ arg = token.data.substr( pos+1 );
+ token.data = token.data.substr( 0, pos );
+ }
+ }
+ tokens.push_back( token );
+ }
+ }
+ std::string separators;
+ };
+
+ template<typename ConfigT>
+ struct CommonArgProperties {
+ CommonArgProperties() {}
+ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+ Detail::BoundArgFunction<ConfigT> boundField;
+ std::string description;
+ std::string detail;
+ std::string placeholder; // Only value if boundField takes an arg
+
+ bool takesArg() const {
+ return !placeholder.empty();
+ }
+ void validate() const {
+ if( !boundField.isSet() )
+ throw std::logic_error( "option not bound" );
+ }
+ };
+ struct OptionArgProperties {
+ std::vector<std::string> shortNames;
+ std::string longName;
+
+ bool hasShortName( std::string const& shortName ) const {
+ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+ }
+ bool hasLongName( std::string const& _longName ) const {
+ return _longName == longName;
+ }
+ };
+ struct PositionalArgProperties {
+ PositionalArgProperties() : position( -1 ) {}
+ int position; // -1 means non-positional (floating)
+
+ bool isFixedPositional() const {
+ return position != -1;
+ }
+ };
+
+ template<typename ConfigT>
+ class CommandLine {
+
+ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+ Arg() {}
+ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+ using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+ std::string dbgName() const {
+ if( !longName.empty() )
+ return "--" + longName;
+ if( !shortNames.empty() )
+ return "-" + shortNames[0];
+ return "positional args";
+ }
+ std::string commands() const {
+ std::ostringstream oss;
+ bool first = true;
+ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+ for(; it != itEnd; ++it ) {
+ if( first )
+ first = false;
+ else
+ oss << ", ";
+ oss << "-" << *it;
+ }
+ if( !longName.empty() ) {
+ if( !first )
+ oss << ", ";
+ oss << "--" << longName;
+ }
+ if( !placeholder.empty() )
+ oss << " <" << placeholder << ">";
+ return oss.str();
+ }
+ };
+
+ // NOTE: std::auto_ptr is deprecated in c++11/c++0x
+#if defined(__cplusplus) && __cplusplus > 199711L
+ typedef std::unique_ptr<Arg> ArgAutoPtr;
+#else
+ typedef std::auto_ptr<Arg> ArgAutoPtr;
+#endif
+
+ friend void addOptName( Arg& arg, std::string const& optName )
+ {
+ if( optName.empty() )
+ return;
+ if( Detail::startsWith( optName, "--" ) ) {
+ if( !arg.longName.empty() )
+ throw std::logic_error( "Only one long opt may be specified. '"
+ + arg.longName
+ + "' already specified, now attempting to add '"
+ + optName + "'" );
+ arg.longName = optName.substr( 2 );
+ }
+ else if( Detail::startsWith( optName, "-" ) )
+ arg.shortNames.push_back( optName.substr( 1 ) );
+ else
+ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+ }
+ friend void setPositionalArg( Arg& arg, int position )
+ {
+ arg.position = position;
+ }
+
+ class ArgBuilder {
+ public:
+ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+ // Bind a non-boolean data member (requires placeholder string)
+ template<typename C, typename M>
+ void bind( M C::* field, std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+ m_arg->placeholder = placeholder;
+ }
+ // Bind a boolean data member (no placeholder required)
+ template<typename C>
+ void bind( bool C::* field ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+ }
+
+ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+ template<typename C, typename M>
+ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+ m_arg->placeholder = placeholder;
+ }
+
+ // Bind a method taking a single, boolean argument (no placeholder string required)
+ template<typename C>
+ void bind( void (C::* unaryMethod)( bool ) ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+ }
+
+ // Bind a method that takes no arguments (will be called if opt is present)
+ template<typename C>
+ void bind( void (C::* nullaryMethod)() ) {
+ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+ template<typename C>
+ void bind( void (* unaryFunction)( C& ) ) {
+ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+ template<typename C, typename T>
+ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+ m_arg->placeholder = placeholder;
+ }
+
+ ArgBuilder& describe( std::string const& description ) {
+ m_arg->description = description;
+ return *this;
+ }
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
+ return *this;
+ }
+
+ protected:
+ Arg* m_arg;
+ };
+
+ class OptBuilder : public ArgBuilder {
+ public:
+ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+ OptBuilder& operator[]( std::string const& optName ) {
+ addOptName( *ArgBuilder::m_arg, optName );
+ return *this;
+ }
+ };
+
+ public:
+
+ CommandLine()
+ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+ m_highestSpecifiedArgPosition( 0 ),
+ m_throwOnUnrecognisedTokens( false )
+ {}
+ CommandLine( CommandLine const& other )
+ : m_boundProcessName( other.m_boundProcessName ),
+ m_options ( other.m_options ),
+ m_positionalArgs( other.m_positionalArgs ),
+ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+ {
+ if( other.m_floatingArg.get() )
+ m_floatingArg = ArgAutoPtr( new Arg( *other.m_floatingArg ) );
+ }
+
+ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+ m_throwOnUnrecognisedTokens = shouldThrow;
+ return *this;
+ }
+
+ OptBuilder operator[]( std::string const& optName ) {
+ m_options.push_back( Arg() );
+ addOptName( m_options.back(), optName );
+ OptBuilder builder( &m_options.back() );
+ return builder;
+ }
+
+ ArgBuilder operator[]( int position ) {
+ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+ if( position > m_highestSpecifiedArgPosition )
+ m_highestSpecifiedArgPosition = position;
+ setPositionalArg( m_positionalArgs[position], position );
+ ArgBuilder builder( &m_positionalArgs[position] );
+ return builder;
+ }
+
+ // Invoke this with the _ instance
+ ArgBuilder operator[]( UnpositionalTag ) {
+ if( m_floatingArg.get() )
+ throw std::logic_error( "Only one unpositional argument can be added" );
+ m_floatingArg = ArgAutoPtr( new Arg() );
+ ArgBuilder builder( m_floatingArg.get() );
+ return builder;
+ }
+
+ template<typename C, typename M>
+ void bindProcessName( M C::* field ) {
+ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+ }
+ template<typename C, typename M>
+ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+ }
+
+ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+ std::size_t maxWidth = 0;
+ for( it = itBegin; it != itEnd; ++it )
+ maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+ for( it = itBegin; it != itEnd; ++it ) {
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
+ .setWidth( maxWidth+indent )
+ .setIndent( indent ) );
+ Detail::Text desc( it->description, Detail::TextAttributes()
+ .setWidth( width - maxWidth - 3 ) );
+
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
+ os << usageCol;
+
+ if( i < desc.size() && !desc[i].empty() )
+ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+ << desc[i];
+ os << "\n";
+ }
+ }
+ }
+ std::string optUsage() const {
+ std::ostringstream oss;
+ optUsage( oss );
+ return oss.str();
+ }
+
+ void argSynopsis( std::ostream& os ) const {
+ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+ if( i > 1 )
+ os << " ";
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+ if( it != m_positionalArgs.end() )
+ os << "<" << it->second.placeholder << ">";
+ else if( m_floatingArg.get() )
+ os << "<" << m_floatingArg->placeholder << ">";
+ else
+ throw std::logic_error( "non consecutive positional arguments with no floating args" );
+ }
+ // !TBD No indication of mandatory args
+ if( m_floatingArg.get() ) {
+ if( m_highestSpecifiedArgPosition > 1 )
+ os << " ";
+ os << "[<" << m_floatingArg->placeholder << "> ...]";
+ }
+ }
+ std::string argSynopsis() const {
+ std::ostringstream oss;
+ argSynopsis( oss );
+ return oss.str();
+ }
+
+ void usage( std::ostream& os, std::string const& procName ) const {
+ validate();
+ os << "usage:\n " << procName << " ";
+ argSynopsis( os );
+ if( !m_options.empty() ) {
+ os << " [options]\n\nwhere options are: \n";
+ optUsage( os, 2 );
+ }
+ os << "\n";
+ }
+ std::string usage( std::string const& procName ) const {
+ std::ostringstream oss;
+ usage( oss, procName );
+ return oss.str();
+ }
+
+ ConfigT parse( int argc, char const * const * argv ) const {
+ ConfigT config;
+ parseInto( argc, argv, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+ std::string processName = argv[0];
+ std::size_t lastSlash = processName.find_last_of( "/\\" );
+ if( lastSlash != std::string::npos )
+ processName = processName.substr( lastSlash+1 );
+ m_boundProcessName.set( config, processName );
+ std::vector<Parser::Token> tokens;
+ Parser parser;
+ parser.parseIntoTokens( argc, argv, tokens );
+ return populate( tokens, config );
+ }
+
+ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ validate();
+ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+ unusedTokens = populateFixedArgs( unusedTokens, config );
+ unusedTokens = populateFloatingArgs( unusedTokens, config );
+ return unusedTokens;
+ }
+
+ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ std::vector<std::string> errors;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+ for(; it != itEnd; ++it ) {
+ Arg const& arg = *it;
+
+ try {
+ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+ if( arg.takesArg() ) {
+ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+ errors.push_back( "Expected argument to option: " + token.data );
+ else
+ arg.boundField.set( config, tokens[++i].data );
+ }
+ else {
+ arg.boundField.setFlag( config );
+ }
+ break;
+ }
+ }
+ catch( std::exception& ex ) {
+ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+ }
+ }
+ if( it == itEnd ) {
+ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+ unusedTokens.push_back( token );
+ else if( m_throwOnUnrecognisedTokens )
+ errors.push_back( "unrecognised option: " + token.data );
+ }
+ }
+ if( !errors.empty() ) {
+ std::ostringstream oss;
+ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+ it != itEnd;
+ ++it ) {
+ if( it != errors.begin() )
+ oss << "\n";
+ oss << *it;
+ }
+ throw std::runtime_error( oss.str() );
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ int position = 1;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+ if( it != m_positionalArgs.end() )
+ it->second.boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ if( token.type == Parser::Token::Positional )
+ position++;
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ if( !m_floatingArg.get() )
+ return tokens;
+ std::vector<Parser::Token> unusedTokens;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ if( token.type == Parser::Token::Positional )
+ m_floatingArg->boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ }
+ return unusedTokens;
+ }
+
+ void validate() const
+ {
+ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+ throw std::logic_error( "No options or arguments specified" );
+
+ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
+ itEnd = m_options.end();
+ it != itEnd; ++it )
+ it->validate();
+ }
+
+ private:
+ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+ std::vector<Arg> m_options;
+ std::map<int, Arg> m_positionalArgs;
+ ArgAutoPtr m_floatingArg;
+ int m_highestSpecifiedArgPosition;
+ bool m_throwOnUnrecognisedTokens;
+ };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+
+namespace Catch {
+
+ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+ inline void abortAfterX( ConfigData& config, int x ) {
+ if( x < 1 )
+ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+ config.abortAfter = x;
+ }
+ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+
+ inline void addWarning( ConfigData& config, std::string const& _warning ) {
+ if( _warning == "NoAssertions" )
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+ else
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
+
+ }
+ inline void setVerbosity( ConfigData& config, int level ) {
+ // !TBD: accept strings?
+ config.verbosity = static_cast<Verbosity::Level>( level );
+ }
+ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+ config.showDurations = _showDurations
+ ? ShowDurations::Always
+ : ShowDurations::Never;
+ }
+ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+ std::ifstream f( _filename.c_str() );
+ if( !f.is_open() )
+ throw std::domain_error( "Unable to load input file: " + _filename );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, "#" ) )
+ addTestOrTags( config, "\"" + line + "\"," );
+ }
+ }
+
+ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+ using namespace Clara;
+ CommandLine<ConfigData> cli;
+
+ cli.bindProcessName( &ConfigData::processName );
+
+ cli["-?"]["-h"]["--help"]
+ .describe( "display usage information" )
+ .bind( &ConfigData::showHelp );
+
+ cli["-l"]["--list-tests"]
+ .describe( "list all/matching test cases" )
+ .bind( &ConfigData::listTests );
+
+ cli["-t"]["--list-tags"]
+ .describe( "list all/matching tags" )
+ .bind( &ConfigData::listTags );
+
+ cli["-s"]["--success"]
+ .describe( "include successful tests in output" )
+ .bind( &ConfigData::showSuccessfulTests );
+
+ cli["-b"]["--break"]
+ .describe( "break into debugger on failure" )
+ .bind( &ConfigData::shouldDebugBreak );
+
+ cli["-e"]["--nothrow"]
+ .describe( "skip exception tests" )
+ .bind( &ConfigData::noThrow );
+
+ cli["-i"]["--invisibles"]
+ .describe( "show invisibles (tabs, newlines)" )
+ .bind( &ConfigData::showInvisibles );
+
+ cli["-o"]["--out"]
+ .describe( "output filename" )
+ .bind( &ConfigData::outputFilename, "filename" );
+
+ cli["-r"]["--reporter"]
+// .placeholder( "name[:filename]" )
+ .describe( "reporter to use (defaults to console)" )
+ .bind( &ConfigData::reporterName, "name" );
+
+ cli["-n"]["--name"]
+ .describe( "suite name" )
+ .bind( &ConfigData::name, "name" );
+
+ cli["-a"]["--abort"]
+ .describe( "abort at first failure" )
+ .bind( &abortAfterFirst );
+
+ cli["-x"]["--abortx"]
+ .describe( "abort after x failures" )
+ .bind( &abortAfterX, "no. failures" );
+
+ cli["-w"]["--warn"]
+ .describe( "enable warnings" )
+ .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+// cli.into( &setVerbosity )
+// .describe( "level of verbosity (0=no output)" )
+// .shortOpt( "v")
+// .longOpt( "verbosity" )
+// .placeholder( "level" );
+
+ cli[_]
+ .describe( "which test or tests to use" )
+ .bind( &addTestOrTags, "test name, pattern or tags" );
+
+ cli["-d"]["--durations"]
+ .describe( "show test durations" )
+ .bind( &setShowDurations, "yes/no" );
+
+ cli["-f"]["--input-file"]
+ .describe( "load test names to run from a file" )
+ .bind( &loadTestNamesFromFile, "filename" );
+
+ // Less common commands which don't have a short form
+ cli["--list-test-names-only"]
+ .describe( "list all/matching test cases names only" )
+ .bind( &ConfigData::listTestNamesOnly );
+
+ cli["--list-reporters"]
+ .describe( "list all reporters" )
+ .bind( &ConfigData::listReporters );
+
+ return cli;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# endif
+# else
+# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+ using Tbc::Text;
+ using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+ namespace Detail {
+ struct IColourImpl;
+ }
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = Yellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = Yellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour const& other );
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ static Detail::IColourImpl* impl();
+ bool m_moved;
+ };
+
+ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+#include <assert.h>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig> m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ ReporterPreferences()
+ : shouldRedirectStdOut( false )
+ {}
+
+ bool shouldRedirectStdOut;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat() : used( false ) {}
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name ) : name( _name ) {}
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+ virtual ~AssertionStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = default;
+ AssertionStats& operator = ( AssertionStats && ) = default;
+# endif
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+ virtual ~SectionStats();
+# ifdef CATCH_CPP11_OR_GREATER
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+# endif
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+ virtual ~TestCaseStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+# endif
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+ virtual ~TestGroupStats();
+
+# ifdef CATCH_CPP11_OR_GREATER
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+# endif
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ virtual ~TestRunStats();
+
+# ifndef CATCH_CPP11_OR_GREATER
+ TestRunStats( TestRunStats const& _other )
+ : runInfo( _other.runInfo ),
+ totals( _other.totals ),
+ aborting( _other.aborting )
+ {}
+# else
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+# endif
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct IStreamingReporter : IShared {
+ virtual ~IStreamingReporter();
+
+ // Implementing class must also provide the following static method:
+ // static std::string getDescription();
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+ };
+
+ struct IReporterFactory {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, IReporterFactory*> FactoryMap;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ };
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+ inline std::size_t listTests( Config const& config ) {
+
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ std::cout << "Matching test cases:\n";
+ else {
+ std::cout << "All available test cases:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::size_t matchedTests = 0;
+ TextAttributes nameAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl;
+ if( !testCaseInfo.tags.empty() )
+ std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+ }
+
+ if( !config.testSpec().hasFilters() )
+ std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
+ else
+ std::cout << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
+ return matchedTests;
+ }
+
+ inline std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( !config.testSpec().hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ std::cout << testCaseInfo.name << std::endl;
+ }
+ return matchedTests;
+ }
+
+ struct TagInfo {
+ TagInfo() : count ( 0 ) {}
+ void add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+ std::string all() const {
+ std::string out;
+ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+ it != itEnd;
+ ++it )
+ out += "[" + *it + "]";
+ return out;
+ }
+ std::set<std::string> spellings;
+ std::size_t count;
+ };
+
+ inline std::size_t listTags( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ std::cout << "Tags for matching test cases:\n";
+ else {
+ std::cout << "All available tags:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
+ tagItEnd = it->getTestCaseInfo().tags.end();
+ tagIt != tagItEnd;
+ ++tagIt ) {
+ std::string tagName = *tagIt;
+ std::string lcaseTagName = toLower( tagName );
+ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+ countItEnd = tagCounts.end();
+ countIt != countItEnd;
+ ++countIt ) {
+ std::ostringstream oss;
+ oss << " " << std::setw(2) << countIt->second.count << " ";
+ Text wrapper( countIt->second.all(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( oss.str().size() )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+ std::cout << oss.str() << wrapper << "\n";
+ }
+ std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
+ return tagCounts.size();
+ }
+
+ inline std::size_t listReporters( Config const& /*config*/ ) {
+ std::cout << "Available reports:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+ std::size_t maxNameLen = 0;
+ for(it = itBegin; it != itEnd; ++it )
+ maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+ for(it = itBegin; it != itEnd; ++it ) {
+ Text wrapper( it->second->getDescription(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( 7+maxNameLen )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+ std::cout << " "
+ << it->first
+ << ":"
+ << std::string( maxNameLen - it->first.size() + 2, ' ' )
+ << wrapper << "\n";
+ }
+ std::cout << std::endl;
+ return factories.size();
+ }
+
+ inline Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> listedCount;
+ if( config.listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( config );
+ if( config.listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+ if( config.listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( config );
+ if( config.listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters( config );
+ return listedCount;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_runner_impl.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+
+namespace Catch {
+namespace SectionTracking {
+
+ class TrackedSection {
+
+ typedef std::map<std::string, TrackedSection> TrackedSections;
+
+ public:
+ enum RunState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ Completed
+ };
+
+ TrackedSection( std::string const& name, TrackedSection* parent )
+ : m_name( name ), m_runState( NotStarted ), m_parent( parent )
+ {}
+
+ RunState runState() const { return m_runState; }
+
+ TrackedSection* findChild( std::string const& childName ) {
+ TrackedSections::iterator it = m_children.find( childName );
+ return it != m_children.end()
+ ? &it->second
+ : NULL;
+ }
+ TrackedSection* acquireChild( std::string const& childName ) {
+ if( TrackedSection* child = findChild( childName ) )
+ return child;
+ m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
+ return findChild( childName );
+ }
+ void enter() {
+ if( m_runState == NotStarted )
+ m_runState = Executing;
+ }
+ void leave() {
+ for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
+ it != itEnd;
+ ++it )
+ if( it->second.runState() != Completed ) {
+ m_runState = ExecutingChildren;
+ return;
+ }
+ m_runState = Completed;
+ }
+ TrackedSection* getParent() {
+ return m_parent;
+ }
+ bool hasChildren() const {
+ return !m_children.empty();
+ }
+
+ private:
+ std::string m_name;
+ RunState m_runState;
+ TrackedSections m_children;
+ TrackedSection* m_parent;
+
+ };
+
+ class TestCaseTracker {
+ public:
+ TestCaseTracker( std::string const& testCaseName )
+ : m_testCase( testCaseName, NULL ),
+ m_currentSection( &m_testCase ),
+ m_completedASectionThisRun( false )
+ {}
+
+ bool enterSection( std::string const& name ) {
+ TrackedSection* child = m_currentSection->acquireChild( name );
+ if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
+ return false;
+
+ m_currentSection = child;
+ m_currentSection->enter();
+ return true;
+ }
+ void leaveSection() {
+ m_currentSection->leave();
+ m_currentSection = m_currentSection->getParent();
+ assert( m_currentSection != NULL );
+ m_completedASectionThisRun = true;
+ }
+
+ bool currentSectionHasChildren() const {
+ return m_currentSection->hasChildren();
+ }
+ bool isCompleted() const {
+ return m_testCase.runState() == TrackedSection::Completed;
+ }
+
+ class Guard {
+ public:
+ Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
+ m_tracker.enterTestCase();
+ }
+ ~Guard() {
+ m_tracker.leaveTestCase();
+ }
+ private:
+ Guard( Guard const& );
+ void operator = ( Guard const& );
+ TestCaseTracker& m_tracker;
+ };
+
+ private:
+ void enterTestCase() {
+ m_currentSection = &m_testCase;
+ m_completedASectionThisRun = false;
+ m_testCase.enter();
+ }
+ void leaveTestCase() {
+ m_testCase.leave();
+ }
+
+ TrackedSection m_testCase;
+ TrackedSection* m_currentSection;
+ bool m_completedASectionThisRun;
+ };
+
+} // namespace SectionTracking
+
+using SectionTracking::TestCaseTracker;
+
+} // namespace Catch
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+ class StreamRedirect {
+
+ public:
+ StreamRedirect( std::ostream& stream, std::string& targetString )
+ : m_stream( stream ),
+ m_prevBuf( stream.rdbuf() ),
+ m_targetString( targetString )
+ {
+ stream.rdbuf( m_oss.rdbuf() );
+ }
+
+ ~StreamRedirect() {
+ m_targetString += m_oss.str();
+ m_stream.rdbuf( m_prevBuf );
+ }
+
+ private:
+ std::ostream& m_stream;
+ std::streambuf* m_prevBuf;
+ std::ostringstream m_oss;
+ std::string& m_targetString;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ RunContext( RunContext const& );
+ void operator =( RunContext const& );
+
+ public:
+
+ explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
+ : m_runInfo( config->name() ),
+ m_context( getCurrentMutableContext() ),
+ m_activeTestCase( NULL ),
+ m_config( config ),
+ m_reporter( reporter ),
+ m_prevRunner( m_context.getRunner() ),
+ m_prevResultCapture( m_context.getResultCapture() ),
+ m_prevConfig( m_context.getConfig() )
+ {
+ m_context.setRunner( this );
+ m_context.setConfig( m_config );
+ m_context.setResultCapture( this );
+ m_reporter->testRunStarting( m_runInfo );
+ }
+
+ virtual ~RunContext() {
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+ m_context.setRunner( m_prevRunner );
+ m_context.setConfig( NULL );
+ m_context.setResultCapture( m_prevResultCapture );
+ m_context.setConfig( m_prevConfig );
+ }
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+ }
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+ }
+
+ Totals runTest( TestCase const& testCase ) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting( testInfo );
+
+ m_activeTestCase = &testCase;
+ m_testCaseTracker = TestCaseTracker( testInfo.name );
+
+ do {
+ do {
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isCompleted() && !aborting() );
+ }
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = NULL;
+ m_testCaseTracker.reset();
+
+ return deltaTotals;
+ }
+
+ Ptr<IConfig const> config() const {
+ return m_config;
+ }
+
+ private: // IResultCapture
+
+ virtual void assertionEnded( AssertionResult const& result ) {
+ if( result.getResultType() == ResultWas::Ok ) {
+ m_totals.assertions.passed++;
+ }
+ else if( !result.isOk() ) {
+ m_totals.assertions.failed++;
+ }
+
+ if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+ m_messages.clear();
+
+ // Reset working state
+ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastResult = result;
+ }
+
+ virtual bool sectionStarted (
+ SectionInfo const& sectionInfo,
+ Counts& assertions
+ )
+ {
+ std::ostringstream oss;
+ oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
+
+ if( !m_testCaseTracker->enterSection( oss.str() ) )
+ return false;
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 ||
+ !m_config->warnAboutMissingAssertions() ||
+ m_testCaseTracker->currentSectionHasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
+ if( std::uncaught_exception() ) {
+ m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
+ return;
+ }
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ m_testCaseTracker->leaveSection();
+
+ m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void pushScopedMessage( MessageInfo const& message ) {
+ m_messages.push_back( message );
+ }
+
+ virtual void popScopedMessage( MessageInfo const& message ) {
+ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+ }
+
+ virtual std::string getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : "";
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const {
+ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+ }
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+ m_reporter->sectionStarting( testCaseSection );
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+ TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+ Timer timer;
+ timer.start();
+ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+ StreamRedirect coutRedir( std::cout, redirectedCout );
+ StreamRedirect cerrRedir( std::cerr, redirectedCerr );
+ m_activeTestCase->invoke();
+ }
+ else {
+ m_activeTestCase->invoke();
+ }
+ duration = timer.getElapsedSeconds();
+ }
+ catch( TestFailureException& ) {
+ // This just means the test was aborted due to failure
+ }
+ catch(...) {
+ ResultBuilder exResult( m_lastAssertionInfo.macroName.c_str(),
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression.c_str(),
+ m_lastAssertionInfo.resultDisposition );
+ exResult.useActiveException();
+ }
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+ m_unfinishedSections.clear();
+ m_messages.clear();
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( testCaseInfo.okToFail() ) {
+ std::swap( assertions.failedButOk, assertions.failed );
+ m_totals.assertions.failed -= assertions.failedButOk;
+ m_totals.assertions.failedButOk += assertions.failedButOk;
+ }
+
+ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+ m_reporter->sectionEnded( testCaseSectionStats );
+ }
+
+ private:
+ struct UnfinishedSections {
+ UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
+ : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo info;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ Option<TestCaseTracker> m_testCaseTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ IRunner* m_prevRunner;
+ IResultCapture* m_prevResultCapture;
+ Ptr<IConfig const> m_prevConfig;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<UnfinishedSections> m_unfinishedSections;
+ };
+
+ IResultCapture& getResultCapture() {
+ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+ return *capture;
+ else
+ throw std::logic_error( "No result capture instance" );
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _buildNumber,
+ char const* const _branchName )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ buildNumber( _buildNumber ),
+ branchName( _branchName )
+ {}
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const buildNumber;
+ char const* const branchName;
+
+ private:
+ void operator=( Version const& );
+ };
+
+ extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ class Runner {
+
+ public:
+ Runner( Ptr<Config> const& config )
+ : m_config( config )
+ {
+ openStream();
+ makeReporter();
+ }
+
+ Totals runTests() {
+
+ RunContext context( m_config.get(), m_reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( "", 1, 1 ); // deprecated?
+
+ TestSpec testSpec = m_config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> testCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+
+ int testsRunForGroup = 0;
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it ) {
+ testsRunForGroup++;
+ if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+
+ if( context.aborting() )
+ break;
+
+ totals += context.runTest( *it );
+ m_testsAlreadyRun.insert( *it );
+ }
+ }
+ context.testGroupEnded( "", totals, 1, 1 );
+ return totals;
+ }
+
+ private:
+ void openStream() {
+ // Open output file, if specified
+ if( !m_config->getFilename().empty() ) {
+ m_ofs.open( m_config->getFilename().c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << m_config->getFilename() << "'";
+ throw std::domain_error( oss.str() );
+ }
+ m_config->setStreamBuf( m_ofs.rdbuf() );
+ }
+ }
+ void makeReporter() {
+ std::string reporterName = m_config->getReporterName().empty()
+ ? "console"
+ : m_config->getReporterName();
+
+ m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
+ if( !m_reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ private:
+ Ptr<Config> m_config;
+ std::ofstream m_ofs;
+ Ptr<IStreamingReporter> m_reporter;
+ std::set<TestCase> m_testsAlreadyRun;
+ };
+
+ class Session {
+ static bool alreadyInstantiated;
+
+ public:
+
+ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+ Session()
+ : m_cli( makeCommandLineParser() ) {
+ if( alreadyInstantiated ) {
+ std::string msg = "Only one instance of Catch::Session can ever be used";
+ std::cerr << msg << std::endl;
+ throw std::logic_error( msg );
+ }
+ alreadyInstantiated = true;
+ }
+ ~Session() {
+ Catch::cleanUp();
+ }
+
+ void showHelp( std::string const& processName ) {
+ std::cout << "\nCatch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " build "
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ std::cout << " (" << libraryVersion.branchName << " branch)";
+ std::cout << "\n";
+
+ m_cli.usage( std::cout, processName );
+ std::cout << "For more detail usage please see the project docs\n" << std::endl;
+ }
+
+ int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+ if( m_configData.showHelp )
+ showHelp( m_configData.processName );
+ m_config.reset();
+ }
+ catch( std::exception& ex ) {
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr << "\nError(s) in input:\n"
+ << Text( ex.what(), TextAttributes().setIndent(2) )
+ << "\n\n";
+ }
+ m_cli.usage( std::cout, m_configData.processName );
+ return (std::numeric_limits<int>::max)();
+ }
+ return 0;
+ }
+
+ void useConfigData( ConfigData const& _configData ) {
+ m_configData = _configData;
+ m_config.reset();
+ }
+
+ int run( int argc, char* const argv[] ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+ Runner runner( m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runner.runTests().assertions.failed );
+ }
+ catch( std::exception& ex ) {
+ std::cerr << ex.what() << std::endl;
+ return (std::numeric_limits<int>::max)();
+ }
+ }
+
+ Clara::CommandLine<ConfigData> const& cli() const {
+ return m_cli;
+ }
+ std::vector<Clara::Parser::Token> const& unusedTokens() const {
+ return m_unusedTokens;
+ }
+ ConfigData& configData() {
+ return m_configData;
+ }
+ Config& config() {
+ if( !m_config )
+ m_config = new Config( m_configData );
+ return *m_config;
+ }
+
+ private:
+ Clara::CommandLine<ConfigData> m_cli;
+ std::vector<Clara::Parser::Token> m_unusedTokens;
+ ConfigData m_configData;
+ Ptr<Config> m_config;
+ };
+
+ bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <iostream>
+
+namespace Catch {
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ TestRegistry() : m_unnamedCount( 0 ) {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name == "" ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+
+ if( m_functions.find( testCase ) == m_functions.end() ) {
+ m_functions.insert( testCase );
+ m_functionsInOrder.push_back( testCase );
+ if( !testCase.isHidden() )
+ m_nonHiddenFunctions.push_back( testCase );
+ }
+ else {
+ TestCase const& prev = *m_functions.find( testCase );
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
+ << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functionsInOrder;
+ }
+
+ virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
+ return m_nonHiddenFunctions;
+ }
+
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
+ for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
+ itEnd = m_functionsInOrder.end();
+ it != itEnd;
+ ++it ) {
+ if( testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ) )
+ matchingTestCases.push_back( *it );
+ }
+ }
+
+ private:
+
+ std::set<TestCase> m_functions;
+ std::vector<TestCase> m_functionsInOrder;
+ std::vector<TestCase> m_nonHiddenFunctions;
+ size_t m_unnamedCount;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+ public:
+
+ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+ virtual void invoke() const {
+ m_fun();
+ }
+
+ private:
+ virtual ~FreeFunctionTestCase();
+
+ TestFunction m_fun;
+ };
+
+ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+ std::string className = classOrQualifiedMethodName;
+ if( startsWith( className, "&" ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ AutoReg::~AutoReg() {}
+
+ void AutoReg::registerTestCase( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ virtual ~ReporterRegistry() {
+ deleteAllValues( m_factories );
+ }
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+
+ FactoryMap const& getFactories() const {
+ return m_factories;
+ }
+
+ private:
+ FactoryMap m_factories;
+ };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry() {
+ deleteAll( m_translators );
+ }
+
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( translator );
+ }
+
+ virtual std::string translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ throw;
+ }
+ @catch (NSException *exception) {
+ return toString( [exception description] );
+ }
+#else
+ throw;
+#endif
+ }
+ catch( TestFailureException& ) {
+ throw;
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return tryTranslators( m_translators.begin() );
+ }
+ }
+
+ std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
+ if( it == m_translators.end() )
+ return "Unknown exception";
+
+ try {
+ return (*it)->translate();
+ }
+ catch(...) {
+ return tryTranslators( it+1 );
+ }
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+ return m_exceptionTranslatorRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = NULL;
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <ostream>
+
+namespace Catch {
+
+ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+ : m_lineInfo( lineInfo ) {
+ std::ostringstream oss;
+ oss << lineInfo << ": function ";
+ oss << "not implemented";
+ m_what = oss.str();
+ }
+
+ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+ return m_what.c_str();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+ class StreamBufBase : public std::streambuf {
+ public:
+ virtual ~StreamBufBase() CATCH_NOEXCEPT;
+ };
+}
+
+#include <stdexcept>
+#include <cstdio>
+
+namespace Catch {
+
+ template<typename WriterF, size_t bufferSize=256>
+ class StreamBufImpl : public StreamBufBase {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() CATCH_NOEXCEPT {
+ sync();
+ }
+
+ private:
+ int overflow( int c ) {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ Stream::Stream()
+ : streamBuf( NULL ), isOwned( false )
+ {}
+
+ Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
+ : streamBuf( _streamBuf ), isOwned( _isOwned )
+ {}
+
+ void Stream::release() {
+ if( isOwned ) {
+ delete streamBuf;
+ streamBuf = NULL;
+ isOwned = false;
+ }
+ }
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public: // IContext
+ virtual IResultCapture* getResultCapture() {
+ return m_resultCapture;
+ }
+ virtual IRunner* getRunner() {
+ return m_runner;
+ }
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+ return getGeneratorsForCurrentTest()
+ .getGeneratorInfo( fileInfo, totalSize )
+ .getCurrentIndex();
+ }
+ virtual bool advanceGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ return generators && generators->moveNext();
+ }
+
+ virtual Ptr<IConfig const> getConfig() const {
+ return m_config;
+ }
+
+ public: // IMutableContext
+ virtual void setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
+ }
+ virtual void setRunner( IRunner* runner ) {
+ m_runner = runner;
+ }
+ virtual void setConfig( Ptr<IConfig const> const& config ) {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IGeneratorsForTest* findGeneratorsForCurrentTest() {
+ std::string testName = getResultCapture()->getCurrentTestName();
+
+ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+ m_generatorsByTestName.find( testName );
+ return it != m_generatorsByTestName.end()
+ ? it->second
+ : NULL;
+ }
+
+ IGeneratorsForTest& getGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ if( !generators ) {
+ std::string testName = getResultCapture()->getCurrentTestName();
+ generators = createGeneratorsForTest();
+ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+ }
+ return *generators;
+ }
+
+ private:
+ Ptr<IConfig const> m_config;
+ IRunner* m_runner;
+ IResultCapture* m_resultCapture;
+ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+ };
+
+ namespace {
+ Context* currentContext = NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ Stream createStream( std::string const& streamName ) {
+ if( streamName == "stdout" ) return Stream( std::cout.rdbuf(), false );
+ if( streamName == "stderr" ) return Stream( std::cerr.rdbuf(), false );
+ if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
+
+ throw std::domain_error( "Unknown stream: " + streamName );
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch { namespace Detail {
+ struct IColourImpl {
+ virtual ~IColourImpl() {}
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+}}
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public Detail::IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalAttributes = csbiInfo.wAttributes;
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+ }
+ HANDLE stdoutHandle;
+ WORD originalAttributes;
+ };
+
+ inline bool shouldUseColourForPlatform() {
+ return true;
+ }
+
+ static Detail::IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+ return &s_instance;
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public Detail::IColourImpl {
+ public:
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0:34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+ private:
+ void setColour( const char* _escapeCode ) {
+ std::cout << '\033' << _escapeCode;
+ }
+ };
+
+ inline bool shouldUseColourForPlatform() {
+ return isatty(STDOUT_FILENO);
+ }
+
+ static Detail::IColourImpl* platformColourInstance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#endif // not Windows
+
+namespace Catch {
+
+ namespace {
+ struct NoColourImpl : Detail::IColourImpl {
+ void use( Colour::Code ) {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+ static bool shouldUseColour() {
+ return shouldUseColourForPlatform() && !isDebuggerActive();
+ }
+ }
+
+ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+ void Colour::use( Code _colourCode ) {
+ impl()->use( _colourCode );
+ }
+
+ Detail::IColourImpl* Colour::impl() {
+ return shouldUseColour()
+ ? platformColourInstance()
+ : NoColourImpl::instance();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+ struct GeneratorInfo : IGeneratorInfo {
+
+ GeneratorInfo( std::size_t size )
+ : m_size( size ),
+ m_currentIndex( 0 )
+ {}
+
+ bool moveNext() {
+ if( ++m_currentIndex == m_size ) {
+ m_currentIndex = 0;
+ return false;
+ }
+ return true;
+ }
+
+ std::size_t getCurrentIndex() const {
+ return m_currentIndex;
+ }
+
+ std::size_t m_size;
+ std::size_t m_currentIndex;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class GeneratorsForTest : public IGeneratorsForTest {
+
+ public:
+ ~GeneratorsForTest() {
+ deleteAll( m_generatorsInOrder );
+ }
+
+ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+ if( it == m_generatorsByName.end() ) {
+ IGeneratorInfo* info = new GeneratorInfo( size );
+ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+ m_generatorsInOrder.push_back( info );
+ return *info;
+ }
+ return *it->second;
+ }
+
+ bool moveNext() {
+ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+ for(; it != itEnd; ++it ) {
+ if( (*it)->moveNext() )
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+ std::vector<IGeneratorInfo*> m_generatorsInOrder;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest()
+ {
+ return new GeneratorsForTest();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+ AssertionInfo::AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition )
+ {}
+
+ AssertionResult::AssertionResult() {}
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ AssertionResult::~AssertionResult() {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return "!" + m_info.capturedExpression;
+ else
+ return m_info.capturedExpression;
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName.empty() )
+ return m_info.capturedExpression;
+ else
+ return m_info.macroName + "( " + m_info.capturedExpression + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructedExpression;
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ std::string AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+namespace Catch {
+
+ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( tag == "." ||
+ tag == "hide" ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ {
+ Colour colourGuard( Colour::Red );
+ std::cerr
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n";
+ }
+ {
+ Colour colourGuard( Colour::FileName );
+ std::cerr << _lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ TestCase makeTestCase( ITestCase* _testCase,
+ std::string const& _className,
+ std::string const& _name,
+ std::string const& _descOrTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+ // Parse out tags
+ std::set<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+ char c = _descOrTags[i];
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ enforceNotReservedTag( tag, _lineInfo );
+
+ inTag = false;
+ if( tag == "hide" || tag == "." )
+ isHidden = true;
+ else
+ tags.insert( tag );
+ tag.clear();
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ tags.insert( "hide" );
+ tags.insert( "." );
+ }
+
+ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, info );
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ tags( _tags ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ std::ostringstream oss;
+ for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
+ oss << "[" << *it << "]";
+ std::string lcaseTag = toLower( *it );
+ properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
+ lcaseTags.insert( lcaseTag );
+ }
+ tagsAsString = oss.str();
+ }
+
+ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+ : name( other.name ),
+ className( other.className ),
+ description( other.description ),
+ tags( other.tags ),
+ lcaseTags( other.lcaseTags ),
+ tagsAsString( other.tagsAsString ),
+ lineInfo( other.lineInfo ),
+ properties( other.properties )
+ {}
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+ TestCase::TestCase( TestCase const& other )
+ : TestCaseInfo( other ),
+ test( other.test )
+ {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::swap( TestCase& other ) {
+ test.swap( other.test );
+ name.swap( other.name );
+ className.swap( other.className );
+ description.swap( other.description );
+ tags.swap( other.tags );
+ lcaseTags.swap( other.lcaseTags );
+ tagsAsString.swap( other.tagsAsString );
+ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+ std::swap( lineInfo, other.lineInfo );
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+ TestCase& TestCase::operator = ( TestCase const& other ) {
+ TestCase temp( other );
+ swap( temp );
+ return *this;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+ // These numbers are maintained by a script
+ Version libraryVersion( 1, 0, 53, "master" );
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info )
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+ ScopedMessage::ScopedMessage( ScopedMessage const& other )
+ : m_info( other.m_info )
+ {}
+
+ ScopedMessage::~ScopedMessage() {
+ getResultCapture().popScopedMessage( m_info );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+ // Deprecated
+ struct IReporter : IShared {
+ virtual ~IReporter();
+
+ virtual bool shouldRedirectStdout() const = 0;
+
+ virtual void StartTesting() = 0;
+ virtual void EndTesting( Totals const& totals ) = 0;
+ virtual void StartGroup( std::string const& groupName ) = 0;
+ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+ virtual void Aborted() = 0;
+ virtual void Result( AssertionResult const& result ) = 0;
+ };
+
+ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+ {
+ public:
+ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+ virtual ~LegacyReporterAdapter();
+
+ virtual ReporterPreferences getPreferences() const;
+ virtual void noMatchingTestCases( std::string const& );
+ virtual void testRunStarting( TestRunInfo const& );
+ virtual void testGroupStarting( GroupInfo const& groupInfo );
+ virtual void testCaseStarting( TestCaseInfo const& testInfo );
+ virtual void sectionStarting( SectionInfo const& sectionInfo );
+ virtual void assertionStarting( AssertionInfo const& );
+ virtual bool assertionEnded( AssertionStats const& assertionStats );
+ virtual void sectionEnded( SectionStats const& sectionStats );
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+ virtual void testRunEnded( TestRunStats const& testRunStats );
+
+ private:
+ Ptr<IReporter> m_legacyReporter;
+ };
+}
+
+namespace Catch
+{
+ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+ : m_legacyReporter( legacyReporter )
+ {}
+ LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+ return prefs;
+ }
+
+ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+ m_legacyReporter->StartTesting();
+ }
+ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+ m_legacyReporter->StartGroup( groupInfo.name );
+ }
+ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ m_legacyReporter->StartTestCase( testInfo );
+ }
+ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+ }
+ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+ // Not on legacy interface
+ }
+
+ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+ rb << it->message;
+ rb.setResultType( ResultWas::Info );
+ AssertionResult result = rb.build();
+ m_legacyReporter->Result( result );
+ }
+ }
+ }
+ m_legacyReporter->Result( assertionStats.assertionResult );
+ return true;
+ }
+ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+ if( sectionStats.missingAssertions )
+ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+ }
+ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ m_legacyReporter->EndTestCase
+ ( testCaseStats.testInfo,
+ testCaseStats.totals,
+ testCaseStats.stdOut,
+ testCaseStats.stdErr );
+ }
+ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ if( testGroupStats.aborting )
+ m_legacyReporter->Aborted();
+ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+ }
+ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+ m_legacyReporter->EndTesting( testRunStats.totals );
+ }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ uint64_t getCurrentTicks() {
+ static uint64_t hz=0, hzo=0;
+ if (!hz) {
+ QueryPerformanceFrequency((LARGE_INTEGER*)&hz);
+ QueryPerformanceCounter((LARGE_INTEGER*)&hzo);
+ }
+ uint64_t t;
+ QueryPerformanceCounter((LARGE_INTEGER*)&t);
+ return ((t-hzo)*1000000)/hz;
+ }
+#else
+ uint64_t getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,NULL);
+ return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
+ }
+#endif
+ }
+
+ void Timer::start() {
+ m_ticks = getCurrentTicks();
+ }
+ unsigned int Timer::getElapsedNanoseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ unsigned int Timer::getElapsedMilliseconds() const {
+ return static_cast<unsigned int>((getCurrentTicks() - m_ticks)/1000);
+ }
+ double Timer::getElapsedSeconds() const {
+ return (getCurrentTicks() - m_ticks)/1000000.0;
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), ::tolower );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << " " << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << "s";
+ return os;
+ }
+
+ SourceLineInfo::SourceLineInfo() : line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
+ : file( other.file ),
+ line( other.line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file.empty();
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && file == other.file;
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << "(" << info.line << ")";
+#else
+ os << info.file << ":" << info.line;
+#endif
+ return os;
+ }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+ std::ostringstream oss;
+ oss << locationInfo << ": Internal Catch error: '" << message << "'";
+ if( alwaysTrue() )
+ throw std::logic_error( oss.str() );
+ }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description )
+ : name( _name ),
+ description( _description ),
+ lineInfo( _lineInfo )
+ {}
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+ Section::~Section() {
+ if( m_sectionIncluded )
+ getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ }
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#include <iostream>
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+
+ namespace Catch{
+
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+ std::cerr << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ } // namespace Catch
+
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ inline bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+#else
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ std::cout << text;
+ }
+ }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+ namespace {
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ union _{
+ int asInt;
+ char asChar[sizeof (int)];
+ } u;
+
+ u.asInt = 1;
+ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size )
+ {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str();
+ }
+}
+
+std::string toString( std::string const& value ) {
+ std::string s = value;
+ if( getCurrentContext().getConfig()->showInvisibles() ) {
+ for(size_t i = 0; i < s.size(); ++i ) {
+ std::string subs;
+ switch( s[i] ) {
+ case '\n': subs = "\\n"; break;
+ case '\t': subs = "\\t"; break;
+ default: break;
+ }
+ if( !subs.empty() ) {
+ s = s.substr( 0, i ) + subs + s.substr( i+1 );
+ ++i;
+ }
+ }
+ }
+ return "\"" + s + "\"";
+}
+std::string toString( std::wstring const& value ) {
+
+ std::string s;
+ s.reserve( value.size() );
+ for(size_t i = 0; i < value.size(); ++i )
+ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+ return toString( s );
+}
+
+std::string toString( const char* const value ) {
+ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+ return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+ std::ostringstream oss;
+ oss << value;
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ if( value > 8192 )
+ oss << "0x" << std::hex << value;
+ else
+ oss << value;
+ return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+ return toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ std::ostringstream oss;
+ oss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = oss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+std::string toString( const double value ) {
+ return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+ return fpToString( value, 5 ) + "f";
+}
+
+std::string toString( bool value ) {
+ return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+ return value < ' '
+ ? toString( static_cast<unsigned int>( value ) )
+ : Detail::makeString( value );
+}
+
+std::string toString( signed char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+ return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSObject* const& nsObject ) {
+ return toString( [nsObject description] );
+ }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+ ResultBuilder::ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition )
+ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false )
+ {}
+
+ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+ m_data.resultType = result;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setResultType( bool result ) {
+ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
+ m_exprComponents.lhs = lhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
+ m_exprComponents.rhs = rhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
+ m_exprComponents.op = op;
+ return *this;
+ }
+
+ void ResultBuilder::endExpression() {
+ m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
+ captureExpression();
+ }
+
+ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+ m_assertionInfo.resultDisposition = resultDisposition;
+ m_stream.oss << Catch::translateActiveException();
+ captureResult( ResultWas::ThrewException );
+ }
+
+ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+ setResultType( resultType );
+ captureExpression();
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ getResultCapture().assertionEnded( result );
+
+ if( !result.isOk() ) {
+ if( getCurrentContext().getConfig()->shouldDebugBreak() )
+ m_shouldDebugBreak = true;
+ if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal )
+ m_shouldThrow = true;
+ }
+ }
+ void ResultBuilder::react() {
+ if( m_shouldThrow )
+ throw Catch::TestFailureException();
+ }
+
+ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+ AssertionResult ResultBuilder::build() const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+
+ AssertionResultData data = m_data;
+
+ // Flip bool results if testFalse is set
+ if( m_exprComponents.testFalse ) {
+ if( data.resultType == ResultWas::Ok )
+ data.resultType = ResultWas::ExpressionFailed;
+ else if( data.resultType == ResultWas::ExpressionFailed )
+ data.resultType = ResultWas::Ok;
+ }
+
+ data.message = m_stream.oss.str();
+ data.reconstructedExpression = reconstructExpression();
+ if( m_exprComponents.testFalse ) {
+ if( m_exprComponents.op == "" )
+ data.reconstructedExpression = "!" + data.reconstructedExpression;
+ else
+ data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
+ }
+ return AssertionResult( m_assertionInfo, data );
+ }
+ std::string ResultBuilder::reconstructExpression() const {
+ if( m_exprComponents.op == "" )
+ return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+ else if( m_exprComponents.op == "matches" )
+ return m_exprComponents.lhs + " " + m_exprComponents.rhs;
+ else if( m_exprComponents.op != "!" ) {
+ if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
+ m_exprComponents.lhs.find("\n") == std::string::npos &&
+ m_exprComponents.rhs.find("\n") == std::string::npos )
+ return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
+ else
+ return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
+ }
+ else
+ return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ virtual ~TagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+ void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ static TagAliasRegistry& get();
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+#include <map>
+#include <iostream>
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return it->second;
+ else
+ return Option<TagAlias>();
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+ it != itEnd;
+ ++it ) {
+ std::size_t pos = expandedTestSpec.find( it->first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ it->second.tag +
+ expandedTestSpec.substr( pos + it->first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+ << "\tRedefined at " << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ TagAliasRegistry& TagAliasRegistry::get() {
+ static TagAliasRegistry instance;
+ return instance;
+
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+ ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ try {
+ TagAliasRegistry::get().add( alias, tag, lineInfo );
+ }
+ catch( std::exception& ex ) {
+ Colour colourGuard( Colour::Red );
+ std::cerr << ex.what() << std::endl;
+ exit(1);
+ }
+ }
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+namespace Catch {
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+
+ virtual ~StreamingReporterBase();
+
+ virtual void noMatchingTestCases( std::string const& ) {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+ currentTestCaseInfo.reset();
+ assert( m_sectionStack.empty() );
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ };
+
+ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+ template<typename T, typename ChildNodeT>
+ struct Node : SharedImpl<> {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode : SharedImpl<> {
+ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+ virtual ~SectionNode();
+
+ bool operator == ( SectionNode const& other ) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == ( Ptr<SectionNode> const& other ) const {
+ return operator==( *other );
+ }
+
+ SectionStats stats;
+ typedef std::vector<Ptr<SectionNode> > ChildSections;
+ typedef std::vector<AssertionStats> Assertions;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() ( Ptr<SectionNode> const& node ) const {
+ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+ }
+ private:
+ void operator=( BySectionInfo const& );
+ SectionInfo const& m_other;
+ };
+
+ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+ ~CumulativeReporterBase();
+
+ virtual void testRunStarting( TestRunInfo const& ) {}
+ virtual void testGroupStarting( GroupInfo const& ) {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ Ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = new SectionNode( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ SectionNode::ChildSections::const_iterator it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = new SectionNode( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = node;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+ assert( m_sectionStack.size() == 0 );
+ node->children.push_back( m_rootSection );
+ m_testCases.push_back( node );
+ m_rootSection.reset();
+
+ assert( m_deepestSection );
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) {
+ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+ node->children.swap( m_testGroups );
+ m_testRuns.push_back( node );
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+ std::vector<Ptr<TestCaseNode> > m_testCases;
+ std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+ std::vector<Ptr<TestRunNode> > m_testRuns;
+
+ Ptr<SectionNode> m_rootSection;
+ Ptr<SectionNode> m_deepestSection;
+ std::vector<Ptr<SectionNode> > m_sectionStack;
+
+ };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+ template<typename T>
+ class LegacyReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new LegacyReporterAdapter( new T( config ) );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ LegacyReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+
+ // *** Please Note ***:
+ // - If you end up here looking at a compiler error because it's trying to register
+ // your custom reporter class be aware that the native reporter interface has changed
+ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+ // However please consider updating to the new interface as the old one is now
+ // deprecated and will probably be removed quite soon!
+ // Please contact me via github if you have any questions at all about this.
+ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+ // no idea who is actually using custom reporters at all (possibly no-one!).
+ // The new interface is designed to minimise exposure to interface changes in the future.
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = NULL;
+ }
+
+ ~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ ScopedElement& writeText( std::string const& text, bool indent = true ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer;
+ };
+
+ XmlWriter()
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &std::cout )
+ {}
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &os )
+ {}
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+//# ifndef CATCH_CPP11_OR_GREATER
+// XmlWriter& operator = ( XmlWriter const& other ) {
+// XmlWriter temp( other );
+// swap( temp );
+// return *this;
+// }
+//# else
+// XmlWriter( XmlWriter const& ) = default;
+// XmlWriter( XmlWriter && ) = default;
+// XmlWriter& operator = ( XmlWriter const& ) = default;
+// XmlWriter& operator = ( XmlWriter && ) = default;
+//# endif
+//
+// void swap( XmlWriter& other ) {
+// std::swap( m_tagIsOpen, other.m_tagIsOpen );
+// std::swap( m_needsNewline, other.m_needsNewline );
+// std::swap( m_tags, other.m_tags );
+// std::swap( m_indent, other.m_indent );
+// std::swap( m_os, other.m_os );
+// }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ stream() << m_indent << "<" << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ ScopedElement scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ stream() << "/>\n";
+ m_tagIsOpen = false;
+ }
+ else {
+ stream() << m_indent << "</" << m_tags.back() << ">\n";
+ }
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() ) {
+ stream() << " " << name << "=\"";
+ writeEncodedText( attribute );
+ stream() << "\"";
+ }
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ if( !name.empty() )
+ stream() << " " << name << "=\"" << attribute << "\"";
+ return *this;
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ stream() << m_indent;
+ writeEncodedText( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ stream() << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ stream() << "\n";
+ return *this;
+ }
+
+ void setStream( std::ostream& os ) {
+ m_os = &os;
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ std::ostream& stream() {
+ return *m_os;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ stream() << ">\n";
+ m_tagIsOpen = false;
+ }
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ stream() << "\n";
+ m_needsNewline = false;
+ }
+ }
+
+ void writeEncodedText( std::string const& text ) {
+ static const char* charsToEncode = "<&\"";
+ std::string mtext = text;
+ std::string::size_type pos = mtext.find_first_of( charsToEncode );
+ while( pos != std::string::npos ) {
+ stream() << mtext.substr( 0, pos );
+
+ switch( mtext[pos] ) {
+ case '<':
+ stream() << "<";
+ break;
+ case '&':
+ stream() << "&";
+ break;
+ case '\"':
+ stream() << """;
+ break;
+ }
+ mtext = mtext.substr( pos+1 );
+ pos = mtext.find_first_of( charsToEncode );
+ }
+ stream() << mtext;
+ }
+
+ bool m_tagIsOpen;
+ bool m_needsNewline;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream* m_os;
+ };
+
+}
+namespace Catch {
+ class XmlReporter : public SharedImpl<IReporter> {
+ public:
+ XmlReporter( ReporterConfig const& config ) : m_config( config ), m_sectionDepth( 0 ) {}
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+ virtual ~XmlReporter();
+
+ private: // IReporter
+
+ virtual bool shouldRedirectStdout() const {
+ return true;
+ }
+
+ virtual void StartTesting() {
+ m_xml.setStream( m_config.stream() );
+ m_xml.startElement( "Catch" );
+ if( !m_config.fullConfig()->name().empty() )
+ m_xml.writeAttribute( "name", m_config.fullConfig()->name() );
+ }
+
+ virtual void EndTesting( const Totals& totals ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", totals.assertions.passed )
+ .writeAttribute( "failures", totals.assertions.failed )
+ .writeAttribute( "expectedFailures", totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void StartGroup( const std::string& groupName ) {
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupName );
+ }
+
+ virtual void EndGroup( const std::string&, const Totals& totals ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", totals.assertions.passed )
+ .writeAttribute( "failures", totals.assertions.failed )
+ .writeAttribute( "expectedFailures", totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void StartSection( const std::string& sectionName, const std::string& description ) {
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionName ) )
+ .writeAttribute( "description", description );
+ }
+ }
+ virtual void NoAssertionsInSection( const std::string& ) {}
+ virtual void NoAssertionsInTestCase( const std::string& ) {}
+
+ virtual void EndSection( const std::string& /*sectionName*/, const Counts& assertions ) {
+ if( --m_sectionDepth > 0 ) {
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", assertions.passed )
+ .writeAttribute( "failures", assertions.failed )
+ .writeAttribute( "expectedFailures", assertions.failedButOk );
+ m_xml.endElement();
+ }
+ }
+
+ virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) {
+ m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
+ m_currentTestSuccess = true;
+ }
+
+ virtual void Result( const Catch::AssertionResult& assertionResult ) {
+ if( !m_config.fullConfig()->includeSuccessfulResults() && assertionResult.getResultType() == ResultWas::Ok )
+ return;
+
+ if( assertionResult.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", assertionResult.succeeded() )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( assertionResult.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( assertionResult.getExpandedExpression() );
+ m_currentTestSuccess &= assertionResult.succeeded();
+ }
+
+ switch( assertionResult.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.scopedElement( "Exception" )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line )
+ .writeText( assertionResult.getMessage() );
+ m_currentTestSuccess = false;
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::Warning:
+ m_xml.scopedElement( "Warning" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.scopedElement( "Failure" )
+ .writeText( assertionResult.getMessage() );
+ m_currentTestSuccess = false;
+ break;
+ case ResultWas::Unknown:
+ case ResultWas::Ok:
+ case ResultWas::FailureBit:
+ case ResultWas::ExpressionFailed:
+ case ResultWas::Exception:
+ case ResultWas::DidntThrowException:
+ break;
+ }
+ if( assertionResult.hasExpression() )
+ m_xml.endElement();
+ }
+
+ virtual void Aborted() {
+ // !TBD
+ }
+
+ virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) {
+ m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess );
+ m_xml.endElement();
+ }
+
+ private:
+ ReporterConfig m_config;
+ bool m_currentTestSuccess;
+ XmlWriter m_xml;
+ int m_sectionDepth;
+ };
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() )
+ {}
+
+ ~JunitReporter();
+
+ static std::string getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = true;
+ return prefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() {
+ xml.endElement();
+ }
+
+ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "timestamp", "tbd" ); // !TBD
+
+ // Write test cases
+ for( TestGroupNode::ChildNodes::const_iterator
+ it = groupNode.children.begin(), itEnd = groupNode.children.end();
+ it != itEnd;
+ ++it )
+ writeTestCase( **it );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+ }
+
+ void writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ if( rootSection.childSections.empty() )
+ className = "global";
+ }
+ writeSection( className, "", rootSection );
+ }
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode ) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + "/" + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", toString( sectionNode.stats.durationInSeconds ) );
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ }
+ for( SectionNode::ChildSections::const_iterator
+ it = sectionNode.childSections.begin(),
+ itEnd = sectionNode.childSections.end();
+ it != itEnd;
+ ++it )
+ if( className.empty() )
+ writeSection( name, "", **it );
+ else
+ writeSection( className, name, **it );
+ }
+
+ void writeAssertions( SectionNode const& sectionNode ) {
+ for( SectionNode::Assertions::const_iterator
+ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+ it != itEnd;
+ ++it )
+ writeAssertion( *it );
+ }
+ void writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ elementName = "failure";
+ break;
+ case ResultWas::ExpressionFailed:
+ elementName = "failure";
+ break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ std::ostringstream oss;
+ if( !result.getMessage().empty() )
+ oss << result.getMessage() << "\n";
+ for( std::vector<MessageInfo>::const_iterator
+ it = stats.infoMessages.begin(),
+ itEnd = stats.infoMessages.end();
+ it != itEnd;
+ ++it )
+ if( it->type == ResultWas::Info )
+ oss << it->message << "\n";
+
+ oss << "at " << result.getSourceInfo();
+ xml.writeText( oss.str(), false );
+ }
+ }
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::ostringstream stdOutForSuite;
+ std::ostringstream stdErrForSuite;
+ unsigned int unexpectedExceptions;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cstring>
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter();
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) {
+ if( _sectionStats.missingAssertions ) {
+ lazyPrint();
+ Colour colour( Colour::ResultError );
+ if( m_sectionStack.size() > 1 )
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ if( m_headerPrinted ) {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ m_headerPrinted = false;
+ }
+ else {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+ if( currentGroupInfo.used ) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals( _testGroupStats.totals );
+ stream << "\n" << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded( _testGroupStats );
+ }
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotalsDivider( _testRunStats.totals );
+ printTotals( _testRunStats.totals );
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream ),
+ stats( _stats ),
+ result( _stats.assertionResult ),
+ colour( Colour::None ),
+ message( result.getMessage() ),
+ messages( _stats.infoMessages ),
+ printInfoMessages( _printInfoMessages )
+ {
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() ) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ }
+ else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with message";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "explicitly with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if( stats.totals.assertions.total() > 0 ) {
+ if( result.isOk() )
+ stream << "\n";
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ }
+ else {
+ stream << "\n";
+ }
+ printMessage();
+ }
+
+ private:
+ void printResultType() const {
+ if( !passOrFail.empty() ) {
+ Colour colourGuard( colour );
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ Colour colourGuard( Colour::OriginalExpression );
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << "\n";
+ }
+ }
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ stream << "with expansion:\n";
+ Colour colourGuard( Colour::ReconstructedExpression );
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printMessage() const {
+ if( !messageLabel.empty() )
+ stream << messageLabel << ":" << "\n";
+ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+ it != itEnd;
+ ++it ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || it->type != ResultWas::Info )
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+ };
+
+ void lazyPrint() {
+
+ if( !currentTestRunInfo.used )
+ lazyPrintRunInfo();
+ if( !currentGroupInfo.used )
+ lazyPrintGroupInfo();
+
+ if( !m_headerPrinted ) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+ }
+ void lazyPrintRunInfo() {
+ stream << "\n" << getLineOfChars<'~'>() << "\n";
+ Colour colour( Colour::SecondaryText );
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " b"
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ stream << " (" << libraryVersion.branchName << ")";
+ stream << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ currentTestRunInfo.used = true;
+ }
+ void lazyPrintGroupInfo() {
+ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+ printClosedHeader( "Group: " + currentGroupInfo->name );
+ currentGroupInfo.used = true;
+ }
+ }
+ void printTestCaseAndSectionHeader() {
+ assert( !m_sectionStack.empty() );
+ printOpenHeader( currentTestCaseInfo->name );
+
+ if( m_sectionStack.size() > 1 ) {
+ Colour colourGuard( Colour::Headers );
+
+ std::vector<SectionInfo>::const_iterator
+ it = m_sectionStack.begin()+1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for( ; it != itEnd; ++it )
+ printHeaderString( it->name, 2 );
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+ if( !lineInfo.empty() ){
+ stream << getLineOfChars<'-'>() << "\n";
+ Colour colourGuard( Colour::FileName );
+ stream << lineInfo << "\n";
+ }
+ stream << getLineOfChars<'.'>() << "\n" << std::endl;
+ }
+
+ void printClosedHeader( std::string const& _name ) {
+ printOpenHeader( _name );
+ stream << getLineOfChars<'.'>() << "\n";
+ }
+ void printOpenHeader( std::string const& _name ) {
+ stream << getLineOfChars<'-'>() << "\n";
+ {
+ Colour colourGuard( Colour::Headers );
+ printHeaderString( _name );
+ }
+ }
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+ std::size_t i = _string.find( ": " );
+ if( i != std::string::npos )
+ i+=2;
+ else
+ i = 0;
+ stream << Text( _string, TextAttributes()
+ .setIndent( indent+i)
+ .setInitialIndent( indent ) ) << "\n";
+ }
+
+ struct SummaryColumn {
+
+ SummaryColumn( std::string const& _label, Colour::Code _colour )
+ : label( _label ),
+ colour( _colour )
+ {}
+ SummaryColumn addRow( std::size_t count ) {
+ std::ostringstream oss;
+ oss << count;
+ std::string row = oss.str();
+ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+ while( it->size() < row.size() )
+ *it = " " + *it;
+ while( it->size() > row.size() )
+ row = " " + row;
+ }
+ rows.push_back( row );
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+ };
+
+ void printTotals( Totals const& totals ) {
+ if( totals.testCases.total() == 0 ) {
+ stream << Colour( Colour::Warning ) << "No tests ran\n";
+ }
+ else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+ stream << " ("
+ << pluralise( totals.assertions.passed, "assertion" ) << " in "
+ << pluralise( totals.testCases.passed, "test case" ) << ")"
+ << "\n";
+ }
+ else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back( SummaryColumn( "", Colour::None )
+ .addRow( totals.testCases.total() )
+ .addRow( totals.assertions.total() ) );
+ columns.push_back( SummaryColumn( "passed", Colour::Success )
+ .addRow( totals.testCases.passed )
+ .addRow( totals.assertions.passed ) );
+ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+ .addRow( totals.testCases.failed )
+ .addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+ .addRow( totals.testCases.failedButOk )
+ .addRow( totals.assertions.failedButOk ) );
+
+ printSummaryRow( "test cases", columns, 0 );
+ printSummaryRow( "assertions", columns, 1 );
+ }
+ }
+ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+ std::string value = it->rows[row];
+ if( it->label.empty() ) {
+ stream << label << ": ";
+ if( value != "0" )
+ stream << value;
+ else
+ stream << Colour( Colour::Warning ) << "- none -";
+ }
+ else if( value != "0" ) {
+ stream << Colour( Colour::LightGrey ) << " | ";
+ stream << Colour( it->colour )
+ << value << " " << it->label;
+ }
+ }
+ stream << "\n";
+ }
+
+ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+ }
+ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+ if( i > j && i > k )
+ return i;
+ else if( j > k )
+ return j;
+ else
+ return k;
+ }
+
+ void printTotalsDivider( Totals const& totals ) {
+ if( totals.testCases.total() > 0 ) {
+ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )++;
+ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+ if( totals.testCases.allPassed() )
+ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+ else
+ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+ }
+ else {
+ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+ }
+ stream << "\n";
+ }
+ void printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << "\n";
+ }
+ template<char C>
+ static char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ private:
+ bool m_headerPrinted;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase {
+
+ CompactReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual ~CompactReporter();
+
+ static std::string getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( _testRunStats.totals );
+ stream << "\n" << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream )
+ , stats( _stats )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( _printInfoMessages )
+ {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( Colour::ResultSuccess, passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() )
+ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+ else
+ printResultType( Colour::Error, failedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( Colour::None, "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( Colour::None, "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "explicitly" );
+ printRemainingMessages( Colour::None );
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType( Colour::Error, "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ // Colour::LightGrey
+
+ static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+ static const char* failedString() { return "FAILED"; }
+ static const char* passedString() { return "PASSED"; }
+#else
+ static const char* failedString() { return "failed"; }
+ static const char* passedString() { return "passed"; }
+#endif
+
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ":";
+ }
+
+ void printResultType( Colour::Code colour, std::string passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << " " << passOrFail;
+ }
+ stream << ":";
+ }
+ }
+
+ void printIssue( std::string issue ) const {
+ stream << " " << issue;
+ }
+
+ void printExpressionWas() {
+ if( result.hasExpression() ) {
+ stream << ";";
+ {
+ Colour colour( dimColour() );
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ stream << " " << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ {
+ Colour colour( dimColour() );
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if ( itMessage != messages.end() ) {
+ stream << " '" << itMessage->message << "'";
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages( Colour::Code colour = dimColour() ) {
+ if ( itMessage == messages.end() )
+ return;
+
+ // using messages.end() directly yields compilation error:
+ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+ {
+ Colour colourGuard( colour );
+ stream << " with " << pluralise( N, "message" ) << ":";
+ }
+
+ for(; itMessage != itEnd; ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+ stream << " '" << itMessage->message << "'";
+ if ( ++itMessage != itEnd ) {
+ Colour colourGuard( dimColour() );
+ stream << " and";
+ }
+ }
+ }
+ }
+
+ private:
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+ };
+
+ // Colour, message variants:
+ // - white: No tests ran.
+ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+ // - white: Passed [both/all] N test cases (no assertions).
+ // - red: Failed N tests cases, failed M assertions.
+ // - green: Passed [both/all] N tests cases with M assertions.
+
+ std::string bothOrAll( std::size_t count ) const {
+ return count == 1 ? "" : count == 2 ? "both " : "all " ;
+ }
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "No tests ran.";
+ }
+ else if( totals.testCases.failed == totals.testCases.total() ) {
+ Colour colour( Colour::ResultError );
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll( totals.assertions.failed ) : "";
+ stream <<
+ "Failed " << bothOrAll( totals.testCases.failed )
+ << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else if( totals.assertions.total() == 0 ) {
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.total() )
+ << pluralise( totals.testCases.total(), "test case" )
+ << " (no assertions).";
+ }
+ else if( totals.assertions.failed ) {
+ Colour colour( Colour::ResultError );
+ stream <<
+ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else {
+ Colour colour( Colour::ResultSuccess );
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.passed )
+ << pluralise( totals.testCases.passed, "test case" ) <<
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
+ }
+ }
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+ IContext::~IContext() {}
+ IResultCapture::~IResultCapture() {}
+ ITestCase::~ITestCase() {}
+ ITestCaseRegistry::~ITestCaseRegistry() {}
+ IRegistryHub::~IRegistryHub() {}
+ IMutableRegistryHub::~IMutableRegistryHub() {}
+ IExceptionTranslator::~IExceptionTranslator() {}
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+ IReporter::~IReporter() {}
+ IReporterFactory::~IReporterFactory() {}
+ IReporterRegistry::~IReporterRegistry() {}
+ IStreamingReporter::~IStreamingReporter() {}
+ AssertionStats::~AssertionStats() {}
+ SectionStats::~SectionStats() {}
+ TestCaseStats::~TestCaseStats() {}
+ TestGroupStats::~TestGroupStats() {}
+ TestRunStats::~TestRunStats() {}
+ CumulativeReporterBase::SectionNode::~SectionNode() {}
+ CumulativeReporterBase::~CumulativeReporterBase() {}
+
+ StreamingReporterBase::~StreamingReporterBase() {}
+ ConsoleReporter::~ConsoleReporter() {}
+ CompactReporter::~CompactReporter() {}
+ IRunner::~IRunner() {}
+ IMutableContext::~IMutableContext() {}
+ IConfig::~IConfig() {}
+ XmlReporter::~XmlReporter() {}
+ JunitReporter::~JunitReporter() {}
+ TestRegistry::~TestRegistry() {}
+ FreeFunctionTestCase::~FreeFunctionTestCase() {}
+ IGeneratorInfo::~IGeneratorInfo() {}
+ IGeneratorsForTest::~IGeneratorsForTest() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+ Matchers::Impl::StdString::Equals::~Equals() {}
+ Matchers::Impl::StdString::Contains::~Contains() {}
+ Matchers::Impl::StdString::StartsWith::~StartsWith() {}
+ Matchers::Impl::StdString::EndsWith::~EndsWith() {}
+
+ void Config::dummy() {}
+
+ INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter )
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * const argv[]) {
+ return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return result;
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
+#else
+ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
+#else
+ #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc ) SECTION( " Given: " desc, "" )
+#define WHEN( desc ) SECTION( " When: " desc, "" )
+#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
+#define THEN( desc ) SECTION( " Then: " desc, "" )
+#define AND_THEN( desc ) SECTION( " And: " desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#elif defined __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/test/include/utils.hpp b/test/include/utils.hpp
new file mode 100644
index 0000000..662155a
--- /dev/null
+++ b/test/include/utils.hpp
@@ -0,0 +1,18 @@
+
+#include <cstdlib>
+#include <string>
+
+inline std::string with_data_dir(const char* filename) {
+ const char* data_dir = getenv("OSMIUM_TEST_DATA_DIR");
+
+ std::string result;
+ if (data_dir) {
+ result = data_dir;
+ result += '/';
+ }
+
+ result += filename;
+
+ return result;
+}
+
diff --git a/test/include/win_mkstemp.hpp b/test/include/win_mkstemp.hpp
new file mode 100644
index 0000000..693c266
--- /dev/null
+++ b/test/include/win_mkstemp.hpp
@@ -0,0 +1,42 @@
+/*
+ * mkstemp.c
+ *
+ * Provides a trivial replacement for the POSIX `mkstemp()' function,
+ * suitable for use in MinGW (Win32) applications.
+ *
+ * This file is part of the MinGW32 package set.
+ *
+ * Contributed by Keith Marshall <keithmarshall at users.sourceforge.net>
+ * Patched to VS2013 by alex85k
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef WIN_MKSTEMP_H
+#define WIN_MKSTEMP_H
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <share.h>
+
+inline int mkstemp( char *templ )
+{
+ int maxtry = 26, rtn = -1;
+
+ while( maxtry-- && (rtn < 0) )
+ {
+ char *r = _mktemp( templ );
+ if( r == NULL )
+ return -1;
+ rtn = sopen( r, O_RDWR | O_CREAT | O_EXCL | O_BINARY, SH_DENYRW, 0600 );
+ }
+ return rtn;
+}
+#endif
diff --git a/test/t/area/test_area_id.cpp b/test/t/area/test_area_id.cpp
new file mode 100644
index 0000000..45bf87c
--- /dev/null
+++ b/test/t/area/test_area_id.cpp
@@ -0,0 +1,25 @@
+#include "catch.hpp"
+
+#include <osmium/osm/area.hpp>
+
+TEST_CASE("area_id") {
+
+SECTION("object_id_to_area_id_conversion") {
+ REQUIRE( 46 == osmium::object_id_to_area_id( 23, osmium::item_type::way));
+ REQUIRE( 47 == osmium::object_id_to_area_id( 23, osmium::item_type::relation));
+ REQUIRE( 0 == osmium::object_id_to_area_id( 0, osmium::item_type::way));
+ REQUIRE( 1 == osmium::object_id_to_area_id( 0, osmium::item_type::relation));
+ REQUIRE(-24 == osmium::object_id_to_area_id(-12, osmium::item_type::way));
+ REQUIRE(-25 == osmium::object_id_to_area_id(-12, osmium::item_type::relation));
+}
+
+SECTION("area_id_to_object_id_conversion") {
+ REQUIRE( 23 == osmium::area_id_to_object_id( 46));
+ REQUIRE( 23 == osmium::area_id_to_object_id( 47));
+ REQUIRE( 0 == osmium::area_id_to_object_id( 0));
+ REQUIRE( 0 == osmium::area_id_to_object_id( 1));
+ REQUIRE(-12 == osmium::area_id_to_object_id(-24));
+ REQUIRE(-12 == osmium::area_id_to_object_id(-25));
+}
+
+}
diff --git a/test/t/area/test_node_ref_segment.cpp b/test/t/area/test_node_ref_segment.cpp
new file mode 100644
index 0000000..fd67e6c
--- /dev/null
+++ b/test/t/area/test_node_ref_segment.cpp
@@ -0,0 +1,115 @@
+#include "catch.hpp"
+
+#include <osmium/area/detail/node_ref_segment.hpp>
+
+using osmium::area::detail::NodeRefSegment;
+
+TEST_CASE("NodeRefSegmentClass") {
+
+SECTION("instantiation_with_default_parameters") {
+ NodeRefSegment s;
+ REQUIRE(s.first().ref() == 0);
+ REQUIRE(s.first().location() == osmium::Location());
+ REQUIRE(s.second().ref() == 0);
+ REQUIRE(s.second().location() == osmium::Location());
+}
+
+SECTION("instantiation") {
+ osmium::NodeRef nr1(1, { 1.2, 3.4 });
+ osmium::NodeRef nr2(2, { 1.4, 3.1 });
+ osmium::NodeRef nr3(3, { 1.2, 3.6 });
+ osmium::NodeRef nr4(4, { 1.2, 3.7 });
+
+ NodeRefSegment s1(nr1, nr2, nullptr, nullptr);
+ REQUIRE(s1.first().ref() == 1);
+ REQUIRE(s1.second().ref() == 2);
+
+ NodeRefSegment s2(nr2, nr3, nullptr, nullptr);
+ REQUIRE(s2.first().ref() == 3);
+ REQUIRE(s2.second().ref() == 2);
+
+ NodeRefSegment s3(nr3, nr4, nullptr, nullptr);
+ 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);
+
+ REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0));
+ REQUIRE(calculate_intersection(s1, s3) == osmium::Location());
+ REQUIRE(calculate_intersection(s2, s3) == osmium::Location());
+ REQUIRE(calculate_intersection(s1, s4) == 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());
+}
+
+SECTION("to_left_of") {
+ osmium::Location loc { 2.0, 2.0 };
+
+ 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(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(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(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+ 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(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(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(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(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(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);
+}
+
+SECTION("ordering") {
+ osmium::NodeRef node_ref1(1, { 1.0, 3.0 });
+ osmium::NodeRef node_ref2(2, { 1.4, 2.9 });
+ osmium::NodeRef node_ref3(3, { 1.2, 3.0 });
+ osmium::NodeRef node_ref4(4, { 1.2, 3.3 });
+
+ REQUIRE(node_ref1 < node_ref2);
+ REQUIRE(node_ref2 < node_ref3);
+ REQUIRE(node_ref1 < node_ref3);
+ REQUIRE(node_ref1 >= node_ref1);
+
+ REQUIRE( osmium::location_less()(node_ref1, node_ref2));
+ REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
+ REQUIRE( osmium::location_less()(node_ref1, node_ref3));
+ REQUIRE( osmium::location_less()(node_ref3, node_ref4));
+ REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
+}
+
+}
+
diff --git a/test/t/basic/helper.hpp b/test/t/basic/helper.hpp
new file mode 100644
index 0000000..61e0769
--- /dev/null
+++ b/test/t/basic/helper.hpp
@@ -0,0 +1,97 @@
+#ifndef TEST_BASIC_HELPER_HPP
+#define TEST_BASIC_HELPER_HPP
+
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <osmium/builder/osm_object_builder.hpp>
+
+inline void add_tags(osmium::memory::Buffer& buffer, osmium::builder::Builder& builder, const std::vector<std::pair<const char*, const char*>>& tags) {
+ osmium::builder::TagListBuilder tl_builder(buffer, &builder);
+ for (auto& tag : tags) {
+ tl_builder.add_tag(tag.first, tag.second);
+ }
+}
+
+inline osmium::Node& buffer_add_node(osmium::memory::Buffer& buffer, const char* user, const std::vector<std::pair<const char*, const char*>>& tags, const osmium::Location& location) {
+ osmium::builder::NodeBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+ buffer.commit();
+ return builder.object().set_location(location);
+}
+
+inline osmium::Way& buffer_add_way(osmium::memory::Buffer& buffer, const char* user, const std::vector<std::pair<const char*, const char*>>& tags, const std::vector<osmium::object_id_type>& nodes) {
+ osmium::builder::WayBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+ osmium::builder::WayNodeListBuilder wnl_builder(buffer, &builder);
+ for (const osmium::object_id_type ref : nodes) {
+ wnl_builder.add_node_ref(ref);
+ }
+ buffer.commit();
+ return builder.object();
+}
+
+inline osmium::Way& buffer_add_way(osmium::memory::Buffer& buffer, const char* user, const std::vector<std::pair<const char*, const char*>>& tags, const std::vector<std::pair<osmium::object_id_type, osmium::Location>>& nodes) {
+ osmium::builder::WayBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+ osmium::builder::WayNodeListBuilder wnl_builder(buffer, &builder);
+ for (auto& p : nodes) {
+ wnl_builder.add_node_ref(p.first, p.second);
+ }
+ buffer.commit();
+ return builder.object();
+}
+
+inline osmium::Relation& buffer_add_relation(
+ osmium::memory::Buffer& buffer,
+ const char* user,
+ const std::vector<std::pair<const char*, const char*>>& tags, const std::vector<std::tuple<char, osmium::object_id_type, const char*>>& members) {
+ osmium::builder::RelationBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+ osmium::builder::RelationMemberListBuilder rml_builder(buffer, &builder);
+ for (const auto& member : members) {
+ rml_builder.add_member(osmium::char_to_item_type(std::get<0>(member)), std::get<1>(member), std::get<2>(member));
+ }
+ buffer.commit();
+ return builder.object();
+}
+
+inline osmium::Area& buffer_add_area(osmium::memory::Buffer& buffer, const char* user,
+ const std::vector<std::pair<const char*, const char*>>& tags,
+ const std::vector<std::pair<bool,
+ std::vector<std::pair<osmium::object_id_type, osmium::Location>>>>& rings) {
+ osmium::builder::AreaBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+
+ for (auto& ring : rings) {
+ if (ring.first) {
+ osmium::builder::OuterRingBuilder ring_builder(buffer, &builder);
+ for (auto& p : ring.second) {
+ ring_builder.add_node_ref(p.first, p.second);
+ }
+ } else {
+ osmium::builder::InnerRingBuilder ring_builder(buffer, &builder);
+ for (auto& p : ring.second) {
+ ring_builder.add_node_ref(p.first, p.second);
+ }
+ }
+ }
+ buffer.commit();
+ return builder.object();
+}
+
+inline osmium::Changeset& buffer_add_changeset(osmium::memory::Buffer& buffer, const char* user, const std::vector<std::pair<const char*, const char*>>& tags) {
+ osmium::builder::ChangesetBuilder builder(buffer);
+ builder.add_user(user);
+ add_tags(buffer, builder, tags);
+ buffer.commit();
+ return builder.object();
+}
+
+#endif // TEST_BASIC_HELPER_HPP
diff --git a/test/t/basic/test_box.cpp b/test/t/basic/test_box.cpp
new file mode 100644
index 0000000..73a45f4
--- /dev/null
+++ b/test/t/basic/test_box.cpp
@@ -0,0 +1,81 @@
+#include "catch.hpp"
+
+#include <sstream>
+
+#include <osmium/osm/box.hpp>
+#include <osmium/geom/relations.hpp>
+
+TEST_CASE("Box") {
+
+SECTION("instantiation") {
+ osmium::Box b;
+ REQUIRE(!b);
+ REQUIRE(!b.bottom_left());
+ REQUIRE(!b.top_right());
+ REQUIRE_THROWS_AS(b.size(), osmium::invalid_location);
+}
+
+SECTION("instantiation_and_extend_with_undefined") {
+ osmium::Box b;
+ REQUIRE(!b);
+ b.extend(osmium::Location());
+ REQUIRE(!b.bottom_left());
+ REQUIRE(!b.top_right());
+}
+
+SECTION("instantiation_and_extend") {
+ osmium::Box b;
+ b.extend(osmium::Location(1.2, 3.4));
+ REQUIRE(!!b);
+ REQUIRE(!!b.bottom_left());
+ REQUIRE(!!b.top_right());
+ b.extend(osmium::Location(3.4, 4.5));
+ b.extend(osmium::Location(5.6, 7.8));
+ REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
+ REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
+
+ // extend with undefined doesn't change anything
+ b.extend(osmium::Location());
+ REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
+ REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
+}
+
+SECTION("output_defined") {
+ osmium::Box b;
+ b.extend(osmium::Location(1.2, 3.4));
+ b.extend(osmium::Location(5.6, 7.8));
+ std::stringstream out;
+ out << b;
+ REQUIRE(out.str() == "(1.2,3.4,5.6,7.8)");
+ REQUIRE(b.size() == Approx(19.36).epsilon(0.000001));
+}
+
+SECTION("output_undefined") {
+ osmium::Box b;
+ std::stringstream out;
+ out << b;
+ REQUIRE(out.str() == "(undefined)");
+}
+
+SECTION("box_inside_box") {
+ osmium::Box outer;
+ outer.extend(osmium::Location(1, 1));
+ outer.extend(osmium::Location(10, 10));
+
+ osmium::Box inner;
+ inner.extend(osmium::Location(2, 2));
+ inner.extend(osmium::Location(4, 4));
+
+ osmium::Box overlap;
+ overlap.extend(osmium::Location(3, 3));
+ overlap.extend(osmium::Location(5, 5));
+
+ REQUIRE( osmium::geom::contains(inner, outer));
+ REQUIRE(!osmium::geom::contains(outer, inner));
+
+ REQUIRE(!osmium::geom::contains(overlap, inner));
+ REQUIRE(!osmium::geom::contains(inner, overlap));
+}
+
+}
+
diff --git a/test/t/basic/test_changeset.cpp b/test/t/basic/test_changeset.cpp
new file mode 100644
index 0000000..2549c1e
--- /dev/null
+++ b/test/t/basic/test_changeset.cpp
@@ -0,0 +1,57 @@
+#include "catch.hpp"
+
+#include <osmium/osm/changeset.hpp>
+
+#include "helper.hpp"
+
+TEST_CASE("Basic_Changeset") {
+
+SECTION("changeset_builder") {
+ osmium::memory::Buffer buffer(10 * 1000);
+
+ osmium::Changeset& cs1 = buffer_add_changeset(buffer,
+ "user",
+ {{"comment", "foo"}});
+
+ cs1.set_id(42)
+ .set_created_at(100)
+ .set_closed_at(200)
+ .set_num_changes(7)
+ .set_uid(9);
+
+ REQUIRE(42 == cs1.id());
+ REQUIRE(9 == cs1.uid());
+ REQUIRE(7 == cs1.num_changes());
+ REQUIRE(true == cs1.closed());
+ REQUIRE(osmium::Timestamp(100) == cs1.created_at());
+ REQUIRE(osmium::Timestamp(200) == cs1.closed_at());
+ REQUIRE(1 == cs1.tags().size());
+ REQUIRE(std::string("user") == cs1.user());
+
+ osmium::Changeset& cs2 = buffer_add_changeset(buffer,
+ "user",
+ {{"comment", "foo"}, {"foo", "bar"}});
+
+ cs2.set_id(43)
+ .set_created_at(120)
+ .set_num_changes(21)
+ .set_uid(9);
+
+ REQUIRE(43 == cs2.id());
+ REQUIRE(9 == cs2.uid());
+ REQUIRE(21 == cs2.num_changes());
+ REQUIRE(false == cs2.closed());
+ REQUIRE(osmium::Timestamp(120) == cs2.created_at());
+ REQUIRE(osmium::Timestamp() == cs2.closed_at());
+ REQUIRE(2 == cs2.tags().size());
+ REQUIRE(std::string("user") == cs2.user());
+
+ REQUIRE(cs1 != cs2);
+
+ REQUIRE(cs1 < cs2);
+ REQUIRE(cs1 <= cs2);
+ REQUIRE(false == (cs1 > cs2));
+ REQUIRE(false == (cs1 >= cs2));
+}
+
+}
diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp
new file mode 100644
index 0000000..a200f48
--- /dev/null
+++ b/test/t/basic/test_entity_bits.cpp
@@ -0,0 +1,31 @@
+#include "catch.hpp"
+
+#include <osmium/osm/entity_bits.hpp>
+
+TEST_CASE("entity_bits") {
+
+SECTION("can_be_set_and_checked") {
+ osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way;
+ REQUIRE(entities == (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way));
+
+ entities |= osmium::osm_entity_bits::relation;
+ REQUIRE((entities & osmium::osm_entity_bits::object));
+
+ entities |= osmium::osm_entity_bits::area;
+ REQUIRE(entities == osmium::osm_entity_bits::object);
+
+ REQUIRE(! (entities & osmium::osm_entity_bits::changeset));
+
+ entities &= osmium::osm_entity_bits::node;
+ REQUIRE((entities & osmium::osm_entity_bits::node));
+ REQUIRE(! (entities & osmium::osm_entity_bits::way));
+ REQUIRE(entities == osmium::osm_entity_bits::node);
+
+ 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));
+ REQUIRE(osmium::osm_entity_bits::changeset == osmium::osm_entity_bits::from_item_type(osmium::item_type::changeset));
+ REQUIRE(osmium::osm_entity_bits::area == osmium::osm_entity_bits::from_item_type(osmium::item_type::area));
+}
+
+}
diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp
new file mode 100644
index 0000000..25545a4
--- /dev/null
+++ b/test/t/basic/test_location.cpp
@@ -0,0 +1,154 @@
+#include "catch.hpp"
+
+#include <sstream>
+#include <type_traits>
+
+#include <osmium/osm/location.hpp>
+
+TEST_CASE("Location") {
+
+// fails on MSVC and doesn't really matter
+// static_assert(std::is_literal_type<osmium::Location>::value, "osmium::Location not literal type");
+
+SECTION("instantiation_with_default_parameters") {
+ osmium::Location loc;
+ REQUIRE(!loc);
+ REQUIRE_THROWS_AS(loc.lon(), osmium::invalid_location);
+ REQUIRE_THROWS_AS(loc.lat(), osmium::invalid_location);
+}
+
+SECTION("instantiation_with_double_parameters") {
+ osmium::Location loc1(1.2, 4.5);
+ REQUIRE(!!loc1);
+ REQUIRE(12000000 == loc1.x());
+ REQUIRE(45000000 == loc1.y());
+ REQUIRE(1.2 == loc1.lon());
+ REQUIRE(4.5 == loc1.lat());
+
+ osmium::Location loc2(loc1);
+ REQUIRE(4.5 == loc2.lat());
+
+ osmium::Location loc3 = loc1;
+ REQUIRE(4.5 == loc3.lat());
+}
+
+SECTION("instantiation_with_double_parameters_constructor_with_universal_initializer") {
+ osmium::Location loc { 2.2, 3.3 };
+ REQUIRE(2.2 == loc.lon());
+ REQUIRE(3.3 == loc.lat());
+}
+
+SECTION("instantiation_with_double_parameters_constructor_with_initializer_list") {
+ osmium::Location loc({ 4.4, 5.5 });
+ REQUIRE(4.4 == loc.lon());
+ REQUIRE(5.5 == loc.lat());
+}
+
+SECTION("instantiation_with_double_parameters_operator_equal") {
+ osmium::Location loc = { 5.5, 6.6 };
+ REQUIRE(5.5 == loc.lon());
+ REQUIRE(6.6 == loc.lat());
+}
+
+SECTION("equality") {
+ osmium::Location loc1(1.2, 4.5);
+ osmium::Location loc2(1.2, 4.5);
+ osmium::Location loc3(1.5, 1.5);
+ REQUIRE(loc1 == loc2);
+ REQUIRE(loc1 != loc3);
+}
+
+SECTION("order") {
+ REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0));
+ REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0));
+
+ REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2));
+ REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0));
+ REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0));
+}
+
+SECTION("validity") {
+ REQUIRE(osmium::Location(0.0, 0.0).valid());
+ REQUIRE(osmium::Location(1.2, 4.5).valid());
+ REQUIRE(osmium::Location(-1.2, 4.5).valid());
+ REQUIRE(osmium::Location(-180.0, -90.0).valid());
+ REQUIRE(osmium::Location(180.0, -90.0).valid());
+ REQUIRE(osmium::Location(-180.0, 90.0).valid());
+ REQUIRE(osmium::Location(180.0, 90.0).valid());
+
+ REQUIRE(!osmium::Location(200.0, 4.5).valid());
+ REQUIRE(!osmium::Location(-1.2, -100.0).valid());
+ REQUIRE(!osmium::Location(-180.0, 90.005).valid());
+}
+
+
+SECTION("output_to_iterator_comma_separator") {
+ char buffer[100];
+ osmium::Location loc(-3.2, 47.3);
+ *loc.as_string(buffer, ',') = 0;
+ REQUIRE(std::string("-3.2,47.3") == buffer);
+}
+
+SECTION("output_to_iterator_space_separator") {
+ char buffer[100];
+ osmium::Location loc(0.0, 7.0);
+ *loc.as_string(buffer, ' ') = 0;
+ REQUIRE(std::string("0 7") == buffer);
+}
+
+SECTION("output_to_iterator_check_precision") {
+ char buffer[100];
+ osmium::Location loc(-179.9999999, -90.0);
+ *loc.as_string(buffer, ' ') = 0;
+ REQUIRE(std::string("-179.9999999 -90") == buffer);
+}
+
+SECTION("output_to_iterator_undefined_location") {
+ char buffer[100];
+ osmium::Location loc;
+ REQUIRE_THROWS_AS(loc.as_string(buffer, ','), osmium::invalid_location);
+}
+
+SECTION("output_to_string_comman_separator") {
+ std::string s;
+ osmium::Location loc(-3.2, 47.3);
+ loc.as_string(std::back_inserter(s), ',');
+ REQUIRE(s == "-3.2,47.3");
+}
+
+SECTION("output_to_string_space_separator") {
+ std::string s;
+ osmium::Location loc(0.0, 7.0);
+ loc.as_string(std::back_inserter(s), ' ');
+ REQUIRE(s == "0 7");
+}
+
+SECTION("output_to_string_check_precision") {
+ std::string s;
+ osmium::Location loc(-179.9999999, -90.0);
+ loc.as_string(std::back_inserter(s), ' ');
+ REQUIRE(s == "-179.9999999 -90");
+}
+
+SECTION("output_to_string_undefined_location") {
+ std::string s;
+ osmium::Location loc;
+ REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), osmium::invalid_location);
+}
+
+SECTION("output_defined") {
+ osmium::Location p(-3.2, 47.3);
+ std::stringstream out;
+ out << p;
+ REQUIRE(out.str() == "(-3.2,47.3)");
+}
+
+SECTION("output_undefined") {
+ osmium::Location p;
+ std::stringstream out;
+ out << p;
+ REQUIRE(out.str() == "(undefined,undefined)");
+}
+
+}
+
diff --git a/test/t/basic/test_node.cpp b/test/t/basic/test_node.cpp
new file mode 100644
index 0000000..6c2c899
--- /dev/null
+++ b/test/t/basic/test_node.cpp
@@ -0,0 +1,117 @@
+#include "catch.hpp"
+
+#include <osmium/osm/node.hpp>
+
+#include "helper.hpp"
+
+TEST_CASE("Basic_Node") {
+
+SECTION("node_builder") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Node& node = buffer_add_node(buffer,
+ "foo",
+ {{"amenity", "pub"}, {"name", "OSM BAR"}},
+ {3.5, 4.7});
+
+ node.set_id(17)
+ .set_version(3)
+ .set_visible(true)
+ .set_changeset(333)
+ .set_uid(21)
+ .set_timestamp(123);
+
+ REQUIRE(osmium::item_type::node == node.type());
+ REQUIRE(node.type_is_in(osmium::osm_entity_bits::node));
+ REQUIRE(node.type_is_in(osmium::osm_entity_bits::nwr));
+ REQUIRE(17l == node.id());
+ REQUIRE(17ul == node.positive_id());
+ REQUIRE(3 == node.version());
+ REQUIRE(true == node.visible());
+ REQUIRE(false == node.deleted());
+ REQUIRE(333 == node.changeset());
+ REQUIRE(21 == node.uid());
+ REQUIRE(std::string("foo") == node.user());
+ REQUIRE(123 == node.timestamp());
+ REQUIRE(osmium::Location(3.5, 4.7) == node.location());
+ REQUIRE(2 == node.tags().size());
+
+ node.set_visible(false);
+ REQUIRE(false == node.visible());
+ REQUIRE(true == node.deleted());
+}
+
+SECTION("node_default_attributes") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Node& node = buffer_add_node(buffer, "", {}, osmium::Location{});
+
+ REQUIRE(0l == node.id());
+ REQUIRE(0ul == node.positive_id());
+ REQUIRE(0 == node.version());
+ REQUIRE(true == node.visible());
+ REQUIRE(0 == node.changeset());
+ REQUIRE(0 == node.uid());
+ REQUIRE(std::string("") == node.user());
+ REQUIRE(0 == node.timestamp());
+ REQUIRE(osmium::Location() == node.location());
+ REQUIRE(0 == node.tags().size());
+}
+
+SECTION("set_node_attributes_from_string") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Node& node = buffer_add_node(buffer,
+ "foo",
+ {{"amenity", "pub"}, {"name", "OSM BAR"}},
+ {3.5, 4.7});
+
+ node.set_id("-17")
+ .set_version("3")
+ .set_visible(true)
+ .set_changeset("333")
+ .set_uid("21");
+
+ REQUIRE(-17l == node.id());
+ REQUIRE(17ul == node.positive_id());
+ REQUIRE(3 == node.version());
+ REQUIRE(true == node.visible());
+ REQUIRE(333 == node.changeset());
+ REQUIRE(21 == node.uid());
+}
+
+SECTION("large_id") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Node& node = buffer_add_node(buffer, "", {}, osmium::Location{});
+
+ int64_t id = 3000000000l;
+ node.set_id(id);
+
+ REQUIRE(id == node.id());
+ REQUIRE(static_cast<osmium::unsigned_object_id_type>(id) == node.positive_id());
+
+ node.set_id(-id);
+ REQUIRE(-id == node.id());
+ REQUIRE(static_cast<osmium::unsigned_object_id_type>(id) == node.positive_id());
+}
+
+SECTION("tags") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Node& node = buffer_add_node(buffer,
+ "foo",
+ {{"amenity", "pub"}, {"name", "OSM BAR"}},
+ {3.5, 4.7});
+
+ 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("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/test/t/basic/test_node_ref.cpp b/test/t/basic/test_node_ref.cpp
new file mode 100644
index 0000000..5c7b670
--- /dev/null
+++ b/test/t/basic/test_node_ref.cpp
@@ -0,0 +1,57 @@
+#include "catch.hpp"
+
+#include <osmium/osm/node_ref.hpp>
+
+TEST_CASE("NodeRef") {
+
+SECTION("instantiation_with_default_parameters") {
+ osmium::NodeRef node_ref;
+ REQUIRE(node_ref.ref() == 0);
+// REQUIRE(!node_ref.has_location());
+}
+
+SECTION("instantiation_with_id") {
+ osmium::NodeRef node_ref(7);
+ REQUIRE(node_ref.ref() == 7);
+}
+
+SECTION("equality") {
+ osmium::NodeRef node_ref1(7, { 1.2, 3.4 });
+ osmium::NodeRef node_ref2(7, { 1.4, 3.1 });
+ osmium::NodeRef node_ref3(9, { 1.2, 3.4 });
+ REQUIRE(node_ref1 == node_ref2);
+ REQUIRE(node_ref1 != node_ref3);
+ REQUIRE(!osmium::location_equal()(node_ref1, node_ref2));
+ REQUIRE(!osmium::location_equal()(node_ref2, node_ref3));
+ REQUIRE(osmium::location_equal()(node_ref1, node_ref3));
+}
+
+SECTION("set_location") {
+ osmium::NodeRef node_ref(7);
+ REQUIRE(!node_ref.location().valid());
+ REQUIRE(node_ref.location() == osmium::Location());
+ node_ref.set_location(osmium::Location(13.5, -7.2));
+ REQUIRE(node_ref.location().lon() == 13.5);
+ REQUIRE(node_ref.location().valid());
+}
+
+SECTION("ordering") {
+ osmium::NodeRef node_ref1(1, { 1.0, 3.0 });
+ osmium::NodeRef node_ref2(2, { 1.4, 2.9 });
+ osmium::NodeRef node_ref3(3, { 1.2, 3.0 });
+ osmium::NodeRef node_ref4(4, { 1.2, 3.3 });
+
+ REQUIRE(node_ref1 < node_ref2);
+ REQUIRE(node_ref2 < node_ref3);
+ REQUIRE(node_ref1 < node_ref3);
+ REQUIRE(node_ref1 >= node_ref1);
+
+ REQUIRE(osmium::location_less()(node_ref1, node_ref2));
+ REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
+ REQUIRE(osmium::location_less()(node_ref1, node_ref3));
+ REQUIRE(osmium::location_less()(node_ref3, node_ref4));
+ REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
+}
+
+}
+
diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp
new file mode 100644
index 0000000..ffffbcc
--- /dev/null
+++ b/test/t/basic/test_object_comparisons.cpp
@@ -0,0 +1,147 @@
+#include "catch.hpp"
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/object_comparisons.hpp>
+
+TEST_CASE("Object_Comparisons") {
+
+SECTION("order") {
+ osmium::memory::Buffer buffer(10 * 1000);
+
+ {
+ // add node 1
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ buffer.commit();
+ }
+
+ {
+ // add node 2
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ buffer.commit();
+ }
+
+ auto it = buffer.begin();
+ osmium::Node& node1 = static_cast<osmium::Node&>(*it);
+ osmium::Node& node2 = static_cast<osmium::Node&>(*(++it));
+
+ node1.set_id(10);
+ node1.set_version(1);
+ node2.set_id(15);
+ node2.set_version(2);
+ REQUIRE(true == (node1 < node2));
+ REQUIRE(false == (node1 > node2));
+ node1.set_id(20);
+ node1.set_version(1);
+ node2.set_id(20);
+ node2.set_version(2);
+ REQUIRE(true == (node1 < node2));
+ REQUIRE(false == (node1 > node2));
+ node1.set_id(-10);
+ node1.set_version(2);
+ node2.set_id(-15);
+ node2.set_version(1);
+ REQUIRE(true == (node1 < node2));
+ REQUIRE(false == (node1 > node2));
+}
+
+SECTION("order_types") {
+ osmium::memory::Buffer buffer(10 * 1000);
+
+ {
+ // add node 1
+ osmium::builder::NodeBuilder node_builder(buffer);
+ osmium::Node& node = node_builder.object();
+ REQUIRE(osmium::item_type::node == node.type());
+
+ node.set_id(3);
+ node.set_version(3);
+ node_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ {
+ // add node 2
+ osmium::builder::NodeBuilder node_builder(buffer);
+ osmium::Node& node = node_builder.object();
+ REQUIRE(osmium::item_type::node == node.type());
+
+ node.set_id(3);
+ node.set_version(4);
+ node_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ {
+ // add node 3
+ osmium::builder::NodeBuilder node_builder(buffer);
+ osmium::Node& node = node_builder.object();
+ REQUIRE(osmium::item_type::node == node.type());
+
+ node.set_id(3);
+ node.set_version(4);
+ node_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ {
+ // add way
+ osmium::builder::WayBuilder way_builder(buffer);
+ osmium::Way& way = way_builder.object();
+ REQUIRE(osmium::item_type::way == way.type());
+
+ way.set_id(2);
+ way.set_version(2);
+ way_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ {
+ // add relation
+ osmium::builder::RelationBuilder relation_builder(buffer);
+ osmium::Relation& relation = relation_builder.object();
+ REQUIRE(osmium::item_type::relation == relation.type());
+
+ relation.set_id(1);
+ relation.set_version(1);
+ relation_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ auto it = buffer.begin();
+ const osmium::Node& node1 = static_cast<const osmium::Node&>(*it);
+ const osmium::Node& node2 = static_cast<const osmium::Node&>(*(++it));
+ const osmium::Node& node3 = static_cast<const osmium::Node&>(*(++it));
+ const osmium::Way& way = static_cast<const osmium::Way&>(*(++it));
+ const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it));
+
+ REQUIRE(true == (node1 < node2));
+ REQUIRE(true == (node2 < way));
+ REQUIRE(false == (node2 > way));
+ REQUIRE(true == (way < relation));
+ REQUIRE(true == (node1 < relation));
+
+ REQUIRE(true == osmium::object_order_type_id_version()(node1, node2));
+ REQUIRE(true == osmium::object_order_type_id_reverse_version()(node2, node1));
+ REQUIRE(true == osmium::object_order_type_id_version()(node1, way));
+ REQUIRE(true == osmium::object_order_type_id_reverse_version()(node1, way));
+
+ REQUIRE(false == osmium::object_equal_type_id_version()(node1, node2));
+ REQUIRE(true == osmium::object_equal_type_id_version()(node2, node3));
+
+ REQUIRE(true == osmium::object_equal_type_id()(node1, node2));
+ REQUIRE(true == osmium::object_equal_type_id()(node2, node3));
+
+ REQUIRE(false == osmium::object_equal_type_id_version()(node1, way));
+ REQUIRE(false == osmium::object_equal_type_id_version()(node1, relation));
+ REQUIRE(false == osmium::object_equal_type_id()(node1, relation));
+}
+
+}
diff --git a/test/t/basic/test_relation.cpp b/test/t/basic/test_relation.cpp
new file mode 100644
index 0000000..4c62a41
--- /dev/null
+++ b/test/t/basic/test_relation.cpp
@@ -0,0 +1,60 @@
+#include "catch.hpp"
+
+#include <osmium/osm/relation.hpp>
+
+#include "helper.hpp"
+
+TEST_CASE("Basic_Relation") {
+
+SECTION("relation_builder") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Relation& relation = buffer_add_relation(buffer,
+ "foo", {
+ {"type", "multipolygon"},
+ {"name", "Sherwood Forest"}
+ }, {
+ std::make_tuple('w', 1, "inner"),
+ std::make_tuple('w', 2, ""),
+ std::make_tuple('w', 3, "outer")
+ });
+
+ relation.set_id(17)
+ .set_version(3)
+ .set_visible(true)
+ .set_changeset(333)
+ .set_uid(21)
+ .set_timestamp(123);
+
+ REQUIRE(17 == relation.id());
+ REQUIRE(3 == relation.version());
+ REQUIRE(true == relation.visible());
+ REQUIRE(333 == relation.changeset());
+ REQUIRE(21 == relation.uid());
+ REQUIRE(std::string("foo") == relation.user());
+ REQUIRE(123 == relation.timestamp());
+ REQUIRE(2 == relation.tags().size());
+ REQUIRE(3 == relation.members().size());
+
+ int n=1;
+ for (auto& member : relation.members()) {
+ REQUIRE(osmium::item_type::way == member.type());
+ REQUIRE(n == member.ref());
+ switch (n) {
+ case 1:
+ REQUIRE(std::string("inner") == member.role());
+ break;
+ case 2:
+ REQUIRE(std::string("") == member.role());
+ break;
+ case 3:
+ REQUIRE(std::string("outer") == member.role());
+ break;
+ default:
+ REQUIRE(false);
+ }
+ ++n;
+ }
+}
+
+}
diff --git a/test/t/basic/test_timestamp.cpp b/test/t/basic/test_timestamp.cpp
new file mode 100644
index 0000000..453b94b
--- /dev/null
+++ b/test/t/basic/test_timestamp.cpp
@@ -0,0 +1,45 @@
+#include "catch.hpp"
+
+#include <sstream>
+
+#include <osmium/osm/timestamp.hpp>
+
+TEST_CASE("Timestamp") {
+
+SECTION("can be default initialized to invalid value") {
+ osmium::Timestamp t;
+ REQUIRE(0 == t);
+ REQUIRE("" == t.to_iso());
+}
+
+SECTION("invalid value is zero") {
+ osmium::Timestamp t(static_cast<time_t>(0));
+ REQUIRE(0 == t);
+ REQUIRE("" == t.to_iso());
+}
+
+SECTION("can be initialized from time_t") {
+ osmium::Timestamp t(static_cast<time_t>(1));
+ REQUIRE(1 == t);
+ REQUIRE("1970-01-01T00:00:01Z" == t.to_iso());
+}
+
+SECTION("can be initialized from string") {
+ osmium::Timestamp t("2000-01-01T00:00:00Z");
+ REQUIRE("2000-01-01T00:00:00Z" == t.to_iso());
+}
+
+SECTION("can be compared") {
+ osmium::Timestamp t1(10);
+ osmium::Timestamp t2(50);
+ REQUIRE(t1 < t2);
+}
+
+SECTION("can be written to stream") {
+ std::stringstream ss;
+ osmium::Timestamp t(1);
+ ss << t;
+ REQUIRE("1970-01-01T00:00:01Z" == ss.str());
+}
+
+}
diff --git a/test/t/basic/test_way.cpp b/test/t/basic/test_way.cpp
new file mode 100644
index 0000000..9d2ba06
--- /dev/null
+++ b/test/t/basic/test_way.cpp
@@ -0,0 +1,82 @@
+#include "catch.hpp"
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/osm/way.hpp>
+
+#include "helper.hpp"
+
+TEST_CASE("Basic_Way") {
+
+SECTION("way_builder") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Way& way = buffer_add_way(buffer,
+ "foo",
+ {{"highway", "residential"}, {"name", "High Street"}},
+ {1, 3, 2});
+
+ way.set_id(17)
+ .set_version(3)
+ .set_visible(true)
+ .set_changeset(333)
+ .set_uid(21)
+ .set_timestamp(123);
+
+ REQUIRE(osmium::item_type::way == way.type());
+ REQUIRE(way.type_is_in(osmium::osm_entity_bits::way));
+ REQUIRE(way.type_is_in(osmium::osm_entity_bits::node | osmium::osm_entity_bits::way));
+ REQUIRE(17 == way.id());
+ REQUIRE(3 == way.version());
+ REQUIRE(true == way.visible());
+ REQUIRE(333 == way.changeset());
+ REQUIRE(21 == way.uid());
+ REQUIRE(std::string("foo") == way.user());
+ REQUIRE(123 == way.timestamp());
+ REQUIRE(2 == way.tags().size());
+ REQUIRE(3 == way.nodes().size());
+ REQUIRE(1 == way.nodes()[0].ref());
+ REQUIRE(3 == way.nodes()[1].ref());
+ REQUIRE(2 == way.nodes()[2].ref());
+ REQUIRE(! way.is_closed());
+}
+
+SECTION("closed_way") {
+ osmium::memory::Buffer buffer(10000);
+
+ osmium::Way& way = buffer_add_way(buffer,
+ "foo",
+ {{"highway", "residential"}, {"name", "High Street"}},
+ {1, 3, 1});
+
+ REQUIRE(way.is_closed());
+}
+
+SECTION("way_builder_with_helpers") {
+ osmium::memory::Buffer buffer(10000);
+ {
+ osmium::builder::WayBuilder builder(buffer);
+ builder.add_user("username");
+ builder.add_tags({
+ {"amenity", "restaurant"},
+ {"name", "Zum goldenen Schwanen"}
+ });
+ builder.add_node_refs({
+ {22, {3.5, 4.7}},
+ {67, {4.1, 2.2}}
+ });
+ }
+ buffer.commit();
+ osmium::Way& way = buffer.get<osmium::Way>(0);
+
+ REQUIRE(std::string("username") == way.user());
+
+ REQUIRE(2 == way.tags().size());
+ REQUIRE(std::string("amenity") == way.tags().begin()->key());
+ REQUIRE(std::string("Zum goldenen Schwanen") == way.tags()["name"]);
+
+ REQUIRE(2 == way.nodes().size());
+ REQUIRE(22 == way.nodes()[0].ref());
+ REQUIRE(4.1 == way.nodes()[1].location().lon());
+}
+
+}
diff --git a/test/t/buffer/test_buffer_node.cpp b/test/t/buffer/test_buffer_node.cpp
new file mode 100644
index 0000000..aabe6cb
--- /dev/null
+++ b/test/t/buffer/test_buffer_node.cpp
@@ -0,0 +1,135 @@
+#include "catch.hpp"
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/osm/node.hpp>
+
+void check_node_1(osmium::Node& node) {
+ REQUIRE(1 == node.id());
+ REQUIRE(3 == node.version());
+ REQUIRE(true == node.visible());
+ REQUIRE(333 == node.changeset());
+ REQUIRE(21 == node.uid());
+ REQUIRE(123 == node.timestamp());
+ REQUIRE(osmium::Location(3.5, 4.7) == node.location());
+ REQUIRE(std::string("testuser") == node.user());
+
+ for (osmium::memory::Item& item : node) {
+ REQUIRE(osmium::item_type::tag_list == item.type());
+ }
+
+ REQUIRE(node.tags().begin() == node.tags().end());
+ REQUIRE(node.tags().empty());
+ REQUIRE(0 == std::distance(node.tags().begin(), node.tags().end()));
+}
+
+void check_node_2(osmium::Node& node) {
+ REQUIRE(2 == node.id());
+ REQUIRE(3 == node.version());
+ REQUIRE(true == node.visible());
+ REQUIRE(333 == node.changeset());
+ REQUIRE(21 == node.uid());
+ REQUIRE(123 == node.timestamp());
+ REQUIRE(osmium::Location(3.5, 4.7) == node.location());
+ REQUIRE(std::string("testuser") == node.user());
+
+ for (osmium::memory::Item& item : node) {
+ REQUIRE(osmium::item_type::tag_list == item.type());
+ }
+
+ REQUIRE(!node.tags().empty());
+ REQUIRE(2 == std::distance(node.tags().begin(), node.tags().end()));
+
+ int n = 0;
+ for (const osmium::Tag& tag : node.tags()) {
+ switch (n) {
+ case 0:
+ REQUIRE(std::string("amenity") == tag.key());
+ REQUIRE(std::string("bank") == tag.value());
+ break;
+ case 1:
+ REQUIRE(std::string("name") == tag.key());
+ REQUIRE(std::string("OSM Savings") == tag.value());
+ break;
+ }
+ ++n;
+ }
+ REQUIRE(2 == n);
+}
+
+TEST_CASE("Buffer_Node") {
+
+SECTION("buffer_node") {
+ constexpr size_t buffer_size = 10000;
+ unsigned char data[buffer_size];
+
+ osmium::memory::Buffer buffer(data, buffer_size, 0);
+
+ {
+ // add node 1
+ osmium::builder::NodeBuilder node_builder(buffer);
+ osmium::Node& node = node_builder.object();
+ REQUIRE(osmium::item_type::node == node.type());
+
+ node.set_id(1);
+ node.set_version(3);
+ node.set_visible(true);
+ node.set_changeset(333);
+ node.set_uid(21);
+ node.set_timestamp(123);
+ node.set_location(osmium::Location(3.5, 4.7));
+
+ node_builder.add_user("testuser");
+
+ buffer.commit();
+ }
+
+ {
+ // add node 2
+ osmium::builder::NodeBuilder node_builder(buffer);
+ osmium::Node& node = node_builder.object();
+ REQUIRE(osmium::item_type::node == node.type());
+
+ node.set_id(2);
+ node.set_version(3);
+ node.set_visible(true);
+ node.set_changeset(333);
+ node.set_uid(21);
+ node.set_timestamp(123);
+ node.set_location(osmium::Location(3.5, 4.7));
+
+ node_builder.add_user("testuser");
+
+ {
+ osmium::builder::TagListBuilder tag_builder(buffer, &node_builder);
+ tag_builder.add_tag("amenity", "bank");
+ tag_builder.add_tag("name", "OSM Savings");
+ }
+
+ buffer.commit();
+ }
+
+ REQUIRE(2 == std::distance(buffer.begin(), buffer.end()));
+ int item_no = 0;
+ for (osmium::memory::Item& item : buffer) {
+ REQUIRE(osmium::item_type::node == item.type());
+
+ osmium::Node& node = static_cast<osmium::Node&>(item);
+
+ switch (item_no) {
+ case 0:
+ check_node_1(node);
+ break;
+ case 1:
+ check_node_2(node);
+ break;
+ default:
+ break;
+ }
+
+ ++item_no;
+
+ }
+
+}
+
+}
diff --git a/test/t/buffer/test_buffer_purge.cpp b/test/t/buffer/test_buffer_purge.cpp
new file mode 100644
index 0000000..10cdfe7
--- /dev/null
+++ b/test/t/buffer/test_buffer_purge.cpp
@@ -0,0 +1,186 @@
+#include "catch.hpp"
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/osm/node.hpp>
+
+struct CallbackClass {
+
+ int count = 0;
+
+ void moving_in_buffer(size_t old_offset, size_t new_offset) {
+ REQUIRE(old_offset > new_offset);
+ ++count;
+ }
+
+}; // struct CallbackClass
+
+TEST_CASE("Purge data from buffer") {
+
+ constexpr size_t buffer_size = 10000;
+
+ SECTION("purge empty buffer") {
+ osmium::memory::Buffer buffer(buffer_size);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 0);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 0);
+ REQUIRE(buffer.committed() == 0);
+ }
+
+ SECTION("purge buffer with one object but nothing to delete") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ }
+ buffer.commit();
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
+ size_t committed = buffer.committed();
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 0);
+ REQUIRE(committed == buffer.committed());
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
+ }
+
+ SECTION("purge buffer with one object which gets deleted") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 0);
+ REQUIRE(buffer.committed() == 0);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 0);
+ }
+
+ SECTION("purge buffer with two objects, first gets deleted") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size1 = buffer.committed();
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ }
+ buffer.commit();
+ size_t size2 = buffer.committed() - size1;
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 1);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
+ REQUIRE(buffer.committed() == size2);
+ }
+
+ SECTION("purge buffer with two objects, second gets deleted") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser_longer_name");
+ }
+ buffer.commit();
+ size_t size1 = buffer.committed();
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size2 = buffer.committed() - size1;
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 0);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
+ REQUIRE(buffer.committed() == size1);
+ }
+
+ SECTION("purge buffer with three objects, middle one gets deleted") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser_longer_name");
+ }
+ buffer.commit();
+ size_t size1 = buffer.committed();
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size2 = buffer.committed() - size1;
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("sn");
+ }
+ buffer.commit();
+ size_t size3 = buffer.committed() - (size1 + size2);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 1);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
+ }
+
+ SECTION("purge buffer with three objects, all get deleted") {
+ osmium::memory::Buffer buffer(buffer_size);
+
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser_longer_name");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size1 = buffer.committed();
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("testuser");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size2 = buffer.committed() - size1;
+ {
+ osmium::builder::NodeBuilder node_builder(buffer);
+ node_builder.add_user("sn");
+ node_builder.object().set_removed(true);
+ }
+ buffer.commit();
+ size_t size3 = buffer.committed() - (size1 + size2);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
+
+ CallbackClass callback;
+ buffer.purge_removed(&callback);
+
+ REQUIRE(callback.count == 0);
+ REQUIRE(std::distance(buffer.begin(), buffer.end()) == 0);
+ }
+
+}
diff --git a/test/t/geom/helper.hpp b/test/t/geom/helper.hpp
new file mode 100644
index 0000000..e0cefe6
--- /dev/null
+++ b/test/t/geom/helper.hpp
@@ -0,0 +1,15 @@
+#ifndef TEST_GEOM_HELPER_HPP
+#define TEST_GEOM_HELPER_HPP
+
+#include <string>
+
+#include <geos/io/WKBWriter.h>
+
+inline std::string geos_to_wkb(const geos::geom::Geometry* geometry) {
+ std::stringstream ss;
+ geos::io::WKBWriter wkb_writer;
+ wkb_writer.writeHEX(*geometry, ss);
+ return ss.str();
+}
+
+#endif // TEST_GEOM_HELPER_HPP
diff --git a/test/t/geom/test_factory_with_projection.cpp b/test/t/geom/test_factory_with_projection.cpp
new file mode 100644
index 0000000..9bc8c06
--- /dev/null
+++ b/test/t/geom/test_factory_with_projection.cpp
@@ -0,0 +1,41 @@
+#include "catch.hpp"
+
+#include <osmium/geom/geos.hpp>
+#include <osmium/geom/mercator_projection.hpp>
+#include <osmium/geom/projection.hpp>
+#include <osmium/geom/wkb.hpp>
+#include <osmium/geom/wkt.hpp>
+
+#include "helper.hpp"
+
+TEST_CASE("Projection") {
+
+SECTION("point_mercator") {
+ osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory(2);
+
+ std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"POINT(356222.37 467961.14)"} == wkt);
+}
+
+SECTION("point_epsg_3857") {
+ osmium::geom::WKTFactory<osmium::geom::Projection> factory(osmium::geom::Projection(3857), 2);
+
+ std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"POINT(356222.37 467961.14)"} == wkt);
+}
+
+SECTION("wkb_with_parameter") {
+ osmium::geom::WKBFactory<osmium::geom::Projection> wkb_factory(osmium::geom::Projection(3857), osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<osmium::geom::Projection> geos_factory(osmium::geom::Projection(3857));
+
+ std::string wkb = wkb_factory.create_point(osmium::Location(3.2, 4.2));
+ std::unique_ptr<geos::geom::Point> geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2));
+ REQUIRE(geos_to_wkb(geos_point.get()) == wkb);
+}
+
+SECTION("cleanup") {
+ // trying to make valgrind happy, but there is still a memory leak in proj library
+ pj_deallocate_grids();
+}
+
+}
diff --git a/test/t/geom/test_geojson.cpp b/test/t/geom/test_geojson.cpp
new file mode 100644
index 0000000..8d7df35
--- /dev/null
+++ b/test/t/geom/test_geojson.cpp
@@ -0,0 +1,236 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/geojson.hpp>
+
+#include "../basic/helper.hpp"
+
+TEST_CASE("GeoJSON_Geometry") {
+
+SECTION("point") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ std::string json {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"{\"type\":\"Point\",\"coordinates\":[3.2,4.2]}"} == json);
+}
+
+SECTION("empty_point") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
+}
+
+SECTION("linestring") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::string json {factory.create_linestring(wnl)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.2,4.2],[3.5,4.7],[3.6,4.9]]}"} == json);
+ }
+
+ {
+ std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.6,4.9],[3.5,4.7],[3.2,4.2]]}"} == json);
+ }
+
+ {
+ std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.2,4.2],[3.5,4.7],[3.5,4.7],[3.6,4.9]]}"} == json);
+ }
+
+ {
+ std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.6,4.9],[3.5,4.7],[3.5,4.7],[3.2,4.2]]}"} == json);
+ }
+}
+
+SECTION("empty_linestring") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(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);
+}
+
+SECTION("linestring_with_two_same_locations") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, {3.5, 4.7}}
+ });
+
+ 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);
+
+ {
+ std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json);
+ }
+
+ {
+ std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+ REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json);
+ }
+}
+
+SECTION("linestring_with_undefined_location") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, osmium::Location()}
+ });
+
+ REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location);
+}
+
+SECTION("area_1outer_0inner") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {3.2, 4.2}},
+ {2, {3.5, 4.7}},
+ {3, {3.6, 4.9}},
+ {1, {3.2, 4.2}}
+ }}
+ });
+
+ REQUIRE(!area.is_multipolygon());
+ REQUIRE(std::distance(area.cbegin(), area.cend()) == 2);
+ REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
+
+ {
+ std::string json {factory.create_multipolygon(area)};
+ REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[3.2,4.2],[3.5,4.7],[3.6,4.9],[3.2,4.2]]]]}"} == json);
+ }
+}
+
+SECTION("area_1outer_1inner") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {8.0, 1.0}},
+ {7, {8.0, 8.0}},
+ {8, {1.0, 8.0}},
+ {5, {1.0, 1.0}}
+ }}
+ });
+
+ REQUIRE(!area.is_multipolygon());
+ REQUIRE(std::distance(area.cbegin(), area.cend()) == 3);
+ REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
+ REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+
+ {
+ std::string json {factory.create_multipolygon(area)};
+ REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[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]]]]}"} == json);
+ }
+}
+
+SECTION("area_2outer_2inner") {
+ osmium::geom::GeoJSONFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {4.0, 1.0}},
+ {7, {4.0, 4.0}},
+ {8, {1.0, 4.0}},
+ {5, {1.0, 1.0}}
+ }},
+ { false, {
+ {10, {5.0, 5.0}},
+ {11, {5.0, 7.0}},
+ {12, {7.0, 7.0}},
+ {10, {5.0, 5.0}}
+ }},
+ { true, {
+ {100, {10.0, 10.0}},
+ {101, {11.0, 10.0}},
+ {102, {11.0, 11.0}},
+ {103, {10.0, 11.0}},
+ {100, {10.0, 10.0}}
+ }}
+ });
+
+ REQUIRE(area.is_multipolygon());
+ REQUIRE(std::distance(area.cbegin(), area.cend()) == 5);
+ REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
+ REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+
+ int outer_ring=0;
+ int inner_ring=0;
+ for (auto it_outer = area.cbegin<osmium::OuterRing>(); it_outer != area.cend<osmium::OuterRing>(); ++it_outer) {
+ if (outer_ring == 0) {
+ REQUIRE(it_outer->front().ref() == 1);
+ } else if (outer_ring == 1) {
+ REQUIRE(it_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) {
+ if (outer_ring == 0 && inner_ring == 0) {
+ REQUIRE(it_inner->front().ref() == 5);
+ } else if (outer_ring == 0 && inner_ring == 1) {
+ REQUIRE(it_inner->front().ref() == 10);
+ } else {
+ REQUIRE(false);
+ }
+ ++inner_ring;
+ }
+ inner_ring = 0;
+ ++outer_ring;
+ }
+
+ {
+ std::string json {factory.create_multipolygon(area)};
+ REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[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]]]]}"} == json);
+ }
+}
+
+}
+
diff --git a/test/t/geom/test_geos.cpp b/test/t/geom/test_geos.cpp
new file mode 100644
index 0000000..e93228b
--- /dev/null
+++ b/test/t/geom/test_geos.cpp
@@ -0,0 +1,198 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/geos.hpp>
+
+#include "../basic/helper.hpp"
+
+TEST_CASE("GEOS_Geometry") {
+
+SECTION("point") {
+ osmium::geom::GEOSFactory<> factory;
+
+ std::unique_ptr<geos::geom::Point> point {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(3.2 == point->getX());
+ REQUIRE(4.2 == point->getY());
+ REQUIRE(-1 == point->getSRID());
+}
+
+SECTION("non_default_srid") {
+ osmium::geom::GEOSFactory<> factory(4326);
+
+ std::unique_ptr<geos::geom::Point> point {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(3.2 == point->getX());
+ REQUIRE(4.2 == point->getY());
+ REQUIRE(4326 == point->getSRID());
+}
+
+SECTION("empty_point") {
+ osmium::geom::GEOSFactory<> factory;
+
+ REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
+}
+
+SECTION("linestring") {
+ osmium::geom::GEOSFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl)};
+ REQUIRE(3 == linestring->getNumPoints());
+
+ std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+ REQUIRE(3.2 == p0->getX());
+ std::unique_ptr<geos::geom::Point> p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
+ REQUIRE(3.6 == p2->getX());
+ }
+
+ {
+ std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+ REQUIRE(3 == linestring->getNumPoints());
+ std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+ REQUIRE(3.6 == p0->getX());
+ std::unique_ptr<geos::geom::Point> p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
+ REQUIRE(3.2 == p2->getX());
+ }
+
+ {
+ std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(4 == linestring->getNumPoints());
+ std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+ REQUIRE(3.2 == p0->getX());
+ }
+
+ {
+ std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+ REQUIRE(4 == linestring->getNumPoints());
+ std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+ REQUIRE(3.6 == p0->getX());
+ }
+}
+
+SECTION("area_1outer_0inner") {
+ osmium::geom::GEOSFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {3.2, 4.2}},
+ {2, {3.5, 4.7}},
+ {3, {3.6, 4.9}},
+ {1, {3.2, 4.2}}
+ }}
+ });
+
+ std::unique_ptr<geos::geom::MultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(1 == mp->getNumGeometries());
+
+ const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(0 == p0->getNumInteriorRing());
+
+ const geos::geom::LineString* l0e = p0->getExteriorRing();
+ REQUIRE(4 == l0e->getNumPoints());
+
+ std::unique_ptr<geos::geom::Point> l0e_p0 = std::unique_ptr<geos::geom::Point>(l0e->getPointN(1));
+ REQUIRE(3.5 == l0e_p0->getX());
+}
+
+SECTION("area_1outer_1inner") {
+ osmium::geom::GEOSFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {8.0, 1.0}},
+ {7, {8.0, 8.0}},
+ {8, {1.0, 8.0}},
+ {5, {1.0, 1.0}}
+ }}
+ });
+
+ std::unique_ptr<geos::geom::MultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(1 == mp->getNumGeometries());
+
+ const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(1 == p0->getNumInteriorRing());
+
+ const geos::geom::LineString* l0e = p0->getExteriorRing();
+ REQUIRE(5 == l0e->getNumPoints());
+
+ const geos::geom::LineString* l0i0 = p0->getInteriorRingN(0);
+ REQUIRE(5 == l0i0->getNumPoints());
+}
+
+SECTION("area_2outer_2inner") {
+ osmium::geom::GEOSFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {4.0, 1.0}},
+ {7, {4.0, 4.0}},
+ {8, {1.0, 4.0}},
+ {5, {1.0, 1.0}}
+ }},
+ { false, {
+ {10, {5.0, 5.0}},
+ {11, {5.0, 7.0}},
+ {12, {7.0, 7.0}},
+ {10, {5.0, 5.0}}
+ }},
+ { true, {
+ {100, {10.0, 10.0}},
+ {101, {11.0, 10.0}},
+ {102, {11.0, 11.0}},
+ {103, {10.0, 11.0}},
+ {100, {10.0, 10.0}}
+ }}
+ });
+
+ std::unique_ptr<geos::geom::MultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(2 == mp->getNumGeometries());
+
+ const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(2 == p0->getNumInteriorRing());
+
+ const geos::geom::LineString* l0e = p0->getExteriorRing();
+ REQUIRE(5 == l0e->getNumPoints());
+
+ const geos::geom::Polygon* p1 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(1));
+ REQUIRE(0 == p1->getNumInteriorRing());
+
+ const geos::geom::LineString* l1e = p1->getExteriorRing();
+ REQUIRE(5 == l1e->getNumPoints());
+}
+
+}
diff --git a/test/t/geom/test_geos_wkb.cpp b/test/t/geom/test_geos_wkb.cpp
new file mode 100644
index 0000000..0f54427
--- /dev/null
+++ b/test/t/geom/test_geos_wkb.cpp
@@ -0,0 +1,156 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/geos.hpp>
+#include <osmium/geom/wkb.hpp>
+
+#include "../basic/helper.hpp"
+#include "helper.hpp"
+
+TEST_CASE("WKB_Geometry_with_GEOS") {
+
+SECTION("point") {
+ osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<> geos_factory;
+
+ std::string wkb {wkb_factory.create_point(osmium::Location(3.2, 4.2))};
+
+ std::unique_ptr<geos::geom::Point> geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2));
+ REQUIRE(geos_to_wkb(geos_point.get()) == wkb);
+}
+
+
+SECTION("linestring") {
+ osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<> geos_factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::string wkb = wkb_factory.create_linestring(wnl);
+ std::unique_ptr<geos::geom::LineString> geos = geos_factory.create_linestring(wnl);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+ }
+
+ {
+ std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward);
+ std::unique_ptr<geos::geom::LineString> geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+ }
+
+ {
+ std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all);
+ std::unique_ptr<geos::geom::LineString> geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::all);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+ }
+
+ {
+ std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward);
+ std::unique_ptr<geos::geom::LineString> geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+ }
+}
+
+SECTION("area_1outer_0inner") {
+ osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<> geos_factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {3.2, 4.2}},
+ {2, {3.5, 4.7}},
+ {3, {3.6, 4.9}},
+ {1, {3.2, 4.2}}
+ }}
+ });
+
+ std::string wkb = wkb_factory.create_multipolygon(area);
+ std::unique_ptr<geos::geom::MultiPolygon> geos = geos_factory.create_multipolygon(area);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+}
+
+SECTION("area_1outer_1inner") {
+ osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<> geos_factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {8.0, 1.0}},
+ {7, {8.0, 8.0}},
+ {8, {1.0, 8.0}},
+ {5, {1.0, 1.0}}
+ }}
+ });
+
+ std::string wkb = wkb_factory.create_multipolygon(area);
+ std::unique_ptr<geos::geom::MultiPolygon> geos = geos_factory.create_multipolygon(area);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+}
+
+SECTION("area_2outer_2inner") {
+ osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+ osmium::geom::GEOSFactory<> geos_factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {4.0, 1.0}},
+ {7, {4.0, 4.0}},
+ {8, {1.0, 4.0}},
+ {5, {1.0, 1.0}}
+ }},
+ { false, {
+ {10, {5.0, 5.0}},
+ {11, {5.0, 7.0}},
+ {12, {7.0, 7.0}},
+ {10, {5.0, 5.0}}
+ }},
+ { true, {
+ {100, {10.0, 10.0}},
+ {101, {11.0, 10.0}},
+ {102, {11.0, 11.0}},
+ {103, {10.0, 11.0}},
+ {100, {10.0, 10.0}}
+ }}
+ });
+
+ std::string wkb = wkb_factory.create_multipolygon(area);
+ std::unique_ptr<geos::geom::MultiPolygon> geos = geos_factory.create_multipolygon(area);
+ REQUIRE(geos_to_wkb(geos.get()) == wkb);
+}
+
+}
+
diff --git a/test/t/geom/test_mercator.cpp b/test/t/geom/test_mercator.cpp
new file mode 100644
index 0000000..a6ce2ee
--- /dev/null
+++ b/test/t/geom/test_mercator.cpp
@@ -0,0 +1,37 @@
+#include "catch.hpp"
+
+#include <osmium/geom/mercator_projection.hpp>
+
+TEST_CASE("Mercator") {
+
+SECTION("mercator_projection") {
+ osmium::geom::MercatorProjection projection;
+ REQUIRE(3857 == projection.epsg());
+ REQUIRE("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" == projection.proj_string());
+}
+
+SECTION("low_level_mercator_functions") {
+ osmium::geom::Coordinates c1(17.839, -3.249);
+ osmium::geom::Coordinates r1 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c1));
+ REQUIRE(r1.x == Approx(c1.x).epsilon(0.000001));
+ REQUIRE(r1.y == Approx(c1.y).epsilon(0.000001));
+
+ osmium::geom::Coordinates c2(-89.2, 15.915);
+ osmium::geom::Coordinates r2 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c2));
+ REQUIRE(r2.x == Approx(c2.x).epsilon(0.000001));
+ REQUIRE(r2.y == Approx(c2.y).epsilon(0.000001));
+
+ osmium::geom::Coordinates c3(180.0, 85.0);
+ osmium::geom::Coordinates r3 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c3));
+ REQUIRE(r3.x == Approx(c3.x).epsilon(0.000001));
+ REQUIRE(r3.y == Approx(c3.y).epsilon(0.000001));
+}
+
+SECTION("mercator_bounds") {
+ osmium::Location mmax(180.0, osmium::geom::MERCATOR_MAX_LAT);
+ osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(mmax);
+ REQUIRE(c.x == Approx(c.y).epsilon(0.001));
+ REQUIRE(osmium::geom::detail::y_to_lat(osmium::geom::detail::lon_to_x(180.0)) == Approx(osmium::geom::MERCATOR_MAX_LAT).epsilon(0.0000001));
+}
+
+}
diff --git a/test/t/geom/test_ogr.cpp b/test/t/geom/test_ogr.cpp
new file mode 100644
index 0000000..28a1071
--- /dev/null
+++ b/test/t/geom/test_ogr.cpp
@@ -0,0 +1,185 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/ogr.hpp>
+
+#include "../basic/helper.hpp"
+
+TEST_CASE("OGR_Geometry") {
+
+SECTION("point") {
+ osmium::geom::OGRFactory<> factory;
+
+ std::unique_ptr<OGRPoint> point {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(3.2 == point->getX());
+ REQUIRE(4.2 == point->getY());
+}
+
+SECTION("empty_point") {
+ osmium::geom::OGRFactory<> factory;
+
+ REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
+}
+
+SECTION("linestring") {
+ osmium::geom::OGRFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::unique_ptr<OGRLineString> linestring {factory.create_linestring(wnl)};
+ REQUIRE(3 == linestring->getNumPoints());
+
+ REQUIRE(3.2 == linestring->getX(0));
+ REQUIRE(3.6 == linestring->getX(2));
+ }
+
+ {
+ std::unique_ptr<OGRLineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+ REQUIRE(3 == linestring->getNumPoints());
+
+ REQUIRE(3.6 == linestring->getX(0));
+ REQUIRE(3.2 == linestring->getX(2));
+ }
+
+ {
+ std::unique_ptr<OGRLineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(4 == linestring->getNumPoints());
+
+ REQUIRE(3.2 == linestring->getX(0));
+ }
+
+ {
+ std::unique_ptr<OGRLineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+ REQUIRE(4 == linestring->getNumPoints());
+
+ REQUIRE(3.6 == linestring->getX(0));
+ }
+}
+
+SECTION("area_1outer_0inner") {
+ osmium::geom::OGRFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {3.2, 4.2}},
+ {2, {3.5, 4.7}},
+ {3, {3.6, 4.9}},
+ {1, {3.2, 4.2}}
+ }}
+ });
+
+ std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(1 == mp->getNumGeometries());
+
+ const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(0 == p0->getNumInteriorRings());
+
+ const OGRLineString* l0e = p0->getExteriorRing();
+ REQUIRE(4 == l0e->getNumPoints());
+
+ REQUIRE(3.5 == l0e->getX(1));
+}
+
+SECTION("area_1outer_1inner") {
+ osmium::geom::OGRFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {8.0, 1.0}},
+ {7, {8.0, 8.0}},
+ {8, {1.0, 8.0}},
+ {5, {1.0, 1.0}}
+ }}
+ });
+
+ std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(1 == mp->getNumGeometries());
+
+ const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(1 == p0->getNumInteriorRings());
+
+ const OGRLineString* l0e = p0->getExteriorRing();
+ REQUIRE(5 == l0e->getNumPoints());
+
+ const OGRLineString* l0i0 = p0->getInteriorRing(0);
+ REQUIRE(5 == l0i0->getNumPoints());
+}
+
+SECTION("area_2outer_2inner") {
+ osmium::geom::OGRFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {4.0, 1.0}},
+ {7, {4.0, 4.0}},
+ {8, {1.0, 4.0}},
+ {5, {1.0, 1.0}}
+ }},
+ { false, {
+ {10, {5.0, 5.0}},
+ {11, {5.0, 7.0}},
+ {12, {7.0, 7.0}},
+ {10, {5.0, 5.0}}
+ }},
+ { true, {
+ {100, {10.0, 10.0}},
+ {101, {11.0, 10.0}},
+ {102, {11.0, 11.0}},
+ {103, {10.0, 11.0}},
+ {100, {10.0, 10.0}}
+ }}
+ });
+
+ std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+ REQUIRE(2 == mp->getNumGeometries());
+
+ const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(2 == p0->getNumInteriorRings());
+
+ const OGRLineString* l0e = p0->getExteriorRing();
+ REQUIRE(5 == l0e->getNumPoints());
+
+ const OGRPolygon* p1 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(1));
+ REQUIRE(0 == p1->getNumInteriorRings());
+
+ const OGRLineString* l1e = p1->getExteriorRing();
+ REQUIRE(5 == l1e->getNumPoints());
+}
+
+}
+
diff --git a/test/t/geom/test_projection.cpp b/test/t/geom/test_projection.cpp
new file mode 100644
index 0000000..2257d7f
--- /dev/null
+++ b/test/t/geom/test_projection.cpp
@@ -0,0 +1,131 @@
+#include "catch.hpp"
+
+#include <osmium/geom/factory.hpp>
+#include <osmium/geom/mercator_projection.hpp>
+#include <osmium/geom/projection.hpp>
+
+TEST_CASE("Projection") {
+
+SECTION("identity_projection") {
+ osmium::geom::IdentityProjection projection;
+ REQUIRE(4326 == projection.epsg());
+ REQUIRE("+proj=longlat +datum=WGS84 +no_defs" == projection.proj_string());
+}
+
+SECTION("project_location_4326") {
+ osmium::geom::Projection projection(4326);
+ REQUIRE(4326 == projection.epsg());
+ REQUIRE("+init=epsg:4326" == projection.proj_string());
+
+ const osmium::Location loc(1.0, 2.0);
+ const osmium::geom::Coordinates c {1.0, 2.0};
+ REQUIRE(c == projection(loc));
+}
+
+SECTION("project_location_4326_string") {
+ osmium::geom::Projection projection("+init=epsg:4326");
+ REQUIRE(-1 == projection.epsg());
+ REQUIRE("+init=epsg:4326" == projection.proj_string());
+
+ const osmium::Location loc(1.0, 2.0);
+ const osmium::geom::Coordinates c {1.0, 2.0};
+ REQUIRE(c == projection(loc));
+}
+
+SECTION("unknown_projection_string") {
+ REQUIRE_THROWS_AS(osmium::geom::Projection projection("abc"), osmium::projection_error);
+}
+
+SECTION("unknown_epsg_code") {
+ REQUIRE_THROWS_AS(osmium::geom::Projection projection(9999999), osmium::projection_error);
+}
+
+SECTION("project_location_3857") {
+ osmium::geom::Projection projection(3857);
+ REQUIRE(3857 == projection.epsg());
+ REQUIRE("+init=epsg:3857" == projection.proj_string());
+
+ {
+ const osmium::Location loc(0.0, 0.0);
+ const osmium::geom::Coordinates c {0.0, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(180.0, 0.0);
+ const osmium::geom::Coordinates c {20037508.34, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(180.0, 0.0);
+ const osmium::geom::Coordinates c {20037508.34, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(0.0, 85.0511288);
+ const osmium::geom::Coordinates c {0.0, 20037508.34};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+}
+
+SECTION("project_location_mercator") {
+ osmium::geom::MercatorProjection projection;
+
+ {
+ const osmium::Location loc(0.0, 0.0);
+ const osmium::geom::Coordinates c {0.0, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(180.0, 0.0);
+ const osmium::geom::Coordinates c {20037508.34, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(180.0, 0.0);
+ const osmium::geom::Coordinates c {20037508.34, 0.0};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(0.0, 85.0511288);
+ const osmium::geom::Coordinates c {0.0, 20037508.34};
+ REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1));
+ REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1));
+ }
+}
+
+SECTION("compare_mercators") {
+ osmium::geom::MercatorProjection projection_merc;
+ osmium::geom::Projection projection_3857(3857);
+ REQUIRE(3857 == projection_3857.epsg());
+ REQUIRE("+init=epsg:3857" == projection_3857.proj_string());
+
+ {
+ const osmium::Location loc(4.2, 27.3);
+ REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+ REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(160.789, -42.42);
+ REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+ REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(-0.001, 0.001);
+ REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+ REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+ }
+ {
+ const osmium::Location loc(-85.2, -85.2);
+ REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+ REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+ }
+}
+
+}
diff --git a/test/t/geom/test_wkb.cpp b/test/t/geom/test_wkb.cpp
new file mode 100644
index 0000000..730ee75
--- /dev/null
+++ b/test/t/geom/test_wkb.cpp
@@ -0,0 +1,133 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/wkb.hpp>
+
+#include "../basic/helper.hpp"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+TEST_CASE("WKB_Geometry_byte_order_dependent") {
+
+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_ewkb") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
+
+ std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"0101000020E61000009A99999999990940CDCCCCCCCCCC1040"} == wkb);
+}
+
+SECTION("linestring") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::string wkb {factory.create_linestring(wnl)};
+ REQUIRE(std::string{"0102000000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb);
+ }
+
+ {
+ std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+ REQUIRE(std::string{"010200000003000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"} == wkb);
+ }
+
+ {
+ std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(std::string{"0102000000040000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb);
+ }
+
+ {
+ 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);
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ std::string ewkb {factory.create_linestring(wnl)};
+ REQUIRE(std::string{"0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == ewkb);
+}
+
+SECTION("linestring_with_two_same_locations") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, {3.5, 4.7}}
+ });
+
+ 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);
+
+ {
+ std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+ REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb);
+ }
+
+ {
+ std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+ REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb);
+ }
+}
+
+SECTION("linestring_with_undefined_location") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, osmium::Location()}
+ });
+
+ REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location);
+}
+
+}
+
+#endif
+
+TEST_CASE("WKB_Geometry_byte_order_independent") {
+
+SECTION("empty_point") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
+}
+
+SECTION("empty_linestring") {
+ osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {});
+
+ 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/test/t/geom/test_wkt.cpp b/test/t/geom/test_wkt.cpp
new file mode 100644
index 0000000..ff1417c
--- /dev/null
+++ b/test/t/geom/test_wkt.cpp
@@ -0,0 +1,198 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/geom/wkt.hpp>
+
+#include "../basic/helper.hpp"
+
+TEST_CASE("WKT_Geometry") {
+
+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("empty_point") {
+ osmium::geom::WKTFactory<> factory;
+
+ REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
+}
+
+SECTION("linestring") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.2, 4.2}},
+ {3, {3.5, 4.7}},
+ {4, {3.5, 4.7}},
+ {2, {3.6, 4.9}}
+ });
+
+ {
+ std::string wkt {factory.create_linestring(wnl)};
+ REQUIRE(std::string{"LINESTRING(3.2 4.2,3.5 4.7,3.6 4.9)"} == wkt);
+ }
+
+ {
+ 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);
+ }
+
+ {
+ 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);
+ }
+
+ {
+ 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);
+ }
+}
+
+SECTION("empty_linestring") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(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);
+}
+
+SECTION("linestring_with_two_same_locations") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, {3.5, 4.7}}
+ });
+
+ 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);
+
+ {
+ 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);
+ }
+
+ {
+ 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_undefined_location") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ auto& wnl = osmium::builder::build_way_node_list(buffer, {
+ {1, {3.5, 4.7}},
+ {2, osmium::Location()}
+ });
+
+ REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location);
+}
+
+SECTION("area_1outer_0inner") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {3.2, 4.2}},
+ {2, {3.5, 4.7}},
+ {3, {3.6, 4.9}},
+ {1, {3.2, 4.2}}
+ }}
+ });
+
+ {
+ 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);
+ }
+}
+
+SECTION("area_1outer_1inner") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {8.0, 1.0}},
+ {7, {8.0, 8.0}},
+ {8, {1.0, 8.0}},
+ {5, {1.0, 1.0}}
+ }}
+ });
+
+ {
+ 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);
+ }
+}
+
+SECTION("area_2outer_2inner") {
+ osmium::geom::WKTFactory<> factory;
+
+ osmium::memory::Buffer buffer(10000);
+ osmium::Area& area = buffer_add_area(buffer,
+ "foo",
+ {},
+ {
+ { true, {
+ {1, {0.1, 0.1}},
+ {2, {9.1, 0.1}},
+ {3, {9.1, 9.1}},
+ {4, {0.1, 9.1}},
+ {1, {0.1, 0.1}}
+ }},
+ { false, {
+ {5, {1.0, 1.0}},
+ {6, {4.0, 1.0}},
+ {7, {4.0, 4.0}},
+ {8, {1.0, 4.0}},
+ {5, {1.0, 1.0}}
+ }},
+ { false, {
+ {10, {5.0, 5.0}},
+ {11, {5.0, 7.0}},
+ {12, {7.0, 7.0}},
+ {10, {5.0, 5.0}}
+ }},
+ { true, {
+ {100, {10.0, 10.0}},
+ {101, {11.0, 10.0}},
+ {102, {11.0, 11.0}},
+ {103, {10.0, 11.0}},
+ {100, {10.0, 10.0}}
+ }}
+ });
+
+ {
+ 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);
+ }
+}
+
+}
+
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
new file mode 100644
index 0000000..fbb3f6a
--- /dev/null
+++ b/test/t/index/test_id_to_location.cpp
@@ -0,0 +1,170 @@
+#include "catch.hpp"
+
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/location.hpp>
+
+#include <osmium/index/map/dense_file_array.hpp>
+#include <osmium/index/map/dense_mem_array.hpp>
+#include <osmium/index/map/dense_mmap_array.hpp>
+#include <osmium/index/map/dummy.hpp>
+#include <osmium/index/map/sparse_file_array.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+#include <osmium/index/map/sparse_mem_map.hpp>
+#include <osmium/index/map/sparse_mem_table.hpp>
+#include <osmium/index/map/sparse_mmap_array.hpp>
+
+#include <osmium/index/node_locations_map.hpp>
+
+template <typename TIndex>
+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);
+
+ REQUIRE_THROWS_AS(index.get(id1), osmium::not_found);
+
+ index.set(id1, loc1);
+ index.set(id2, loc2);
+
+ index.sort();
+
+ REQUIRE_THROWS_AS(index.get(5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+}
+
+template <typename TIndex>
+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);
+
+ index.set(id1, loc1);
+ index.set(id2, loc2);
+
+ index.sort();
+
+ REQUIRE(loc1 == index.get(id1));
+ REQUIRE(loc2 == index.get(id2));
+
+ 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);
+}
+
+TEST_CASE("IdToLocation") {
+
+SECTION("Dummy") {
+ typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+
+ REQUIRE(0 == index1.size());
+ REQUIRE(0 == index1.used_memory());
+
+ test_func_all<index_type>(index1);
+
+ REQUIRE(0 == index1.size());
+ REQUIRE(0 == index1.used_memory());
+}
+
+SECTION("DenseMemArray") {
+ typedef osmium::index::map::DenseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+ index1.reserve(1000);
+ test_func_all<index_type>(index1);
+
+ index_type index2;
+ index2.reserve(1000);
+ test_func_real<index_type>(index2);
+}
+
+#ifdef __linux__
+SECTION("DenseMmapArray") {
+ typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+ test_func_all<index_type>(index1);
+
+ index_type index2;
+ test_func_real<index_type>(index2);
+}
+#else
+# pragma message("not running 'DenseMapMmap' test case on this machine")
+#endif
+
+SECTION("DenseFileArray") {
+ typedef osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+ test_func_all<index_type>(index1);
+
+ index_type index2;
+ test_func_real<index_type>(index2);
+}
+
+#ifdef OSMIUM_WITH_SPARSEHASH
+
+SECTION("SparseMemTable") {
+ typedef osmium::index::map::SparseMemTable<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+ test_func_all<index_type>(index1);
+
+ index_type index2;
+ test_func_real<index_type>(index2);
+}
+
+#endif
+
+SECTION("SparseMemMap") {
+ typedef osmium::index::map::SparseMemMap<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+ test_func_all<index_type>(index1);
+
+ index_type index2;
+ test_func_real<index_type>(index2);
+}
+
+SECTION("SparseMemArray") {
+ typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+ index_type index1;
+
+ REQUIRE(0 == index1.size());
+ REQUIRE(0 == index1.used_memory());
+
+ test_func_all<index_type>(index1);
+
+ REQUIRE(2 == index1.size());
+
+ index_type index2;
+ test_func_real<index_type>(index2);
+}
+
+SECTION("Dynamic map choice") {
+ typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> map_type;
+ const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+
+ std::vector<std::string> map_type_names = map_factory.map_types();
+ REQUIRE(map_type_names.size() >= 5);
+
+ for (const auto& map_type_name : map_type_names) {
+ std::unique_ptr<map_type> index1 = map_factory.create_map(map_type_name);
+ index1->reserve(1000);
+ test_func_all<map_type>(*index1);
+
+ std::unique_ptr<map_type> index2 = map_factory.create_map(map_type_name);
+ index2->reserve(1000);
+ test_func_real<map_type>(*index2);
+ }
+}
+
+}
+
diff --git a/test/t/index/test_typed_mmap.cpp b/test/t/index/test_typed_mmap.cpp
new file mode 100644
index 0000000..df95dc2
--- /dev/null
+++ b/test/t/index/test_typed_mmap.cpp
@@ -0,0 +1,99 @@
+#include "catch.hpp"
+
+#include <osmium/index/detail/typed_mmap.hpp>
+
+#if defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32))
+ #include "win_mkstemp.hpp"
+#endif
+
+TEST_CASE("TypedMmap") {
+
+SECTION("Mmap") {
+ uint64_t* data = osmium::detail::typed_mmap<uint64_t>::map(10);
+
+ data[0] = 4ul;
+ data[3] = 9ul;
+ data[9] = 25ul;
+
+ REQUIRE(4ul == data[0]);
+ REQUIRE(9ul == data[3]);
+ REQUIRE(25ul == data[9]);
+
+ osmium::detail::typed_mmap<uint64_t>::unmap(data, 10);
+}
+
+SECTION("MmapSizeZero") {
+ REQUIRE_THROWS_AS(osmium::detail::typed_mmap<uint64_t>::map(0), std::system_error);
+}
+
+SECTION("MmapHugeSize") {
+ // this is a horrible hack to only run the test on 64bit machines.
+ if (sizeof(size_t) >= 8) {
+ REQUIRE_THROWS_AS(osmium::detail::typed_mmap<uint64_t>::map(1ULL << (sizeof(size_t) * 6)), std::system_error);
+ }
+}
+
+#ifdef __linux__
+SECTION("Remap") {
+ uint64_t* data = osmium::detail::typed_mmap<uint64_t>::map(10);
+
+ data[0] = 4ul;
+ data[3] = 9ul;
+ data[9] = 25ul;
+
+ uint64_t* new_data = osmium::detail::typed_mmap<uint64_t>::remap(data, 10, 1000);
+
+ REQUIRE(4ul == new_data[0]);
+ REQUIRE(9ul == new_data[3]);
+ REQUIRE(25ul == new_data[9]);
+}
+#else
+# pragma message("not running 'Remap' test case on this machine")
+#endif
+
+SECTION("FileSize") {
+ const int size = 100;
+ char filename[] = "test_mmap_file_size_XXXXXX";
+ const int fd = mkstemp(filename);
+ REQUIRE(fd > 0);
+ REQUIRE(0 == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+ REQUIRE(0 == ftruncate(fd, size * sizeof(uint64_t)));
+ REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+
+ osmium::detail::typed_mmap<uint64_t>::grow_file(size / 2, fd);
+ REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+
+ osmium::detail::typed_mmap<uint64_t>::grow_file(size, fd);
+ REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+
+ osmium::detail::typed_mmap<uint64_t>::grow_file(size * 2, fd);
+ REQUIRE((size * 2) == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+
+ REQUIRE(0 == close(fd));
+ REQUIRE(0 == unlink(filename));
+}
+
+SECTION("GrowAndMap") {
+ const int size = 100;
+ char filename[] = "test_mmap_grow_and_map_XXXXXX";
+ const int fd = mkstemp(filename);
+ REQUIRE(fd > 0);
+
+ uint64_t* data = osmium::detail::typed_mmap<uint64_t>::grow_and_map(size, fd);
+ REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
+
+ data[0] = 1ul;
+ data[1] = 8ul;
+ data[99] = 27ul;
+
+ REQUIRE(1ul == data[0]);
+ REQUIRE(8ul == data[1]);
+ REQUIRE(27ul == data[99]);
+
+ osmium::detail::typed_mmap<uint64_t>::unmap(data, size);
+
+ REQUIRE(0 == close(fd));
+ REQUIRE(0 == unlink(filename));
+}
+
+}
diff --git a/test/t/io/data.osm b/test/t/io/data.osm
new file mode 100644
index 0000000..fffb77a
--- /dev/null
+++ b/test/t/io/data.osm
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="testdata" upload="false">
+ <node id="1" version="1" timestamp="2014-01-01T00:00:00Z" uid="1" user="test" changeset="1" lon="1.02" lat="1.02"/>
+</osm>
diff --git a/test/t/io/data.osm.bz2 b/test/t/io/data.osm.bz2
new file mode 100644
index 0000000..0f38797
Binary files /dev/null and b/test/t/io/data.osm.bz2 differ
diff --git a/test/t/io/data.osm.gz b/test/t/io/data.osm.gz
new file mode 100644
index 0000000..07edb90
Binary files /dev/null and b/test/t/io/data.osm.gz differ
diff --git a/test/t/io/data_bzip2.txt b/test/t/io/data_bzip2.txt
new file mode 100644
index 0000000..755d6af
--- /dev/null
+++ b/test/t/io/data_bzip2.txt
@@ -0,0 +1 @@
+TESTDATA
diff --git a/test/t/io/data_bzip2.txt.bz2 b/test/t/io/data_bzip2.txt.bz2
new file mode 100644
index 0000000..f0ee7e7
Binary files /dev/null and b/test/t/io/data_bzip2.txt.bz2 differ
diff --git a/test/t/io/test_bzip2.cpp b/test/t/io/test_bzip2.cpp
new file mode 100644
index 0000000..e6342e1
--- /dev/null
+++ b/test/t/io/test_bzip2.cpp
@@ -0,0 +1,33 @@
+#include "catch.hpp"
+#include "utils.hpp"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <osmium/io/bzip2_compression.hpp>
+
+TEST_CASE("Bzip2") {
+
+SECTION("read_compressed_file") {
+ std::string input_file = with_data_dir("t/io/data_bzip2.txt.bz2");
+
+ int fd = ::open(input_file.c_str(), O_RDONLY);
+ REQUIRE(fd > 0);
+
+ size_t size = 0;
+ std::string all;
+ {
+ osmium::io::Bzip2Decompressor decomp(fd);
+ for (std::string data = decomp.read(); !data.empty(); data = decomp.read()) {
+ size += data.size();
+ all += data;
+ }
+ }
+
+ REQUIRE(9 == size);
+ REQUIRE("TESTDATA\n" == all);
+}
+
+}
+
diff --git a/test/t/io/test_file_formats.cpp b/test/t/io/test_file_formats.cpp
new file mode 100644
index 0000000..03f9578
--- /dev/null
+++ b/test/t/io/test_file_formats.cpp
@@ -0,0 +1,251 @@
+#include "catch.hpp"
+
+#include <iterator>
+
+#include <osmium/io/file.hpp>
+
+TEST_CASE("FileFormats") {
+
+SECTION("default_file_format") {
+ osmium::io::File f;
+ REQUIRE(osmium::io::file_format::unknown == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("stdin_stdout_empty") {
+ osmium::io::File f {""};
+ REQUIRE(osmium::io::file_format::unknown == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("stdin_stdout_dash") {
+ osmium::io::File f {"-"};
+ REQUIRE(osmium::io::file_format::unknown == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("stdin_stdout_bz2") {
+ osmium::io::File f {"-", "osm.bz2"};
+ REQUIRE("" == f.filename());
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osm") {
+ osmium::io::File f {"test.osm"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_pbf") {
+ osmium::io::File f {"test.pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osm_pbf") {
+ osmium::io::File f {"test.osm.pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_opl") {
+ osmium::io::File f {"test.opl"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osm_opl") {
+ osmium::io::File f {"test.osm.opl"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osm_gz") {
+ osmium::io::File f {"test.osm.gz"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_opl_bz2") {
+ osmium::io::File f {"test.osm.opl.bz2"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osc_gz") {
+ osmium::io::File f {"test.osc.gz"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_opl_gz") {
+ osmium::io::File f {"test.osh.opl.gz"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("detect_file_format_by_suffix_osh_pbf") {
+ osmium::io::File f {"test.osh.pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osm") {
+ osmium::io::File f {"test", "osm"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_pbf") {
+ osmium::io::File f {"test", "pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osm_pbf") {
+ osmium::io::File f {"test", "osm.pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_opl") {
+ osmium::io::File f {"test", "opl"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osm_opl") {
+ osmium::io::File f {"test", "osm.opl"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osm_gz") {
+ osmium::io::File f {"test", "osm.gz"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osm_opl_bz2") {
+ osmium::io::File f {"test", "osm.opl.bz2"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+ REQUIRE(false == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osc_gz") {
+ osmium::io::File f {"test", "osc.gz"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osh_opl_gz") {
+ osmium::io::File f {"test", "osh.opl.gz"};
+ REQUIRE(osmium::io::file_format::opl == f.format());
+ REQUIRE(osmium::io::file_compression::gzip == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("override_file_format_by_suffix_osh_pbf") {
+ osmium::io::File f {"test", "osh.pbf"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("format_options_pbf_history") {
+ osmium::io::File f {"test", "pbf,history=true"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE(true == f.has_multiple_object_versions());
+ f.check();
+}
+
+SECTION("format_options_pbf_foo") {
+ osmium::io::File f {"test.osm", "pbf,foo=bar"};
+ REQUIRE(osmium::io::file_format::pbf == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE("bar" == f.get("foo"));
+ f.check();
+}
+
+SECTION("format_options_xml_abc_something") {
+ osmium::io::File f {"test.bla", "xml,abc,some=thing"};
+ REQUIRE(osmium::io::file_format::xml == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE("true" == f.get("abc"));
+ REQUIRE("thing" == f.get("some"));
+ REQUIRE(2 == std::distance(f.begin(), f.end()));
+ f.check();
+}
+
+SECTION("unknown_format_foo_bar") {
+ osmium::io::File f {"test.foo.bar"};
+ REQUIRE(osmium::io::file_format::unknown == f.format());
+ REQUIRE(osmium::io::file_compression::none == f.compression());
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("unknown_format_foo") {
+ osmium::io::File f {"test", "foo"};
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("unknown_format_osm_foo") {
+ osmium::io::File f {"test", "osm.foo"};
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+SECTION("unknown_format_bla_equals_foo") {
+ osmium::io::File f {"test", "bla=foo"};
+ REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+}
+
diff --git a/test/t/io/test_output_iterator.cpp b/test/t/io/test_output_iterator.cpp
new file mode 100644
index 0000000..7a1f570
--- /dev/null
+++ b/test/t/io/test_output_iterator.cpp
@@ -0,0 +1,37 @@
+#include "catch.hpp"
+
+#include <osmium/io/xml_output.hpp>
+#include <osmium/io/output_iterator.hpp>
+#include <osmium/io/writer.hpp>
+
+TEST_CASE("output iterator") {
+
+ SECTION("should be copy constructable") {
+ osmium::io::Header header;
+ osmium::io::Writer writer("test.osm", header, osmium::io::overwrite::allow);
+ osmium::io::OutputIterator<osmium::io::Writer> out1(writer);
+
+ osmium::io::OutputIterator<osmium::io::Writer> out2(out1);
+ }
+
+ SECTION("should be copy assignable") {
+ osmium::io::Header header;
+ osmium::io::Writer writer1("test1.osm", header, osmium::io::overwrite::allow);
+ osmium::io::Writer writer2("test2.osm", header, osmium::io::overwrite::allow);
+
+ osmium::io::OutputIterator<osmium::io::Writer> out1(writer1);
+ osmium::io::OutputIterator<osmium::io::Writer> out2(writer2);
+
+ out2 = out1;
+ }
+
+ SECTION("should be incrementable") {
+ osmium::io::Header header;
+ osmium::io::Writer writer("test.osm", header, osmium::io::overwrite::allow);
+ osmium::io::OutputIterator<osmium::io::Writer> out(writer);
+
+ ++out;
+ }
+
+}
+
diff --git a/test/t/io/test_reader.cpp b/test/t/io/test_reader.cpp
new file mode 100644
index 0000000..9a06d84
--- /dev/null
+++ b/test/t/io/test_reader.cpp
@@ -0,0 +1,117 @@
+#include "catch.hpp"
+#include "utils.hpp"
+
+#include <osmium/handler.hpp>
+#include <osmium/io/any_compression.hpp>
+#include <osmium/io/xml_input.hpp>
+#include <osmium/visitor.hpp>
+#include <osmium/memory/buffer.hpp>
+
+struct CountHandler : public osmium::handler::Handler {
+
+ int count = 0;
+
+ void node(osmium::Node&) {
+ ++count;
+ }
+
+}; // class CountHandler
+
+TEST_CASE("Reader") {
+
+ SECTION("reader can be initialized with file") {
+ osmium::io::File file(with_data_dir("t/io/data.osm"));
+ osmium::io::Reader reader(file);
+ osmium::handler::Handler handler;
+
+ osmium::apply(reader, handler);
+ }
+
+ SECTION("reader can be initialized with string") {
+ osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+ osmium::handler::Handler handler;
+
+ osmium::apply(reader, handler);
+ }
+
+ SECTION("should return invalid buffer after eof") {
+ osmium::io::File file(with_data_dir("t/io/data.osm"));
+ osmium::io::Reader reader(file);
+
+ REQUIRE(!reader.eof());
+
+ while (osmium::memory::Buffer buffer = reader.read()) {
+ }
+
+ REQUIRE(reader.eof());
+
+ // extra read always returns invalid buffer
+ osmium::memory::Buffer buffer = reader.read();
+ REQUIRE(!buffer);
+ }
+
+ SECTION("should not hang when apply() is called twice on reader") {
+ osmium::io::File file(with_data_dir("t/io/data.osm"));
+ osmium::io::Reader reader(file);
+ osmium::handler::Handler handler;
+
+ osmium::apply(reader, handler);
+ osmium::apply(reader, handler);
+ }
+
+ SECTION("should work with a buffer with uncompressed data") {
+ int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm"));
+ REQUIRE(fd >= 0);
+
+ const size_t buffer_size = 1000;
+ char buffer[buffer_size];
+ auto length = ::read(fd, buffer, buffer_size);
+ REQUIRE(length > 0);
+
+ osmium::io::File file(buffer, static_cast<size_t>(length), "osm");
+ osmium::io::Reader reader(file);
+ CountHandler handler;
+
+ REQUIRE(handler.count == 0);
+ osmium::apply(reader, handler);
+ REQUIRE(handler.count == 1);
+ }
+
+ SECTION("should work with a buffer with gzip-compressed data") {
+ int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz"));
+ REQUIRE(fd >= 0);
+
+ const size_t buffer_size = 1000;
+ char buffer[buffer_size];
+ auto length = ::read(fd, buffer, buffer_size);
+ REQUIRE(length > 0);
+
+ osmium::io::File file(buffer, static_cast<size_t>(length), "osm.gz");
+ osmium::io::Reader reader(file);
+ CountHandler handler;
+
+ REQUIRE(handler.count == 0);
+ osmium::apply(reader, handler);
+ REQUIRE(handler.count == 1);
+ }
+
+ SECTION("should work with a buffer with bzip2-compressed data") {
+ int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.bz2"));
+ REQUIRE(fd >= 0);
+
+ const size_t buffer_size = 1000;
+ char buffer[buffer_size];
+ auto length = ::read(fd, buffer, buffer_size);
+ REQUIRE(length > 0);
+
+ osmium::io::File file(buffer, static_cast<size_t>(length), "osm.bz2");
+ osmium::io::Reader reader(file);
+ CountHandler handler;
+
+ REQUIRE(handler.count == 0);
+ osmium::apply(reader, handler);
+ REQUIRE(handler.count == 1);
+ }
+
+}
+
diff --git a/test/t/tags/test_filter.cpp b/test/t/tags/test_filter.cpp
new file mode 100644
index 0000000..e65f63d
--- /dev/null
+++ b/test/t/tags/test_filter.cpp
@@ -0,0 +1,216 @@
+#include "catch.hpp"
+
+#include <algorithm>
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/tag.hpp>
+#include <osmium/tags/taglist.hpp>
+#include <osmium/tags/filter.hpp>
+#include <osmium/tags/regex_filter.hpp>
+
+template <class TFilter>
+void check_filter(const osmium::TagList& tag_list, const TFilter filter, const std::vector<bool>& reference) {
+ REQUIRE(tag_list.size() == reference.size());
+ auto t_it = tag_list.begin();
+ for (auto it = reference.begin(); it != reference.end(); ++t_it, ++it) {
+ REQUIRE(filter(*t_it) == *it);
+ }
+
+ typename TFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
+ typename TFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
+
+ REQUIRE(std::distance(fi_begin, fi_end) == std::count(reference.begin(), reference.end(), true));
+}
+
+TEST_CASE("Filter") {
+
+SECTION("KeyFilter_matches_some_tags") {
+ osmium::tags::KeyFilter filter(false);
+ filter.add(true, "highway").add(true, "railway");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" }, // match
+ { "name", "Main Street" }, // no match
+ { "source", "GPS" } // no match
+ });
+
+ std::vector<bool> results = { true, false, false };
+
+ check_filter(tag_list, filter, results);
+}
+
+SECTION("KeyFilter_iterator_filters_tags") {
+ osmium::tags::KeyFilter filter(false);
+ filter.add(true, "highway").add(true, "source");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" }, // match
+ { "name", "Main Street" }, // no match
+ { "source", "GPS" } // no match
+ });
+
+ osmium::tags::KeyFilter::iterator it(filter, tl.begin(), tl.end());
+ const osmium::tags::KeyFilter::iterator end(filter, tl.end(), tl.end());
+
+ REQUIRE(2 == std::distance(it, end));
+
+ REQUIRE(it != end);
+ REQUIRE(std::string("highway") == it->key());
+ REQUIRE(std::string("primary") == it->value());
+ ++it;
+ REQUIRE(std::string("source") == it->key());
+ REQUIRE(std::string("GPS") == it->value());
+ REQUIRE(++it == end);
+}
+
+SECTION("KeyValueFilter_matches_some_tags") {
+ osmium::tags::KeyValueFilter filter(false);
+
+ filter.add(true, "highway", "residential").add(true, "highway", "primary").add(true, "railway");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "railway", "tram" },
+ { "source", "GPS" }
+ });
+
+ std::vector<bool> results = {true, true, false};
+
+ check_filter(tag_list, filter, results);
+}
+
+SECTION("KeyValueFilter_ordering_matters") {
+ osmium::tags::KeyValueFilter filter1(false);
+ filter1.add(true, "highway").add(false, "highway", "road");
+
+ osmium::tags::KeyValueFilter filter2(false);
+ filter2.add(false, "highway", "road").add(true, "highway");
+
+ osmium::memory::Buffer buffer(10240);
+
+ const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, {
+ { "highway", "road" },
+ { "name", "Main Street" }
+ });
+
+ const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name", "Main Street" }
+ });
+
+ check_filter(tag_list1, filter1, {true, false});
+ check_filter(tag_list1, filter2, {false, false});
+ check_filter(tag_list2, filter2, {true, false});
+}
+
+SECTION("KeyValueFilter_matches_against_taglist_with_any") {
+ osmium::tags::KeyValueFilter filter(false);
+
+ filter.add(true, "highway", "primary").add(true, "name");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "railway", "tram" },
+ { "source", "GPS" }
+ });
+
+ REQUIRE( osmium::tags::match_any_of(tag_list, filter));
+ REQUIRE(!osmium::tags::match_all_of(tag_list, filter));
+ REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
+}
+
+SECTION("KeyValueFilter_matches_against_taglist_with_all") {
+ osmium::tags::KeyValueFilter filter(false);
+
+ filter.add(true, "highway", "primary").add(true, "name");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name", "Main Street" }
+ });
+
+ REQUIRE( osmium::tags::match_any_of(tag_list, filter));
+ REQUIRE( osmium::tags::match_all_of(tag_list, filter));
+ REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
+}
+
+SECTION("KeyValueFilter_matches_against_taglist_with_none") {
+ osmium::tags::KeyValueFilter filter(false);
+
+ filter.add(true, "highway", "road").add(true, "source");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name", "Main Street" }
+ });
+
+ REQUIRE(!osmium::tags::match_any_of(tag_list, filter));
+ REQUIRE(!osmium::tags::match_all_of(tag_list, filter));
+ REQUIRE( osmium::tags::match_none_of(tag_list, filter));
+}
+
+SECTION("KeyValueFilter_matches_against_taglist_with_any_called_with_rvalue") {
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "railway", "tram" },
+ { "source", "GPS" }
+ });
+
+ REQUIRE(osmium::tags::match_any_of(tag_list,
+ osmium::tags::KeyValueFilter().add(true, "highway", "primary").add(true, "name")));
+}
+
+SECTION("RegexFilter_matches_some_tags") {
+ osmium::tags::RegexFilter filter(false);
+ filter.add(true, "highway", std::regex(".*_link"));
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary_link" },
+ { "source", "GPS" }
+ });
+ const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "source", "GPS" }
+ });
+
+ check_filter(tag_list1, filter, {true, false});
+ check_filter(tag_list2, filter, {false, false});
+}
+
+SECTION("RegexFilter_matches_some_tags_with_lvalue_regex") {
+ osmium::tags::RegexFilter filter(false);
+ std::regex r(".*straße");
+ filter.add(true, "name", r);
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name", "Hauptstraße" }
+ });
+
+ check_filter(tag_list, filter, {false, true});
+}
+
+SECTION("KeyPrefixFilter_matches_some_tags") {
+ osmium::tags::KeyPrefixFilter filter(false);
+ filter.add(true, "name:");
+
+ osmium::memory::Buffer buffer(10240);
+ const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name:de", "Hauptstraße" }
+ });
+
+ check_filter(tag_list, filter, {false, true});
+}
+
+}
diff --git a/test/t/tags/test_operators.cpp b/test/t/tags/test_operators.cpp
new file mode 100644
index 0000000..e2c22a4
--- /dev/null
+++ b/test/t/tags/test_operators.cpp
@@ -0,0 +1,61 @@
+#include "catch.hpp"
+
+#include <iterator>
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/tag.hpp>
+
+TEST_CASE("Operators") {
+
+SECTION("Equal") {
+ osmium::memory::Buffer buffer1(10240);
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer1);
+ tl_builder.add_tag("highway", "primary");
+ tl_builder.add_tag("name", "Main Street");
+ tl_builder.add_tag("source", "GPS");
+ }
+ buffer1.commit();
+
+ osmium::memory::Buffer buffer2(10240);
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer2);
+ tl_builder.add_tag("highway", "primary");
+ }
+ buffer2.commit();
+
+ const osmium::TagList& tl1 = buffer1.get<const osmium::TagList>(0);
+ const osmium::TagList& tl2 = buffer2.get<const osmium::TagList>(0);
+
+ auto tagit1 = tl1.begin();
+ auto tagit2 = tl2.begin();
+ REQUIRE(*tagit1 == *tagit2);
+ ++tagit1;
+ REQUIRE(!(*tagit1 == *tagit2));
+}
+
+SECTION("Order") {
+ osmium::memory::Buffer buffer(10240);
+ {
+ osmium::builder::TagListBuilder tl_builder(buffer);
+ tl_builder.add_tag("highway", "residential");
+ tl_builder.add_tag("highway", "primary");
+ tl_builder.add_tag("name", "Main Street");
+ tl_builder.add_tag("amenity", "post_box");
+ }
+ buffer.commit();
+
+ const osmium::TagList& tl = buffer.get<const osmium::TagList>(0);
+ const osmium::Tag& t1 = *(tl.begin());
+ const osmium::Tag& t2 = *(std::next(tl.begin(), 1));
+ const osmium::Tag& t3 = *(std::next(tl.begin(), 2));
+ const osmium::Tag& t4 = *(std::next(tl.begin(), 3));
+
+ REQUIRE(t2 < t1);
+ REQUIRE(t1 < t3);
+ REQUIRE(t2 < t3);
+ REQUIRE(t4 < t1);
+}
+
+}
diff --git a/test/t/tags/test_tag_list.cpp b/test/t/tags/test_tag_list.cpp
new file mode 100644
index 0000000..b25d8ca
--- /dev/null
+++ b/test/t/tags/test_tag_list.cpp
@@ -0,0 +1,76 @@
+#include "catch.hpp"
+
+#include <osmium/builder/builder_helper.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/tag.hpp>
+
+TEST_CASE("tag_list") {
+
+SECTION("can_be_created_from_initializer_list") {
+ osmium::memory::Buffer buffer(10240);
+
+ const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
+ { "highway", "primary" },
+ { "name", "Main Street" },
+ { "source", "GPS" }
+ });
+
+ REQUIRE(osmium::item_type::tag_list == tl.type());
+ REQUIRE(3 == tl.size());
+ REQUIRE(std::string("highway") == tl.begin()->key());
+ REQUIRE(std::string("primary") == tl.begin()->value());
+}
+
+SECTION("can_be_created_from_map") {
+ osmium::memory::Buffer buffer(10240);
+
+ const osmium::TagList& tl = osmium::builder::build_tag_list_from_map(buffer, std::map<const char*, const char*>({
+ { "highway", "primary" },
+ { "name", "Main Street" }
+ }));
+
+ REQUIRE(osmium::item_type::tag_list == tl.type());
+ REQUIRE(2 == tl.size());
+
+ if (std::string("highway") == tl.begin()->key()) {
+ REQUIRE(std::string("primary") == tl.begin()->value());
+ REQUIRE(std::string("name") == std::next(tl.begin(), 1)->key());
+ REQUIRE(std::string("Main Street") == std::next(tl.begin(), 1)->value());
+ } else {
+ REQUIRE(std::string("highway") == std::next(tl.begin(), 1)->key());
+ REQUIRE(std::string("primary") == std::next(tl.begin(), 1)->value());
+ REQUIRE(std::string("name") == tl.begin()->key());
+ REQUIRE(std::string("Main Street") == tl.begin()->value());
+ }
+}
+
+SECTION("can_be_created_with_callback") {
+ osmium::memory::Buffer buffer(10240);
+
+ const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
+ tlb.add_tag("highway", "primary");
+ tlb.add_tag("bridge", "true");
+ });
+
+ REQUIRE(osmium::item_type::tag_list == tl.type());
+ REQUIRE(2 == tl.size());
+ REQUIRE(std::string("bridge") == std::next(tl.begin(), 1)->key());
+ REQUIRE(std::string("true") == std::next(tl.begin(), 1)->value());
+}
+
+SECTION("returns_value_by_key") {
+ osmium::memory::Buffer buffer(10240);
+
+ const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
+ tlb.add_tag("highway", "primary");
+ tlb.add_tag("bridge", "true");
+ });
+
+ REQUIRE(std::string("primary") == tl.get_value_by_key("highway"));
+ REQUIRE(nullptr == tl.get_value_by_key("name"));
+ REQUIRE(std::string("foo") == tl.get_value_by_key("name", "foo"));
+
+ REQUIRE(std::string("true") == tl["bridge"]);
+}
+
+}
diff --git a/test/t/thread/test_pool.cpp b/test/t/thread/test_pool.cpp
new file mode 100644
index 0000000..4bbf9ee
--- /dev/null
+++ b/test/t/thread/test_pool.cpp
@@ -0,0 +1,69 @@
+#include "catch.hpp"
+
+#include <chrono>
+#include <stdexcept>
+#include <thread>
+
+#include <osmium/thread/pool.hpp>
+
+static std::atomic<int> result;
+
+struct test_job_ok {
+ void operator()() const {
+ result = 1;
+ }
+};
+
+struct test_job_with_result {
+ int operator()() const {
+ return 42;
+ }
+};
+
+struct test_job_throw {
+ void operator()() const {
+ throw std::runtime_error("exception in pool thread");
+ }
+};
+
+TEST_CASE("thread") {
+
+ SECTION("can get access to thread pool") {
+ auto& pool = osmium::thread::Pool::instance();
+ REQUIRE(pool.queue_empty());
+ }
+
+ SECTION("can send job to thread pool") {
+ auto& pool = osmium::thread::Pool::instance();
+ result = 0;
+ auto future = pool.submit(test_job_ok{});
+
+ // wait a bit for the other thread to get a chance to run
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ REQUIRE(result == 1);
+
+ future.get();
+
+ REQUIRE(true);
+ }
+
+ SECTION("can send job to thread pool") {
+ auto& pool = osmium::thread::Pool::instance();
+ auto future = pool.submit(test_job_with_result{});
+
+ REQUIRE(future.get() == 42);
+ }
+
+ SECTION("can throw from job in thread pool") {
+ auto& pool = osmium::thread::Pool::instance();
+ result = 0;
+
+ bool got_exception = false;
+ auto future = pool.submit(test_job_throw{});
+
+ REQUIRE_THROWS_AS(future.get(), std::runtime_error);
+ }
+
+}
+
diff --git a/test/t/util/test_cast_with_assert.cpp b/test/t/util/test_cast_with_assert.cpp
new file mode 100644
index 0000000..0231f30
--- /dev/null
+++ b/test/t/util/test_cast_with_assert.cpp
@@ -0,0 +1,89 @@
+#include "catch.hpp"
+
+// 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) {
+ }
+};
+#define assert(x) if (!(x)) { throw(assert_error(#x)); }
+
+#include <osmium/util/cast.hpp>
+
+TEST_CASE("static_cast_with_assert") {
+
+ SECTION("same types is always okay") {
+ int f = 10;
+ auto t = osmium::static_cast_with_assert<int>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("casting to larger type is always okay") {
+ int16_t f = 10;
+ auto t = osmium::static_cast_with_assert<int32_t>(f);
+ REQUIRE(t == f);
+ }
+
+
+ SECTION("cast int32_t -> int_16t should not trigger assert for small int") {
+ int32_t f = 100;
+ auto t = osmium::static_cast_with_assert<int16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast int32_t -> int_16t should trigger assert for large int") {
+ int32_t f = 100000;
+ REQUIRE_THROWS_AS(osmium::static_cast_with_assert<int16_t>(f), assert_error);
+ }
+
+
+ SECTION("cast int16_t -> uint16_t should not trigger assert for zero") {
+ int16_t f = 0;
+ auto t = osmium::static_cast_with_assert<uint16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast int16_t -> uint16_t should not trigger assert for positive int") {
+ int16_t f = 1;
+ auto t = osmium::static_cast_with_assert<uint16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast int16_t -> uint16_t should trigger assert for negative int") {
+ int16_t f = -1;
+ REQUIRE_THROWS_AS(osmium::static_cast_with_assert<uint16_t>(f), assert_error);
+ }
+
+
+ SECTION("cast uint32_t -> uint_16t should not trigger assert for zero") {
+ uint32_t f = 0;
+ auto t = osmium::static_cast_with_assert<uint16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast uint32_t -> uint_16t should not trigger assert for small int") {
+ uint32_t f = 100;
+ auto t = osmium::static_cast_with_assert<uint16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast int32_t -> int_16t should trigger assert for large int") {
+ uint32_t f = 100000;
+ REQUIRE_THROWS_AS(osmium::static_cast_with_assert<uint16_t>(f), assert_error);
+ }
+
+
+ SECTION("cast uint16_t -> int16_t should not trigger assert for small int") {
+ uint16_t f = 1;
+ auto t = osmium::static_cast_with_assert<int16_t>(f);
+ REQUIRE(t == f);
+ }
+
+ SECTION("cast uint16_t -> int16_t should trigger assert for large int") {
+ uint16_t f = 65000;
+ REQUIRE_THROWS_AS(osmium::static_cast_with_assert<int16_t>(f), assert_error);
+ }
+
+
+}
+
diff --git a/test/t/util/test_double.cpp b/test/t/util/test_double.cpp
new file mode 100644
index 0000000..79e8509
--- /dev/null
+++ b/test/t/util/test_double.cpp
@@ -0,0 +1,33 @@
+#include "catch.hpp"
+
+#include <osmium/util/double.hpp>
+
+TEST_CASE("Double") {
+
+SECTION("double2string") {
+ std::string s1;
+ osmium::util::double2string(s1, 1.123, 7);
+ REQUIRE(s1 == "1.123");
+
+ std::string s2;
+ osmium::util::double2string(s2, 1.000, 7);
+ REQUIRE(s2 == "1");
+
+ std::string s3;
+ osmium::util::double2string(s3, 0.0, 7);
+ REQUIRE(s3 == "0");
+
+ std::string s4;
+ osmium::util::double2string(s4, 0.020, 7);
+ REQUIRE(s4 == "0.02");
+
+ std::string s5;
+ osmium::util::double2string(s5, -0.020, 7);
+ REQUIRE(s5 == "-0.02");
+
+ std::string s6;
+ osmium::util::double2string(s6, -0.0, 7);
+ REQUIRE(s6 == "-0");
+}
+}
+
diff --git a/test/t/util/test_options.cpp b/test/t/util/test_options.cpp
new file mode 100644
index 0000000..203b9db
--- /dev/null
+++ b/test/t/util/test_options.cpp
@@ -0,0 +1,48 @@
+#include "catch.hpp"
+
+#include <iterator>
+
+#include <osmium/util/options.hpp>
+
+TEST_CASE("Options") {
+
+SECTION("set_simple") {
+ osmium::util::Options o;
+ o.set("foo", "bar");
+ REQUIRE("bar" == o.get("foo"));
+ REQUIRE("" == o.get("empty"));
+ REQUIRE("default" == o.get("empty", "default"));
+ REQUIRE(!o.is_true("foo"));
+ REQUIRE(!o.is_true("empty"));
+ REQUIRE(1 == o.size());
+}
+
+SECTION("set_from_bool") {
+ osmium::util::Options o;
+ o.set("t", true);
+ o.set("f", false);
+ REQUIRE("true" == o.get("t"));
+ REQUIRE("false" == o.get("f"));
+ REQUIRE("" == o.get("empty"));
+ REQUIRE(o.is_true("t"));
+ REQUIRE(!o.is_true("f"));
+ REQUIRE(2 == o.size());
+}
+
+SECTION("set_from_single_string_with_equals") {
+ osmium::util::Options o;
+ o.set("foo=bar");
+ REQUIRE("bar" == o.get("foo"));
+ REQUIRE(1 == o.size());
+}
+
+SECTION("set_from_single_string_without_equals") {
+ osmium::util::Options o;
+ o.set("foo");
+ REQUIRE("true" == o.get("foo"));
+ REQUIRE(o.is_true("foo"));
+ REQUIRE(1 == o.size());
+}
+
+}
+
diff --git a/test/t/util/test_string.cpp b/test/t/util/test_string.cpp
new file mode 100644
index 0000000..c7b75cf
--- /dev/null
+++ b/test/t/util/test_string.cpp
@@ -0,0 +1,57 @@
+#include "catch.hpp"
+
+#include <osmium/util/string.hpp>
+
+TEST_CASE("split_string") {
+
+SECTION("split_string string") {
+ std::string str { "foo,baramba,baz" };
+ std::vector<std::string> result = {"foo", "baramba", "baz"};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string string without sep") {
+ std::string str { "foo" };
+ std::vector<std::string> result = {"foo"};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string string with empty at end") {
+ std::string str { "foo,bar," };
+ std::vector<std::string> result = {"foo", "bar", ""};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string string with empty in middle") {
+ std::string str { "foo,,bar" };
+ std::vector<std::string> result = {"foo", "", "bar"};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string string with empty at start") {
+ std::string str { ",bar,baz" };
+ std::vector<std::string> result = {"", "bar", "baz"};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string sep") {
+ std::string str { "," };
+ std::vector<std::string> result = {"", ""};
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+SECTION("split_string empty string") {
+ std::string str { "" };
+ std::vector<std::string> result;
+
+ REQUIRE(result == osmium::split_string(str, ','));
+}
+
+}
+
diff --git a/test/test_main.cpp b/test/test_main.cpp
new file mode 100644
index 0000000..0c7c351
--- /dev/null
+++ b/test/test_main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
diff --git a/test/valgrind.supp b/test/valgrind.supp
new file mode 100644
index 0000000..f6ef1f6
--- /dev/null
+++ b/test/valgrind.supp
@@ -0,0 +1,47 @@
+#-----------------------------------------------------------------------------
+#
+# This file describes messages that Valgrind should suppress, because they
+# are about problems outside Libosmium that we can't fix anyway.
+#
+# See http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress
+#
+#-----------------------------------------------------------------------------
+
+{
+ dl_error1
+ Memcheck:Cond
+ fun:index
+ fun:expand_dynamic_string_token
+ fun:fillin_rpath
+ fun:_dl_init_paths
+ fun:dl_main
+ fun:_dl_sysdep_start
+ fun:_dl_start
+}
+{
+ dl_error2
+ Memcheck:Cond
+ fun:index
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+ fun:map_doit
+ fun:_dl_catch_error
+ fun:do_preload
+ fun:dl_main
+ fun:_dl_sysdep_start
+ fun:_dl_start
+}
+{
+ libpoppler_leak
+ Memcheck:Leak
+ fun:malloc
+ fun:gmalloc
+ fun:copyString
+}
+{
+ tmpfile
+ Memcheck:Leak
+ fun:malloc
+ fun:fdopen@@GLIBC_*
+ fun:tmpfile@@GLIBC_*
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/libosmium.git
More information about the Pkg-grass-devel
mailing list