[libosmium] 01/05: Imported Upstream version 2.10.0really2.9.0

Bas Couwenberg sebastic at debian.org
Mon Nov 14 19:21:48 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository libosmium.

commit 3f0c934b2b739412052c3d90f7624b9a316777e4
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Mon Nov 14 19:58:39 2016 +0100

    Imported Upstream version 2.10.0really2.9.0
---
 CHANGELOG.md                                       |  58 +--
 CMakeLists.txt                                     |   6 +-
 README.md                                          |   4 +-
 benchmarks/CMakeLists.txt                          |   3 -
 benchmarks/download_data.sh                        |  10 +-
 benchmarks/osmium_benchmark_count.cpp              |   6 +-
 benchmarks/osmium_benchmark_count_tag.cpp          |   6 +-
 benchmarks/osmium_benchmark_mercator.cpp           |  43 --
 benchmarks/run_benchmark_mercator.sh               |  22 -
 benchmarks/setup.sh                                |  16 +-
 cmake/FindOsmium.cmake                             |  62 +--
 examples/CMakeLists.txt                            |   8 +-
 examples/README.md                                 |  12 -
 examples/osmium_area_test.cpp                      |   4 +-
 examples/osmium_change_tags.cpp                    | 203 ----------
 examples/osmium_convert.cpp                        |   8 +-
 examples/osmium_create_pois.cpp                    |  96 -----
 examples/osmium_dump_internal.cpp                  | 179 ---------
 examples/osmium_filter_discussions.cpp             |   2 +-
 examples/osmium_index.cpp                          | 260 ++++++++++++
 examples/osmium_index_lookup.cpp                   | 333 ----------------
 examples/osmium_pub_names.cpp                      |   0
 examples/osmium_road_length.cpp                    |   0
 examples/osmium_serdump.cpp                        | 206 ++++++++++
 include/osmium/area/assembler.hpp                  |  52 +--
 include/osmium/area/detail/node_ref_segment.hpp    |   4 +-
 include/osmium/area/detail/segment_list.hpp        |   2 +-
 include/osmium/area/detail/vector.hpp              |  20 +-
 include/osmium/builder/attr.hpp                    |  64 ++-
 include/osmium/builder/builder.hpp                 |  91 +++--
 include/osmium/builder/osm_object_builder.hpp      | 314 ++-------------
 include/osmium/geom/factory.hpp                    |   2 +-
 include/osmium/geom/geos.hpp                       |  16 +-
 include/osmium/geom/relations.hpp                  |  10 +-
 include/osmium/geom/tile.hpp                       |  20 +-
 include/osmium/handler/disk_store.hpp              |   5 +-
 include/osmium/handler/node_locations_for_ways.hpp |   2 +-
 include/osmium/handler/object_relations.hpp        |   2 -
 include/osmium/index/bool_vector.hpp               |  41 +-
 include/osmium/index/detail/vector_map.hpp         |   6 +-
 include/osmium/index/id_set.hpp                    | 431 --------------------
 include/osmium/index/index.hpp                     |  12 +-
 include/osmium/index/map.hpp                       |  27 +-
 include/osmium/index/map/dummy.hpp                 |   2 +-
 include/osmium/index/map/sparse_mem_map.hpp        |   2 +-
 include/osmium/index/map/sparse_mem_table.hpp      |   4 +-
 include/osmium/io/compression.hpp                  |  60 +--
 include/osmium/io/detail/input_format.hpp          |  23 +-
 include/osmium/io/detail/o5m_input_format.hpp      | 104 ++---
 include/osmium/io/detail/opl_input_format.hpp      |   8 +-
 include/osmium/io/detail/opl_parser_functions.hpp  |  80 ++--
 include/osmium/io/detail/pbf_decoder.hpp           | 170 ++------
 include/osmium/io/detail/pbf_input_format.hpp      |  10 +-
 include/osmium/io/detail/queue_util.hpp            |  13 +-
 include/osmium/io/detail/xml_input_format.hpp      |  94 ++---
 include/osmium/io/file_format.hpp                  |   5 -
 include/osmium/io/header.hpp                       |  79 +---
 include/osmium/io/reader.hpp                       |  63 +--
 include/osmium/memory/buffer.hpp                   |  82 ++--
 include/osmium/memory/collection.hpp               |  47 +--
 include/osmium/memory/item.hpp                     |   4 +-
 include/osmium/osm/area.hpp                        |  12 +-
 include/osmium/osm/changeset.hpp                   |  18 +-
 include/osmium/osm/crc.hpp                         |  42 +-
 include/osmium/osm/entity_bits.hpp                 |  24 +-
 include/osmium/osm/location.hpp                    |  52 +--
 include/osmium/osm/node.hpp                        |   8 +-
 include/osmium/osm/node_ref_list.hpp               |  33 +-
 include/osmium/osm/object.hpp                      |   8 -
 include/osmium/osm/relation.hpp                    |  17 +-
 include/osmium/osm/tag.hpp                         |  19 +-
 include/osmium/osm/way.hpp                         |   6 +-
 include/osmium/relations/collector.hpp             |  58 +--
 include/osmium/relations/detail/member_meta.hpp    |  40 +-
 include/osmium/thread/queue.hpp                    |  57 +--
 include/osmium/util/progress_bar.hpp               |  12 -
 include/osmium/version.hpp                         |   4 +-
 test/CMakeLists.txt                                |  52 +--
 test/data-tests/testdata-xml.cpp                   |   2 +-
 test/examples/CMakeLists.txt                       |  21 -
 test/examples/t/pub_names/CMakeLists.txt           |   7 -
 test/examples/t/pub_names/pubs.osm                 |   7 -
 test/examples/t/road_length/CMakeLists.txt         |   8 -
 test/examples/t/road_length/road.osm               |  59 ---
 test/include/catch.hpp                             |  50 +--
 test/t/{osm => basic}/test_area.cpp                |   0
 test/t/{osm => basic}/test_box.cpp                 |   0
 test/t/{osm => basic}/test_changeset.cpp           |  67 ++--
 test/t/{osm => basic}/test_crc.cpp                 |   0
 test/t/basic/test_entity_bits.cpp                  |  32 ++
 test/t/{osm => basic}/test_location.cpp            | 119 +++---
 test/t/{osm => basic}/test_node.cpp                |   0
 test/t/{osm => basic}/test_node_ref.cpp            |   0
 test/t/{osm => basic}/test_object_comparisons.cpp  |   0
 test/t/{osm => basic}/test_relation.cpp            |   0
 test/t/{osm => basic}/test_timestamp.cpp           |   0
 test/t/{osm => basic}/test_types_from_string.cpp   |   0
 test/t/{osm => basic}/test_way.cpp                 |   2 +-
 test/t/{memory => buffer}/test_buffer_basics.cpp   |   0
 test/t/{memory => buffer}/test_buffer_node.cpp     | 140 ++++---
 test/t/{memory => buffer}/test_buffer_purge.cpp    |  82 ++--
 test/t/builder/test_object_builder.cpp             | 444 ---------------------
 test/t/geom/helper.hpp                             |  15 +
 test/t/geom/test_crs.cpp                           |  10 +-
 test/t/geom/test_exception.cpp                     |   8 +-
 test/t/geom/test_factory_with_projection.cpp       |  40 +-
 test/t/geom/test_geojson.cpp                       | 193 +++++----
 test/t/geom/test_geos.cpp                          |  56 ++-
 test/t/geom/test_geos_wkb.cpp                      |  92 +++++
 test/t/geom/test_ogr.cpp                           | 128 +++---
 test/t/geom/test_ogr_wkb.cpp                       | 100 -----
 test/t/geom/test_projection.cpp                    | 143 ++++---
 test/t/geom/test_tile.cpp                          |   2 +
 test/t/geom/test_wkb.cpp                           |  18 +-
 test/t/geom/wnl_helper.hpp                         |   6 +-
 test/t/index/test_id_set.cpp                       | 166 --------
 test/t/index/test_id_to_location.cpp               | 165 ++++----
 test/t/io/test_compression_factory.cpp             |  27 --
 test/t/io/test_reader_with_mock_parser.cpp         |   8 +-
 test/t/osm/test_entity_bits.cpp                    |  62 ---
 120 files changed, 2012 insertions(+), 4247 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 25327c8..0d132ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,53 +13,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
-## [2.10.0] - 2016-11-11
-
-### Added
-
-- The `Reader` can take an additional optional `read_meta` flag. If this is
-  set to false the PBF input will ignore metadata on OSM objects (like version,
-  timestamp, uid, ...) which speeds up file reading by 10 to 20%.
-- New `IdSet` virtual class with two implementations: `IdSetDense` and
-  `IdSetSmall`. Used to efficiently store a set of Ids. This is often needed
-  to track, for instance, which nodes are needed for ways, etc.
-- Added more examples and better documented existing examples.
-- Add a benchmark "mercator" converting all node locations in a file to
-  WebMercator and creating geometries in WKB format.
-
-### Changed
-
-- Better queue handling makes I/O faster in some circumstances.
-- The `FindOsmium.cmake` CMake script can now check a current enough libosmium
-  version is found.
-- Builders can now be constructed with a reference to parent builder.
-- Made builders more robust by adding asserts that will catch common usage
-  problems.
-- Calling `OSMObjectBuilder::add_user()` is now optional, and the method was
-  renamed to `set_user()`. (`add_user()` is marked as deprecated.)
-- Benchmarks now show compiler and compiler options used.
-- `Builder::add_item()` now takes a reference instead of pointer (old version
-  of the function marked as deprecated).
-- GEOS support is deprecated. It does not work any more for GEOS 3.6 or newer.
-  Reason is the changed interface in GEOS 3.6. If there is interest for the
-  GEOS support, we can add support back in later (but probably using the
-  GEOS C API which is more stable than the C++ API). Some tests using GEOS
-  were rewritten to work without it.
-- The `BoolVector` has been deprecated in favour of the new `IdSet` classes.
-- Lots of code cleanups and improved API documentation in many places.
-- The relations collector can now tell you whether a relation member was in
-  the input data. See the new `is_available()` and
-  `get_availability_and_offset()` methods.
-- Updated embedded Catch unit test header to version 1.5.8.
-
-### Fixed
-
-- Parsing of coordinates starting with decimal dot and coordinates in
-  scientific notation.
-- `~` operator for `entity_bits` doesn't set unused bits any more.
-- Progress bar can now be (temporarily) removed, to allow other output.
-
-
 ## [2.9.0] - 2016-09-15
 
 ### Added
@@ -157,7 +110,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - New functions for iterating over specific item types in buffers
   (`osmium::memory::Buffer::select()`), over specific subitems
   (`osmium::OSMObject::subitems()`), and for iterating over all rings of
-  an area (`osmium::Areas::outer_rings()`, `inner_rings()`).
+  an area (`osmium::Areas::outer_rings(`), `inner_rings()`).
 - Debug output optionally prints CRC32 when `add_crc32` file option is set.
 
 ### Changed
@@ -314,9 +267,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   one in Writer. Calling flush() on the OutputIterator isn't needed any
   more.
 - Reader now throws when trying to read after eof or an error.
-- I/O functions that used to throw `std::runtime_error` now throw
-  `osmium::io_error` or derived.
-- Optional parameters on `osmium::io::Writer` now work in any order.
+- I/O functions that used to throw std::runtime_error now throw
+  osmium::io_error or derived.
+- Optional parameters on osmium::io::Writer now work in any order.
 
 ### Fixed
 
@@ -457,8 +410,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   Doxygen (up to version 1.8.8). This version contains a workaround to fix
   this.
 
-[unreleased]: https://github.com/osmcode/libosmium/compare/v2.10.0...HEAD
-[2.10.0]: https://github.com/osmcode/libosmium/compare/v2.9.0...v2.10.0
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.9.0...HEAD
 [2.9.0]: https://github.com/osmcode/libosmium/compare/v2.8.0...v2.9.0
 [2.8.0]: https://github.com/osmcode/libosmium/compare/v2.7.2...v2.8.0
 [2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 21cf98e..095137b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,7 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover
 project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 10)
+set(LIBOSMIUM_VERSION_MINOR 9)
 set(LIBOSMIUM_VERSION_PATCH 0)
 
 set(LIBOSMIUM_VERSION
@@ -285,10 +285,6 @@ if(BUILD_DATA_TESTS)
     add_subdirectory(test/data-tests)
 endif()
 
-if(BUILD_EXAMPLES)
-    add_subdirectory(test/examples)
-endif()
-
 
 #-----------------------------------------------------------------------------
 #
diff --git a/README.md b/README.md
index d526f13..68fc2f6 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ http://osmcode.org/libosmium
 
 A fast and flexible C++ library for working with OpenStreetMap data.
 
-[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium)
-[![Build status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
+[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.png)](https://travis-ci.org/osmcode/libosmium)
+[![Build status](https://ci.appveyor.com/api/projects/status/mkbg6e6stdgq7c1b?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
 
 Libosmium is developed on Linux, but also works on OSX and Windows (with some
 limitations).
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
index 4b76fcb..e46c833 100644
--- a/benchmarks/CMakeLists.txt
+++ b/benchmarks/CMakeLists.txt
@@ -12,7 +12,6 @@ set(BENCHMARKS
     count
     count_tag
     index_map
-    mercator
     static_vs_dynamic_index
     write_pbf
     CACHE STRING "Benchmark programs"
@@ -38,8 +37,6 @@ foreach(benchmark ${BENCHMARKS})
                    @ONLY)
 endforeach()
 
-string(TOUPPER "${CMAKE_BUILD_TYPE}" _cmake_build_type)
-set(_cxx_flags "${CMAKE_CXX_FLAGS_${_cmake_build_type}}")
 foreach(file setup run_benchmarks)
     configure_file(${file}.sh ${CMAKE_CURRENT_BINARY_DIR}/${file}.sh @ONLY)
 endforeach()
diff --git a/benchmarks/download_data.sh b/benchmarks/download_data.sh
index be6adb9..8a6a8ff 100755
--- a/benchmarks/download_data.sh
+++ b/benchmarks/download_data.sh
@@ -4,9 +4,9 @@
 #
 
 cd $DATA_DIR
-curl --location --output 1_liechtenstein.osm.pbf http://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf   # about   2 MB
-curl --location --output 2_bremen.osm.pbf        http://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf  # about  16 MB
-curl --location --output 3_sachsen.osm.pbf       http://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 160 MB
-curl --location --output 4_germany.osm.pbf       http://download.geofabrik.de/europe/germany-latest.osm.pbf         # about   3 GB
-curl --location --output 5_planet.osm.pbf        http://planet.osm.org/pbf/planet-latest.osm.pbf                    # about  35 GB
+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
index 41f9aa0..1a16275 100644
--- a/benchmarks/osmium_benchmark_count.cpp
+++ b/benchmarks/osmium_benchmark_count.cpp
@@ -19,15 +19,15 @@ struct CountHandler : public osmium::handler::Handler {
     uint64_t ways = 0;
     uint64_t relations = 0;
 
-    void node(const osmium::Node&) {
+    void node(osmium::Node&) {
         ++nodes;
     }
 
-    void way(const osmium::Way&) {
+    void way(osmium::Way&) {
         ++ways;
     }
 
-    void relation(const osmium::Relation&) {
+    void relation(osmium::Relation&) {
         ++relations;
     }
 
diff --git a/benchmarks/osmium_benchmark_count_tag.cpp b/benchmarks/osmium_benchmark_count_tag.cpp
index 5b82a7c..6062ecc 100644
--- a/benchmarks/osmium_benchmark_count_tag.cpp
+++ b/benchmarks/osmium_benchmark_count_tag.cpp
@@ -18,7 +18,7 @@ struct CountHandler : public osmium::handler::Handler {
     uint64_t counter = 0;
     uint64_t all = 0;
 
-    void node(const osmium::Node& node) {
+    void node(osmium::Node& node) {
         ++all;
         const char* amenity = node.tags().get_value_by_key("amenity");
         if (amenity && !strcmp(amenity, "post_box")) {
@@ -26,11 +26,11 @@ struct CountHandler : public osmium::handler::Handler {
         }
     }
 
-    void way(const osmium::Way&) {
+    void way(osmium::Way&) {
         ++all;
     }
 
-    void relation(const osmium::Relation&) {
+    void relation(osmium::Relation&) {
         ++all;
     }
 
diff --git a/benchmarks/osmium_benchmark_mercator.cpp b/benchmarks/osmium_benchmark_mercator.cpp
deleted file mode 100644
index 091e5c0..0000000
--- a/benchmarks/osmium_benchmark_mercator.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-
-  The code in this file is released into the Public Domain.
-
-*/
-
-#include <cstdint>
-#include <cstdlib>
-#include <iostream>
-#include <string>
-
-#include <osmium/io/any_input.hpp>
-#include <osmium/handler.hpp>
-#include <osmium/visitor.hpp>
-#include <osmium/geom/wkb.hpp>
-#include <osmium/geom/mercator_projection.hpp>
-
-struct GeomHandler : public osmium::handler::Handler {
-
-    osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory;
-
-    void node(const osmium::Node& node) {
-        const std::string geom = factory.create_point(node);
-    }
-
-};
-
-
-int main(int argc, char* argv[]) {
-    if (argc != 2) {
-        std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
-        std::exit(1);
-    }
-
-    const std::string input_filename{argv[1]};
-
-    osmium::io::Reader reader{input_filename};
-
-    GeomHandler handler;
-    osmium::apply(reader, handler);
-    reader.close();
-}
-
diff --git a/benchmarks/run_benchmark_mercator.sh b/benchmarks/run_benchmark_mercator.sh
deleted file mode 100755
index a520727..0000000
--- a/benchmarks/run_benchmark_mercator.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-#
-#  run_benchmark_mercator.sh
-#
-
-set -e
-
-BENCHMARK_NAME=mercator
-
-. @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/setup.sh b/benchmarks/setup.sh
index f8901c2..9733bfe 100755
--- a/benchmarks/setup.sh
+++ b/benchmarks/setup.sh
@@ -9,10 +9,6 @@ if [ -z $DATA_DIR ]; then
 fi
 
 OB_DIR=@CMAKE_BINARY_DIR@/benchmarks
-OB_BUILD_TYPE=@CMAKE_BUILD_TYPE@
-OB_COMPILER=@CMAKE_CXX_COMPILER@
-OB_COMPILER_VERSION=`$OB_COMPILER --version | head -1`
-OB_CXXFLAGS="@_cxx_flags@"
 
 OB_RUNS=3
 OB_SEQ=`seq -s' ' 1 $OB_RUNS`
@@ -24,17 +20,11 @@ OB_DATA_FILES=`find -L $DATA_DIR -mindepth 1 -maxdepth 1 -type f | sort`
 
 echo "BENCHMARK: $BENCHMARK_NAME"
 echo "---------------------"
-echo "BUILD:"
-echo "build type\t: $OB_BUILD_TYPE"
-echo "compiler\t: $OB_COMPILER"
-echo "CXX version\t: $OB_COMPILER_VERSION"
-echo "CXX flags\t: $OB_CXXFLAGS"
-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
+grep '^cpu MHz' /proc/cpuinfo | tail -1
+grep '^cpu cores' /proc/cpuinfo | tail -1
+grep '^siblings' /proc/cpuinfo | tail -1
 
 echo "---------------------"
 echo "MEMORY:"
diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake
index 2224e18..fba8ffb 100644
--- a/cmake/FindOsmium.cmake
+++ b/cmake/FindOsmium.cmake
@@ -2,8 +2,8 @@
 #
 #  FindOsmium.cmake
 #
-#  Find the Libosmium headers and, optionally, several components needed
-#  for different Libosmium functions.
+#  Find the Libosmium headers and, optionally, several components needed for
+#  different Libosmium functions.
 #
 #----------------------------------------------------------------------
 #
@@ -18,12 +18,9 @@
 #
 #    Then add the following in your CMakeLists.txt:
 #
-#      find_package(Osmium [version] REQUIRED COMPONENTS <XXX>)
+#      find_package(Osmium REQUIRED COMPONENTS <XXX>)
 #      include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
 #
-#    The version number is optional. If it is not set, any version of
-#    libosmium will do.
-#
 #    For the <XXX> substitute a space separated list of one or more of the
 #    following components:
 #
@@ -54,9 +51,10 @@
 #
 #----------------------------------------------------------------------
 
-# This is the list of directories where we look for osmium and protozero
-# includes.
-set(_osmium_include_path
+# Look for the header file.
+find_path(OSMIUM_INCLUDE_DIR osmium/osm.hpp
+    PATH_SUFFIXES include
+    PATHS
         ../libosmium
         ~/Library/Frameworks
         /Library/Frameworks
@@ -64,22 +62,6 @@ set(_osmium_include_path
         /opt
 )
 
-# Look for the header file.
-find_path(OSMIUM_INCLUDE_DIR osmium/version.hpp
-    PATH_SUFFIXES include
-    PATHS ${_osmium_include_path}
-)
-
-# Check libosmium version number
-if(Osmium_FIND_VERSION)
-    file(STRINGS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp" _libosmium_version_define REGEX "#define LIBOSMIUM_VERSION_STRING")
-    if("${_libosmium_version_define}" MATCHES "#define LIBOSMIUM_VERSION_STRING \"([0-9.]+)\"")
-        set(_libosmium_version "${CMAKE_MATCH_1}")
-    else()
-        set(_libosmium_version "unknown")
-    endif()
-endif()
-
 set(OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}")
 
 #----------------------------------------------------------------------
@@ -113,31 +95,17 @@ if(Osmium_USE_PBF)
     find_package(ZLIB)
     find_package(Threads)
 
-    message(STATUS "Looking for protozero")
-    find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp
-        PATH_SUFFIXES include
-        PATHS ${_osmium_include_path}
-              ${OSMIUM_INCLUDE_DIR}
-    )
-    if(PROTOZERO_INCLUDE_DIR)
-        message(STATUS "Looking for protozero - found")
-    else()
-        message(STATUS "Looking for protozero - not found")
-    endif()
-
-    list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND PROTOZERO_INCLUDE_DIR)
-    if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_INCLUDE_DIR)
+    list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND)
+    if(ZLIB_FOUND AND Threads_FOUND)
         list(APPEND OSMIUM_PBF_LIBRARIES
             ${ZLIB_LIBRARIES}
             ${CMAKE_THREAD_LIBS_INIT}
         )
         if(WIN32)
-            # This is needed for the ntohl() function
             list(APPEND OSMIUM_PBF_LIBRARIES ws2_32)
         endif()
         list(APPEND OSMIUM_INCLUDE_DIRS
             ${ZLIB_INCLUDE_DIR}
-            ${PROTOZERO_INCLUDE_DIR}
         )
     else()
         message(WARNING "Osmium: Can not find some libraries for PBF input/output, please install them or configure the paths.")
@@ -235,7 +203,7 @@ if(Osmium_USE_SPARSEHASH)
     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++.
-        if(NOT CMAKE_VERSION VERSION_LESS 3.0)
+        if (NOT CMAKE_VERSION VERSION_LESS 3.0)
            include(CheckTypeSize)
            set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR})
            set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable")
@@ -285,15 +253,13 @@ endif()
 #  Check that all required libraries are available
 #
 #----------------------------------------------------------------------
-if(OSMIUM_EXTRA_FIND_VARS)
+if (OSMIUM_EXTRA_FIND_VARS)
     list(REMOVE_DUPLICATES OSMIUM_EXTRA_FIND_VARS)
 endif()
-# Handle the QUIETLY and REQUIRED arguments and the optional version check
-# and set OSMIUM_FOUND to TRUE if all listed variables are TRUE.
+# Handle the QUIETLY and REQUIRED arguments and set OSMIUM_FOUND to TRUE if
+# all listed variables are TRUE.
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(Osmium
-                                  REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS}
-                                  VERSION_VAR _libosmium_version)
+find_package_handle_standard_args(Osmium REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS})
 unset(OSMIUM_EXTRA_FIND_VARS)
 
 #----------------------------------------------------------------------
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index ac07896..b47cfdc 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -10,20 +10,18 @@ message(STATUS "Configuring examples")
 
 set(EXAMPLES
     area_test
-    change_tags
     convert
     count
-    create_pois
     debug
-    dump_internal
     filter_discussions
-    index_lookup
+    index
     location_cache_create
     location_cache_use
     pub_names
     read
     read_with_progress
     road_length
+    serdump
     tiles
     CACHE STRING "Example programs"
 )
@@ -34,7 +32,7 @@ set(EXAMPLES
 #  Examples depending on wingetopt
 #
 #-----------------------------------------------------------------------------
-set(GETOPT_EXAMPLES area_test convert index_lookup)
+set(GETOPT_EXAMPLES area_test convert index serdump)
 if(NOT GETOPT_MISSING)
     foreach(example ${GETOPT_EXAMPLES})
         list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY})
diff --git a/examples/README.md b/examples/README.md
index 55bd406..e291032 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -18,24 +18,12 @@ them.
 
 ## Still reasonably simple examples
 
-* `osmium_read_with_progress`
 * `osmium_filter_discussions`
 * `osmium_convert`
-* `osmium_pub_names`
-* `osmium_road_length`
 
 ## More advanced examples
 
 * `osmium_area_test`
-* `osmium_create_pois`
-
-## Even more advanced examples
-
-* `osmium_change_tags`
-* `osmium_location_cache_create`
-* `osmium_location_cache_use`
-* `osmium_dump_internal`
-* `osmium_index_lookup`
 
 ## License
 
diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp
index 0c849a7..0303374 100644
--- a/examples/osmium_area_test.cpp
+++ b/examples/osmium_area_test.cpp
@@ -106,7 +106,7 @@ int main(int argc, char* argv[]) {
 
     // Read options from command line.
     while (true) {
-        const int c = getopt_long(argc, argv, "hwo", long_options, 0);
+        int c = getopt_long(argc, argv, "hwo", long_options, 0);
         if (c == -1) {
             break;
         }
@@ -126,7 +126,7 @@ int main(int argc, char* argv[]) {
         }
     }
 
-    const int remaining_args = argc - optind;
+    int remaining_args = argc - optind;
     if (remaining_args != 1) {
         std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n";
         std::exit(1);
diff --git a/examples/osmium_change_tags.cpp b/examples/osmium_change_tags.cpp
deleted file mode 100644
index a7c1904..0000000
--- a/examples/osmium_change_tags.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-
-  EXAMPLE osmium_change_tags
-
-  An example how tags in OSM files can be removed or changed. Removes
-  "created_by" tags and changes tag "landuse=forest" into "natural_wood".
-
-  DEMONSTRATES USE OF:
-  * file input and output
-  * Osmium buffers
-  * your own handler
-  * access to tags
-  * using builders to write data
-
-  SIMPLER EXAMPLES you might want to understand first:
-  * osmium_read
-  * osmium_count
-  * osmium_pub_names
-
-  LICENSE
-  The code in this example file is released into the Public Domain.
-
-*/
-
-#include <cstdlib>   // for std::exit
-#include <cstring>   // for std::strcmp
-#include <exception> // for std::exception
-#include <iostream>  // for std::cout, std::cerr
-#include <string>    // for std::string
-#include <utility>   // for std::move
-
-// Allow any format of input files (XML, PBF, ...)
-#include <osmium/io/any_input.hpp>
-
-// Allow any format of output files (XML, PBF, ...)
-#include <osmium/io/any_output.hpp>
-
-// We want to use the builder interface
-#include <osmium/builder/osm_object_builder.hpp>
-
-// We want to use the handler interface
-#include <osmium/handler.hpp>
-
-// For osmium::apply()
-#include <osmium/visitor.hpp>
-
-// The functions in this class will be called for each object in the input
-// and will write a (changed) copy of those objects to the given buffer.
-class RewriteHandler : public osmium::handler::Handler {
-
-    osmium::memory::Buffer& m_buffer;
-
-    // Copy attributes common to all OSM objects (nodes, ways, and relations).
-    template <typename T>
-    void copy_attributes(T& builder, const osmium::OSMObject& object) {
-        // The setter functions on the builder object all return the same
-        // builder object so they can be chained.
-        builder.set_id(object.id())
-            .set_version(object.version())
-            .set_changeset(object.changeset())
-            .set_timestamp(object.timestamp())
-            .set_uid(object.uid())
-            .set_user(object.user());
-    }
-
-    // Copy all tags with two changes:
-    // * Do not copy "created_by" tags
-    // * Change "landuse=forest" into "natural=wood"
-    void copy_tags(osmium::builder::Builder& parent, const osmium::TagList& tags) {
-
-        // The TagListBuilder is used to create a list of tags. The parameter
-        // to create it is a reference to the builder of the object that
-        // should have those tags.
-        osmium::builder::TagListBuilder builder{parent};
-
-        // Iterate over all tags and build new tags using the new builder
-        // based on the old ones.
-        for (const auto& tag : tags) {
-            if (std::strcmp(tag.key(), "created_by")) {
-                if (!std::strcmp(tag.key(), "landuse") && !std::strcmp(tag.value(), "forest")) {
-                    // add_tag() can be called with key and value C strings
-                    builder.add_tag("natural", "wood");
-                } else {
-                    // add_tag() can also be called with an osmium::Tag
-                    builder.add_tag(tag);
-                }
-            }
-        }
-    }
-
-public:
-
-    // Constructor. New data will be added to the given buffer.
-    RewriteHandler(osmium::memory::Buffer& buffer) :
-        m_buffer(buffer) {
-    }
-
-    // The node handler is called for each node in the input data.
-    void node(const osmium::Node& node) {
-        // Open a new scope, because the NodeBuilder we are creating has to
-        // be destructed, before we can call commit() below.
-        {
-            // To create a node, we need a NodeBuilder object. It will create
-            // the node in the given buffer.
-            osmium::builder::NodeBuilder builder{m_buffer};
-
-            // Copy common object attributes over to the new node.
-            copy_attributes(builder, node);
-
-            // Copy the location over to the new node.
-            builder.set_location(node.location());
-
-            // Copy (changed) tags.
-            copy_tags(builder, node.tags());
-        }
-
-        // Once the object is written to the buffer completely, we have to call
-        // commit().
-        m_buffer.commit();
-    }
-
-    // The way handler is called for each node in the input data.
-    void way(const osmium::Way& way) {
-        {
-            osmium::builder::WayBuilder builder{m_buffer};
-            copy_attributes(builder, way);
-            copy_tags(builder, way.tags());
-
-            // Copy the node list over to the new way.
-            builder.add_item(way.nodes());
-        }
-        m_buffer.commit();
-    }
-
-    // The relation handler is called for each node in the input data.
-    void relation(const osmium::Relation& relation) {
-        {
-            osmium::builder::RelationBuilder builder{m_buffer};
-            copy_attributes(builder, relation);
-            copy_tags(builder, relation.tags());
-
-            // Copy the relation member list over to the new way.
-            builder.add_item(relation.members());
-        }
-        m_buffer.commit();
-    }
-
-}; // class RewriteHandler
-
-int main(int argc, char* argv[]) {
-    if (argc != 3) {
-        std::cerr << "Usage: " << argv[0] << " INFILE OUTFILE\n";
-        std::exit(1);
-    }
-
-    // Get input and output file names from command line.
-    std::string input_file_name{argv[1]};
-    std::string output_file_name{argv[2]};
-
-    try {
-        // Initialize Reader
-        osmium::io::Reader reader{input_file_name};
-
-        // Get header from input file and change the "generator" setting to
-        // ourselves.
-        osmium::io::Header header = reader.header();
-        header.set("generator", "osmium_change_tags");
-
-        // Initialize Writer using the header from above and tell it that it
-        // is allowed to overwrite a possibly existing file.
-        osmium::io::Writer writer{output_file_name, header, osmium::io::overwrite::allow};
-
-        // Read in buffers with OSM objects until there are no more.
-        while (osmium::memory::Buffer input_buffer = reader.read()) {
-            // Create an empty buffer with the same size as the input buffer.
-            // We'll copy the changed data into output buffer, the changes
-            // are small, so the output buffer needs to be about the same size.
-            // In case it has to be bigger, we allow it to grow automatically
-            // by adding the auto_grow::yes parameter.
-            osmium::memory::Buffer output_buffer{input_buffer.committed(), osmium::memory::Buffer::auto_grow::yes};
-
-            // Construct a handler as defined above and feed the input buffer
-            // to it.
-            RewriteHandler handler{output_buffer};
-            osmium::apply(input_buffer, handler);
-
-            // Write out the contents of the output buffer.
-            writer(std::move(output_buffer));
-        }
-
-        // Explicitly close the writer and reader. Will throw an exception if
-        // there is a problem. If you wait for the destructor to close the writer
-        // and reader, you will not notice the problem, because destructors must
-        // not throw.
-        writer.close();
-        reader.close();
-    } catch (const std::exception& e) {
-        // All exceptions used by the Osmium library derive from std::exception.
-        std::cerr << e.what() << "\n";
-        std::exit(1);
-    }
-}
-
diff --git a/examples/osmium_convert.cpp b/examples/osmium_convert.cpp
index 0ced82c..48a0823 100644
--- a/examples/osmium_convert.cpp
+++ b/examples/osmium_convert.cpp
@@ -67,7 +67,7 @@ int main(int argc, char* argv[]) {
 
     // Read options from command line.
     while (true) {
-        const int c = getopt_long(argc, argv, "dhf:t:", long_options, 0);
+        int c = getopt_long(argc, argv, "dhf:t:", long_options, 0);
         if (c == -1) {
             break;
         }
@@ -87,7 +87,7 @@ int main(int argc, char* argv[]) {
         }
     }
 
-    const int remaining_args = argc - optind;
+    int remaining_args = argc - optind;
     if (remaining_args > 2) {
         std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n";
         std::exit(1);
@@ -124,13 +124,13 @@ int main(int argc, char* argv[]) {
         osmium::io::Reader reader{input_file};
 
         // Get header from input file and change the "generator" setting to
-        // ourselves.
+        // outselves.
         osmium::io::Header header = reader.header();
         header.set("generator", "osmium_convert");
 
         // Initialize Writer using the header from above and tell it that it
         // is allowed to overwrite a possibly existing file.
-        osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
+        osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow);
 
         // Copy the contents from the input to the output file one buffer at
         // a time. This is much easier and faster than copying each object
diff --git a/examples/osmium_create_pois.cpp b/examples/osmium_create_pois.cpp
deleted file mode 100644
index ff79cbb..0000000
--- a/examples/osmium_create_pois.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-
-  EXAMPLE osmium_create_pois
-
-  Showing how to create nodes for points of interest out of thin air.
-
-  DEMONSTRATES USE OF:
-  * file output
-  * Osmium buffers
-  * using builders to write data
-
-  SIMPLER EXAMPLES you might want to understand first:
-  * osmium_read
-  * osmium_count
-  * osmium_pub_names
-
-  LICENSE
-  The code in this example file is released into the Public Domain.
-
-*/
-
-#include <cstdlib>   // for std::exit
-#include <cstring>   // for std::strcmp
-#include <ctime>     // for std::time
-#include <exception> // for std::exception
-#include <iostream>  // for std::cout, std::cerr
-#include <string>    // for std::string
-#include <utility>   // for std::move
-
-// Allow any format of output files (XML, PBF, ...)
-#include <osmium/io/any_output.hpp>
-
-// We want to use the builder interface
-#include <osmium/builder/osm_object_builder.hpp>
-#include <osmium/builder/attr.hpp>
-
-// Declare this to use the functions starting with the underscore (_) below.
-using namespace osmium::builder::attr;
-
-int main(int argc, char* argv[]) {
-    if (argc != 2) {
-        std::cerr << "Usage: " << argv[0] << " OUTFILE\n";
-        std::exit(1);
-    }
-
-    // Get output file name from command line.
-    std::string output_file_name{argv[1]};
-
-    try {
-        // Create a buffer where all objects will live. Use a sensible initial
-        // buffer size and set the buffer to automatically grow if needed.
-        const size_t initial_buffer_size = 10000;
-        osmium::memory::Buffer buffer{initial_buffer_size, osmium::memory::Buffer::auto_grow::yes};
-
-        // Add nodes to the buffer. This is, of course, only an example.
-        // You can set any of the attributes and more tags, etc. Ways and
-        // relations can be added in a similar way.
-        osmium::builder::add_node(buffer,
-            _id(-1),
-            _version(1),
-            _timestamp(std::time(nullptr)),
-            _location(osmium::Location{1.23, 3.45}),
-            _tag("amenity", "post_box")
-        );
-
-        osmium::builder::add_node(buffer,
-            _id(-2),
-            _version(1),
-            _timestamp(std::time(nullptr)),
-            _location(1.24, 3.46),
-            _tags({{"amenity", "restaurant"},
-                   {"name", "Chez OSM"}})
-        );
-
-        // Create header and set generator.
-        osmium::io::Header header;
-        header.set("generator", "osmium_create_pois");
-
-        // Initialize Writer using the header from above and tell it that it
-        // is allowed to overwrite a possibly existing file.
-        osmium::io::Writer writer{output_file_name, header, osmium::io::overwrite::allow};
-
-        // Write out the contents of the output buffer.
-        writer(std::move(buffer));
-
-        // Explicitly close the writer. Will throw an exception if there is
-        // a problem. If you wait for the destructor to close the writer, you
-        // will not notice the problem, because destructors must not throw.
-        writer.close();
-    } catch (const std::exception& e) {
-        // All exceptions used by the Osmium library derive from std::exception.
-        std::cerr << e.what() << "\n";
-        std::exit(1);
-    }
-}
-
diff --git a/examples/osmium_dump_internal.cpp b/examples/osmium_dump_internal.cpp
deleted file mode 100644
index dbc50db..0000000
--- a/examples/osmium_dump_internal.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-
-  EXAMPLE osmium_dump_internal
-
-  Reads an OSM file and dumps the internal datastructure to disk including
-  indexes to find objects and object relations.
-
-  Note that this example programm will only work with small and medium sized
-  OSM file, not with the planet.
-
-  You can use the osmium_index example program to inspect the indexes.
-
-  DEMONSTRATES USE OF:
-  * file input
-  * indexes and maps
-  * use of the DiskStore handler
-  * use of the ObjectRelations handler
-
-  SIMPLER EXAMPLES you might want to understand first:
-  * osmium_read
-  * osmium_count
-  * osmium_road_length
-  * osmium_location_cache_create
-  * osmium_location_cache_use
-
-  LICENSE
-  The code in this example file is released into the Public Domain.
-
-*/
-
-#include <cerrno>      // for errno
-#include <cstring>     // for std::strerror
-#include <cstdlib>     // for std::exit
-#include <iostream>    // for std::cout, std::cerr
-#include <string>      // for std::string
-#include <sys/stat.h>  // for open
-#include <sys/types.h> // for open
-
-#ifdef _MSC_VER
-# include <direct.h>
-#endif
-
-// Allow any format of input files (XML, PBF, ...)
-#include <osmium/io/any_input.hpp>
-
-// The DiskStore handler
-#include <osmium/handler/disk_store.hpp>
-
-// The ObjectRelations handler
-#include <osmium/handler/object_relations.hpp>
-
-// The indexes
-#include <osmium/index/map/sparse_mem_array.hpp>
-#include <osmium/index/multimap/sparse_mem_array.hpp>
-
-using offset_index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, size_t>;
-using map_type = osmium::index::multimap::SparseMemArray<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type>;
-
-/**
- * Small class wrapping index files, basically making sure errors are handled
- * and the files are closed on destruction.
- */
-class IndexFile {
-
-    int m_fd;
-
-public:
-
-    IndexFile(const std::string& filename) :
-        m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) {
-        if (m_fd < 0) {
-            std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n";
-            std::exit(2);
-        }
-    }
-
-    ~IndexFile() {
-        if (m_fd >= 0) {
-            close(m_fd);
-        }
-    }
-
-    int fd() const noexcept {
-        return m_fd;
-    }
-
-}; // class IndexFile
-
-int main(int argc, char* argv[]) {
-    if (argc != 3) {
-        std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n";
-        std::exit(2);
-    }
-
-    const std::string input_file_name{argv[1]};
-    const std::string output_dir{argv[2]};
-
-    // Create output directory. Ignore the error if it already exists.
-#ifndef _WIN32
-    const int result = ::mkdir(output_dir.c_str(), 0777);
-#else
-    const int result = mkdir(output_dir.c_str());
-#endif
-    if (result == -1 && errno != EEXIST) {
-        std::cerr << "Problem creating directory '" << output_dir << "': " << std::strerror(errno) << "\n";
-        std::exit(2);
-    }
-
-    // Create the output file which will contain our serialized OSM data
-    const std::string data_file{output_dir + "/data.osm.ser"};
-    const 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 << "': " << std::strerror(errno) << "\n";
-        std::exit(2);
-    }
-
-    // These indexes store the offset in the data file where each node, way,
-    // or relation is stored.
-    offset_index_type node_index;
-    offset_index_type way_index;
-    offset_index_type relation_index;
-
-    // This handler will dump the internal data to disk using the given file
-    // descriptor while updating the indexes.
-    osmium::handler::DiskStore disk_store_handler{data_fd, node_index, way_index, relation_index};
-
-    // These indexes store the mapping from node id to the ids of the ways
-    // containing this node, and from node/way/relation ids to the ids of the
-    // relations containing those objects.
-    map_type map_node2way;
-    map_type map_node2relation;
-    map_type map_way2relation;
-    map_type map_relation2relation;
-
-    // This handler will update the map indexes.
-    osmium::handler::ObjectRelations object_relations_handler{map_node2way, map_node2relation, map_way2relation, map_relation2relation};
-
-    // Read OSM data buffer by buffer.
-    osmium::io::Reader reader{input_file_name};
-
-    while (osmium::memory::Buffer buffer = reader.read()) {
-        // Write buffer to disk and update indexes.
-        disk_store_handler(buffer);
-
-        // Update object relation index maps.
-        osmium::apply(buffer, object_relations_handler);
-    }
-
-    reader.close();
-
-    // Write out node, way, and relation offset indexes to disk.
-    IndexFile nodes_idx{output_dir + "/nodes.idx"};
-    node_index.dump_as_list(nodes_idx.fd());
-
-    IndexFile ways_idx{output_dir + "/ways.idx"};
-    way_index.dump_as_list(ways_idx.fd());
-
-    IndexFile relations_idx{output_dir + "/relations.idx"};
-    relation_index.dump_as_list(relations_idx.fd());
-
-    // Sort the maps (so later binary search will work on them) and write
-    // them to disk.
-    map_node2way.sort();
-    IndexFile node2way_idx{output_dir + "/node2way.map"};
-    map_node2way.dump_as_list(node2way_idx.fd());
-
-    map_node2relation.sort();
-    IndexFile node2relation_idx{output_dir + "/node2rel.map"};
-    map_node2relation.dump_as_list(node2relation_idx.fd());
-
-    map_way2relation.sort();
-    IndexFile way2relation_idx{output_dir + "/way2rel.map"};
-    map_way2relation.dump_as_list(way2relation_idx.fd());
-
-    map_relation2relation.sort();
-    IndexFile relation2relation_idx{output_dir + "/rel2rel.map"};
-    map_relation2relation.dump_as_list(relation2relation_idx.fd());
-}
-
diff --git a/examples/osmium_filter_discussions.cpp b/examples/osmium_filter_discussions.cpp
index c2a94e5..6334981 100644
--- a/examples/osmium_filter_discussions.cpp
+++ b/examples/osmium_filter_discussions.cpp
@@ -67,7 +67,7 @@ int main(int argc, char* argv[]) {
     // file for the output file. This will copy over some header information.
     // The last parameter will tell the writer that it is allowed to overwrite
     // an existing file. Without it, it will refuse to do so.
-    osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
+    osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow);
 
     // Create range of input iterators that will iterator over all changesets
     // delivered from input file through the "reader".
diff --git a/examples/osmium_index.cpp b/examples/osmium_index.cpp
new file mode 100644
index 0000000..478b6c6
--- /dev/null
+++ b/examples/osmium_index.cpp
@@ -0,0 +1,260 @@
+/*
+
+  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 <getopt.h>
+
+#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 (std::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 << "\n";
+        } catch (...) {
+            std::cout << key << " not found\n";
+            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\n";
+            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(const 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
+};
+
+class Options {
+
+    std::vector<osmium::unsigned_object_id_type> m_ids;
+    std::string m_type;
+    std::string m_filename;
+    bool m_dump = false;
+    bool m_array_format = false;
+    bool m_list_format = false;
+
+    void print_help() {
+        std::cout << "Usage: osmium_index [OPTIONS]\n\n"
+                  << "-h, --help        Print this help message\n"
+                  << "-a, --array=FILE  Read given index file in array format\n"
+                  << "-l, --list=FILE   Read given index file in list format\n"
+                  << "-d, --dump        Dump contents of index file to STDOUT\n"
+                  << "-s, --search=ID   Search for given id (Option can appear multiple times)\n"
+                  << "-t, --type=TYPE   Type of value ('location' or 'offset')\n"
+        ;
+    }
+
+public:
+
+    Options(int argc, char* argv[]) {
+        static struct option long_options[] = {
+            {"array",  required_argument, 0, 'a'},
+            {"dump",         no_argument, 0, 'd'},
+            {"help",         no_argument, 0, 'h'},
+            {"list",   required_argument, 0, 'l'},
+            {"search", required_argument, 0, 's'},
+            {"type",   required_argument, 0, 't'},
+            {0, 0, 0, 0}
+        };
+
+        while (true) {
+            int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0);
+            if (c == -1) {
+                break;
+            }
+
+            switch (c) {
+                case 'a':
+                    m_array_format = true;
+                    m_filename = optarg;
+                    break;
+                case 'd':
+                    m_dump = true;
+                    break;
+                case 'h':
+                    print_help();
+                    std::exit(return_code::okay);
+                case 'l':
+                    m_list_format = true;
+                    m_filename = optarg;
+                    break;
+                case 's':
+                    m_ids.push_back(std::atoll(optarg));
+                    break;
+                case 't':
+                    m_type = optarg;
+                    if (m_type != "location" && m_type != "offset") {
+                        std::cerr << "Unknown type '" << m_type << "'. Must be 'location' or 'offset'.\n";
+                        std::exit(return_code::fatal);
+                    }
+                    break;
+                default:
+                    std::exit(return_code::fatal);
+            }
+        }
+
+        if (m_array_format == m_list_format) {
+            std::cerr << "Need option --array or --list, but not both\n";
+            std::exit(return_code::fatal);
+        }
+
+        if (m_type.empty()) {
+            std::cerr << "Need --type argument.\n";
+            std::exit(return_code::fatal);
+        }
+
+    }
+
+    const std::string& filename() const noexcept {
+        return m_filename;
+    }
+
+    bool dense_format() const noexcept {
+        return m_array_format;
+    }
+
+    bool do_dump() const noexcept {
+        return m_dump;
+    }
+
+    const std::vector<osmium::unsigned_object_id_type>& search_keys() const noexcept {
+        return m_ids;
+    }
+
+    bool type_is(const char* type) const noexcept {
+        return m_type == 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());
+        }
+    }
+
+    std::exit(result_okay ? return_code::okay : return_code::not_found);
+}
+
diff --git a/examples/osmium_index_lookup.cpp b/examples/osmium_index_lookup.cpp
deleted file mode 100644
index 01d7e36..0000000
--- a/examples/osmium_index_lookup.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
-
-  EXAMPLE osmium_index
-
-  Example program to look at Osmium indexes on disk.
-
-  You can use the osmium_dump_internal example program to create the offset
-  indexes or osmium_location_cache_create to create a node location index.
-
-  DEMONSTRATES USE OF:
-  * access to indexes on disk
-
-  SIMPLER EXAMPLES you might want to understand first:
-  * osmium_read
-  * osmium_count
-  * osmium_road_length
-  * osmium_location_cache_create
-  * osmium_location_cache_use
-
-  LICENSE
-  The code in this example file is released into the Public Domain.
-
-*/
-
-#include <algorithm>   // for std::all_of, std::equal_range
-#include <cstdlib>     // for std::exit
-#include <fcntl.h>     // for open
-#include <getopt.h>    // for getopt_long
-#include <iostream>    // for std::cout, std::cerr
-#include <memory>      // for std::unique_ptr
-#include <string>      // for std::string
-#include <sys/stat.h>  // for open
-#include <sys/types.h> // for open
-#include <vector>      // for std::vector
-
-// Disk-based indexes
-#include <osmium/index/map/dense_file_array.hpp>
-#include <osmium/index/map/sparse_file_array.hpp>
-
-// osmium::Location
-#include <osmium/osm/location.hpp>
-
-// Basic Osmium types
-#include <osmium/osm/types.hpp>
-
-// Virtual class for disk index access. If offers functions to dump the
-// indexes and to search for ids in the index.
-template <typename TValue>
-class IndexAccess {
-
-    int m_fd;
-
-public:
-
-    IndexAccess(int fd) :
-        m_fd(fd) {
-    }
-
-    int fd() const noexcept {
-        return m_fd;
-    }
-
-    virtual ~IndexAccess() = default;
-
-    virtual void dump() const = 0;
-
-    virtual bool search(const osmium::unsigned_object_id_type& key) const = 0;
-
-    bool search(const std::vector<osmium::unsigned_object_id_type>& keys) const {
-        return std::all_of(keys.cbegin(), keys.cend(), [this](const osmium::unsigned_object_id_type& key) {
-            return search(key);
-        });
-    }
-
-}; // class IndexAccess
-
-// Implementation of IndexAccess for dense indexes usually used for very large
-// extracts or the planet.
-template <typename TValue>
-class IndexAccessDense : public IndexAccess<TValue> {
-
-    using index_type  = typename osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, TValue>;
-
-public:
-
-    IndexAccessDense(int fd) :
-        IndexAccess<TValue>(fd) {
-    }
-
-    void dump() const override {
-        index_type index{this->fd()};
-
-        for (std::size_t i = 0; i < index.size(); ++i) {
-            if (index.get(i) != TValue{}) {
-                std::cout << i << " " << index.get(i) << "\n";
-            }
-        }
-    }
-
-    bool search(const osmium::unsigned_object_id_type& key) const override {
-        index_type index{this->fd()};
-
-        try {
-            TValue value = index.get(key);
-            std::cout << key << " " << value << "\n";
-        } catch (...) {
-            std::cout << key << " not found\n";
-            return false;
-        }
-
-        return true;
-    }
-
-}; // class IndexAccessDense
-
-// Implementation of IndexAccess for sparse indexes usually used for small or
-// medium sized extracts or for "multimap" type indexes.
-template <typename TValue>
-class IndexAccessSparse : public IndexAccess<TValue> {
-
-    using index_type = typename osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, TValue>;
-
-public:
-
-    IndexAccessSparse(int fd) :
-        IndexAccess<TValue>(fd) {
-    }
-
-    void dump() const override {
-        index_type index{this->fd()};
-
-        for (const auto& element : index) {
-            std::cout << element.first << " " << element.second << "\n";
-        }
-    }
-
-    bool search(const osmium::unsigned_object_id_type& key) const override {
-        using element_type = typename index_type::element_type;
-        index_type index{this->fd()};
-
-        element_type elem{key, TValue{}};
-        const 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\n";
-            return false;
-        }
-
-        for (auto it = positions.first; it != positions.second; ++it) {
-            std::cout << it->first << " " << it->second << "\n";
-        }
-
-        return true;
-    }
-
-}; // class IndexAccessSparse
-
-// This class contains the code to parse the command line arguments, check
-// them and present the results to the rest of the program in an easy-to-use
-// way.
-class Options {
-
-    std::vector<osmium::unsigned_object_id_type> m_ids;
-    std::string m_type;
-    std::string m_filename;
-    bool m_dump = false;
-    bool m_array_format = false;
-    bool m_list_format = false;
-
-    void print_help() {
-        std::cout << "Usage: osmium_index_lookup [OPTIONS]\n\n"
-                  << "-h, --help        Print this help message\n"
-                  << "-a, --array=FILE  Read given index file in array format\n"
-                  << "-l, --list=FILE   Read given index file in list format\n"
-                  << "-d, --dump        Dump contents of index file to STDOUT\n"
-                  << "-s, --search=ID   Search for given id (Option can appear multiple times)\n"
-                  << "-t, --type=TYPE   Type of value ('location', 'id', or 'offset')\n"
-        ;
-    }
-
-public:
-
-    Options(int argc, char* argv[]) {
-        if (argc == 1) {
-            print_help();
-            std::exit(1);
-        }
-
-        static struct option long_options[] = {
-            {"array",  required_argument, 0, 'a'},
-            {"dump",         no_argument, 0, 'd'},
-            {"help",         no_argument, 0, 'h'},
-            {"list",   required_argument, 0, 'l'},
-            {"search", required_argument, 0, 's'},
-            {"type",   required_argument, 0, 't'},
-            {0, 0, 0, 0}
-        };
-
-        while (true) {
-            const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0);
-            if (c == -1) {
-                break;
-            }
-
-            switch (c) {
-                case 'a':
-                    m_array_format = true;
-                    m_filename = optarg;
-                    break;
-                case 'd':
-                    m_dump = true;
-                    break;
-                case 'h':
-                    print_help();
-                    std::exit(0);
-                case 'l':
-                    m_list_format = true;
-                    m_filename = optarg;
-                    break;
-                case 's':
-                    m_ids.push_back(std::atoll(optarg));
-                    break;
-                case 't':
-                    m_type = optarg;
-                    if (m_type != "location" && m_type != "id" && m_type != "offset") {
-                        std::cerr << "Unknown type '" << m_type
-                                  << "'. Must be 'location', 'id', or 'offset'.\n";
-                        std::exit(2);
-                    }
-                    break;
-                default:
-                    std::exit(2);
-            }
-        }
-
-        if (m_array_format == m_list_format) {
-            std::cerr << "Need option --array or --list, but not both\n";
-            std::exit(2);
-        }
-
-        if (m_dump && !m_ids.empty()) {
-            std::cerr << "Need option --dump or --search, but not both\n";
-            std::exit(2);
-        }
-
-        if (m_type.empty()) {
-            std::cerr << "Need --type argument.\n";
-            std::exit(2);
-        }
-
-    }
-
-    const char* filename() const noexcept {
-        return m_filename.c_str();
-    }
-
-    bool dense_format() const noexcept {
-        return m_array_format;
-    }
-
-    bool do_dump() const noexcept {
-        return m_dump;
-    }
-
-    const std::vector<osmium::unsigned_object_id_type>& search_keys() const noexcept {
-        return m_ids;
-    }
-
-    bool type_is(const char* type) const noexcept {
-        return m_type == type;
-    }
-
-}; // class Options
-
-
-// Factory function to create the right IndexAccess-derived class.
-template <typename TValue>
-std::unique_ptr<IndexAccess<TValue>> create(bool dense, int fd) {
-    std::unique_ptr<IndexAccess<TValue>> ptr;
-
-    if (dense) {
-        ptr.reset(new IndexAccessDense<TValue>{fd});
-    } else {
-        ptr.reset(new IndexAccessSparse<TValue>{fd});
-    }
-
-    return ptr;
-}
-
-// Do the actual work: Either dump the index or search in the index.
-template <typename TValue>
-int run(const IndexAccess<TValue>& index, const Options& options) {
-    if (options.do_dump()) {
-        index.dump();
-        return 0;
-    } else {
-        return index.search(options.search_keys()) ? 0 : 1;
-    }
-}
-
-int main(int argc, char* argv[]) {
-    // Parse command line options.
-    Options options{argc, argv};
-
-    // Open the index file.
-    const int fd = open(options.filename(), O_RDWR);
-    if (fd < 0) {
-        std::cerr << "Can not open file '" << options.filename()
-                  << "': " << std::strerror(errno) << '\n';
-        std::exit(2);
-    }
-
-    // Depending on the type of index, we have different implementations.
-    if (options.type_is("location")) {
-        // index id -> location
-        const auto index = create<osmium::Location>(options.dense_format(), fd);
-        return run(*index, options);
-    } else if (options.type_is("id")) {
-        // index id -> id
-        const auto index = create<osmium::unsigned_object_id_type>(options.dense_format(), fd);
-        return run(*index, options);
-    } else {
-        // index id -> offset
-        const auto index = create<std::size_t>(options.dense_format(), fd);
-        return run(*index, options);
-    }
-}
-
diff --git a/examples/osmium_pub_names.cpp b/examples/osmium_pub_names.cpp
old mode 100644
new mode 100755
diff --git a/examples/osmium_road_length.cpp b/examples/osmium_road_length.cpp
old mode 100644
new mode 100755
diff --git a/examples/osmium_serdump.cpp b/examples/osmium_serdump.cpp
new file mode 100644
index 0000000..81a6d0c
--- /dev/null
+++ b/examples/osmium_serdump.cpp
@@ -0,0 +1,206 @@
+/*
+
+  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/any_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();
+                std::exit(0);
+            default:
+                std::exit(2);
+        }
+    }
+
+    int remaining_args = argc - optind;
+
+    if (remaining_args != 2) {
+        std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n";
+        std::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";
+        std::exit(2);
+    }
+
+    std::string data_file(dir + "/data.osm.ser");
+    int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+    if (data_fd < 0) {
+        std::cerr << "Can't open data file '" << data_file << "': " << strerror(errno) << "\n";
+        std::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";
+            std::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";
+            std::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";
+            std::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";
+            std::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";
+            std::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";
+            std::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";
+            std::exit(2);
+        }
+        map_relation2relation.dump_as_list(fd);
+        close(fd);
+    }
+}
+
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index 092f4b4..d5bf8d8 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -193,12 +193,12 @@ namespace osmium {
 
             }; // struct location_to_ring_map
 
-            inline bool operator==(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept {
-                return lhs.location == rhs.location;
+            inline bool operator==(const location_to_ring_map& a, const location_to_ring_map& b) noexcept {
+                return a.location == b.location;
             }
 
-            inline bool operator<(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept {
-                return lhs.location < rhs.location;
+            inline bool operator<(const location_to_ring_map& a, const location_to_ring_map& b) noexcept {
+                return a.location < b.location;
             }
 
         } // namespace detail
@@ -288,7 +288,7 @@ namespace osmium {
             }
 
             void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
-                builder.add_item(way.tags());
+                builder.add_item(&way.tags());
             }
 
             void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
@@ -333,7 +333,7 @@ namespace osmium {
             }
 
             static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) {
-                osmium::builder::TagListBuilder tl_builder{builder};
+                osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
                 for (const osmium::Tag& tag : tags) {
                     if (std::strcmp(tag.key(), "type")) {
                         tl_builder.add_tag(tag.key(), tag.value());
@@ -354,7 +354,7 @@ namespace osmium {
                     }
 
                     if (m_config.keep_type_tag) {
-                        builder.add_item(relation.tags());
+                        builder.add_item(&relation.tags());
                     } else {
                         copy_tags_without_type(builder, relation.tags());
                     }
@@ -373,12 +373,12 @@ namespace osmium {
                         if (debug()) {
                             std::cerr << "      only one outer way\n";
                         }
-                        builder.add_item((*ways.cbegin())->tags());
+                        builder.add_item(&(*ways.cbegin())->tags());
                     } else {
                         if (debug()) {
                             std::cerr << "      multiple outer ways, get common tags\n";
                         }
-                        osmium::builder::TagListBuilder tl_builder{builder};
+                        osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
                         add_common_tags(tl_builder, ways);
                     }
                 }
@@ -386,7 +386,7 @@ namespace osmium {
 
             template <typename TBuilder>
             static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) {
-                TBuilder ring_builder{builder};
+                TBuilder ring_builder(builder.buffer(), &builder);
                 ring_builder.add_node_ref(ring.get_node_ref_start());
                 for (const auto& segment : ring.segments()) {
                     ring_builder.add_node_ref(segment->stop());
@@ -458,8 +458,8 @@ namespace osmium {
             }
 
             detail::NodeRefSegment* get_next_segment(const osmium::Location& location) {
-                auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) {
-                    return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location);
+                auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& a, const slocation& b) {
+                    return a.location(m_segment_list, location) < b.location(m_segment_list, location);
                 });
 
                 assert(it != m_locations.end());
@@ -744,8 +744,8 @@ namespace osmium {
                     m_locations.emplace_back(n, true);
                 }
 
-                std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& lhs, const slocation& rhs) {
-                    return lhs.location(m_segment_list) < rhs.location(m_segment_list);
+                std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& a, const slocation& b) {
+                    return a.location(m_segment_list) < b.location(m_segment_list);
                 });
             }
 
@@ -1015,8 +1015,8 @@ namespace osmium {
 
                 std::vector<location_to_ring_map> xrings = create_location_to_ring_map(open_ring_its);
 
-                const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& lhs, const location_to_ring_map& rhs) {
-                    return lhs.ring().min_segment() < rhs.ring().min_segment();
+                const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& a, const location_to_ring_map& b) {
+                    return a.ring().min_segment() < b.ring().min_segment();
                 });
 
                 find_inner_outer_complex();
@@ -1068,11 +1068,11 @@ namespace osmium {
 
                 // Find the candidate with the smallest/largest area
                 const auto chosen_cand = ring_min_is_outer ?
-                     std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) {
-                        return std::abs(lhs.sum) < std::abs(rhs.sum);
+                     std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) {
+                        return std::abs(a.sum) < std::abs(b.sum);
                      }) :
-                     std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) {
-                        return std::abs(lhs.sum) < std::abs(rhs.sum);
+                     std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) {
+                        return std::abs(a.sum) < std::abs(b.sum);
                      });
 
                 if (debug()) {
@@ -1103,8 +1103,8 @@ namespace osmium {
                     const auto locs = make_range(std::equal_range(m_locations.begin(),
                                                                   m_locations.end(),
                                                                   slocation{},
-                                                                  [this, &location](const slocation& lhs, const slocation& rhs) {
-                        return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location);
+                                                                  [this, &location](const slocation& a, const slocation& b) {
+                        return a.location(m_segment_list, location) < b.location(m_segment_list, location);
                     }));
                     for (auto& loc : locs) {
                         if (!m_segment_list[loc.item].is_done()) {
@@ -1267,8 +1267,8 @@ namespace osmium {
                     }
                     for (const auto& location : m_split_locations) {
                         if (m_config.problem_reporter) {
-                            auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) {
-                                return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location);
+                            auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& a, const slocation& b) {
+                                return a.location(m_segment_list, location) < b.location(m_segment_list, location);
                             });
                             assert(it != m_locations.cend());
                             const osmium::object_id_type id = it->node_ref(m_segment_list).ref();
@@ -1362,7 +1362,7 @@ namespace osmium {
 #endif
 
             bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
-                osmium::builder::AreaBuilder builder{out_buffer};
+                osmium::builder::AreaBuilder builder(out_buffer);
                 builder.initialize_from_object(way);
 
                 const bool area_okay = create_rings();
@@ -1382,7 +1382,7 @@ namespace osmium {
 
             bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
                 m_num_members = members.size();
-                osmium::builder::AreaBuilder builder{out_buffer};
+                osmium::builder::AreaBuilder builder(out_buffer);
                 builder.initialize_from_object(relation);
 
                 const bool area_okay = create_rings();
diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp
index 1c03d3e..b131a43 100644
--- a/include/osmium/area/detail/node_ref_segment.hpp
+++ b/include/osmium/area/detail/node_ref_segment.hpp
@@ -379,8 +379,8 @@ namespace osmium {
                     sl[2] = {1, s2.first().location() };
                     sl[3] = {1, s2.second().location()};
 
-                    std::sort(sl, sl+4, [](const seg_loc& lhs, const seg_loc& rhs) {
-                        return lhs.location < rhs.location;
+                    std::sort(sl, sl+4, [](const seg_loc& a, const seg_loc& b) {
+                        return a.location < b.location;
                     });
 
                     if (sl[1].location == sl[2].location) {
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 97d512a..a4361e0 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -101,7 +101,7 @@ namespace osmium {
                  * Calculate the number of segments in all the ways together.
                  */
                 static size_t get_num_segments(const std::vector<const osmium::Way*>& members) noexcept {
-                    return std::accumulate(members.cbegin(), members.cend(), static_cast<size_t>(0), [](size_t sum, const osmium::Way* way) {
+                    return std::accumulate(members.cbegin(), members.cend(), 0, [](size_t sum, const osmium::Way* way) {
                         if (way->nodes().empty()) {
                             return sum;
                         } else {
diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp
index fae1280..44983cc 100644
--- a/include/osmium/area/detail/vector.hpp
+++ b/include/osmium/area/detail/vector.hpp
@@ -73,18 +73,18 @@ namespace osmium {
             }; // struct vec
 
             // addition
-            constexpr inline vec operator+(const vec& lhs, const vec& rhs) noexcept {
-                return vec{lhs.x + rhs.x, lhs.y + rhs.y};
+            constexpr inline vec operator+(const vec& a, const vec& b) noexcept {
+                return vec{a.x + b.x, a.y + b.y};
             }
 
             // subtraction
-            constexpr inline vec operator-(const vec& lhs, const vec& rhs) noexcept {
-                return vec{lhs.x - rhs.x, lhs.y - rhs.y};
+            constexpr inline vec operator-(const vec& a, const vec& b) noexcept {
+                return vec{a.x - b.x, a.y - b.y};
             }
 
             // cross product
-            constexpr inline int64_t operator*(const vec& lhs, const vec& rhs) noexcept {
-                return lhs.x * rhs.y - lhs.y * rhs.x;
+            constexpr inline int64_t operator*(const vec& a, const vec& b) noexcept {
+                return a.x * b.y - a.y * b.x;
             }
 
             // scale vector
@@ -98,13 +98,13 @@ namespace osmium {
             }
 
             // equality
-            constexpr inline bool operator==(const vec& lhs, const vec& rhs) noexcept {
-                return lhs.x == rhs.x && lhs.y == rhs.y;
+            constexpr inline bool operator==(const vec& a, const vec& b) noexcept {
+                return a.x == b.x && a.y == b.y;
             }
 
             // inequality
-            constexpr inline bool operator!=(const vec& lhs, const vec& rhs) noexcept {
-                return !(lhs == rhs);
+            constexpr inline bool operator!=(const vec& a, const vec& b) noexcept {
+                return !(a == b);
             }
 
             template <typename TChar, typename TTraits>
diff --git a/include/osmium/builder/attr.hpp b/include/osmium/builder/attr.hpp
index 8e0b4a3..2a5b690 100644
--- a/include/osmium/builder/attr.hpp
+++ b/include/osmium/builder/attr.hpp
@@ -617,7 +617,7 @@ namespace osmium {
 
             template <typename TBuilder, typename... TArgs>
             inline void add_user(TBuilder& builder, const TArgs&... args) {
-                builder.set_user(get_user(args...));
+                builder.add_user(get_user(args...));
             }
 
             // ==============================================================
@@ -761,13 +761,11 @@ namespace osmium {
             static_assert(sizeof...(args) > 0, "add_node() must have buffer and at least one additional argument");
             static_assert(detail::are_all_handled_by<detail::any_node_handlers, TArgs...>::value, "Attribute not allowed in add_node()");
 
-            {
-                NodeBuilder builder(buffer);
+            NodeBuilder builder(buffer);
 
-                detail::add_basic<detail::node_handler>(builder, args...);
-                detail::add_user(builder, args...);
-                detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
-            }
+            detail::add_basic<detail::node_handler>(builder, args...);
+            detail::add_user(builder, args...);
+            detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
 
             return buffer.commit();
         }
@@ -784,14 +782,12 @@ namespace osmium {
             static_assert(sizeof...(args) > 0, "add_way() must have buffer and at least one additional argument");
             static_assert(detail::are_all_handled_by<detail::any_way_handlers, TArgs...>::value, "Attribute not allowed in add_way()");
 
-            {
-                WayBuilder builder(buffer);
+            WayBuilder builder(buffer);
 
-                detail::add_basic<detail::object_handler>(builder, args...);
-                detail::add_user(builder, args...);
-                detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
-                detail::add_list<WayNodeListBuilder, detail::nodes_handler>(builder, args...);
-            }
+            detail::add_basic<detail::object_handler>(builder, args...);
+            detail::add_user(builder, args...);
+            detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
+            detail::add_list<WayNodeListBuilder, detail::nodes_handler>(builder, args...);
 
             return buffer.commit();
         }
@@ -808,14 +804,12 @@ namespace osmium {
             static_assert(sizeof...(args) > 0, "add_relation() must have buffer and at least one additional argument");
             static_assert(detail::are_all_handled_by<detail::any_relation_handlers, TArgs...>::value, "Attribute not allowed in add_relation()");
 
-            {
-                RelationBuilder builder(buffer);
+            RelationBuilder builder(buffer);
 
-                detail::add_basic<detail::object_handler>(builder, args...);
-                detail::add_user(builder, args...);
-                detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
-                detail::add_list<RelationMemberListBuilder, detail::members_handler>(builder, args...);
-            }
+            detail::add_basic<detail::object_handler>(builder, args...);
+            detail::add_user(builder, args...);
+            detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
+            detail::add_list<RelationMemberListBuilder, detail::members_handler>(builder, args...);
 
             return buffer.commit();
         }
@@ -832,14 +826,12 @@ namespace osmium {
             static_assert(sizeof...(args) > 0, "add_changeset() must have buffer and at least one additional argument");
             static_assert(detail::are_all_handled_by<detail::any_changeset_handlers, TArgs...>::value, "Attribute not allowed in add_changeset()");
 
-            {
-                ChangesetBuilder builder(buffer);
+            ChangesetBuilder builder(buffer);
 
-                detail::add_basic<detail::changeset_handler>(builder, args...);
-                detail::add_user(builder, args...);
-                detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
-                detail::add_list<ChangesetDiscussionBuilder, detail::discussion_handler>(builder, args...);
-            }
+            detail::add_basic<detail::changeset_handler>(builder, args...);
+            detail::add_user(builder, args...);
+            detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
+            detail::add_list<ChangesetDiscussionBuilder, detail::discussion_handler>(builder, args...);
 
             return buffer.commit();
         }
@@ -856,17 +848,15 @@ namespace osmium {
             static_assert(sizeof...(args) > 0, "add_area() must have buffer and at least one additional argument");
             static_assert(detail::are_all_handled_by<detail::any_area_handlers, TArgs...>::value, "Attribute not allowed in add_area()");
 
-            {
-                AreaBuilder builder(buffer);
+            AreaBuilder builder(buffer);
 
-                detail::add_basic<detail::object_handler>(builder, args...);
-                detail::add_user(builder, args...);
-                detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
+            detail::add_basic<detail::object_handler>(builder, args...);
+            detail::add_user(builder, args...);
+            detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
 
-                (void)std::initializer_list<int>{
-                    (detail::ring_handler::set_value(builder, args), 0)...
-                };
-            }
+            (void)std::initializer_list<int>{
+                (detail::ring_handler::set_value(builder, args), 0)...
+            };
 
             return buffer.commit();
         }
diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp
index 044da1e..1b274ad 100644
--- a/include/osmium/builder/builder.hpp
+++ b/include/osmium/builder/builder.hpp
@@ -45,7 +45,6 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/memory/item.hpp>
 #include <osmium/osm/types.hpp>
 #include <osmium/util/cast.hpp>
-#include <osmium/util/compatibility.hpp>
 
 namespace osmium {
 
@@ -54,10 +53,6 @@ namespace osmium {
      */
     namespace builder {
 
-        /**
-         * Parent class for individual builder classes. Instantiate one of
-         * its derived classes.
-         */
         class Builder {
 
             osmium::memory::Buffer& m_buffer;
@@ -76,34 +71,20 @@ namespace osmium {
                 m_buffer(buffer),
                 m_parent(parent),
                 m_item_offset(buffer.written()) {
-                reserve_space(size);
+                m_buffer.reserve_space(size);
                 assert(buffer.is_aligned());
                 if (m_parent) {
-                    assert(m_buffer.builder_count() == 1 && "Only one sub-builder can be open at any time.");
                     m_parent->add_size(size);
-                } else {
-                    assert(m_buffer.builder_count() == 0 && "Only one builder can be open at any time.");
                 }
-#ifndef NDEBUG
-                m_buffer.increment_builder_count();
-#endif
             }
 
-#ifdef NDEBUG
             ~Builder() = default;
-#else
-            ~Builder() noexcept {
-                m_buffer.decrement_builder_count();
-            }
-#endif
 
             osmium::memory::Item& item() const {
                 return *reinterpret_cast<osmium::memory::Item*>(m_buffer.data() + m_item_offset);
             }
 
-            unsigned char* reserve_space(size_t size) {
-                return m_buffer.reserve_space(size);
-            }
+        public:
 
             /**
              * Add padding to buffer (if needed) to align data properly.
@@ -121,7 +102,7 @@ namespace osmium {
             void add_padding(bool self = false) {
                 const auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes);
                 if (padding != osmium::memory::align_bytes) {
-                    std::fill_n(reserve_space(padding), padding, 0);
+                    std::fill_n(m_buffer.reserve_space(padding), padding, 0);
                     if (self) {
                         add_size(padding);
                     } else if (m_parent) {
@@ -142,6 +123,12 @@ namespace osmium {
                 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.
@@ -149,7 +136,7 @@ namespace osmium {
             template <typename T>
             T* reserve_space_for() {
                 assert(m_buffer.is_aligned());
-                return reinterpret_cast<T*>(reserve_space(sizeof(T)));
+                return reinterpret_cast<T*>(m_buffer.reserve_space(sizeof(T)));
             }
 
             /**
@@ -162,7 +149,7 @@ namespace osmium {
              * @returns The number of bytes appended (length).
              */
             osmium::memory::item_size_type append(const char* data, const osmium::memory::item_size_type length) {
-                unsigned char* target = reserve_space(length);
+                unsigned char* target = m_buffer.reserve_space(length);
                 std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
                 return length;
             }
@@ -183,36 +170,64 @@ namespace osmium {
              * @returns The number of bytes appended (always 1).
              */
             osmium::memory::item_size_type append_zero() {
-                *reserve_space(1) = '\0';
+                *m_buffer.reserve_space(1) = '\0';
                 return 1;
             }
 
-        public:
-
             /// Return the buffer this builder is using.
             osmium::memory::Buffer& buffer() noexcept {
                 return m_buffer;
             }
 
+        }; // class Builder
+
+        template <typename 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 (without \0 termination).
+             */
+            void add_user(const char* user, const string_size_type length) {
+                object().set_user_size(length + 1);
+                add_size(append(user, length) + append_zero());
+                add_padding(true);
+            }
+
             /**
-             * Add a subitem to the object being built. This can be something
-             * like a TagList or RelationMemberList.
+             * Add user name to buffer.
+             *
+             * @param user Pointer to \0-terminated user name.
              */
-            void add_item(const osmium::memory::Item& item) {
-                m_buffer.add_item(item);
-                add_size(item.padded_size());
+            void add_user(const char* user) {
+                add_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
             }
 
             /**
-             * @deprecated Use the version of add_item() taking a
-             *             reference instead.
+             * Add user name to buffer.
+             *
+             * @param user User name.
              */
-            OSMIUM_DEPRECATED void add_item(const osmium::memory::Item* item) {
-                assert(item);
-                add_item(*item);
+            void add_user(const std::string& user) {
+                add_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
             }
 
-        }; // class Builder
+        }; // class ObjectBuilder
 
     } // namespace builder
 
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
index b1c7220..e7a8298 100644
--- a/include/osmium/builder/osm_object_builder.hpp
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -45,7 +45,6 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/builder/builder.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/tag.hpp>
@@ -56,7 +55,6 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/relation.hpp>
 #include <osmium/osm/timestamp.hpp>
 #include <osmium/osm/way.hpp>
-#include <osmium/util/compatibility.hpp>
 
 namespace osmium {
 
@@ -68,18 +66,12 @@ namespace osmium {
 
     namespace builder {
 
-        class TagListBuilder : public Builder {
+        class TagListBuilder : public ObjectBuilder<TagList> {
 
         public:
 
             explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(TagList)) {
-                new (&item()) TagList();
-            }
-
-            explicit TagListBuilder(Builder& parent) :
-                Builder(parent.buffer(), &parent, sizeof(TagList)) {
-                new (&item()) TagList();
+                ObjectBuilder<TagList>(buffer, parent) {
             }
 
             ~TagListBuilder() {
@@ -177,27 +169,21 @@ namespace osmium {
         }; // class TagListBuilder
 
         template <typename T>
-        class NodeRefListBuilder : public Builder {
+        class NodeRefListBuilder : public ObjectBuilder<T> {
 
         public:
 
             explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(T)) {
-                new (&item()) T();
-            }
-
-            explicit NodeRefListBuilder(Builder& parent) :
-                Builder(parent.buffer(), &parent, sizeof(T)) {
-                new (&item()) T();
+                ObjectBuilder<T>(buffer, parent) {
             }
 
             ~NodeRefListBuilder() {
-                add_padding();
+                static_cast<Builder*>(this)->add_padding();
             }
 
             void add_node_ref(const NodeRef& node_ref) {
-                new (reserve_space_for<osmium::NodeRef>()) osmium::NodeRef(node_ref);
-                add_size(sizeof(osmium::NodeRef));
+                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{}) {
@@ -210,7 +196,7 @@ namespace osmium {
         using OuterRingBuilder   = NodeRefListBuilder<OuterRing>;
         using InnerRingBuilder   = NodeRefListBuilder<InnerRing>;
 
-        class RelationMemberListBuilder : public Builder {
+        class RelationMemberListBuilder : public ObjectBuilder<RelationMemberList> {
 
             /**
              * Add role to buffer.
@@ -233,13 +219,7 @@ namespace osmium {
         public:
 
             explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(RelationMemberList)) {
-                new (&item()) RelationMemberList();
-            }
-
-            explicit RelationMemberListBuilder(Builder& parent) :
-                Builder(parent.buffer(), &parent, sizeof(RelationMemberList)) {
-                new (&item()) RelationMemberList();
+                ObjectBuilder<RelationMemberList>(buffer, parent) {
             }
 
             ~RelationMemberListBuilder() {
@@ -265,7 +245,7 @@ namespace osmium {
                 add_size(sizeof(RelationMember));
                 add_role(*member, role, role_length);
                 if (full_member) {
-                    add_item(*full_member);
+                    add_item(full_member);
                 }
             }
 
@@ -301,7 +281,7 @@ namespace osmium {
 
         }; // class RelationMemberListBuilder
 
-        class ChangesetDiscussionBuilder : public Builder {
+        class ChangesetDiscussionBuilder : public ObjectBuilder<ChangesetDiscussion> {
 
             osmium::ChangesetComment* m_comment = nullptr;
 
@@ -329,13 +309,7 @@ namespace osmium {
         public:
 
             explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(ChangesetDiscussion)) {
-                new (&item()) ChangesetDiscussion();
-            }
-
-            explicit ChangesetDiscussionBuilder(Builder& parent) :
-                Builder(parent.buffer(), &parent, sizeof(ChangesetDiscussion)) {
-                new (&item()) ChangesetDiscussion();
+                ObjectBuilder<ChangesetDiscussion>(buffer, parent) {
             }
 
             ~ChangesetDiscussionBuilder() {
@@ -365,101 +339,19 @@ namespace osmium {
 
         }; // class ChangesetDiscussionBuilder
 
-#define OSMIUM_FORWARD(setter) \
-    template <typename... TArgs> \
-    type& setter(TArgs&&... args) { \
-        object().setter(std::forward<TArgs>(args)...); \
-        return static_cast<type&>(*this); \
-    }
-
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder : public Builder {
-
-            using type = TDerived;
-
-            constexpr static const size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1);
+        template <typename T>
+        class OSMObjectBuilder : public ObjectBuilder<T> {
 
         public:
 
             explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(T) + min_size_for_user) {
-                new (&item()) T();
-                add_size(min_size_for_user);
-                std::fill_n(object().data() + sizeof(T), min_size_for_user, 0);
-                object().set_user_size(1);
-            }
-
-            /**
-             * Get a reference to the object buing built.
-             *
-             * Note that this reference will be invalidated by every action
-             * on the builder that might make the buffer grow. This includes
-             * calls to set_user() and any time a new sub-builder is created.
-             */
-            T& object() noexcept {
-                return static_cast<T&>(item());
-            }
-
-            /**
-             * Set user name.
-             *
-             * @param user Pointer to user name.
-             * @param length Length of user name (without \0 termination).
-             */
-            TDerived& set_user(const char* user, const string_size_type length) {
-                const auto size_of_object = sizeof(T) + sizeof(string_size_type);
-                assert(object().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1))
-                       && "set_user() must be called at most once and before any sub-builders");
-                const auto available_space = min_size_for_user - sizeof(string_size_type) - 1;
-                if (length > available_space) {
-                    const auto space_needed = osmium::memory::padded_length(length - available_space);
-                    reserve_space(space_needed);
-                    add_size(static_cast<uint32_t>(space_needed));
-                }
-                std::copy_n(user, length, object().data() + size_of_object);
-                std::fill_n(object().data() + size_of_object + length, osmium::memory::padded_length(length + 1) - length, 0);
-                object().set_user_size(length + 1);
-
-                return static_cast<TDerived&>(*this);
+                ObjectBuilder<T>(buffer, parent) {
+                static_cast<Builder*>(this)->reserve_space_for<string_size_type>();
+                static_cast<Builder*>(this)->add_size(sizeof(string_size_type));
             }
 
-            /**
-             * Set user name.
-             *
-             * @param user Pointer to \0-terminated user name.
-             */
-            TDerived& set_user(const char* user) {
-                return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
-            }
-
-            /**
-             * Set user name.
-             *
-             * @param user User name.
-             */
-            TDerived& set_user(const std::string& user) {
-                return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
-            }
-
-            /// @deprecated Use set_user(...) instead.
-            template <typename... TArgs>
-            OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
-                set_user(std::forward<TArgs>(args)...);
-            }
-
-            OSMIUM_FORWARD(set_id)
-            OSMIUM_FORWARD(set_visible)
-            OSMIUM_FORWARD(set_deleted)
-            OSMIUM_FORWARD(set_version)
-            OSMIUM_FORWARD(set_changeset)
-            OSMIUM_FORWARD(set_uid)
-            OSMIUM_FORWARD(set_uid_from_signed)
-            OSMIUM_FORWARD(set_timestamp)
-            OSMIUM_FORWARD(set_attribute)
-            OSMIUM_FORWARD(set_removed)
-
             void add_tags(const std::initializer_list<std::pair<const char*, const char*>>& tags) {
-                osmium::builder::TagListBuilder tl_builder{buffer(), this};
+                osmium::builder::TagListBuilder tl_builder(static_cast<Builder*>(this)->buffer(), this);
                 for (const auto& p : tags) {
                     tl_builder.add_tag(p.first, p.second);
                 }
@@ -467,40 +359,19 @@ namespace osmium {
 
         }; // class OSMObjectBuilder
 
-        class NodeBuilder : public OSMObjectBuilder<NodeBuilder, Node> {
+        using NodeBuilder     = OSMObjectBuilder<osmium::Node>;
+        using RelationBuilder = OSMObjectBuilder<osmium::Relation>;
 
-            using type = NodeBuilder;
-
-        public:
-
-            explicit NodeBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                OSMObjectBuilder<NodeBuilder, Node>(buffer, parent) {
-            }
-
-            explicit NodeBuilder(Builder& parent) :
-                OSMObjectBuilder<NodeBuilder, Node>(parent.buffer(), &parent) {
-            }
-
-            OSMIUM_FORWARD(set_location)
-
-        }; // class NodeBuilder
-
-        class WayBuilder : public OSMObjectBuilder<WayBuilder, Way> {
-
-            using type = WayBuilder;
+        class WayBuilder : public OSMObjectBuilder<osmium::Way> {
 
         public:
 
             explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                OSMObjectBuilder<WayBuilder, Way>(buffer, parent) {
-            }
-
-            explicit WayBuilder(Builder& parent) :
-                OSMObjectBuilder<WayBuilder, Way>(parent.buffer(), &parent) {
+                OSMObjectBuilder<osmium::Way>(buffer, parent) {
             }
 
             void add_node_refs(const std::initializer_list<osmium::NodeRef>& nodes) {
-                osmium::builder::WayNodeListBuilder builder{buffer(), this};
+                osmium::builder::WayNodeListBuilder builder(buffer(), this);
                 for (const auto& node_ref : nodes) {
                     builder.add_node_ref(node_ref);
                 }
@@ -508,147 +379,32 @@ namespace osmium {
 
         }; // class WayBuilder
 
-        class RelationBuilder : public OSMObjectBuilder<RelationBuilder, Relation> {
-
-            using type = RelationBuilder;
-
-        public:
-
-            explicit RelationBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                OSMObjectBuilder<RelationBuilder, Relation>(buffer, parent) {
-            }
-
-            explicit RelationBuilder(Builder& parent) :
-                OSMObjectBuilder<RelationBuilder, Relation>(parent.buffer(), &parent) {
-            }
-
-        }; // class RelationBuilder
-
-        class AreaBuilder : public OSMObjectBuilder<AreaBuilder, Area> {
-
-            using type = AreaBuilder;
+        class AreaBuilder : public OSMObjectBuilder<osmium::Area> {
 
         public:
 
             explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                OSMObjectBuilder<AreaBuilder, Area>(buffer, parent) {
-            }
-
-            explicit AreaBuilder(Builder& parent) :
-                OSMObjectBuilder<AreaBuilder, Area>(parent.buffer(), &parent) {
+                OSMObjectBuilder<osmium::Area>(buffer, parent) {
             }
 
             /**
              * Initialize area attributes from the attributes of the given object.
              */
             void initialize_from_object(const osmium::OSMObject& source) {
-                set_id(osmium::object_id_to_area_id(source.id(), source.type()));
-                set_version(source.version());
-                set_changeset(source.changeset());
-                set_timestamp(source.timestamp());
-                set_visible(source.visible());
-                set_uid(source.uid());
-                set_user(source.user());
-            }
-
-        }; // class AreaBuilder
-
-        class ChangesetBuilder : public Builder {
-
-            using type = ChangesetBuilder;
-
-            constexpr static const size_t min_size_for_user = osmium::memory::padded_length(1);
-
-        public:
-
-            explicit ChangesetBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
-                Builder(buffer, parent, sizeof(Changeset) + min_size_for_user) {
-                new (&item()) Changeset();
-                add_size(min_size_for_user);
-                std::fill_n(object().data() + sizeof(Changeset), min_size_for_user, 0);
-                object().set_user_size(1);
-            }
-
-            /**
-             * Get a reference to the changeset buing built.
-             *
-             * Note that this reference will be invalidated by every action
-             * on the builder that might make the buffer grow. This includes
-             * calls to set_user() and any time a new sub-builder is created.
-             */
-            Changeset& object() noexcept {
-                return static_cast<Changeset&>(item());
-            }
+                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());
 
-            OSMIUM_FORWARD(set_id)
-            OSMIUM_FORWARD(set_uid)
-            OSMIUM_FORWARD(set_uid_from_signed)
-            OSMIUM_FORWARD(set_created_at)
-            OSMIUM_FORWARD(set_closed_at)
-            OSMIUM_FORWARD(set_num_changes)
-            OSMIUM_FORWARD(set_num_comments)
-            OSMIUM_FORWARD(set_attribute)
-            OSMIUM_FORWARD(set_removed)
-
-            // @deprecated Use set_bounds() instead.
-            OSMIUM_DEPRECATED osmium::Box& bounds() noexcept {
-                return object().bounds();
+                add_user(source.user());
             }
 
-            ChangesetBuilder& set_bounds(const osmium::Box& box) noexcept {
-                object().bounds() = box;
-                return *this;
-            }
-
-            /**
-             * Set user name.
-             *
-             * @param user Pointer to user name.
-             * @param length Length of user name (without \0 termination).
-             */
-            ChangesetBuilder& set_user(const char* user, const string_size_type length) {
-                assert(object().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1))
-                       && "set_user() must be called at most once and before any sub-builders");
-                const auto available_space = min_size_for_user - 1;
-                if (length > available_space) {
-                    const auto space_needed = osmium::memory::padded_length(length - available_space);
-                    reserve_space(space_needed);
-                    add_size(static_cast<uint32_t>(space_needed));
-                }
-                std::copy_n(user, length, object().data() + sizeof(Changeset));
-                std::fill_n(object().data() + sizeof(Changeset) + length, osmium::memory::padded_length(length + 1) - length, 0);
-                object().set_user_size(length + 1);
-
-                return *this;
-            }
-
-            /**
-             * Set user name.
-             *
-             * @param user Pointer to \0-terminated user name.
-             */
-            ChangesetBuilder& set_user(const char* user) {
-                return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
-            }
-
-            /**
-             * Set user name.
-             *
-             * @param user User name.
-             */
-            ChangesetBuilder& set_user(const std::string& user) {
-                return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
-            }
-
-            /// @deprecated Use set_user(...) instead.
-            template <typename... TArgs>
-            OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
-                set_user(std::forward<TArgs>(args)...);
-            }
-
-        }; // class ChangesetBuilder
+        }; // class AreaBuilder
 
-#undef OSMIUM_FORWARD
+        using ChangesetBuilder = ObjectBuilder<osmium::Changeset>;
 
     } // namespace builder
 
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index f2db402..14c51df 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -95,7 +95,7 @@ namespace osmium {
             return m_message.c_str();
         }
 
-    }; // class geometry_error
+    }; // struct geometry_error
 
     /**
      * @brief Everything related to geometry handling.
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index d59e7ce..f406076 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -33,22 +33,12 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <geos/version.h>
-#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && (GEOS_VERSION_MAJOR < 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 5))
-
-#define OSMIUM_WITH_GEOS
-
 /**
  * @file
  *
- * This file contains code for conversion of OSM geometries into GEOS
+ * This file contains code for conversion of OSM geometries into GDAL
  * geometries.
  *
- * Note that everything in this file is deprecated and only works up to
- * GEOS 3.5. It uses the GEOS C++ API which the GEOS project does not consider
- * to be a stable, external API. We probably should have used the GEOS C API
- * instead.
- *
  * @attention If you include this file, you'll need to link with `libgeos`.
  */
 
@@ -98,7 +88,6 @@ namespace osmium {
 
         namespace detail {
 
-            /// @deprecated
             class GEOSFactoryImpl {
 
                 std::unique_ptr<const geos::geom::PrecisionModel> m_precision_model;
@@ -256,7 +245,6 @@ namespace osmium {
 
         } // namespace detail
 
-        /// @deprecated
         template <typename TProjection = IdentityProjection>
         using GEOSFactory = GeometryFactory<osmium::geom::detail::GEOSFactoryImpl, TProjection>;
 
@@ -266,6 +254,4 @@ namespace osmium {
 
 #undef THROW
 
-#endif
-
 #endif // OSMIUM_GEOM_GEOS_HPP
diff --git a/include/osmium/geom/relations.hpp b/include/osmium/geom/relations.hpp
index 5e6e773..76d452e 100644
--- a/include/osmium/geom/relations.hpp
+++ b/include/osmium/geom/relations.hpp
@@ -43,11 +43,11 @@ namespace osmium {
         /**
          * Check whether one geometry contains another.
          */
-        inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) {
-            return ((lhs.bottom_left().x() >= rhs.bottom_left().x()) &&
-                    (lhs.top_right().x()   <= rhs.top_right().x())   &&
-                    (lhs.bottom_left().y() >= rhs.bottom_left().y()) &&
-                    (lhs.top_right().y()   <= rhs.top_right().y()));
+        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
diff --git a/include/osmium/geom/tile.hpp b/include/osmium/geom/tile.hpp
index 2c33017..672ae54 100644
--- a/include/osmium/geom/tile.hpp
+++ b/include/osmium/geom/tile.hpp
@@ -118,23 +118,23 @@ namespace osmium {
         }; // struct Tile
 
         /// Tiles are equal if all their attributes are equal.
-        inline bool operator==(const Tile& lhs, const Tile& rhs) {
-            return lhs.z == rhs.z && lhs.x == rhs.x && lhs.y == rhs.y;
+        inline bool operator==(const Tile& a, const Tile& b) {
+            return a.z == b.z && a.x == b.x && a.y == b.y;
         }
 
-        inline bool operator!=(const Tile& lhs, const Tile& rhs) {
-            return ! (lhs == rhs);
+        inline bool operator!=(const Tile& a, const Tile& b) {
+            return ! (a == b);
         }
 
         /**
          * This defines an arbitrary order on tiles for use in std::map etc.
          */
-        inline bool operator<(const Tile& lhs, const Tile& rhs) {
-            if (lhs.z < rhs.z) return true;
-            if (lhs.z > rhs.z) return false;
-            if (lhs.x < rhs.x) return true;
-            if (lhs.x > rhs.x) return false;
-            return lhs.y < rhs.y;
+        inline bool operator<(const Tile& a, const Tile& b) {
+            if (a.z < b.z) return true;
+            if (a.z > b.z) return false;
+            if (a.x < b.x) return true;
+            if (a.x > b.x) return false;
+            return a.y < b.y;
         }
 
     } // namespace geom
diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp
index d112ac0..4e95573 100644
--- a/include/osmium/handler/disk_store.hpp
+++ b/include/osmium/handler/disk_store.hpp
@@ -51,9 +51,6 @@ namespace osmium {
     namespace handler {
 
         /**
-         * Writes OSM data in the Osmium-internal serialized format to disk
-         * keeping track of object offsets in the indexes given to the
-         * constructor.
          *
          * Note: This handler will only work if either all object IDs are
          *       positive or all object IDs are negative.
@@ -98,8 +95,10 @@ namespace osmium {
                 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);
             }
 
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
index 61c6de2..a490f9e 100644
--- a/include/osmium/handler/node_locations_for_ways.hpp
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -165,7 +165,7 @@ namespace osmium {
                     }
                 }
                 if (error && !m_ignore_errors) {
-                    throw osmium::not_found{"location for one or more nodes not found in node location index"};
+                    throw osmium::not_found("location for one or more nodes not found in node location index");
                 }
             }
 
diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp
index 5f1956d..279365d 100644
--- a/include/osmium/handler/object_relations.hpp
+++ b/include/osmium/handler/object_relations.hpp
@@ -46,8 +46,6 @@ namespace osmium {
     namespace handler {
 
         /**
-         * This handler updates the indexes given to the constructor with
-         * the relations between objects.
          *
          * Note: This handler will only work if either all object IDs are
          *       positive or all object IDs are negative.
diff --git a/include/osmium/index/bool_vector.hpp b/include/osmium/index/bool_vector.hpp
index 2ef80f9..e6e190e 100644
--- a/include/osmium/index/bool_vector.hpp
+++ b/include/osmium/index/bool_vector.hpp
@@ -33,15 +33,50 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <osmium/index/id_set.hpp>
+#include <type_traits>
+#include <vector>
 
 namespace osmium {
 
     namespace index {
 
-        /// @deprecated Use osmium::index::IdSet instead.
+        /**
+         * Index storing one bit for each Id. The index automatically scales
+         * with the Ids stored. Default value is 'false'. Storage uses
+         * std::vector<bool> and needs a minimum of memory if the Ids are
+         * dense.
+         */
         template <typename T>
-        using BoolVector = IdSet<T>;
+        class BoolVector {
+
+            static_assert(std::is_unsigned<T>::value, "Needs unsigned type");
+
+            std::vector<bool> m_bits;
+
+        public:
+
+            BoolVector() = default;
+
+            BoolVector(const BoolVector&) = default;
+            BoolVector(BoolVector&&) = default;
+            BoolVector& operator=(const BoolVector&) = default;
+            BoolVector& operator=(BoolVector&&) = default;
+
+            ~BoolVector() noexcept = default;
+
+            void set(T id, bool value = true) {
+                if (m_bits.size() <= id) {
+                    m_bits.resize(id + 1024 * 1024);
+                }
+
+                m_bits[id] = value;
+            }
+
+            bool get(T id) const {
+                return id < m_bits.size() && m_bits[id];
+            }
+
+        }; // class BoolVector
 
     } // namespace index
 
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
index 9f4af1f..ac87c2f 100644
--- a/include/osmium/index/detail/vector_map.hpp
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -85,11 +85,11 @@ namespace osmium {
                     try {
                         const TValue& value = m_vector.at(id);
                         if (value == osmium::index::empty_value<TValue>()) {
-                            throw osmium::not_found{id};
+                            not_found_error(id);
                         }
                         return value;
                     } catch (const std::out_of_range&) {
-                        throw osmium::not_found{id};
+                        not_found_error(id);
                     }
                 }
 
@@ -180,7 +180,7 @@ namespace osmium {
                         return a.first < b.first;
                     });
                     if (result == m_vector.end() || result->first != id) {
-                        throw osmium::not_found{id};
+                        not_found_error(id);
                     } else {
                         return result->second;
                     }
diff --git a/include/osmium/index/id_set.hpp b/include/osmium/index/id_set.hpp
deleted file mode 100644
index 4a894a0..0000000
--- a/include/osmium/index/id_set.hpp
+++ /dev/null
@@ -1,431 +0,0 @@
-#ifndef OSMIUM_INDEX_ID_SET_HPP
-#define OSMIUM_INDEX_ID_SET_HPP
-
-/*
-
-This file is part of Osmium (http://osmcode.org/libosmium).
-
-Copyright 2013-2016 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 <cstring>
-#include <memory>
-#include <type_traits>
-#include <unordered_set>
-#include <vector>
-
-#include <osmium/osm/item_type.hpp>
-#include <osmium/osm/types.hpp>
-
-namespace osmium {
-
-    namespace index {
-
-        /**
-         * Virtual parent class for IdSets. Use one of the implementations
-         * provided.
-         */
-        template <typename T>
-        class IdSet {
-
-        public:
-
-            virtual ~IdSet() {
-            }
-
-            /**
-             * Add the given Id to the set.
-             */
-            virtual void set(T id) = 0;
-
-            /**
-             * Is the Id in the set?
-             */
-            virtual bool get(T id) const noexcept = 0;
-
-            /**
-             * Is the set empty?
-             */
-            virtual bool empty() const = 0;
-
-            /**
-             * Clear the set.
-             */
-            virtual void clear() = 0;
-
-        }; // class IdSet
-
-        template <typename T>
-        class IdSetDense;
-
-        /**
-         * Const_iterator for iterating over a IdSetDense.
-         */
-        template <typename T>
-        class IdSetDenseIterator {
-
-            const IdSetDense<T>* m_set;
-            T m_value;
-            T m_last;
-
-            void next() noexcept {
-                while (m_value != m_last && !m_set->get(m_value)) {
-                    const auto cid = IdSetDense<T>::chunk_id(m_value);
-                    assert(cid < m_set->m_data.size());
-                    if (!m_set->m_data[cid]) {
-                        m_value = (cid + 1) << (IdSetDense<T>::chunk_bits + 3);
-                    } else {
-                        const auto slot = m_set->m_data[cid][IdSetDense<T>::offset(m_value)];
-                        if (slot == 0) {
-                            m_value += 8;
-                            m_value &= ~0x7;
-                        } else {
-                            ++m_value;
-                        }
-                    }
-                }
-            }
-
-        public:
-
-            using iterator_category = std::forward_iterator_tag;
-            using value_type        = T;
-            using pointer           = value_type*;
-            using reference         = value_type&;
-
-            IdSetDenseIterator(const IdSetDense<T>* set, T value, T last) noexcept :
-                m_set(set),
-                m_value(value),
-                m_last(last) {
-                next();
-            }
-
-            IdSetDenseIterator<T>& operator++() noexcept {
-                if (m_value != m_last) {
-                    ++m_value;
-                    next();
-                }
-                return *this;
-            }
-
-            IdSetDenseIterator<T> operator++(int) noexcept {
-                IdSetDenseIterator<T> tmp(*this);
-                operator++();
-                return tmp;
-            }
-
-            bool operator==(const IdSetDenseIterator<T>& rhs) const noexcept {
-                return m_set == rhs.m_set && m_value == rhs.m_value;
-            }
-
-            bool operator!=(const IdSetDenseIterator<T>& rhs) const noexcept {
-                return ! (*this == rhs);
-            }
-
-            T operator*() const noexcept {
-                assert(m_value < m_last);
-                return m_value;
-            }
-
-        }; // class IdSetDenseIterator
-
-        /**
-         * A set of Ids of the given type. Internal storage is in chunks of
-         * arrays used as bit fields. Internally those chunks will be allocated
-         * as needed, so it works relatively efficiently with both smaller
-         * and larger Id sets. If it is not used, no memory is allocated at
-         * all.
-         */
-        template <typename T>
-        class IdSetDense : public IdSet<T> {
-
-            static_assert(std::is_unsigned<T>::value, "Needs unsigned type");
-            static_assert(sizeof(T) >= 4, "Needs at least 32bit type");
-
-            friend class IdSetDenseIterator<T>;
-
-            // This value is a compromise. For node Ids it could be bigger
-            // which would mean less (but larger) memory allocations. For
-            // relations Ids it could be smaller, because they would all fit
-            // into a smaller allocation.
-            constexpr static const size_t chunk_bits = 22;
-            constexpr static const size_t chunk_size = 1 << chunk_bits;
-
-            std::vector<std::unique_ptr<unsigned char[]>> m_data;
-            size_t m_size = 0;
-
-            static size_t chunk_id(T id) noexcept {
-                return id >> (chunk_bits + 3);
-            }
-
-            static size_t offset(T id) noexcept {
-                return (id >> 3) & ((1 << chunk_bits) - 1);
-            }
-
-            static unsigned char bitmask(T id) noexcept {
-                return 1 << (id & 0x7);
-            }
-
-            T last() const noexcept {
-                return m_data.size() * chunk_size * 8;
-            }
-
-            unsigned char& get_element(T id) {
-                const auto cid = chunk_id(id);
-                if (cid >= m_data.size()) {
-                    m_data.resize(cid + 1);
-                }
-
-                auto& chunk = m_data[cid];
-                if (!chunk) {
-                    chunk.reset(new unsigned char[chunk_size]);
-                    ::memset(chunk.get(), 0, chunk_size);
-                }
-
-                return chunk[offset(id)];
-            }
-
-        public:
-
-            using const_iterator = IdSetDenseIterator<T>;
-
-            IdSetDense() = default;
-
-            /**
-             * Add the Id to the set if it is not already in there.
-             *
-             * @param id The Id to set.
-             * @returns true if the Id was added, false if it was already set.
-             */
-            bool check_and_set(T id) {
-                auto& element = get_element(id);
-
-                if ((element & bitmask(id)) == 0) {
-                    element |= bitmask(id);
-                    ++m_size;
-                    return true;
-                }
-
-                return false;
-            }
-
-            /**
-             * Add the given Id to the set.
-             *
-             * @param id The Id to set.
-             */
-            void set(T id) override final {
-                (void)check_and_set(id);
-            }
-
-            /**
-             * Remove the given Id from the set.
-             *
-             * @param id The Id to set.
-             */
-            void unset(T id) {
-                auto& element = get_element(id);
-
-                if ((element & bitmask(id)) != 0) {
-                    element &= ~bitmask(id);
-                    --m_size;
-                }
-            }
-
-            /**
-             * Is the Id in the set?
-             *
-             * @param id The Id to check.
-             */
-            bool get(T id) const noexcept override final {
-                if (chunk_id(id) >= m_data.size()) {
-                    return false;
-                }
-                auto* r = m_data[chunk_id(id)].get();
-                if (!r) {
-                    return false;
-                }
-                return (r[offset(id)] & bitmask(id)) != 0;
-            }
-
-            /**
-             * Is the set empty?
-             */
-            bool empty() const noexcept override final {
-                return m_size == 0;
-            }
-
-            /**
-             * The number of Ids stored in the set.
-             */
-            size_t size() const noexcept {
-                return m_size;
-            }
-
-            /**
-             * Clear the set.
-             */
-            void clear() override final {
-                m_data.clear();
-                m_size = 0;
-            }
-
-            IdSetDenseIterator<T> begin() const {
-                return IdSetDenseIterator<T>{this, 0, last()};
-            }
-
-            IdSetDenseIterator<T> end() const {
-                return IdSetDenseIterator<T>{this, last(), last()};
-            }
-
-        }; // class IdSetDense
-
-        /**
-         * IdSet implementation for small Id sets. It writes the Ids
-         * into a vector and uses linear search.
-         */
-        template <typename T>
-        class IdSetSmall : public IdSet<T> {
-
-            std::vector<T> m_data;
-
-        public:
-
-            /**
-             * Add the given Id to the set.
-             */
-            void set(T id) override final {
-                m_data.push_back(id);
-            }
-
-            /**
-             * Is the Id in the set? Uses linear search.
-             *
-             * @param id The Id to check.
-             */
-            bool get(T id) const noexcept override final {
-                const auto it = std::find(m_data.cbegin(), m_data.cend(), id);
-                return it != m_data.cend();
-            }
-
-            /**
-             * Is the Id in the set? Uses a binary search. For larger sets
-             * this might be more efficient than calling get(), the set
-             * must be sorted.
-             *
-             * @param id The Id to check.
-             * @pre You must have called sort_unique() before calling this
-             *      or be sure there are no duplicates and the Ids have been
-             *      set in order.
-             */
-            bool get_binary_search(T id) const noexcept {
-                return std::binary_search(m_data.cbegin(), m_data.cend(), id);
-            }
-
-            /**
-             * Is the set empty?
-             */
-            bool empty() const noexcept override final {
-                return m_data.empty();
-            }
-
-            /**
-             * Clear the set.
-             */
-            void clear() override final {
-                m_data.clear();
-            }
-
-            /**
-             * Sort the internal vector and remove any duplicates. Call this
-             * before using size(), get_binary_search() or using an iterator.
-             */
-            void sort_unique() {
-                std::sort(m_data.begin(), m_data.end());
-                const auto last = std::unique(m_data.begin(), m_data.end());
-                m_data.erase(last, m_data.end());
-
-            }
-
-            /**
-             * The number of Ids stored in the set.
-             *
-             * @pre You must have called sort_unique() before calling this
-             *      or be sure there are no duplicates.
-             */
-            size_t size() const noexcept {
-                return m_data.size();
-            }
-
-            /// Iterator type. There is no non-const iterator.
-            using const_iterator = typename std::vector<T>::const_iterator;
-
-            const_iterator begin() const noexcept {
-                return m_data.cbegin();
-            }
-
-            const_iterator end() const noexcept {
-                return m_data.cend();
-            }
-
-            const_iterator cbegin() const noexcept {
-                return m_data.cbegin();
-            }
-
-            const_iterator cend() const noexcept {
-                return m_data.cend();
-            }
-
-        }; // class IdSetSmall
-
-        template <template<typename> class IdSetType>
-        class NWRIdSet {
-
-            using id_set_type = IdSetType<osmium::unsigned_object_id_type>;
-
-            id_set_type m_sets[3];
-
-        public:
-
-            id_set_type& operator()(osmium::item_type type) noexcept {
-                return m_sets[osmium::item_type_to_nwr_index(type)];
-            }
-
-            const id_set_type& operator()(osmium::item_type type) const noexcept {
-                return m_sets[osmium::item_type_to_nwr_index(type)];
-            }
-
-        }; // class NWRIdSet
-
-    } // namespace index
-
-} // namespace osmium
-
-#endif // OSMIUM_INDEX_ID_SET_HPP
diff --git a/include/osmium/index/index.hpp b/include/osmium/index/index.hpp
index 65871f2..c3deead 100644
--- a/include/osmium/index/index.hpp
+++ b/include/osmium/index/index.hpp
@@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <cstddef>
 #include <limits>
+#include <sstream>
 #include <stdexcept>
 #include <string>
 
@@ -56,10 +57,6 @@ namespace osmium {
             std::runtime_error(what) {
         }
 
-        explicit not_found(uint64_t id) :
-            std::runtime_error(std::string{"id "} + std::to_string(id) + " not found") {
-        }
-
     }; // struct not_found
 
     /**
@@ -67,6 +64,13 @@ namespace osmium {
      */
     namespace index {
 
+        template <typename TKey>
+        OSMIUM_NORETURN void not_found_error(TKey key) {
+            std::stringstream s;
+            s << "id " << key << " not 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
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index d26a4aa..1d2d5aa 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -48,18 +48,6 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
-    struct map_factory_error : public std::runtime_error {
-
-        explicit map_factory_error(const char* message) :
-            std::runtime_error(message) {
-        }
-
-        explicit map_factory_error(const std::string& message) :
-            std::runtime_error(message) {
-        }
-
-    }; // struct map_factory_error
-
     namespace index {
 
         /**
@@ -160,14 +148,14 @@ namespace osmium {
                     // default implementation is empty
                 }
 
-                // This function can usually be const in derived classes,
+                // This function could usually be const in derived classes,
                 // but not always. It could, for instance, sort internal data.
                 // This is why it is not declared const here.
                 virtual void dump_as_list(const int /*fd*/) {
                     throw std::runtime_error("can't dump as list");
                 }
 
-                // This function can usually be const in derived classes,
+                // This function could usually be const in derived classes,
                 // but not always. It could, for instance, sort internal data.
                 // This is why it is not declared const here.
                 virtual void dump_as_array(const int /*fd*/) {
@@ -200,6 +188,13 @@ namespace osmium {
             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() {
@@ -231,7 +226,7 @@ namespace osmium {
                 std::vector<std::string> config = osmium::split_string(config_string, ',');
 
                 if (config.empty()) {
-                    throw map_factory_error{"Need non-empty map type name"};
+                    throw std::runtime_error("Need non-empty map type name.");
                 }
 
                 auto it = m_callbacks.find(config[0]);
@@ -239,7 +234,7 @@ namespace osmium {
                     return std::unique_ptr<map_type>((it->second)(config));
                 }
 
-                throw map_factory_error{std::string{"Support for map type '"} + config[0] + "' not compiled into this binary"};
+                error(config[0]);
             }
 
         }; // class MapFactory
diff --git a/include/osmium/index/map/dummy.hpp b/include/osmium/index/map/dummy.hpp
index 529ad25..98d082b 100644
--- a/include/osmium/index/map/dummy.hpp
+++ b/include/osmium/index/map/dummy.hpp
@@ -63,7 +63,7 @@ namespace osmium {
                 }
 
                 const TValue get(const TId id) const final {
-                    throw osmium::not_found{id};
+                    not_found_error(id);
                 }
 
                 size_t size() const final {
diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp
index 43fea36..1e3c58c 100644
--- a/include/osmium/index/map/sparse_mem_map.hpp
+++ b/include/osmium/index/map/sparse_mem_map.hpp
@@ -79,7 +79,7 @@ namespace osmium {
                 const TValue get(const TId id) const final {
                     auto it = m_elements.find(id);
                     if (it == m_elements.end()) {
-                        throw osmium::not_found{id};
+                        not_found_error(id);
                     }
                     return it->second;
                 }
diff --git a/include/osmium/index/map/sparse_mem_table.hpp b/include/osmium/index/map/sparse_mem_table.hpp
index 68f5797..241a64f 100644
--- a/include/osmium/index/map/sparse_mem_table.hpp
+++ b/include/osmium/index/map/sparse_mem_table.hpp
@@ -99,10 +99,10 @@ namespace osmium {
 
                 const TValue get(const TId id) const final {
                     if (id >= m_elements.size()) {
-                        throw osmium::not_found{id};
+                        not_found_error(id);
                     }
                     if (m_elements[id] == osmium::index::empty_value<TValue>()) {
-                        throw osmium::not_found{id};
+                        not_found_error(id);
                     }
                     return m_elements[id];
                 }
diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp
index a38bba6..e7f93bd 100644
--- a/include/osmium/io/compression.hpp
+++ b/include/osmium/io/compression.hpp
@@ -145,11 +145,10 @@ namespace osmium {
 
         private:
 
-            using callbacks_type = std::tuple<create_compressor_type,
-                                              create_decompressor_type_fd,
-                                              create_decompressor_type_buffer>;
-
-            using compression_map_type = std::map<const osmium::io::file_compression, callbacks_type>;
+            using compression_map_type = std::map<const osmium::io::file_compression,
+                                                  std::tuple<create_compressor_type,
+                                                             create_decompressor_type_fd,
+                                                             create_decompressor_type_buffer>>;
 
             compression_map_type m_callbacks;
 
@@ -161,17 +160,11 @@ namespace osmium {
             CompressionFactory(CompressionFactory&&) = delete;
             CompressionFactory& operator=(CompressionFactory&&) = delete;
 
-            const callbacks_type& find_callbacks(osmium::io::file_compression compression) const {
-                const auto it = m_callbacks.find(compression);
-
-                if (it != m_callbacks.end()) {
-                    return it->second;
-                }
-
-                std::string error_message{"Support for compression '"};
+            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 unsupported_file_format_error{error_message};
+                error_message += "' not compiled into this binary.";
+                throw unsupported_file_format_error(error_message);
             }
 
         public:
@@ -196,21 +189,36 @@ namespace osmium {
             }
 
             template <typename... TArgs>
-            std::unique_ptr<osmium::io::Compressor> create_compressor(osmium::io::file_compression compression, TArgs&&... args) const {
-                const auto callbacks = find_callbacks(compression);
-                return std::unique_ptr<osmium::io::Compressor>(std::get<0>(callbacks)(std::forward<TArgs>(args)...));
+            std::unique_ptr<osmium::io::Compressor> create_compressor(osmium::io::file_compression compression, TArgs&&... args) {
+                auto it = m_callbacks.find(compression);
+
+                if (it != m_callbacks.end()) {
+                    return std::unique_ptr<osmium::io::Compressor>(std::get<0>(it->second)(std::forward<TArgs>(args)...));
+                }
+
+                error(compression);
             }
 
-            std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, int fd) const {
-                const auto callbacks = find_callbacks(compression);
-                auto p = std::unique_ptr<osmium::io::Decompressor>(std::get<1>(callbacks)(fd));
-                p->set_file_size(osmium::util::file_size(fd));
-                return p;
+            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()) {
+                    auto p = std::unique_ptr<osmium::io::Decompressor>(std::get<1>(it->second)(fd));
+                    p->set_file_size(osmium::util::file_size(fd));
+                    return p;
+                }
+
+                error(compression);
             }
 
-            std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, const char* buffer, size_t size) const {
-                const auto callbacks = find_callbacks(compression);
-                return std::unique_ptr<osmium::io::Decompressor>(std::get<2>(callbacks)(buffer, size));
+            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
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
index 67b05a8..98081e3 100644
--- a/include/osmium/io/detail/input_format.hpp
+++ b/include/osmium/io/detail/input_format.hpp
@@ -55,17 +55,12 @@ namespace osmium {
 
         namespace detail {
 
-            struct reader_options {
-                osmium::osm_entity_bits::type read_which_entities = osm_entity_bits::all;
-                osmium::io::read_meta read_metadata = read_meta::yes;
-            };
-
             class Parser {
 
                 future_buffer_queue_type& m_output_queue;
                 std::promise<osmium::io::Header>& m_header_promise;
                 queue_wrapper<std::string> m_input_queue;
-                reader_options m_options;
+                osmium::osm_entity_bits::type m_read_types;
                 bool m_header_is_done;
 
             protected:
@@ -78,15 +73,11 @@ namespace osmium {
                     return m_input_queue.has_reached_end_of_data();
                 }
 
-                osmium::osm_entity_bits::type read_types() const noexcept {
-                    return m_options.read_which_entities;
-                }
-
-                osmium::io::read_meta read_metadata() const noexcept {
-                    return m_options.read_metadata;
+                osmium::osm_entity_bits::type read_types() const {
+                    return m_read_types;
                 }
 
-                bool header_is_done() const noexcept {
+                bool header_is_done() const {
                     return m_header_is_done;
                 }
 
@@ -120,11 +111,11 @@ namespace osmium {
                 Parser(future_string_queue_type& input_queue,
                        future_buffer_queue_type& output_queue,
                        std::promise<osmium::io::Header>& header_promise,
-                       osmium::io::detail::reader_options options) :
+                       osmium::osm_entity_bits::type read_types) :
                     m_output_queue(output_queue),
                     m_header_promise(header_promise),
                     m_input_queue(input_queue),
-                    m_options(options),
+                    m_read_types(read_types),
                     m_header_is_done(false) {
                 }
 
@@ -166,7 +157,7 @@ namespace osmium {
                 using create_parser_type = std::function<std::unique_ptr<Parser>(future_string_queue_type&,
                                                                                  future_buffer_queue_type&,
                                                                                  std::promise<osmium::io::Header>& header_promise,
-                                                                                 osmium::io::detail::reader_options options)>;
+                                                                                 osmium::osm_entity_bits::type read_which_entities)>;
 
             private:
 
diff --git a/include/osmium/io/detail/o5m_input_format.hpp b/include/osmium/io/detail/o5m_input_format.hpp
index de4595e..d642879 100644
--- a/include/osmium/io/detail/o5m_input_format.hpp
+++ b/include/osmium/io/detail/o5m_input_format.hpp
@@ -80,7 +80,7 @@ namespace osmium {
     struct o5m_error : public io_error {
 
         explicit o5m_error(const char* what) :
-            io_error(std::string{"o5m format error: "} + what) {
+            io_error(std::string("o5m format error: ") + what) {
         }
 
     }; // struct o5m_error
@@ -135,7 +135,7 @@ namespace osmium {
 
                 const char* get(uint64_t index) const {
                     if (m_table.empty() || index == 0 || index > number_of_entries) {
-                        throw o5m_error{"reference to non-existing string in table"};
+                        throw o5m_error("reference to non-existing string in table");
                     }
                     auto entry = (current_entry + number_of_entries - index) % number_of_entries;
                     return &m_table[entry * entry_size];
@@ -191,7 +191,7 @@ namespace osmium {
                     static const unsigned char header_magic[] = { 0xff, 0xe0, 0x04, 'o', '5' };
 
                     if (std::strncmp(reinterpret_cast<const char*>(header_magic), m_data, sizeof(header_magic))) {
-                        throw o5m_error{"wrong header magic"};
+                        throw o5m_error("wrong header magic");
                     }
 
                     m_data += sizeof(header_magic);
@@ -203,7 +203,7 @@ namespace osmium {
                     } else if (*m_data == 'c') {  // o5c change file
                         m_header.set_has_multiple_object_versions(true);
                     } else {
-                        throw o5m_error{"wrong header magic"};
+                        throw o5m_error("wrong header magic");
                     }
 
                     m_data++;
@@ -211,7 +211,7 @@ namespace osmium {
 
                 void check_file_format_version() {
                     if (*m_data != '2') {
-                        throw o5m_error{"wrong header magic"};
+                        throw o5m_error("wrong header magic");
                     }
 
                     m_data++;
@@ -219,7 +219,7 @@ namespace osmium {
 
                 void decode_header() {
                     if (! ensure_bytes_available(7)) { // overall length of header
-                        throw o5m_error{"file too short (incomplete header info)"};
+                        throw o5m_error("file too short (incomplete header info)");
                     }
 
                     check_header_magic();
@@ -260,7 +260,7 @@ namespace osmium {
                     if (**dataptr == 0x00) { // get inline string
                         (*dataptr)++;
                         if (*dataptr == end) {
-                            throw o5m_error{"string format error"};
+                            throw o5m_error("string format error");
                         }
                         return *dataptr;
                     } else { // get from reference table
@@ -277,7 +277,7 @@ namespace osmium {
                     auto uid = protozero::decode_varint(&data, end);
 
                     if (data == end) {
-                        throw o5m_error{"missing user name"};
+                        throw o5m_error("missing user name");
                     }
 
                     const char* user = ++data;
@@ -290,7 +290,7 @@ namespace osmium {
 
                     while (*data++) {
                         if (data == end) {
-                            throw o5m_error{"no null byte in user name"};
+                            throw o5m_error("no null byte in user name");
                         }
                     }
 
@@ -302,24 +302,24 @@ namespace osmium {
                     return std::make_pair(static_cast_with_assert<osmium::user_id_type>(uid), user);
                 }
 
-                void decode_tags(osmium::builder::Builder& parent, const char** dataptr, const char* const end) {
-                    osmium::builder::TagListBuilder builder{parent};
+                void decode_tags(osmium::builder::Builder* builder, const char** dataptr, const char* const end) {
+                    osmium::builder::TagListBuilder tl_builder(m_buffer, builder);
 
-                    while (*dataptr != end) {
+                    while(*dataptr != end) {
                         bool update_pointer = (**dataptr == 0x00);
                         const char* data = decode_string(dataptr, end);
                         const char* start = data;
 
                         while (*data++) {
                             if (data == end) {
-                                throw o5m_error{"no null byte in tag key"};
+                                throw o5m_error("no null byte in tag key");
                             }
                         }
 
                         const char* value = data;
                         while (*data++) {
                             if (data == end) {
-                                throw o5m_error{"no null byte in tag value"};
+                                throw o5m_error("no null byte in tag value");
                             }
                         }
 
@@ -328,7 +328,7 @@ namespace osmium {
                             *dataptr = data;
                         }
 
-                        builder.add_tag(start, value);
+                        tl_builder.add_tag(start, value);
                     }
                 }
 
@@ -357,46 +357,50 @@ namespace osmium {
                 }
 
                 void decode_node(const char* data, const char* const end) {
-                    osmium::builder::NodeBuilder builder{m_buffer};
+                    osmium::builder::NodeBuilder builder(m_buffer);
+                    osmium::Node& node = builder.object();
 
-                    builder.set_id(m_delta_id.update(zvarint(&data, end)));
+                    node.set_id(m_delta_id.update(zvarint(&data, end)));
 
-                    builder.set_user(decode_info(builder.object(), &data, end));
+                    builder.add_user(decode_info(node, &data, end));
 
                     if (data == end) {
                         // no location, object is deleted
-                        builder.set_visible(false);
-                        builder.set_location(osmium::Location{});
+                        builder.object().set_visible(false);
+                        builder.object().set_location(osmium::Location{});
                     } else {
                         auto lon = m_delta_lon.update(zvarint(&data, end));
                         auto lat = m_delta_lat.update(zvarint(&data, end));
-                        builder.set_location(osmium::Location{lon, lat});
+                        builder.object().set_location(osmium::Location{lon, lat});
 
                         if (data != end) {
-                            decode_tags(builder, &data, end);
+                            decode_tags(&builder, &data, end);
                         }
                     }
+
+                    m_buffer.commit();
                 }
 
                 void decode_way(const char* data, const char* const end) {
-                    osmium::builder::WayBuilder builder{m_buffer};
+                    osmium::builder::WayBuilder builder(m_buffer);
+                    osmium::Way& way = builder.object();
 
-                    builder.set_id(m_delta_id.update(zvarint(&data, end)));
+                    way.set_id(m_delta_id.update(zvarint(&data, end)));
 
-                    builder.set_user(decode_info(builder.object(), &data, end));
+                    builder.add_user(decode_info(way, &data, end));
 
                     if (data == end) {
                         // no reference section, object is deleted
-                        builder.set_visible(false);
+                        builder.object().set_visible(false);
                     } else {
                         auto reference_section_length = protozero::decode_varint(&data, end);
                         if (reference_section_length > 0) {
                             const char* const end_refs = data + reference_section_length;
                             if (end_refs > end) {
-                                throw o5m_error{"way nodes ref section too long"};
+                                throw o5m_error("way nodes ref section too long");
                             }
 
-                            osmium::builder::WayNodeListBuilder wn_builder{builder};
+                            osmium::builder::WayNodeListBuilder wn_builder(m_buffer, &builder);
 
                             while (data < end_refs) {
                                 wn_builder.add_node_ref(m_delta_way_node_id.update(zvarint(&data, end)));
@@ -404,14 +408,16 @@ namespace osmium {
                         }
 
                         if (data != end) {
-                            decode_tags(builder, &data, end);
+                            decode_tags(&builder, &data, end);
                         }
                     }
+
+                    m_buffer.commit();
                 }
 
                 osmium::item_type decode_member_type(char c) {
                     if (c < '0' || c > '2') {
-                        throw o5m_error{"unknown member type"};
+                        throw o5m_error("unknown member type");
                     }
                     return osmium::nwr_index_to_item_type(c - '0');
                 }
@@ -423,13 +429,13 @@ namespace osmium {
 
                     auto member_type = decode_member_type(*data++);
                     if (data == end) {
-                        throw o5m_error{"missing role"};
+                        throw o5m_error("missing role");
                     }
                     const char* role = data;
 
                     while (*data++) {
                         if (data == end) {
-                            throw o5m_error{"no null byte in role"};
+                            throw o5m_error("no null byte in role");
                         }
                     }
 
@@ -442,29 +448,30 @@ namespace osmium {
                 }
 
                 void decode_relation(const char* data, const char* const end) {
-                    osmium::builder::RelationBuilder builder{m_buffer};
+                    osmium::builder::RelationBuilder builder(m_buffer);
+                    osmium::Relation& relation = builder.object();
 
-                    builder.set_id(m_delta_id.update(zvarint(&data, end)));
+                    relation.set_id(m_delta_id.update(zvarint(&data, end)));
 
-                    builder.set_user(decode_info(builder.object(), &data, end));
+                    builder.add_user(decode_info(relation, &data, end));
 
                     if (data == end) {
                         // no reference section, object is deleted
-                        builder.set_visible(false);
+                        builder.object().set_visible(false);
                     } else {
                         auto reference_section_length = protozero::decode_varint(&data, end);
                         if (reference_section_length > 0) {
                             const char* const end_refs = data + reference_section_length;
                             if (end_refs > end) {
-                                throw o5m_error{"relation format error"};
+                                throw o5m_error("relation format error");
                             }
 
-                            osmium::builder::RelationMemberListBuilder rml_builder{builder};
+                            osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
 
                             while (data < end_refs) {
                                 auto delta_id = zvarint(&data, end);
                                 if (data == end) {
-                                    throw o5m_error{"relation member format error"};
+                                    throw o5m_error("relation member format error");
                                 }
                                 auto type_role = decode_role(&data, end);
                                 auto i = osmium::item_type_to_nwr_index(type_role.first);
@@ -474,9 +481,11 @@ namespace osmium {
                         }
 
                         if (data != end) {
-                            decode_tags(builder, &data, end);
+                            decode_tags(&builder, &data, end);
                         }
                     }
+
+                    m_buffer.commit();
                 }
 
                 void decode_bbox(const char* data, const char* const end) {
@@ -528,11 +537,11 @@ namespace osmium {
                             try {
                                 length = protozero::decode_varint(&m_data, m_end);
                             } catch (const protozero::end_of_buffer_exception&) {
-                                throw o5m_error{"premature end of file"};
+                                throw o5m_error("premature end of file");
                             }
 
                             if (! ensure_bytes_available(length)) {
-                                throw o5m_error{"premature end of file"};
+                                throw o5m_error("premature end of file");
                             }
 
                             switch (ds_type) {
@@ -540,21 +549,18 @@ namespace osmium {
                                     mark_header_as_done();
                                     if (read_types() & osmium::osm_entity_bits::node) {
                                         decode_node(m_data, m_data + length);
-                                        m_buffer.commit();
                                     }
                                     break;
                                 case dataset_type::way:
                                     mark_header_as_done();
                                     if (read_types() & osmium::osm_entity_bits::way) {
                                         decode_way(m_data, m_data + length);
-                                        m_buffer.commit();
                                     }
                                     break;
                                 case dataset_type::relation:
                                     mark_header_as_done();
                                     if (read_types() & osmium::osm_entity_bits::relation) {
                                         decode_relation(m_data, m_data + length);
-                                        m_buffer.commit();
                                     }
                                     break;
                                 case dataset_type::bounding_box:
@@ -592,8 +598,8 @@ namespace osmium {
                 O5mParser(future_string_queue_type& input_queue,
                           future_buffer_queue_type& output_queue,
                           std::promise<osmium::io::Header>& header_promise,
-                          osmium::io::detail::reader_options options) :
-                    Parser(input_queue, output_queue, header_promise, options),
+                          osmium::osm_entity_bits::type read_types) :
+                    Parser(input_queue, output_queue, header_promise, read_types),
                     m_header(),
                     m_buffer(buffer_size),
                     m_input(),
@@ -619,8 +625,8 @@ namespace osmium {
                 [](future_string_queue_type& input_queue,
                     future_buffer_queue_type& output_queue,
                     std::promise<osmium::io::Header>& header_promise,
-                    osmium::io::detail::reader_options options) {
-                    return std::unique_ptr<Parser>(new O5mParser(input_queue, output_queue, header_promise, options));
+                    osmium::osm_entity_bits::type read_which_entities) {
+                    return std::unique_ptr<Parser>(new O5mParser(input_queue, output_queue, header_promise, read_which_entities));
             });
 
             // dummy function to silence the unused variable warning from above
diff --git a/include/osmium/io/detail/opl_input_format.hpp b/include/osmium/io/detail/opl_input_format.hpp
index 1bd8b07..15a31f3 100644
--- a/include/osmium/io/detail/opl_input_format.hpp
+++ b/include/osmium/io/detail/opl_input_format.hpp
@@ -82,8 +82,8 @@ namespace osmium {
                 OPLParser(future_string_queue_type& input_queue,
                           future_buffer_queue_type& output_queue,
                           std::promise<osmium::io::Header>& header_promise,
-                          osmium::io::detail::reader_options options) :
-                    Parser(input_queue, output_queue, header_promise, options) {
+                          osmium::osm_entity_bits::type read_types) :
+                    Parser(input_queue, output_queue, header_promise, read_types) {
                     set_header_value(osmium::io::Header{});
                 }
 
@@ -137,8 +137,8 @@ namespace osmium {
                 [](future_string_queue_type& input_queue,
                     future_buffer_queue_type& output_queue,
                     std::promise<osmium::io::Header>& header_promise,
-                    osmium::io::detail::reader_options options) {
-                    return std::unique_ptr<Parser>(new OPLParser(input_queue, output_queue, header_promise, options));
+                    osmium::osm_entity_bits::type read_which_entities) {
+                    return std::unique_ptr<Parser>(new OPLParser(input_queue, output_queue, header_promise, read_which_entities));
             });
 
             // dummy function to silence the unused variable warning from above
diff --git a/include/osmium/io/detail/opl_parser_functions.hpp b/include/osmium/io/detail/opl_parser_functions.hpp
index ee35b36..97c5927 100644
--- a/include/osmium/io/detail/opl_parser_functions.hpp
+++ b/include/osmium/io/detail/opl_parser_functions.hpp
@@ -365,8 +365,9 @@ namespace osmium {
 
             inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) {
                 osmium::builder::NodeBuilder builder{buffer};
+                osmium::Node& node = builder.object();
 
-                builder.set_id(opl_parse_id(data));
+                node.set_id(opl_parse_id(data));
 
                 const char* tags_begin = nullptr;
 
@@ -381,19 +382,19 @@ namespace osmium {
                     ++(*data);
                     switch (c) {
                         case 'v':
-                            builder.set_version(opl_parse_version(data));
+                            node.set_version(opl_parse_version(data));
                             break;
                         case 'd':
-                            builder.set_visible(opl_parse_visible(data));
+                            node.set_visible(opl_parse_visible(data));
                             break;
                         case 'c':
-                            builder.set_changeset(opl_parse_changeset_id(data));
+                            node.set_changeset(opl_parse_changeset_id(data));
                             break;
                         case 't':
-                            builder.set_timestamp(opl_parse_timestamp(data));
+                            node.set_timestamp(opl_parse_timestamp(data));
                             break;
                         case 'i':
-                            builder.set_uid(opl_parse_uid(data));
+                            node.set_uid(opl_parse_uid(data));
                             break;
                         case 'u':
                             opl_parse_string(data, user);
@@ -421,20 +422,23 @@ namespace osmium {
                 }
 
                 if (location.valid()) {
-                    builder.set_location(location);
+                    node.set_location(location);
                 }
 
-                builder.set_user(user);
+                builder.add_user(user);
 
                 if (tags_begin) {
                     opl_parse_tags(tags_begin, buffer, &builder);
                 }
+
+                buffer.commit();
             }
 
             inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) {
                 osmium::builder::WayBuilder builder{buffer};
+                osmium::Way& way = builder.object();
 
-                builder.set_id(opl_parse_id(data));
+                way.set_id(opl_parse_id(data));
 
                 const char* tags_begin = nullptr;
 
@@ -451,19 +455,19 @@ namespace osmium {
                     ++(*data);
                     switch (c) {
                         case 'v':
-                            builder.set_version(opl_parse_version(data));
+                            way.set_version(opl_parse_version(data));
                             break;
                         case 'd':
-                            builder.set_visible(opl_parse_visible(data));
+                            way.set_visible(opl_parse_visible(data));
                             break;
                         case 'c':
-                            builder.set_changeset(opl_parse_changeset_id(data));
+                            way.set_changeset(opl_parse_changeset_id(data));
                             break;
                         case 't':
-                            builder.set_timestamp(opl_parse_timestamp(data));
+                            way.set_timestamp(opl_parse_timestamp(data));
                             break;
                         case 'i':
-                            builder.set_uid(opl_parse_uid(data));
+                            way.set_uid(opl_parse_uid(data));
                             break;
                         case 'u':
                             opl_parse_string(data, user);
@@ -484,13 +488,15 @@ namespace osmium {
                     }
                 }
 
-                builder.set_user(user);
+                builder.add_user(user);
 
                 if (tags_begin) {
                     opl_parse_tags(tags_begin, buffer, &builder);
                 }
 
                 opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder);
+
+                buffer.commit();
             }
 
             inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) {
@@ -530,8 +536,9 @@ namespace osmium {
 
             inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) {
                 osmium::builder::RelationBuilder builder{buffer};
+                osmium::Relation& relation = builder.object();
 
-                builder.set_id(opl_parse_id(data));
+                relation.set_id(opl_parse_id(data));
 
                 const char* tags_begin = nullptr;
 
@@ -548,19 +555,19 @@ namespace osmium {
                     ++(*data);
                     switch (c) {
                         case 'v':
-                            builder.set_version(opl_parse_version(data));
+                            relation.set_version(opl_parse_version(data));
                             break;
                         case 'd':
-                            builder.set_visible(opl_parse_visible(data));
+                            relation.set_visible(opl_parse_visible(data));
                             break;
                         case 'c':
-                            builder.set_changeset(opl_parse_changeset_id(data));
+                            relation.set_changeset(opl_parse_changeset_id(data));
                             break;
                         case 't':
-                            builder.set_timestamp(opl_parse_timestamp(data));
+                            relation.set_timestamp(opl_parse_timestamp(data));
                             break;
                         case 'i':
-                            builder.set_uid(opl_parse_uid(data));
+                            relation.set_uid(opl_parse_uid(data));
                             break;
                         case 'u':
                             opl_parse_string(data, user);
@@ -581,7 +588,7 @@ namespace osmium {
                     }
                 }
 
-                builder.set_user(user);
+                builder.add_user(user);
 
                 if (tags_begin) {
                     opl_parse_tags(tags_begin, buffer, &builder);
@@ -590,12 +597,15 @@ namespace osmium {
                 if (members_begin != members_end) {
                     opl_parse_relation_members(members_begin, members_end, buffer, &builder);
                 }
+
+                buffer.commit();
             }
 
             inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) {
                 osmium::builder::ChangesetBuilder builder{buffer};
+                osmium::Changeset& changeset = builder.object();
 
-                builder.set_id(opl_parse_changeset_id(data));
+                changeset.set_id(opl_parse_changeset_id(data));
 
                 const char* tags_begin = nullptr;
 
@@ -611,19 +621,19 @@ namespace osmium {
                     ++(*data);
                     switch (c) {
                         case 'k':
-                            builder.set_num_changes(opl_parse_int<osmium::num_changes_type>(data));
+                            changeset.set_num_changes(opl_parse_int<osmium::num_changes_type>(data));
                             break;
                         case 's':
-                            builder.set_created_at(opl_parse_timestamp(data));
+                            changeset.set_created_at(opl_parse_timestamp(data));
                             break;
                         case 'e':
-                            builder.set_closed_at(opl_parse_timestamp(data));
+                            changeset.set_closed_at(opl_parse_timestamp(data));
                             break;
                         case 'd':
-                            builder.set_num_comments(opl_parse_int<osmium::num_comments_type>(data));
+                            changeset.set_num_comments(opl_parse_int<osmium::num_comments_type>(data));
                             break;
                         case 'i':
-                            builder.set_uid(opl_parse_uid(data));
+                            changeset.set_uid(opl_parse_uid(data));
                             break;
                         case 'u':
                             opl_parse_string(data, user);
@@ -662,17 +672,17 @@ namespace osmium {
                 }
 
                 if (location1.valid() && location2.valid()) {
-                    osmium::Box box;
-                    box.extend(location1);
-                    box.extend(location2);
-                    builder.set_bounds(box);
+                    changeset.bounds().extend(location1);
+                    changeset.bounds().extend(location2);
                 }
 
-                builder.set_user(user);
+                builder.add_user(user);
 
                 if (tags_begin) {
                     opl_parse_tags(tags_begin, buffer, &builder);
                 }
+
+                buffer.commit();
             }
 
             inline bool opl_parse_line(uint64_t line_count,
@@ -692,7 +702,6 @@ namespace osmium {
                             if (read_types & osmium::osm_entity_bits::node) {
                                 ++data;
                                 opl_parse_node(&data, buffer);
-                                buffer.commit();
                                 return true;
                             }
                             break;
@@ -700,7 +709,6 @@ namespace osmium {
                             if (read_types & osmium::osm_entity_bits::way) {
                                 ++data;
                                 opl_parse_way(&data, buffer);
-                                buffer.commit();
                                 return true;
                             }
                             break;
@@ -708,7 +716,6 @@ namespace osmium {
                             if (read_types & osmium::osm_entity_bits::relation) {
                                 ++data;
                                 opl_parse_relation(&data, buffer);
-                                buffer.commit();
                                 return true;
                             }
                             break;
@@ -716,7 +723,6 @@ namespace osmium {
                             if (read_types & osmium::osm_entity_bits::changeset) {
                                 ++data;
                                 opl_parse_changeset(&data, buffer);
-                                buffer.commit();
                                 return true;
                             }
                             break;
diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp
index 5164ce3..5df191d 100644
--- a/include/osmium/io/detail/pbf_decoder.hpp
+++ b/include/osmium/io/detail/pbf_decoder.hpp
@@ -50,7 +50,6 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
 #include <osmium/io/detail/protobuf_tags.hpp>
 #include <osmium/io/detail/zlib.hpp>
-#include <osmium/io/file_format.hpp>
 #include <osmium/io/header.hpp>
 #include <osmium/memory/buffer.hpp>
 #include <osmium/osm/box.hpp>
@@ -95,8 +94,6 @@ namespace osmium {
 
                 osmium::memory::Buffer m_buffer { initial_buffer_size };
 
-                osmium::io::read_meta m_read_metadata;
-
                 void decode_stringtable(const data_view& data) {
                     if (!m_stringtable.empty()) {
                         throw osmium::pbf_error("more than one stringtable in pbf file");
@@ -146,19 +143,13 @@ namespace osmium {
                                 case OSMFormat::PrimitiveGroup::repeated_Node_nodes:
                                     if (m_read_types & osmium::osm_entity_bits::node) {
                                         decode_node(pbf_primitive_group.get_view());
-                                        m_buffer.commit();
                                     } else {
                                         pbf_primitive_group.skip();
                                     }
                                     break;
                                 case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense:
                                     if (m_read_types & osmium::osm_entity_bits::node) {
-                                        if (m_read_metadata == osmium::io::read_meta::yes) {
-                                            decode_dense_nodes(pbf_primitive_group.get_view());
-                                        } else {
-                                            decode_dense_nodes_without_metadata(pbf_primitive_group.get_view());
-                                        }
-                                        m_buffer.commit();
+                                        decode_dense_nodes(pbf_primitive_group.get_view());
                                     } else {
                                         pbf_primitive_group.skip();
                                     }
@@ -166,7 +157,6 @@ namespace osmium {
                                 case OSMFormat::PrimitiveGroup::repeated_Way_ways:
                                     if (m_read_types & osmium::osm_entity_bits::way) {
                                         decode_way(pbf_primitive_group.get_view());
-                                        m_buffer.commit();
                                     } else {
                                         pbf_primitive_group.skip();
                                     }
@@ -174,7 +164,6 @@ namespace osmium {
                                 case OSMFormat::PrimitiveGroup::repeated_Relation_relations:
                                     if (m_read_types & osmium::osm_entity_bits::relation) {
                                         decode_relation(pbf_primitive_group.get_view());
-                                        m_buffer.commit();
                                     } else {
                                         pbf_primitive_group.skip();
                                     }
@@ -232,9 +221,9 @@ namespace osmium {
 
                 using kv_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
 
-                void build_tag_list(osmium::builder::Builder& parent, const kv_type& keys, const kv_type& vals) {
+                void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) {
                     if (!keys.empty()) {
-                        osmium::builder::TagListBuilder builder{parent};
+                        osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
                         auto kit = keys.begin();
                         auto vit = vals.begin();
                         while (kit != keys.end()) {
@@ -244,7 +233,7 @@ namespace osmium {
                             }
                             const auto& k = m_stringtable.at(*kit++);
                             const auto& v = m_stringtable.at(*vit++);
-                            builder.add_tag(k.first, k.second, v.first, v.second);
+                            tl_builder.add_tag(k.first, k.second, v.first, v.second);
                         }
                     }
                 }
@@ -254,7 +243,7 @@ namespace osmium {
                 }
 
                 void decode_node(const data_view& data) {
-                    osmium::builder::NodeBuilder builder{m_buffer};
+                    osmium::builder::NodeBuilder builder(m_buffer);
                     osmium::Node& node = builder.object();
 
                     kv_type keys;
@@ -277,11 +266,7 @@ namespace osmium {
                                 vals = pbf_node.get_packed_uint32();
                                 break;
                             case OSMFormat::Node::optional_Info_info:
-                                if (m_read_metadata == osmium::io::read_meta::yes) {
-                                    user = decode_info(pbf_node.get_view(), builder.object());
-                                } else {
-                                    pbf_node.skip();
-                                }
+                                user = decode_info(pbf_node.get_view(), builder.object());
                                 break;
                             case OSMFormat::Node::required_sint64_lat:
                                 lat = pbf_node.get_sint64();
@@ -305,13 +290,15 @@ namespace osmium {
                         ));
                     }
 
-                    builder.set_user(user.first, user.second);
+                    builder.add_user(user.first, user.second);
 
                     build_tag_list(builder, keys, vals);
+
+                    m_buffer.commit();
                 }
 
                 void decode_way(const data_view& data) {
-                    osmium::builder::WayBuilder builder{m_buffer};
+                    osmium::builder::WayBuilder builder(m_buffer);
 
                     kv_type keys;
                     kv_type vals;
@@ -334,11 +321,7 @@ namespace osmium {
                                 vals = pbf_way.get_packed_uint32();
                                 break;
                             case OSMFormat::Way::optional_Info_info:
-                                if (m_read_metadata == osmium::io::read_meta::yes) {
-                                    user = decode_info(pbf_way.get_view(), builder.object());
-                                } else {
-                                    pbf_way.skip();
-                                }
+                                user = decode_info(pbf_way.get_view(), builder.object());
                                 break;
                             case OSMFormat::Way::packed_sint64_refs:
                                 refs = pbf_way.get_packed_sint64();
@@ -354,10 +337,10 @@ namespace osmium {
                         }
                     }
 
-                    builder.set_user(user.first, user.second);
+                    builder.add_user(user.first, user.second);
 
                     if (!refs.empty()) {
-                        osmium::builder::WayNodeListBuilder wnl_builder{builder};
+                        osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
                         osmium::util::DeltaDecode<int64_t> ref;
                         if (lats.empty()) {
                             for (const auto& ref_value : refs) {
@@ -380,10 +363,12 @@ namespace osmium {
                     }
 
                     build_tag_list(builder, keys, vals);
+
+                    m_buffer.commit();
                 }
 
                 void decode_relation(const data_view& data) {
-                    osmium::builder::RelationBuilder builder{m_buffer};
+                    osmium::builder::RelationBuilder builder(m_buffer);
 
                     kv_type keys;
                     kv_type vals;
@@ -406,11 +391,7 @@ namespace osmium {
                                 vals = pbf_relation.get_packed_uint32();
                                 break;
                             case OSMFormat::Relation::optional_Info_info:
-                                if (m_read_metadata == osmium::io::read_meta::yes) {
-                                    user = decode_info(pbf_relation.get_view(), builder.object());
-                                } else {
-                                    pbf_relation.skip();
-                                }
+                                user = decode_info(pbf_relation.get_view(), builder.object());
                                 break;
                             case OSMFormat::Relation::packed_int32_roles_sid:
                                 roles = pbf_relation.get_packed_int32();
@@ -426,10 +407,10 @@ namespace osmium {
                         }
                     }
 
-                    builder.set_user(user.first, user.second);
+                    builder.add_user(user.first, user.second);
 
                     if (!refs.empty()) {
-                        osmium::builder::RelationMemberListBuilder rml_builder{builder};
+                        osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
                         osmium::util::DeltaDecode<int64_t> ref;
                         while (!roles.empty() && !refs.empty() && !types.empty()) {
                             const auto& r = m_stringtable.at(roles.front());
@@ -450,84 +431,8 @@ namespace osmium {
                     }
 
                     build_tag_list(builder, keys, vals);
-                }
-
-                void build_tag_list_from_dense_nodes(osmium::builder::NodeBuilder& builder, protozero::pbf_reader::const_int32_iterator& it, protozero::pbf_reader::const_int32_iterator last) {
-                    osmium::builder::TagListBuilder tl_builder{builder};
-                    while (it != last && *it != 0) {
-                        const auto& k = m_stringtable.at(*it++);
-                        if (it == last) {
-                            throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs
-                        }
-                        const auto& v = m_stringtable.at(*it++);
-                        tl_builder.add_tag(k.first, k.second, v.first, v.second);
-                    }
-
-                    if (it != last) {
-                        ++it;
-                    }
-                }
-
-                void decode_dense_nodes_without_metadata(const data_view& data) {
-                    protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> ids;
-                    protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
-                    protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
-
-                    protozero::iterator_range<protozero::pbf_reader::const_int32_iterator>  tags;
-
-                    protozero::pbf_message<OSMFormat::DenseNodes> pbf_dense_nodes(data);
-                    while (pbf_dense_nodes.next()) {
-                        switch (pbf_dense_nodes.tag()) {
-                            case OSMFormat::DenseNodes::packed_sint64_id:
-                                ids = pbf_dense_nodes.get_packed_sint64();
-                                break;
-                            case OSMFormat::DenseNodes::packed_sint64_lat:
-                                lats = pbf_dense_nodes.get_packed_sint64();
-                                break;
-                            case OSMFormat::DenseNodes::packed_sint64_lon:
-                                lons = pbf_dense_nodes.get_packed_sint64();
-                                break;
-                            case OSMFormat::DenseNodes::packed_int32_keys_vals:
-                                tags = pbf_dense_nodes.get_packed_int32();
-                                break;
-                            default:
-                                pbf_dense_nodes.skip();
-                        }
-                    }
-
-                    osmium::util::DeltaDecode<int64_t> dense_id;
-                    osmium::util::DeltaDecode<int64_t> dense_latitude;
-                    osmium::util::DeltaDecode<int64_t> dense_longitude;
-
-                    auto tag_it = tags.begin();
-
-                    while (!ids.empty()) {
-                        if (lons.empty() ||
-                            lats.empty()) {
-                            // this is against the spec, must have same number of elements
-                            throw osmium::pbf_error("PBF format error");
-                        }
-
-                        osmium::builder::NodeBuilder builder{m_buffer};
-                        osmium::Node& node = builder.object();
-
-                        node.set_id(dense_id.update(ids.front()));
-                        ids.drop_front();
-
-                        const auto lon = dense_longitude.update(lons.front());
-                        lons.drop_front();
-                        const auto lat = dense_latitude.update(lats.front());
-                        lats.drop_front();
-                        builder.object().set_location(osmium::Location(
-                                convert_pbf_coordinate(lon),
-                                convert_pbf_coordinate(lat)
-                        ));
-
-                        if (tag_it != tags.end()) {
-                            build_tag_list_from_dense_nodes(builder, tag_it, tags.end());
-                        }
-                    }
 
+                    m_buffer.commit();
                 }
 
                 void decode_dense_nodes(const data_view& data) {
@@ -617,7 +522,7 @@ namespace osmium {
 
                         bool visible = true;
 
-                        osmium::builder::NodeBuilder builder{m_buffer};
+                        osmium::builder::NodeBuilder builder(m_buffer);
                         osmium::Node& node = builder.object();
 
                         node.set_id(dense_id.update(ids.front()));
@@ -664,7 +569,9 @@ namespace osmium {
 
                             const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front()));
                             user_sids.drop_front();
-                            builder.set_user(u.first, u.second);
+                            builder.add_user(u.first, u.second);
+                        } else {
+                            builder.add_user("");
                         }
 
                         // even if the node isn't visible, there's still a record
@@ -681,18 +588,31 @@ namespace osmium {
                         }
 
                         if (tag_it != tags.end()) {
-                            build_tag_list_from_dense_nodes(builder, tag_it, tags.end());
+                            osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
+                            while (tag_it != tags.end() && *tag_it != 0) {
+                                const auto& k = m_stringtable.at(*tag_it++);
+                                if (tag_it == tags.end()) {
+                                    throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs
+                                }
+                                const auto& v = m_stringtable.at(*tag_it++);
+                                tl_builder.add_tag(k.first, k.second, v.first, v.second);
+                            }
+
+                            if (tag_it != tags.end()) {
+                                ++tag_it;
+                            }
                         }
+
+                        m_buffer.commit();
                     }
 
                 }
 
             public:
 
-                PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) :
+                PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types) :
                     m_data(data),
-                    m_read_types(read_types),
-                    m_read_metadata(read_metadata) {
+                    m_read_types(read_types) {
                 }
 
                 PBFPrimitiveBlockDecoder(const PBFPrimitiveBlockDecoder&) = delete;
@@ -869,14 +789,12 @@ namespace osmium {
 
                 std::shared_ptr<std::string> m_input_buffer;
                 osmium::osm_entity_bits::type m_read_types;
-                osmium::io::read_meta m_read_metadata;
 
             public:
 
-                PBFDataBlobDecoder(std::string&& input_buffer, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) :
+                PBFDataBlobDecoder(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),
-                    m_read_metadata(read_metadata) {
+                    m_read_types(read_types) {
                 }
 
                 PBFDataBlobDecoder(const PBFDataBlobDecoder&) = default;
@@ -889,7 +807,7 @@ namespace osmium {
 
                 osmium::memory::Buffer operator()() {
                     std::string output;
-                    PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types, m_read_metadata);
+                    PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types);
                     return decoder();
                 }
 
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
index 31e778a..1253447 100644
--- a/include/osmium/io/detail/pbf_input_format.hpp
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -180,7 +180,7 @@ namespace osmium {
                     while (const auto size = check_type_and_get_blob_size("OSMData")) {
                         std::string input_buffer = read_from_input_queue_with_check(size);
 
-                        PBFDataBlobDecoder data_blob_parser{std::move(input_buffer), read_types(), read_metadata()};
+                        PBFDataBlobDecoder data_blob_parser{ std::move(input_buffer), read_types() };
 
                         if (osmium::config::use_pool_threads_for_pbf_parsing()) {
                             send_to_output_queue(osmium::thread::Pool::instance().submit(std::move(data_blob_parser)));
@@ -195,8 +195,8 @@ namespace osmium {
                 PBFParser(future_string_queue_type& input_queue,
                           future_buffer_queue_type& output_queue,
                           std::promise<osmium::io::Header>& header_promise,
-                          osmium::io::detail::reader_options options) :
-                    Parser(input_queue, output_queue, header_promise, options),
+                          osmium::osm_entity_bits::type read_types) :
+                    Parser(input_queue, output_queue, header_promise, read_types),
                     m_input_buffer() {
                 }
 
@@ -221,8 +221,8 @@ namespace osmium {
                 [](future_string_queue_type& input_queue,
                     future_buffer_queue_type& output_queue,
                     std::promise<osmium::io::Header>& header_promise,
-                    osmium::io::detail::reader_options options) {
-                    return std::unique_ptr<Parser>(new PBFParser(input_queue, output_queue, header_promise, options));
+                    osmium::osm_entity_bits::type read_which_entities) {
+                    return std::unique_ptr<Parser>(new PBFParser(input_queue, output_queue, header_promise, read_which_entities));
             });
 
             // dummy function to silence the unused variable warning from above
diff --git a/include/osmium/io/detail/queue_util.hpp b/include/osmium/io/detail/queue_util.hpp
index 021ea7d..bc47020 100644
--- a/include/osmium/io/detail/queue_util.hpp
+++ b/include/osmium/io/detail/queue_util.hpp
@@ -33,10 +33,10 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <algorithm>
 #include <exception>
 #include <future>
 #include <string>
-#include <utility>
 
 #include <osmium/memory/buffer.hpp>
 #include <osmium/thread/queue.hpp>
@@ -63,6 +63,13 @@ namespace osmium {
              * This type of queue contains OSM file data in the form it is
              * stored on disk, ie encoded as XML, PBF, etc.
              * The "end of file" is marked by an empty string.
+             */
+            using string_queue_type = osmium::thread::Queue<std::string>;
+
+            /**
+             * This type of queue contains OSM file data in the form it is
+             * stored on disk, ie encoded as XML, PBF, etc.
+             * The "end of file" is marked by an empty string.
              * The strings are wrapped in a std::future so that they can also
              * transport exceptions. The future also helps with keeping the
              * data in order.
@@ -88,11 +95,11 @@ namespace osmium {
                 add_to_queue<T>(queue, T{});
             }
 
-            inline bool at_end_of_data(const std::string& data) noexcept {
+            inline bool at_end_of_data(const std::string& data) {
                 return data.empty();
             }
 
-            inline bool at_end_of_data(osmium::memory::Buffer& buffer) noexcept {
+            inline bool at_end_of_data(osmium::memory::Buffer& buffer) {
                 return !buffer;
             }
 
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index 242ef9b..d8c57d8 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -80,8 +80,8 @@ namespace osmium {
         XML_Error error_code;
         std::string error_string;
 
-        explicit xml_error(const XML_Parser& parser) :
-            io_error(std::string{"XML parsing error at line "}
+        explicit 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))
@@ -117,7 +117,7 @@ namespace osmium {
         }
 
         explicit format_version_error(const char* v) :
-            io_error(std::string{"Can not read file with version "} + v),
+            io_error(std::string("Can not read file with version ") + v),
             version(v) {
         }
 
@@ -201,7 +201,7 @@ namespace osmium {
                     static void entity_declaration_handler(void*,
                             const XML_Char*, int, const XML_Char*, int, const XML_Char*,
                             const XML_Char*, const XML_Char*, const XML_Char*) {
-                        throw osmium::xml_error{"XML entities are not supported"};
+                        throw osmium::xml_error("XML entities are not supported");
                     }
 
                 public:
@@ -209,7 +209,7 @@ namespace osmium {
                     explicit ExpatXMLParser(T* callback_object) :
                         m_parser(XML_ParserCreate(nullptr)) {
                         if (!m_parser) {
-                            throw osmium::io_error{"Internal error: Can not create 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);
@@ -229,7 +229,7 @@ namespace osmium {
 
                     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};
+                            throw osmium::xml_error(m_parser);
                         }
                     }
 
@@ -271,32 +271,37 @@ namespace osmium {
                     return user;
                 }
 
-                void init_changeset(osmium::builder::ChangesetBuilder& builder, const XML_Char** attrs) {
-                    osmium::Box box;
+                void init_changeset(osmium::builder::ChangesetBuilder* builder, const XML_Char** attrs) {
+                    const char* user = "";
+                    osmium::Changeset& new_changeset = builder->object();
 
-                    check_attributes(attrs, [&builder, &box](const XML_Char* name, const XML_Char* value) {
+                    osmium::Location min;
+                    osmium::Location max;
+                    check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) {
                         if (!std::strcmp(name, "min_lon")) {
-                            box.bottom_left().set_lon(value);
+                            min.set_lon(value);
                         } else if (!std::strcmp(name, "min_lat")) {
-                            box.bottom_left().set_lat(value);
+                            min.set_lat(value);
                         } else if (!std::strcmp(name, "max_lon")) {
-                            box.top_right().set_lon(value);
+                            max.set_lon(value);
                         } else if (!std::strcmp(name, "max_lat")) {
-                            box.top_right().set_lat(value);
+                            max.set_lat(value);
                         } else if (!std::strcmp(name, "user")) {
-                            builder.set_user(value);
+                            user = value;
                         } else {
-                            builder.set_attribute(name, value);
+                            new_changeset.set_attribute(name, value);
                         }
                     });
 
-                    builder.set_bounds(box);
+                    new_changeset.bounds().extend(min);
+                    new_changeset.bounds().extend(max);
+
+                    builder->add_user(user);
                 }
 
-                void get_tag(osmium::builder::Builder& builder, const XML_Char** attrs) {
+                void get_tag(osmium::builder::Builder* builder, const XML_Char** attrs) {
                     const char* k = "";
                     const char* v = "";
-
                     check_attributes(attrs, [&k, &v](const XML_Char* name, const XML_Char* value) {
                         if (name[0] == 'k' && name[1] == 0) {
                             k = value;
@@ -304,9 +309,8 @@ namespace osmium {
                             v = value;
                         }
                     });
-
                     if (!m_tl_builder) {
-                        m_tl_builder.reset(new osmium::builder::TagListBuilder{builder});
+                        m_tl_builder = std::unique_ptr<osmium::builder::TagListBuilder>(new osmium::builder::TagListBuilder(m_buffer, builder));
                     }
                     m_tl_builder->add_tag(k, v);
                 }
@@ -326,17 +330,17 @@ namespace osmium {
                                     if (!std::strcmp(name, "version")) {
                                         m_header.set("version", value);
                                         if (std::strcmp(value, "0.6")) {
-                                            throw osmium::format_version_error{value};
+                                            throw osmium::format_version_error(value);
                                         }
                                     } else if (!std::strcmp(name, "generator")) {
                                         m_header.set("generator", value);
                                     }
                                 });
                                 if (m_header.get("version") == "") {
-                                    throw osmium::format_version_error{};
+                                    throw osmium::format_version_error();
                                 }
                             } else {
-                                throw osmium::xml_error{std::string{"Unknown top-level element: "} + element};
+                                throw osmium::xml_error(std::string("Unknown top-level element: ") + element);
                             }
                             m_context = context::top;
                             break;
@@ -345,8 +349,8 @@ namespace osmium {
                             if (!std::strcmp(element, "node")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::node) {
-                                    m_node_builder.reset(new osmium::builder::NodeBuilder{m_buffer});
-                                    m_node_builder->set_user(init_object(m_node_builder->object(), attrs));
+                                    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;
@@ -354,8 +358,8 @@ namespace osmium {
                             } else if (!std::strcmp(element, "way")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::way) {
-                                    m_way_builder.reset(new osmium::builder::WayBuilder{m_buffer});
-                                    m_way_builder->set_user(init_object(m_way_builder->object(), attrs));
+                                    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;
@@ -363,8 +367,8 @@ namespace osmium {
                             } else if (!std::strcmp(element, "relation")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::relation) {
-                                    m_relation_builder.reset(new osmium::builder::RelationBuilder{m_buffer});
-                                    m_relation_builder->set_user(init_object(m_relation_builder->object(), attrs));
+                                    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;
@@ -372,8 +376,8 @@ namespace osmium {
                             } else if (!std::strcmp(element, "changeset")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::changeset) {
-                                    m_changeset_builder.reset(new osmium::builder::ChangesetBuilder{m_buffer});
-                                    init_changeset(*m_changeset_builder, attrs);
+                                    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;
@@ -403,7 +407,7 @@ namespace osmium {
                             m_last_context = context::node;
                             m_context = context::in_object;
                             if (!std::strcmp(element, "tag")) {
-                                get_tag(*m_node_builder, attrs);
+                                get_tag(m_node_builder.get(), attrs);
                             }
                             break;
                         case context::way:
@@ -413,7 +417,7 @@ namespace osmium {
                                 m_tl_builder.reset();
 
                                 if (!m_wnl_builder) {
-                                    m_wnl_builder.reset(new osmium::builder::WayNodeListBuilder{*m_way_builder});
+                                    m_wnl_builder = std::unique_ptr<osmium::builder::WayNodeListBuilder>(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get()));
                                 }
 
                                 NodeRef nr;
@@ -429,7 +433,7 @@ namespace osmium {
                                 m_wnl_builder->add_node_ref(nr);
                             } else if (!std::strcmp(element, "tag")) {
                                 m_wnl_builder.reset();
-                                get_tag(*m_way_builder, attrs);
+                                get_tag(m_way_builder.get(), attrs);
                             }
                             break;
                         case context::relation:
@@ -439,7 +443,7 @@ namespace osmium {
                                 m_tl_builder.reset();
 
                                 if (!m_rml_builder) {
-                                    m_rml_builder.reset(new osmium::builder::RelationMemberListBuilder{*m_relation_builder});
+                                    m_rml_builder = std::unique_ptr<osmium::builder::RelationMemberListBuilder>(new osmium::builder::RelationMemberListBuilder(m_buffer, m_relation_builder.get()));
                                 }
 
                                 item_type type = item_type::undefined;
@@ -455,15 +459,15 @@ namespace osmium {
                                     }
                                 });
                                 if (type != item_type::node && type != item_type::way && type != item_type::relation) {
-                                    throw osmium::xml_error{"Unknown type on relation member"};
+                                    throw osmium::xml_error("Unknown type on relation member");
                                 }
                                 if (ref == 0) {
-                                    throw osmium::xml_error{"Missing ref on relation member"};
+                                    throw osmium::xml_error("Missing ref on relation member");
                                 }
                                 m_rml_builder->add_member(type, ref, role);
                             } else if (!std::strcmp(element, "tag")) {
                                 m_rml_builder.reset();
-                                get_tag(*m_relation_builder, attrs);
+                                get_tag(m_relation_builder.get(), attrs);
                             }
                             break;
                         case context::changeset:
@@ -472,12 +476,12 @@ namespace osmium {
                                 m_context = context::discussion;
                                 m_tl_builder.reset();
                                 if (!m_changeset_discussion_builder) {
-                                    m_changeset_discussion_builder.reset(new osmium::builder::ChangesetDiscussionBuilder{*m_changeset_builder});
+                                    m_changeset_discussion_builder = std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder>(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get()));
                                 }
                             } else if (!std::strcmp(element, "tag")) {
                                 m_context = context::in_object;
                                 m_changeset_discussion_builder.reset();
-                                get_tag(*m_changeset_builder, attrs);
+                                get_tag(m_changeset_builder.get(), attrs);
                             }
                             break;
                         case context::discussion:
@@ -628,8 +632,8 @@ namespace osmium {
                 XMLParser(future_string_queue_type& input_queue,
                           future_buffer_queue_type& output_queue,
                           std::promise<osmium::io::Header>& header_promise,
-                          osmium::io::detail::reader_options options) :
-                    Parser(input_queue, output_queue, header_promise, options),
+                          osmium::osm_entity_bits::type read_types) :
+                    Parser(input_queue, output_queue, header_promise, read_types),
                     m_context(context::root),
                     m_last_context(context::root),
                     m_in_delete_section(false),
@@ -653,7 +657,7 @@ namespace osmium {
                     ExpatXMLParser<XMLParser> parser(this);
 
                     while (!input_done()) {
-                        const std::string data{get_input()};
+                        std::string data = get_input();
                         parser(data, input_done());
                         if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) {
                             break;
@@ -676,8 +680,8 @@ namespace osmium {
                 [](future_string_queue_type& input_queue,
                     future_buffer_queue_type& output_queue,
                     std::promise<osmium::io::Header>& header_promise,
-                    osmium::io::detail::reader_options options) {
-                    return std::unique_ptr<Parser>(new XMLParser{input_queue, output_queue, header_promise, options});
+                    osmium::osm_entity_bits::type read_which_entities) {
+                    return std::unique_ptr<Parser>(new XMLParser(input_queue, output_queue, header_promise, read_which_entities));
             });
 
             // dummy function to silence the unused variable warning from above
diff --git a/include/osmium/io/file_format.hpp b/include/osmium/io/file_format.hpp
index 72b4abc..c447cb4 100644
--- a/include/osmium/io/file_format.hpp
+++ b/include/osmium/io/file_format.hpp
@@ -49,11 +49,6 @@ namespace osmium {
             debug   = 6
         };
 
-        enum class read_meta {
-            no  = 0,
-            yes = 1
-        };
-
 // avoid g++ false positive
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wreturn-type"
diff --git a/include/osmium/io/header.hpp b/include/osmium/io/header.hpp
index abd85f8..55ff5c6 100644
--- a/include/osmium/io/header.hpp
+++ b/include/osmium/io/header.hpp
@@ -44,37 +44,17 @@ namespace osmium {
     namespace io {
 
         /**
-         * Meta information from the header of an OSM file.
-         *
-         * The header can contain any number of bounding boxes, although
-         * usually there is only a single one (or none). PBF files only
-         * allow a single bounding box, but XML files can have multiple ones,
-         * although it is unusual and the semantics are unclear, so it is
-         * discouraged to create files with multiple bounding boxes.
-         *
-         * The header contains a flag telling you whether this file can
-         * contain multiple versions of the same object. This is true for
-         * history files and for change files, but not for normal OSM data
-         * files. Not all OSM file formats can distinguish between those
-         * cases, so the flag might be wrong.
-         *
-         * In addition the header can contain any number of key-value pairs
-         * with additional information. Most often this is used to set the
-         * "generator", the program that generated the file. Depending on
-         * the file format some of these key-value pairs are handled
-         * specially. The the Options parent class for details on how to
-         * set and get those key-value pairs.
-         */
+        * 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 should be true for history files
-             * and for change files, but not for normal OSM data files.
-             */
+            * 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:
@@ -85,76 +65,49 @@ namespace osmium {
                 Options(values) {
             }
 
-            /**
-             * Get the bounding boxes defined in the header.
-             */
+            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;
             }
 
-            /**
-             * Get the bounding boxes defined in the header.
-             */
             const std::vector<osmium::Box>& boxes() const noexcept {
                 return m_boxes;
             }
 
-            /**
-             * Set all the bounding boxes in the header.
-             */
             Header& boxes(const std::vector<osmium::Box>& boxes) noexcept {
                 m_boxes = boxes;
                 return *this;
             }
 
-            /**
-             * Get the first (or only if there is only one) bounding box.
-             *
-             * Returns an empty, invalid box if there is none.
-             */
             osmium::Box box() const {
-                return m_boxes.empty() ? osmium::Box{} : m_boxes.front();
+                return m_boxes.empty() ? osmium::Box() : m_boxes.front();
             }
 
-            /**
-             * Join up all the bounding boxes in the header into one and return
-             * it. This method is what you probably want to use unless you want
-             * to handle the possibly multiple bounding boxes yourself.
-             *
-             * Returns an empty, invalid box if there is none.
-             */
             osmium::Box joined_boxes() const {
                 osmium::Box box;
                 for (const auto& b : m_boxes) {
-                    box.extend(b);
+                    box.extend(b.bottom_left());
+                    box.extend(b.top_right());
                 }
                 return box;
             }
 
-            /**
-             * Add the given bounding box to the list of bounding boxes in the
-             * header.
-             *
-             * @returns The header itself to allow chaining.
-             */
             Header& add_box(const osmium::Box& box) {
                 m_boxes.push_back(box);
                 return *this;
             }
 
-            /**
-             * Can this file contain multiple versions of the same object?
-             */
             bool has_multiple_object_versions() const noexcept {
                 return m_has_multiple_object_versions;
             }
 
-            /**
-             * Set the flag that tells us whether this file can contain
-             * multiple versions of the same object?
-             *
-             * @returns The header itself to allow chaining.
-             */
             Header& set_has_multiple_object_versions(bool value) noexcept {
                 m_has_multiple_object_versions = value;
                 return *this;
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
index 89c8564..12f97b8 100644
--- a/include/osmium/io/reader.hpp
+++ b/include/osmium/io/reader.hpp
@@ -91,6 +91,7 @@ namespace osmium {
         class Reader {
 
             osmium::io::File m_file;
+            osmium::osm_entity_bits::type m_read_which_entities;
 
             enum class status {
                 okay   = 0, // normal reading
@@ -117,25 +118,15 @@ namespace osmium {
 
             size_t m_file_size;
 
-            osmium::io::detail::reader_options m_options;
-
-            void set_option(osmium::osm_entity_bits::type value) noexcept {
-                m_options.read_which_entities = value;
-            }
-
-            void set_option(osmium::io::read_meta value) noexcept {
-                m_options.read_metadata = value;
-            }
-
             // This function will run in a separate thread.
             static void parser_thread(const osmium::io::File& file,
                                       detail::future_string_queue_type& input_queue,
                                       detail::future_buffer_queue_type& osmdata_queue,
                                       std::promise<osmium::io::Header>&& header_promise,
-                                      osmium::io::detail::reader_options options) {
+                                      osmium::osm_entity_bits::type read_which_entities) {
                 std::promise<osmium::io::Header> promise = std::move(header_promise);
                 const auto creator = detail::ParserFactory::instance().get_creator_function(file);
-                const auto parser = creator(input_queue, osmdata_queue, promise, options);
+                const auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities);
                 parser->parse();
             }
 
@@ -214,28 +205,15 @@ namespace osmium {
             /**
              * Create new Reader object.
              *
-             * @param file The file (contains name and format info) to open.
-             * @param args All further arguments are optional and can appear
-             *             in any order:
-             *
-             * * osmium::osm_entities::bits: 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.
-             *
-             * * osmium::io::read_meta: Read meta data or not. The default is
-             *      osmium::io::read_meta::yes which means that meta data
-             *      is read normally. If you set this to
-             *      osmium::io::read_meta::no, meta data (like version, uid,
-             *      etc.) is not read possibly speeding up the read. Not all
-             *      file formats use this setting.
-             *
-             * @throws osmium::io_error If there was an error.
-             * @throws std::system_error If the file could not be opened.
+             * @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.
              */
-            template <typename... TArgs>
-            explicit Reader(const osmium::io::File& file, TArgs&&... args) :
+            explicit Reader(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities = osmium::osm_entity_bits::all) :
                 m_file(file.check()),
+                m_read_which_entities(read_which_entities),
                 m_status(status::okay),
                 m_childpid(0),
                 m_input_queue(detail::get_input_queue_size(), "raw_input"),
@@ -249,24 +227,17 @@ namespace osmium {
                 m_header(),
                 m_thread(),
                 m_file_size(m_decompressor->file_size()) {
-
-                (void)std::initializer_list<int>{
-                    (set_option(args), 0)...
-                };
-
                 std::promise<osmium::io::Header> header_promise;
                 m_header_future = header_promise.get_future();
-                m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), m_options};
+                m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), read_which_entities};
             }
 
-            template <typename... TArgs>
-            explicit Reader(const std::string& filename, TArgs&&... args) :
-                Reader(osmium::io::File(filename), std::forward<TArgs>(args)...) {
+            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) {
             }
 
-            template <typename... TArgs>
-            explicit Reader(const char* filename, TArgs&&... args) :
-                Reader(osmium::io::File(filename), std::forward<TArgs>(args)...) {
+            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;
@@ -333,7 +304,7 @@ namespace osmium {
                 try {
                     if (m_header_future.valid()) {
                         m_header = m_header_future.get();
-                        if (m_options.read_which_entities == osmium::osm_entity_bits::nothing) {
+                        if (m_read_which_entities == osmium::osm_entity_bits::nothing) {
                             m_status = status::eof;
                         }
                     }
@@ -359,7 +330,7 @@ namespace osmium {
                 osmium::memory::Buffer buffer;
 
                 if (m_status != status::okay ||
-                    m_options.read_which_entities == osmium::osm_entity_bits::nothing) {
+                    m_read_which_entities == osmium::osm_entity_bits::nothing) {
                     throw io_error("Can not read from reader when in status 'closed', 'eof', or 'error'");
                 }
 
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
index bcf0bd0..8c246db 100644
--- a/include/osmium/memory/buffer.hpp
+++ b/include/osmium/memory/buffer.hpp
@@ -113,9 +113,6 @@ namespace osmium {
             size_t m_capacity;
             size_t m_written;
             size_t m_committed;
-#ifndef NDEBUG
-            uint8_t m_builder_count{0};
-#endif
             auto_grow m_auto_grow {auto_grow::no};
             std::function<void(Buffer&)> m_full;
 
@@ -219,28 +216,13 @@ namespace osmium {
 
             ~Buffer() = default;
 
-#ifndef NDEBUG
-            void increment_builder_count() noexcept {
-                ++m_builder_count;
-            }
-
-            void decrement_builder_count() noexcept {
-                assert(m_builder_count > 0);
-                --m_builder_count;
-            }
-
-            uint8_t builder_count() const noexcept {
-                return m_builder_count;
-            }
-#endif
-
             /**
              * Return a pointer to data inside the buffer.
              *
              * @pre The buffer must be valid.
              */
             unsigned char* data() const noexcept {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return m_data;
             }
 
@@ -276,7 +258,7 @@ namespace osmium {
              * @pre The buffer must be valid.
              */
             bool is_aligned() const noexcept {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return (m_written % align_bytes == 0) && (m_committed % align_bytes == 0);
             }
 
@@ -301,7 +283,7 @@ namespace osmium {
              * than the difference between committed() and capacity().
              */
             OSMIUM_DEPRECATED void set_full_callback(std::function<void(Buffer&)> full) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 m_full = full;
             }
 
@@ -310,6 +292,7 @@ namespace osmium {
              * 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.
              *
              * @pre The buffer must be valid.
              *
@@ -322,7 +305,7 @@ namespace osmium {
              * @throws std::bad_alloc if there isn't enough memory available.
              */
             void grow(size_t size) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 if (!m_memory) {
                     throw std::logic_error("Can't grow Buffer if it doesn't use internal memory management.");
                 }
@@ -342,18 +325,15 @@ namespace osmium {
             /**
              * Mark currently written bytes in the buffer as committed.
              *
-             * @pre The buffer must be valid.
-             * @pre The buffer must be aligned properly (as indicated
+             * @pre The buffer must be valid and aligned properly (as indicated
              *      by is_aligned().
-             * @pre No builder can be open on this buffer.
              *
              * @returns Number of committed bytes before this commit. Can be
              *          used as an offset into the buffer to get to the
              *          object being committed by this call.
              */
             size_t commit() {
-                assert(m_data && "This must be a valid buffer");
-                assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
+                assert(m_data);
                 assert(is_aligned());
 
                 const size_t offset = m_committed;
@@ -365,11 +345,9 @@ namespace osmium {
              * Roll back changes in buffer to last committed state.
              *
              * @pre The buffer must be valid.
-             * @pre No builder can be open on this buffer.
              */
             void rollback() {
-                assert(m_data && "This must be a valid buffer");
-                assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
+                assert(m_data);
                 m_written = m_committed;
             }
 
@@ -378,12 +356,9 @@ namespace osmium {
              *
              * No-op on an invalid buffer.
              *
-             * @pre No builder can be open on this buffer.
-             *
              * @returns Number of bytes in the buffer before it was cleared.
              */
             size_t clear() {
-                assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
                 const size_t committed = m_committed;
                 m_written = 0;
                 m_committed = 0;
@@ -402,7 +377,7 @@ namespace osmium {
              */
             template <typename T>
             T& get(const size_t offset) const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return *reinterpret_cast<T*>(&m_data[offset]);
             }
 
@@ -440,7 +415,7 @@ namespace osmium {
              *         no callback defined and the buffer isn't auto-growing.
              */
             unsigned char* reserve_space(const size_t size) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 // try to flush the buffer empty first.
                 if (m_written + size > m_capacity && m_full) {
                     m_full(*this);
@@ -480,7 +455,7 @@ namespace osmium {
              */
             template <typename T>
             T& add_item(const T& item) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 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);
@@ -490,7 +465,6 @@ namespace osmium {
              * Add committed contents of the given buffer to this buffer.
              *
              * @pre The buffer must be valid.
-             * @pre No builder can be open on this buffer.
              *
              * Note that you have to eventually call commit() to actually
              * commit this data.
@@ -498,9 +472,7 @@ namespace osmium {
              * @param buffer The source of the copy. Must be valid.
              */
             void add_buffer(const Buffer& buffer) {
-                assert(m_data && "This must be a valid buffer");
-                assert(buffer && "Buffer parameter must be a valid buffer");
-                assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
+                assert(m_data && buffer);
                 unsigned char* target = reserve_space(buffer.committed());
                 std::copy_n(buffer.data(), buffer.committed(), target);
             }
@@ -510,13 +482,11 @@ namespace osmium {
              * you can use std::back_inserter.
              *
              * @pre The buffer must be valid.
-             * @pre No builder can be open on this buffer.
              *
              * @param item The item to be added.
              */
             void push_back(const osmium::memory::Item& item) {
-                assert(m_data && "This must be a valid buffer");
-                assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
+                assert(m_data);
                 add_item(item);
                 commit();
             }
@@ -567,7 +537,7 @@ namespace osmium {
              */
             template <typename T>
             t_iterator<T> begin() {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_iterator<T>(m_data, m_data + m_committed);
             }
 
@@ -580,7 +550,7 @@ namespace osmium {
              * @returns Iterator to first OSMEntity in the buffer.
              */
             iterator begin() {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return iterator(m_data, m_data + m_committed);
             }
 
@@ -595,7 +565,7 @@ namespace osmium {
              */
             template <typename T>
             t_iterator<T> get_iterator(size_t offset) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_iterator<T>(m_data + offset, m_data + m_committed);
             }
 
@@ -609,7 +579,7 @@ namespace osmium {
              *          buffer.
              */
             iterator get_iterator(size_t offset) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return iterator(m_data + offset, m_data + m_committed);
             }
 
@@ -623,7 +593,7 @@ namespace osmium {
              */
             template <typename T>
             t_iterator<T> end() {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_iterator<T>(m_data + m_committed, m_data + m_committed);
             }
 
@@ -636,40 +606,40 @@ namespace osmium {
              * @returns End iterator.
              */
             iterator end() {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return iterator(m_data + m_committed, m_data + m_committed);
             }
 
             template <typename T>
             t_const_iterator<T> cbegin() const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_const_iterator<T>(m_data, m_data + m_committed);
             }
 
             const_iterator cbegin() const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return const_iterator(m_data, m_data + m_committed);
             }
 
             template <typename T>
             t_const_iterator<T> get_iterator(size_t offset) const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_const_iterator<T>(m_data + offset, m_data + m_committed);
             }
 
             const_iterator get_iterator(size_t offset) const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return const_iterator(m_data + offset, m_data + m_committed);
             }
 
             template <typename T>
             t_const_iterator<T> cend() const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return t_const_iterator<T>(m_data + m_committed, m_data + m_committed);
             }
 
             const_iterator cend() const {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 return const_iterator(m_data + m_committed, m_data + m_committed);
             }
 
@@ -728,7 +698,7 @@ namespace osmium {
              */
             template <typename TCallbackClass>
             void purge_removed(TCallbackClass* callback) {
-                assert(m_data && "This must be a valid buffer");
+                assert(m_data);
                 if (begin() == end()) {
                     return;
                 }
diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp
index fb413ff..2a2c040 100644
--- a/include/osmium/memory/collection.hpp
+++ b/include/osmium/memory/collection.hpp
@@ -46,9 +46,9 @@ namespace osmium {
         template <typename TMember>
         class CollectionIterator {
 
-            // This data_type is either 'unsigned char*' or 'const unsigned
-            // char*' depending on whether TMember is const. This allows this
-            // class to be used as an iterator and as a const_iterator.
+            // 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.
             using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
 
             data_type m_data;
@@ -92,11 +92,11 @@ namespace osmium {
                 return m_data;
             }
 
-            TMember& operator*() const noexcept {
+            TMember& operator*() const {
                 return *reinterpret_cast<TMember*>(m_data);
             }
 
-            TMember* operator->() const noexcept {
+            TMember* operator->() const {
                 return reinterpret_cast<TMember*>(m_data);
             }
 
@@ -118,12 +118,9 @@ namespace osmium {
 
         public:
 
-            using value_type      = TMember;
-            using reference       = TMember&;
-            using const_reference = const TMember&;
-            using iterator        = CollectionIterator<TMember>;
-            using const_iterator  = CollectionIterator<const TMember>;
-            using size_type       = size_t;
+            using iterator       = CollectionIterator<TMember>;
+            using const_iterator = CollectionIterator<const TMember>;
+            using value_type     = TMember;
 
             static constexpr osmium::item_type itemtype = TCollectionItemType;
 
@@ -131,45 +128,31 @@ namespace osmium {
                 Item(sizeof(Collection<TMember, TCollectionItemType>), TCollectionItemType) {
             }
 
-            /**
-             * Does this collection contain any items?
-             *
-             * Complexity: Constant.
-             */
-            bool empty() const noexcept {
+            bool empty() const {
                 return sizeof(Collection<TMember, TCollectionItemType>) == byte_size();
             }
 
-            /**
-             * Returns the number of items in this collection.
-             *
-             * Complexity: Linear in the number of items.
-             */
-            size_type size() const noexcept {
-                return static_cast<size_type>(std::distance(begin(), end()));
-            }
-
-            iterator begin() noexcept {
+            iterator begin() {
                 return iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
             }
 
-            iterator end() noexcept {
+            iterator end() {
                 return iterator(data() + byte_size());
             }
 
-            const_iterator cbegin() const noexcept {
+            const_iterator cbegin() const {
                 return const_iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
             }
 
-            const_iterator cend() const noexcept {
+            const_iterator cend() const {
                 return const_iterator(data() + byte_size());
             }
 
-            const_iterator begin() const noexcept {
+            const_iterator begin() const {
                 return cbegin();
             }
 
-            const_iterator end() const noexcept {
+            const_iterator end() const {
                 return cend();
             }
 
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index b72ca4d..2df33c7 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -59,9 +59,9 @@ namespace osmium {
         using item_size_type = uint32_t;
 
         // align datastructures to this many bytes
-        constexpr const item_size_type align_bytes = 8;
+        constexpr item_size_type align_bytes = 8;
 
-        inline constexpr std::size_t padded_length(std::size_t length) noexcept {
+        inline std::size_t padded_length(std::size_t length) noexcept {
             return (length + align_bytes - 1) & ~(align_bytes - 1);
         }
 
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index d146e97..490fbe9 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -50,8 +50,7 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     namespace builder {
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder;
+        template <class T> class ObjectBuilder;
     } // namespace builder
 
     /**
@@ -118,8 +117,7 @@ namespace osmium {
      */
     class Area : public OSMObject {
 
-        template <typename TDerived, typename T>
-        friend class osmium::builder::OSMObjectBuilder;
+        friend class osmium::builder::ObjectBuilder<osmium::Area>;
 
         Area() :
             OSMObject(sizeof(Area), osmium::item_type::area) {
@@ -132,8 +130,6 @@ namespace osmium {
         /**
          * Was this area created from a way? (In contrast to areas
          * created from a relation and their members.)
-         *
-         * Complexity: Constant.
          */
         bool from_way() const noexcept {
             return (positive_id() & 0x1) == 0;
@@ -141,8 +137,6 @@ namespace osmium {
 
         /**
          * Return the Id of the way or relation this area was created from.
-         *
-         * Complexity: Constant.
          */
         osmium::object_id_type orig_id() const noexcept {
             return osmium::area_id_to_object_id(id());
@@ -151,8 +145,6 @@ namespace osmium {
         /**
          * Count the number of outer and inner rings of this area.
          *
-         * Complexity: Linear in the number of rings.
-         *
          * @returns Pair (number outer rings, number inner rings)
          */
         std::pair<size_t, size_t> num_rings() const {
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index 828a2c2..8a503ca 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -51,7 +51,7 @@ namespace osmium {
 
     namespace builder {
         class ChangesetDiscussionBuilder;
-        class ChangesetBuilder;
+        template <typename T> class ObjectBuilder;
     } // namespace builder
 
     class Changeset;
@@ -129,12 +129,20 @@ namespace osmium {
 
     class ChangesetDiscussion : public osmium::memory::Collection<ChangesetComment, osmium::item_type::changeset_discussion> {
 
+        friend class osmium::builder::ObjectBuilder<osmium::Changeset>;
+
     public:
 
+        using size_type = size_t;
+
         ChangesetDiscussion() :
             osmium::memory::Collection<ChangesetComment, osmium::item_type::changeset_discussion>() {
         }
 
+        size_type size() const noexcept {
+            return static_cast<size_type>(std::distance(begin(), end()));
+        }
+
     }; // class ChangesetDiscussion
 
     static_assert(sizeof(ChangesetDiscussion) % osmium::memory::align_bytes == 0, "Class osmium::ChangesetDiscussion has wrong size to be aligned properly!");
@@ -148,7 +156,7 @@ namespace osmium {
      */
     class Changeset : public osmium::OSMEntity {
 
-        friend class osmium::builder::ChangesetBuilder;
+        friend class osmium::builder::ObjectBuilder<osmium::Changeset>;
 
         osmium::Box       m_bounds;
         osmium::Timestamp m_created_at;
@@ -165,14 +173,10 @@ namespace osmium {
             OSMEntity(sizeof(Changeset), osmium::item_type::changeset) {
         }
 
-        void set_user_size(string_size_type size) noexcept {
+        void set_user_size(string_size_type size) {
             m_user_size = size;
         }
 
-        string_size_type user_size() const noexcept {
-            return m_user_size;
-        }
-
         unsigned char* subitems_position() {
             return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size);
         }
diff --git a/include/osmium/osm/crc.hpp b/include/osmium/osm/crc.hpp
index bf057fd..2abeac4 100644
--- a/include/osmium/osm/crc.hpp
+++ b/include/osmium/osm/crc.hpp
@@ -100,15 +100,15 @@ namespace osmium {
             return m_crc;
         }
 
-        void update_bool(const bool value) noexcept {
+        void update_bool(const bool value) {
             m_crc.process_byte(value);
         }
 
-        void update_int8(const uint8_t value) noexcept {
+        void update_int8(const uint8_t value) {
             m_crc.process_byte(value);
         }
 
-        void update_int16(const uint16_t value) noexcept {
+        void update_int16(const uint16_t value) {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
             m_crc.process_bytes(&value, sizeof(uint16_t));
 #else
@@ -117,7 +117,7 @@ namespace osmium {
 #endif
         }
 
-        void update_int32(const uint32_t value) noexcept {
+        void update_int32(const uint32_t value) {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
             m_crc.process_bytes(&value, sizeof(uint32_t));
 #else
@@ -126,7 +126,7 @@ namespace osmium {
 #endif
         }
 
-        void update_int64(const uint64_t value) noexcept {
+        void update_int64(const uint64_t value) {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
             m_crc.process_bytes(&value, sizeof(uint64_t));
 #else
@@ -135,57 +135,57 @@ namespace osmium {
 #endif
         }
 
-        void update_string(const char* str) noexcept {
+        void update_string(const char* str) {
             while (*str) {
                 m_crc.process_byte(*str++);
             }
         }
 
-        void update(const Timestamp& timestamp) noexcept {
+        void update(const Timestamp& timestamp) {
             update_int32(uint32_t(timestamp));
         }
 
-        void update(const osmium::Location& location) noexcept {
+        void update(const osmium::Location& location) {
             update_int32(location.x());
             update_int32(location.y());
         }
 
-        void update(const osmium::Box& box) noexcept {
+        void update(const osmium::Box& box) {
             update(box.bottom_left());
             update(box.top_right());
         }
 
-        void update(const NodeRef& node_ref) noexcept {
+        void update(const NodeRef& node_ref) {
             update_int64(node_ref.ref());
             update(node_ref.location());
         }
 
-        void update(const NodeRefList& node_refs) noexcept {
+        void update(const NodeRefList& node_refs) {
             for (const NodeRef& node_ref : node_refs) {
                 update(node_ref);
             }
         }
 
-        void update(const TagList& tags) noexcept {
+        void update(const TagList& tags) {
             for (const Tag& tag : tags) {
                 update_string(tag.key());
                 update_string(tag.value());
             }
         }
 
-        void update(const osmium::RelationMember& member) noexcept {
+        void update(const osmium::RelationMember& member) {
             update_int64(member.ref());
             update_int16(uint16_t(member.type()));
             update_string(member.role());
         }
 
-        void update(const osmium::RelationMemberList& members) noexcept {
+        void update(const osmium::RelationMemberList& members) {
             for (const RelationMember& member : members) {
                 update(member);
             }
         }
 
-        void update(const osmium::OSMObject& object) noexcept {
+        void update(const osmium::OSMObject& object) {
             update_int64(object.id());
             update_bool(object.visible());
             update_int32(object.version());
@@ -195,22 +195,22 @@ namespace osmium {
             update(object.tags());
         }
 
-        void update(const osmium::Node& node) noexcept {
+        void update(const osmium::Node& node) {
             update(static_cast<const osmium::OSMObject&>(node));
             update(node.location());
         }
 
-        void update(const osmium::Way& way) noexcept {
+        void update(const osmium::Way& way) {
             update(static_cast<const osmium::OSMObject&>(way));
             update(way.nodes());
         }
 
-        void update(const osmium::Relation& relation) noexcept {
+        void update(const osmium::Relation& relation) {
             update(static_cast<const osmium::OSMObject&>(relation));
             update(relation.members());
         }
 
-        void update(const osmium::Area& area) noexcept {
+        void update(const osmium::Area& area) {
             update(static_cast<const osmium::OSMObject&>(area));
             for (const auto& subitem : area) {
                 if (subitem.type() == osmium::item_type::outer_ring ||
@@ -220,7 +220,7 @@ namespace osmium {
             }
         }
 
-        void update(const osmium::ChangesetDiscussion& discussion) noexcept {
+        void update(const osmium::ChangesetDiscussion& discussion) {
             for (const auto& comment : discussion) {
                 update(comment.date());
                 update_int32(comment.uid());
@@ -229,7 +229,7 @@ namespace osmium {
             }
         }
 
-        void update(const osmium::Changeset& changeset) noexcept {
+        void update(const osmium::Changeset& changeset) {
             update_int64(changeset.id());
             update(changeset.created_at());
             update(changeset.closed_at());
diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp
index 05afe3b..b8e9ddb 100644
--- a/include/osmium/osm/entity_bits.hpp
+++ b/include/osmium/osm/entity_bits.hpp
@@ -60,9 +60,7 @@ namespace osmium {
          * assert(! (entities & osmium::osm_entity_bits::changeset));
          * @endcode
          */
-        enum type : unsigned char { // this should have been an enum class
-                                    // but now we can't change it any more
-                                    // without breaking lots of code
+        enum type : unsigned char {
 
             nothing    = 0x00,
             node       = 0x01,
@@ -77,21 +75,21 @@ namespace osmium {
 
         }; // enum type
 
-        inline constexpr 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 lhs, const type rhs) noexcept {
+            return static_cast<type>(static_cast<int>(lhs) | static_cast<int> (rhs));
         }
 
-        inline constexpr 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 constexpr type operator~(const type value) noexcept {
-            return all & static_cast<type>(~static_cast<int>(value));
+        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 value) noexcept {
+            return static_cast<type>(~static_cast<int>(value));
         }
 
         inline type operator&=(type& lhs, const type rhs) noexcept {
@@ -106,7 +104,7 @@ namespace osmium {
          *      changeset.
          */
         inline type from_item_type(osmium::item_type item_type) noexcept {
-            const auto ut = static_cast<std::underlying_type<osmium::item_type>::type>(item_type);
+            auto ut = static_cast<std::underlying_type<osmium::item_type>::type>(item_type);
             assert(ut <= 0x05);
             if (ut == 0) {
                 return nothing;
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
index d208717..c5da620 100644
--- a/include/osmium/osm/location.hpp
+++ b/include/osmium/osm/location.hpp
@@ -86,31 +86,23 @@ namespace osmium {
                 ++str;
             }
 
-            if (*str != '.') {
-                // there has to be at least one digit
-                if (*str >= '0' && *str <= '9') {
-                    result = *str - '0';
-                    ++str;
-                } else {
-                    goto error;
-                }
+            // there has to be at least one digit
+            if (*str >= '0' && *str <= '9') {
+                result = *str - '0';
+                ++str;
+            } else {
+                goto error;
+            }
 
-                // optional additional digits before decimal point
-                while (*str >= '0' && *str <= '9' && max_digits > 0) {
-                    result = result * 10 + (*str - '0');
-                    ++str;
-                    --max_digits;
-                }
+            // optional additional digits before decimal point
+            while (*str >= '0' && *str <= '9' && max_digits > 0) {
+                result = result * 10 + (*str - '0');
+                ++str;
+                --max_digits;
+            }
 
-                if (max_digits == 0) {
-                    goto error;
-                }
-            } else {
-                // need at least one digit after decimal dot if there was no
-                // digit before decimal dot
-                if (*(str + 1) < '0' || *(str + 1) > '9') {
-                    goto error;
-                }
+            if (max_digits == 0) {
+                goto error;
             }
 
             // optional decimal point
@@ -171,20 +163,18 @@ namespace osmium {
             }
 
             if (scale < 0) {
-                for (; scale < 0 && result > 0; ++scale) {
-                    result /= 10;
-                }
+                result = 0;
             } else {
                 for (; scale > 0; --scale) {
                     result *= 10;
                 }
-            }
 
-            result = (result + 5) / 10 * sign;
+                result = (result + 5) / 10 * sign;
 
-            if (result > std::numeric_limits<int32_t>::max() ||
-                result < std::numeric_limits<int32_t>::min()) {
-                goto error;
+                if (result > std::numeric_limits<int32_t>::max() ||
+                    result < std::numeric_limits<int32_t>::min()) {
+                    goto error;
+                }
             }
 
             *data = str;
diff --git a/include/osmium/osm/node.hpp b/include/osmium/osm/node.hpp
index f3df5e9..677ffc7 100644
--- a/include/osmium/osm/node.hpp
+++ b/include/osmium/osm/node.hpp
@@ -41,14 +41,12 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     namespace builder {
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder;
+        template <typename T> class ObjectBuilder;
     } // namespace builder
 
     class Node : public OSMObject {
 
-        template <typename TDerived, typename T>
-        friend class osmium::builder::OSMObjectBuilder;
+        friend class osmium::builder::ObjectBuilder<osmium::Node>;
 
         osmium::Location m_location;
 
@@ -64,7 +62,7 @@ namespace osmium {
             return m_location;
         }
 
-        Node& set_location(const osmium::Location& location) noexcept {
+        Node& set_location(const osmium::Location& location) {
             m_location = location;
             return *this;
         }
diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp
index f430b63..6cfdf22 100644
--- a/include/osmium/osm/node_ref_list.hpp
+++ b/include/osmium/osm/node_ref_list.hpp
@@ -52,23 +52,12 @@ namespace osmium {
 
     public:
 
-        using value_type             = NodeRef;
-        using reference              = NodeRef&;
-        using const_reference        = const NodeRef&;
-        using iterator               = NodeRef*;
-        using const_iterator         = const NodeRef*;
-        using const_reverse_iterator = std::reverse_iterator<const NodeRef*>;
-        using difference_type        = std::ptrdiff_t;
-        using size_type              = std::size_t;
-
         explicit NodeRefList(osmium::item_type itemtype) noexcept :
             osmium::memory::Item(sizeof(NodeRefList), itemtype) {
         }
 
         /**
          * Checks whether the collection is empty.
-         *
-         * Complexity: Constant.
          */
         bool empty() const noexcept {
             return sizeof(NodeRefList) == byte_size();
@@ -76,10 +65,8 @@ namespace osmium {
 
         /**
          * Returns the number of NodeRefs in the collection.
-         *
-         * Complexity: Constant.
          */
-        size_type size() const noexcept {
+        size_t size() const noexcept {
             const auto size_node_refs = byte_size() - sizeof(NodeRefList);
             assert(size_node_refs % sizeof(NodeRef) == 0);
             return size_node_refs / sizeof(NodeRef);
@@ -88,13 +75,11 @@ namespace osmium {
         /**
          * Access specified element.
          *
-         * Complexity: Constant.
-         *
          * @pre @code n < size() @endcode
          *
          * @param n Get the n-th element of the collection.
          */
-        const NodeRef& operator[](size_type n) const noexcept {
+        const NodeRef& operator[](size_t n) const noexcept {
             assert(n < size());
             const NodeRef* node_ref = &*(cbegin());
             return node_ref[n];
@@ -103,8 +88,6 @@ namespace osmium {
         /**
          * Access the first element.
          *
-         * Complexity: Constant.
-         *
          * @pre @code !empty() @endcode
          */
         const NodeRef& front() const noexcept {
@@ -115,8 +98,6 @@ namespace osmium {
         /**
          * Access the last element.
          *
-         * Complexity: Constant.
-         *
          * @pre @code !empty() @endcode
          */
         const NodeRef& back() const noexcept {
@@ -128,8 +109,6 @@ namespace osmium {
          * Checks whether the first and last node in the collection have the
          * same ID. The locations are not checked.
          *
-         * Complexity: Constant.
-         *
          * @pre @code !empty() @endcode
          */
         bool is_closed() const noexcept {
@@ -140,8 +119,6 @@ namespace osmium {
          * Checks whether the first and last node in the collection have the
          * same ID. The locations are not checked.
          *
-         * Complexity: Constant.
-         *
          * @pre @code !empty() @endcode
          */
         bool ends_have_same_id() const noexcept {
@@ -152,8 +129,6 @@ namespace osmium {
          * Checks whether the first and last node in the collection have the
          * same location. The IDs are not checked.
          *
-         * Complexity: Constant.
-         *
          * @pre @code !empty() @endcode
          * @pre @code front().location() && back().location() @endcode
          */
@@ -162,6 +137,10 @@ namespace osmium {
             return front().location() == back().location();
         }
 
+        using iterator = NodeRef*;
+        using const_iterator = const NodeRef*;
+        using const_reverse_iterator = std::reverse_iterator<const NodeRef*>;
+
         /// Returns an iterator to the beginning.
         iterator begin() noexcept {
             return iterator(data() + sizeof(NodeRefList));
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index 01fe249..caa6fbc 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -52,19 +52,11 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
-    namespace builder {
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder;
-    } // namespace builder
-
     /**
      * OSMObject (Node, Way, Relation, or Area).
      */
     class OSMObject : public osmium::OSMEntity {
 
-        template <typename TDerived, typename T>
-        friend class osmium::builder::OSMObjectBuilder;
-
         object_id_type      m_id;
         bool                m_deleted : 1;
         object_version_type m_version : 31;
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index 8c09680..2aa9caa 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -43,14 +43,11 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/object.hpp>
 #include <osmium/osm/types.hpp>
-#include <osmium/util/compatibility.hpp>
 
 namespace osmium {
 
     namespace builder {
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder;
-
+        template <typename> class ObjectBuilder;
         class RelationMemberListBuilder;
     } // namespace builder
 
@@ -112,8 +109,7 @@ namespace osmium {
             return m_ref;
         }
 
-        /// @deprecated Use set_ref() instead.
-        OSMIUM_DEPRECATED RelationMember& ref(object_id_type ref) noexcept {
+        RelationMember& ref(object_id_type ref) noexcept {
             m_ref = ref;
             return *this;
         }
@@ -153,18 +149,23 @@ namespace osmium {
 
     public:
 
+        using size_type = size_t;
+
         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 {
 
-        template <typename TDerived, typename T>
-        friend class osmium::builder::OSMObjectBuilder;
+        friend class osmium::builder::ObjectBuilder<osmium::Relation>;
 
         Relation() noexcept :
             OSMObject(sizeof(Relation), osmium::item_type::relation) {
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
index e2537ce..cd2a913 100644
--- a/include/osmium/osm/tag.hpp
+++ b/include/osmium/osm/tag.hpp
@@ -86,14 +86,12 @@ namespace osmium {
 
     }; // class Tag
 
-    inline bool operator==(const Tag& lhs, const Tag& rhs) {
-        return !std::strcmp(lhs.key(), rhs.key()) &&
-               !std::strcmp(lhs.value(), rhs.value());
+    inline bool operator==(const Tag& a, const Tag& b) {
+        return !std::strcmp(a.key(), b.key()) && !std::strcmp(a.value(), b.value());
     }
 
-    inline bool operator<(const Tag& lhs, const Tag& rhs) {
-        const auto c = std::strcmp(lhs.key(), rhs.key());
-        return (c == 0 ? std::strcmp(lhs.value(), rhs.value()) : c) < 0;
+    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);
     }
 
     /**
@@ -114,11 +112,20 @@ namespace osmium {
 
     public:
 
+        using size_type = size_t;
+
         TagList() :
             osmium::memory::Collection<Tag, osmium::item_type::tag_list>() {
         }
 
         /**
+         * Returns the number of tags in this tag list.
+         */
+        size_type size() const noexcept {
+            return static_cast<size_type>(std::distance(begin(), end()));
+        }
+
+        /**
          * Get tag value for the given tag key. If the key is not set, returns
          * the default_value.
          *
diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp
index e4415ef..f6713fe 100644
--- a/include/osmium/osm/way.hpp
+++ b/include/osmium/osm/way.hpp
@@ -44,8 +44,7 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     namespace builder {
-        template <typename TDerived, typename T>
-        class OSMObjectBuilder;
+        template <typename T> class ObjectBuilder;
     } // namespace builder
 
     /**
@@ -67,8 +66,7 @@ namespace osmium {
 
     class Way : public OSMObject {
 
-        template <typename TDerived, typename T>
-        friend class osmium::builder::OSMObjectBuilder;
+        friend class osmium::builder::ObjectBuilder<osmium::Way>;
 
         Way() noexcept :
             OSMObject(sizeof(Way), osmium::item_type::way) {
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index 53f6de9..b8455b4 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -40,7 +40,6 @@ DEALINGS IN THE SOFTWARE.
 #include <functional>
 #include <iomanip>
 #include <iostream>
-#include <utility>
 #include <vector>
 
 #include <osmium/osm/item_type.hpp>
@@ -354,7 +353,7 @@ namespace osmium {
                         member_meta(member.type()).emplace_back(member.ref(), m_relations.size(), n);
                         relation_meta.increment_need_members();
                     } else {
-                        member.set_ref(0); // set member id to zero to indicate we are not interested
+                        member.ref(0); // set member id to zero to indicate we are not interested
                     }
                     ++n;
                 }
@@ -495,65 +494,12 @@ namespace osmium {
                 return m_members_buffer;
             }
 
-            /**
-             * Is the given member available in the members buffer?
-             *
-             * If you also need the offset of the object, use
-             * get_availability_and_offset() instead, it is more efficient
-             * that way.
-             *
-             * @param type Item type
-             * @param id Object Id
-             * @returns True if the object is available, false otherwise.
-             */
-            bool is_available(osmium::item_type type, osmium::object_id_type id) {
-                const auto range = find_member_meta(type, id);
-                assert(!range.empty());
-                return range.begin()->is_available();
-            }
-
-            /**
-             * Get offset of a member in the members buffer.
-             *
-             * @pre The member must be available. If you are not sure, call
-             *      get_availability_and_offset() instead.
-             * @param type Item type
-             * @param id Object Id
-             * @returns The offset of the object in the members buffer.
-             */
             size_t get_offset(osmium::item_type type, osmium::object_id_type id) {
                 const auto range = find_member_meta(type, id);
                 assert(!range.empty());
-                assert(range.begin()->is_available());
                 return range.begin()->buffer_offset();
             }
 
-            /**
-             * Checks whether a member is available in the members buffer
-             * and returns its offset.
-             *
-             * If the member is not available, the boolean returned as the
-             * first element in the pair is false. In that case the offset
-             * in the second element is undefined.
-             *
-             * If the member is available, the boolean returned as the first
-             * element in the pair is true and the second element of the
-             * pair contains the offset into the members buffer.
-             *
-             * @param type Item type
-             * @param id Object Id
-             * @returns Pair of bool (showing availability) and the offset.
-             */
-            std::pair<bool, size_t> get_availability_and_offset(osmium::item_type type, osmium::object_id_type id) {
-                const auto range = find_member_meta(type, id);
-                assert(!range.empty());
-                if (range.begin()->is_available()) {
-                    return std::make_pair(true, range.begin()->buffer_offset());
-                } else {
-                    return std::make_pair(false, 0);
-                }
-            }
-
             template <typename TIter>
             void read_relations(TIter begin, TIter end) {
                 HandlerPass1 handler(*static_cast<TCollector*>(this));
@@ -579,7 +525,7 @@ namespace osmium {
             /**
              * Decide whether to purge removed members and then do it.
              *
-             * Currently the purging is done every 10000 calls.
+             * Currently the purging is done every thousand calls.
              * This could probably be improved upon.
              */
             void possibly_purge_removed_members() {
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
index 7624a60..b28dca1 100644
--- a/include/osmium/relations/detail/member_meta.hpp
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE.
 
 #include <cstddef>
 #include <iosfwd>
-#include <limits>
 
 #include <osmium/osm/types.hpp>
 
@@ -72,44 +71,24 @@ namespace osmium {
 
             /**
              * Offset in the buffer where the object is stored.
-             *
-             * The default value is one that will never be valid, so it is
-             * easier to catch problems.
              */
-            size_t m_buffer_offset = std::numeric_limits<size_t>::max();
+            size_t m_buffer_offset { 0 };
 
-            /**
-             * Has this member been found in the input data.
-             */
-            bool m_available = false;
-
-            /**
-             * Marks this member as removed. It can not be used any more.
-             */
             bool m_removed = false;
 
         public:
 
             /**
-             * Create new MemberMeta.
+             * 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, size_t member_pos) noexcept :
+            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) {
             }
 
-            /**
-             * Create new MemberMeta. This constructor is used to create
-             * dummy MemberMeta objects that can be compared to the
-             * MemberMetas in a vector using the equal_range algorithm.
-             */
-            explicit MemberMeta(osmium::object_id_type member_id) noexcept :
-                m_member_id(member_id),
-                m_relation_pos(0),
-                m_member_pos(0) {
-            }
-
             osmium::object_id_type member_id() const noexcept {
                 return m_member_id;
             }
@@ -128,11 +107,6 @@ namespace osmium {
 
             void set_buffer_offset(size_t offset) noexcept {
                 m_buffer_offset = offset;
-                m_available = true;
-            }
-
-            bool is_available() const noexcept {
-                return m_available;
             }
 
             bool removed() const noexcept {
@@ -150,8 +124,8 @@ namespace osmium {
          * Used to sort a vector of MemberMeta objects and to later find
          * them using binary search.
          */
-        inline bool operator<(const MemberMeta& lhs, const MemberMeta& rhs) noexcept {
-            return lhs.member_id() < rhs.member_id();
+        inline bool operator<(const MemberMeta& a, const MemberMeta& b) noexcept {
+            return a.member_id() < b.member_id();
         }
 
         template <typename TChar, typename TTraits>
diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp
index 5ae9108..6f4f7b1 100644
--- a/include/osmium/thread/queue.hpp
+++ b/include/osmium/thread/queue.hpp
@@ -51,7 +51,7 @@ namespace osmium {
 
     namespace thread {
 
-        static const std::chrono::milliseconds max_wait{10};
+        static const std::chrono::milliseconds full_queue_sleep_duration { 10 }; // XXX
 
         /**
          *  A thread-safe queue.
@@ -70,12 +70,9 @@ namespace osmium {
 
             std::queue<T> m_queue;
 
-            /// Used to signal consumers when data is available in the queue.
+            /// Used to signal readers when data is available in the queue.
             std::condition_variable m_data_available;
 
-            /// Used to signal producers when queue is not full.
-            std::condition_variable m_space_available;
-
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
             /// The largest size the queue has been so far.
             size_t m_largest_size;
@@ -112,8 +109,7 @@ namespace osmium {
                 m_name(name),
                 m_mutex(),
                 m_queue(),
-                m_data_available(),
-                m_space_available()
+                m_data_available()
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 ,
                 m_largest_size(0),
@@ -127,20 +123,13 @@ namespace osmium {
 
             ~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 in " << m_push_counter
-                          << " push() calls and was empty " << m_empty_counter
-                          << " times in " << m_pop_counter
-                          << " pop() calls\n";
+                std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls and was empty " << m_empty_counter << " times in " << m_pop_counter << " pop() calls\n";
 #endif
             }
 
             /**
-             * Push an element onto the queue. If the queue has a max size,
-             * this call will block if the queue is full.
+             * 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) {
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
@@ -148,16 +137,13 @@ namespace osmium {
 #endif
                 if (m_max_size) {
                     while (size() >= m_max_size) {
-                        std::unique_lock<std::mutex> lock{m_mutex};
-                        m_space_available.wait_for(lock, max_wait, [this] {
-                            return m_queue.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};
+                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()) {
@@ -171,7 +157,7 @@ namespace osmium {
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 ++m_pop_counter;
 #endif
-                std::unique_lock<std::mutex> lock{m_mutex};
+                std::unique_lock<std::mutex> lock(m_mutex);
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 if (m_queue.empty()) {
                     ++m_empty_counter;
@@ -183,10 +169,6 @@ namespace osmium {
                 if (!m_queue.empty()) {
                     value = std::move(m_queue.front());
                     m_queue.pop();
-                    lock.unlock();
-                    if (m_max_size) {
-                        m_space_available.notify_one();
-                    }
                 }
             }
 
@@ -194,30 +176,25 @@ namespace osmium {
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 ++m_pop_counter;
 #endif
-                {
-                    std::lock_guard<std::mutex> lock{m_mutex};
-                    if (m_queue.empty()) {
+                std::lock_guard<std::mutex> lock(m_mutex);
+                if (m_queue.empty()) {
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
-                        ++m_empty_counter;
+                    ++m_empty_counter;
 #endif
-                        return false;
-                    }
-                    value = std::move(m_queue.front());
-                    m_queue.pop();
-                }
-                if (m_max_size) {
-                    m_space_available.notify_one();
+                    return false;
                 }
+                value = std::move(m_queue.front());
+                m_queue.pop();
                 return true;
             }
 
             bool empty() const {
-                std::lock_guard<std::mutex> lock{m_mutex};
+                std::lock_guard<std::mutex> lock(m_mutex);
                 return m_queue.empty();
             }
 
             size_t size() const {
-                std::lock_guard<std::mutex> lock{m_mutex};
+                std::lock_guard<std::mutex> lock(m_mutex);
                 return m_queue.size();
             }
 
diff --git a/include/osmium/util/progress_bar.hpp b/include/osmium/util/progress_bar.hpp
index 0e528fc..814aa2c 100644
--- a/include/osmium/util/progress_bar.hpp
+++ b/include/osmium/util/progress_bar.hpp
@@ -172,18 +172,6 @@ namespace osmium {
             }
         }
 
-        /**
-         * Removes the progress bar. Call this before doing any other output.
-         * The next time update() is called, the progress bar will be visible
-         * again.
-         */
-        void remove() {
-            if (m_enable) {
-                std::cerr << spc() << "         \r";
-                m_prev_percent = 100 + 1;
-            }
-        }
-
     }; // class ProgressBar
 
 } // namespace osmium
diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp
index 09c5762..6f3b0a3 100644
--- a/include/osmium/version.hpp
+++ b/include/osmium/version.hpp
@@ -34,9 +34,9 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #define LIBOSMIUM_VERSION_MAJOR 2
-#define LIBOSMIUM_VERSION_MINOR 10
+#define LIBOSMIUM_VERSION_MINOR 9
 #define LIBOSMIUM_VERSION_PATCH 0
 
-#define LIBOSMIUM_VERSION_STRING "2.10.0"
+#define LIBOSMIUM_VERSION_STRING "2.9.0"
 
 #endif // OSMIUM_VERSION_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 051574e..6230cde 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -112,6 +112,12 @@ if(NOT Threads_FOUND)
     set(Threads_FOUND FALSE)
 endif()
 
+if(GEOS_FOUND AND PROJ_FOUND)
+    set(GEOS_AND_PROJ_FOUND TRUE)
+else()
+    set(GEOS_AND_PROJ_FOUND FALSE)
+endif()
+
 
 #-----------------------------------------------------------------------------
 #
@@ -121,45 +127,45 @@ endif()
 add_unit_test(area test_area_id)
 add_unit_test(area test_node_ref_segment)
 
-add_unit_test(osm test_area)
-add_unit_test(osm test_box)
-add_unit_test(osm test_changeset)
-add_unit_test(osm test_crc)
-add_unit_test(osm test_entity_bits)
-add_unit_test(osm test_location)
-add_unit_test(osm test_node)
-add_unit_test(osm test_node_ref)
-add_unit_test(osm test_object_comparisons)
-add_unit_test(osm test_relation)
-add_unit_test(osm test_timestamp)
-add_unit_test(osm test_types_from_string)
-add_unit_test(osm test_way)
-
-add_unit_test(memory test_buffer_basics)
-add_unit_test(memory test_buffer_node)
-add_unit_test(memory test_buffer_purge)
+add_unit_test(basic test_area)
+add_unit_test(basic test_box)
+add_unit_test(basic test_changeset)
+add_unit_test(basic test_crc)
+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_types_from_string)
+add_unit_test(basic test_way)
+
+add_unit_test(buffer test_buffer_basics)
+add_unit_test(buffer test_buffer_node)
+add_unit_test(buffer test_buffer_purge)
 
 add_unit_test(builder test_attr)
-add_unit_test(builder test_object_builder)
+
+add_unit_test(geom test_factory_with_projection
+    ENABLE_IF ${GEOS_AND_PROJ_FOUND}
+    LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY})
 
 add_unit_test(geom test_crs ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
 add_unit_test(geom test_exception)
-add_unit_test(geom test_factory_with_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
 add_unit_test(geom test_geojson)
 add_unit_test(geom test_geos ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY})
+add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY})
 add_unit_test(geom test_mercator)
 add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY})
-add_unit_test(geom test_ogr_wkb ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY})
 add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
-add_unit_test(geom test_tile)
+add_unit_test(geom test_tile ENABLE_IF ${GEOS_FOUND})
 add_unit_test(geom test_wkb)
 add_unit_test(geom test_wkt)
 
-add_unit_test(index test_id_set)
 add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND})
 add_unit_test(index test_file_based_index)
 
-add_unit_test(io test_compression_factory)
 add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES})
 add_unit_test(io test_file_formats)
 add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES};${OSMIUM_PBF_LIBRARIES}")
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
index eb984f8..0d2739a 100644
--- a/test/data-tests/testdata-xml.cpp
+++ b/test/data-tests/testdata-xml.cpp
@@ -83,7 +83,7 @@ header_buffer_type parse_xml(std::string input) {
     osmium::io::detail::add_to_queue(input_queue, std::move(input));
     osmium::io::detail::add_to_queue(input_queue, std::string{});
 
-    osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::io::detail::reader_options{}};
+    osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::osm_entity_bits::all};
     parser.parse();
 
     header_buffer_type result;
diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt
deleted file mode 100644
index 90bf76e..0000000
--- a/test/examples/CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-#-----------------------------------------------------------------------------
-#
-#  CMake Config
-#
-#  Libosmium example tests
-#
-#-----------------------------------------------------------------------------
-
-message(STATUS "Configuring example tests")
-
-file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/t/*)
-
-foreach(_dir ${_dirs})
-    message(STATUS "  adding test: ${_dir}")
-    add_subdirectory("${_dir}")
-endforeach()
-
-message(STATUS "Configuring example tests - done")
-
-
-#-----------------------------------------------------------------------------
diff --git a/test/examples/t/pub_names/CMakeLists.txt b/test/examples/t/pub_names/CMakeLists.txt
deleted file mode 100644
index 9a68ae8..0000000
--- a/test/examples/t/pub_names/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-add_test(NAME examples_pub_names
-         COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pubs.osm)
-
-set_tests_properties(examples_pub_names PROPERTIES
-                     PASS_REGULAR_EXPRESSION "^Im Holze\n$")
-
diff --git a/test/examples/t/pub_names/pubs.osm b/test/examples/t/pub_names/pubs.osm
deleted file mode 100644
index ee54226..0000000
--- a/test/examples/t/pub_names/pubs.osm
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6">
-  <node id="167199652" version="3" timestamp="2010-12-27T13:15:02Z" uid="57645" user="KartoGrapHiti" changeset="6777507" lat="53.0526516" lon="8.8919477">
-    <tag k="amenity" v="pub"/>
-    <tag k="name" v="Im Holze"/>
-  </node>
-</osm>
diff --git a/test/examples/t/road_length/CMakeLists.txt b/test/examples/t/road_length/CMakeLists.txt
deleted file mode 100644
index 6323f07..0000000
--- a/test/examples/t/road_length/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-add_test(NAME examples_road_length
-         COMMAND osmium_road_length ${CMAKE_CURRENT_SOURCE_DIR}/road.osm)
-
-set_tests_properties(examples_road_length PROPERTIES
-                     PASS_REGULAR_EXPRESSION "^Length: 0\\.405.*km\n$"
-)
-
diff --git a/test/examples/t/road_length/road.osm b/test/examples/t/road_length/road.osm
deleted file mode 100644
index 934ef46..0000000
--- a/test/examples/t/road_length/road.osm
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<osm version="0.6" generator="CGImap 0.3.3 (31041 thorn-01.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
- <node id="5599384" visible="true" version="5" changeset="829716" timestamp="2009-03-18T17:16:26Z" user="burts" uid="97529" lat="51.0271601" lon="13.7252197"/>
- <node id="250384970" visible="true" version="3" changeset="855544" timestamp="2009-01-30T21:58:37Z" user="saftl" uid="7989" lat="51.0288568" lon="13.7248159">
-  <tag k="created_by" v="JOSM"/>
- </node>
- <node id="250996316" visible="true" version="5" changeset="838711" timestamp="2009-03-21T13:10:39Z" user="burts" uid="97529" lat="51.0274683" lon="13.7251464"/>
- <node id="250996321" visible="true" version="3" changeset="855544" timestamp="2009-01-30T21:59:14Z" user="saftl" uid="7989" lat="51.0284283" lon="13.7249179">
-  <tag k="created_by" v="JOSM"/>
- </node>
- <node id="252587568" visible="true" version="3" changeset="855544" timestamp="2009-01-30T21:58:45Z" user="saftl" uid="7989" lat="51.0275477" lon="13.7251275">
-  <tag k="created_by" v="JOSM"/>
- </node>
- <node id="5599381" visible="true" version="6" changeset="15768585" timestamp="2013-04-18T01:20:26Z" user="Wolle DD" uid="1161559" lat="51.0307642" lon="13.7243263"/>
- <node id="1122039499" visible="true" version="2" changeset="26063898" timestamp="2014-10-14T04:09:11Z" user="Seandebasti" uid="550560" lat="51.0285248" lon="13.7248970"/>
- <node id="1122039521" visible="true" version="2" changeset="12753110" timestamp="2012-08-16T17:03:47Z" user="TEAM_CN_TUD" uid="716608" lat="51.0277456" lon="13.7250806"/>
- <node id="5599382" visible="true" version="6" changeset="8191054" timestamp="2011-05-19T16:19:51Z" user="stw1701" uid="102899" lat="51.0297991" lon="13.7245892"/>
- <node id="1299329303" visible="true" version="1" changeset="8242335" timestamp="2011-05-25T07:46:01Z" user="bigbug21" uid="15748" lat="51.0290875" lon="13.7247628"/>
- <node id="1868844753" visible="true" version="2" changeset="13260925" timestamp="2012-09-26T15:26:31Z" user="TEAM_CN_TUD" uid="716608" lat="51.0289617" lon="13.7247917"/>
- <node id="1868844765" visible="true" version="2" changeset="13260925" timestamp="2012-09-26T15:26:31Z" user="TEAM_CN_TUD" uid="716608" lat="51.0292872" lon="13.7247140"/>
- <node id="1868844782" visible="true" version="2" changeset="13260925" timestamp="2012-09-26T15:26:31Z" user="TEAM_CN_TUD" uid="716608" lat="51.0295717" lon="13.7246429"/>
- <node id="1922091528" visible="true" version="2" changeset="13260925" timestamp="2012-09-26T15:26:31Z" user="TEAM_CN_TUD" uid="716608" lat="51.0281700" lon="13.7249813"/>
- <node id="1953249124" visible="true" version="1" changeset="13418280" timestamp="2012-10-08T20:18:35Z" user="TEAM_CN_TUD" uid="716608" lat="51.0292437" lon="13.7247246"/>
- <node id="2015120752" visible="true" version="1" changeset="13883494" timestamp="2012-11-15T14:22:31Z" user="TEAM_CN_TUD" uid="716608" lat="51.0293536" lon="13.7246974"/>
- <node id="2056871900" visible="true" version="1" changeset="14207491" timestamp="2012-12-09T00:03:11Z" user="bigbug21" uid="15748" lat="51.0305821" lon="13.7243895"/>
- <node id="2458246647" visible="true" version="1" changeset="17836591" timestamp="2013-09-14T17:05:06Z" user="bigbug21" uid="15748" lat="51.0295979" lon="13.7246367"/>
- <node id="250384969" visible="true" version="4" changeset="17857555" timestamp="2013-09-15T20:17:55Z" user="4b696d" uid="1420318" lat="51.0282021" lon="13.7249734"/>
- <node id="3128723784" visible="true" version="1" changeset="26063898" timestamp="2014-10-14T04:09:07Z" user="Seandebasti" uid="550560" lat="51.0281018" lon="13.7249973"/>
- <way id="4428564" visible="true" version="21" changeset="26063898" timestamp="2014-10-14T04:09:13Z" user="Seandebasti" uid="550560">
-  <nd ref="5599381"/>
-  <nd ref="2056871900"/>
-  <nd ref="5599382"/>
-  <nd ref="2458246647"/>
-  <nd ref="1868844782"/>
-  <nd ref="2015120752"/>
-  <nd ref="1868844765"/>
-  <nd ref="1953249124"/>
-  <nd ref="1299329303"/>
-  <nd ref="1868844753"/>
-  <nd ref="250384970"/>
-  <nd ref="1122039499"/>
-  <nd ref="250996321"/>
-  <nd ref="250384969"/>
-  <nd ref="1922091528"/>
-  <nd ref="3128723784"/>
-  <nd ref="1122039521"/>
-  <nd ref="252587568"/>
-  <nd ref="250996316"/>
-  <nd ref="5599384"/>
-  <tag k="highway" v="residential"/>
-  <tag k="lit" v="yes"/>
-  <tag k="maxspeed" v="30"/>
-  <tag k="name" v="Helmholtzstraße"/>
-  <tag k="postal_code" v="01069"/>
-  <tag k="sidewalk" v="both"/>
-  <tag k="smoothness" v="good"/>
-  <tag k="surface" v="asphalt"/>
- </way>
-</osm>
\ No newline at end of file
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index 2e6fe8d..879fc5b 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.5.8
- *  Generated: 2016-10-26 12:07:30.938259
+ *  Catch v1.5.6
+ *  Generated: 2016-06-09 19:20:41.460328
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -3223,11 +3223,10 @@ namespace Catch {
 
             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 ) {
+                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;
+                    return true;
             }
         };
 
@@ -4720,11 +4719,8 @@ namespace Catch {
         std::string line;
         while( std::getline( f, line ) ) {
             line = trim(line);
-            if( !line.empty() && !startsWith( line, "#" ) ) {
-                if( !startsWith( line, "\"" ) )
-                    line = "\"" + line + "\"";
-                addTestOrTags( config, line + "," );
-            }
+            if( !line.empty() && !startsWith( line, "#" ) )
+                addTestOrTags( config, "\"" + line + "\"," );
         }
     }
 
@@ -5372,10 +5368,7 @@ namespace Catch {
                 ++it ) {
             matchedTests++;
             TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
-            if( startsWith( testCaseInfo.name, "#" ) )
-               Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl;
-            else
-               Catch::cout() << testCaseInfo.name << std::endl;
+            Catch::cout() << testCaseInfo.name << std::endl;
         }
         return matchedTests;
     }
@@ -6461,7 +6454,7 @@ namespace Catch {
 namespace Catch {
 
     struct RandomNumberGenerator {
-        typedef std::ptrdiff_t result_type;
+        typedef int result_type;
 
         result_type operator()( result_type n ) const { return std::rand() % n; }
 
@@ -7578,7 +7571,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 5, 8, "", 0 );
+    Version libraryVersion( 1, 5, 6, "", 0 );
 
 }
 
@@ -7809,11 +7802,8 @@ namespace Catch {
     bool contains( std::string const& s, std::string const& infix ) {
         return s.find( infix ) != std::string::npos;
     }
-    char toLowerCh(char c) {
-        return static_cast<char>( ::tolower( c ) );
-    }
     void toLowerInPlace( std::string& s ) {
-        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+        std::transform( s.begin(), s.end(), s.begin(), ::tolower );
     }
     std::string toLower( std::string const& s ) {
         std::string lc = s;
@@ -8961,10 +8951,9 @@ namespace Catch {
                         break;
 
                     default:
-                        // Escape control chars - based on contribution by @espenalb in PR #465 and
-                        // by @mrpi PR #588
+                        // Escape control chars - based on contribution by @espenalb in PR #465
                         if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
-                            os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>( c ) << ';';
+                            os << "&#x" << std::uppercase << std::hex << static_cast<int>( c );
                         else
                             os << c;
                 }
@@ -9019,20 +9008,13 @@ namespace Catch {
         :   m_tagIsOpen( false ),
             m_needsNewline( false ),
             m_os( &Catch::cout() )
-        {
-            // We encode control characters, which requires
-            // XML 1.1
-            // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
-            *m_os << "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n";
-        }
+        {}
 
         XmlWriter( std::ostream& os )
         :   m_tagIsOpen( false ),
             m_needsNewline( false ),
             m_os( &os )
-        {
-            *m_os << "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n";
-        }
+        {}
 
         ~XmlWriter() {
             while( !m_tags.empty() )
@@ -9199,7 +9181,7 @@ namespace Catch {
 
         virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseStarting(testInfo);
-            m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name );
+            m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
 
             if ( m_config->showDurations() == ShowDurations::Always )
                 m_testCaseTimer.start();
@@ -9261,7 +9243,7 @@ namespace Catch {
                         .writeText( assertionResult.getMessage() );
                     break;
                 case ResultWas::FatalErrorCondition:
-                    m_xml.scopedElement( "FatalErrorCondition" )
+                    m_xml.scopedElement( "Fatal Error Condition" )
                         .writeAttribute( "filename", assertionResult.getSourceInfo().file )
                         .writeAttribute( "line", assertionResult.getSourceInfo().line )
                         .writeText( assertionResult.getMessage() );
diff --git a/test/t/osm/test_area.cpp b/test/t/basic/test_area.cpp
similarity index 100%
rename from test/t/osm/test_area.cpp
rename to test/t/basic/test_area.cpp
diff --git a/test/t/osm/test_box.cpp b/test/t/basic/test_box.cpp
similarity index 100%
rename from test/t/osm/test_box.cpp
rename to test/t/basic/test_box.cpp
diff --git a/test/t/osm/test_changeset.cpp b/test/t/basic/test_changeset.cpp
similarity index 73%
rename from test/t/osm/test_changeset.cpp
rename to test/t/basic/test_changeset.cpp
index 9be8fbb..36c4778 100644
--- a/test/t/osm/test_changeset.cpp
+++ b/test/t/basic/test_changeset.cpp
@@ -9,7 +9,7 @@
 using namespace osmium::builder::attr;
 
 TEST_CASE("Build changeset") {
-    osmium::memory::Buffer buffer{10 * 1000};
+    osmium::memory::Buffer buffer(10 * 1000);
 
     osmium::builder::add_changeset(buffer,
         _cid(42),
@@ -88,57 +88,58 @@ TEST_CASE("Build changeset") {
 }
 
 TEST_CASE("Create changeset without helper") {
-    osmium::memory::Buffer buffer{10 * 1000};
+    osmium::memory::Buffer buffer(10 * 1000);
+    osmium::builder::ChangesetBuilder builder(buffer);
+
+    osmium::Changeset& cs1 = builder.object();
+    cs1.set_id(42)
+       .set_created_at(100)
+       .set_closed_at(200)
+       .set_num_changes(7)
+       .set_num_comments(2)
+       .set_uid(9);
+
+    builder.add_user("user");
     {
-        osmium::builder::ChangesetBuilder builder{buffer};
-
-        builder.set_id(42)
-            .set_created_at(100)
-            .set_closed_at(200)
-            .set_num_changes(7)
-            .set_num_comments(2)
-            .set_uid(9)
-            .set_user("user");
-
-        {
-            osmium::builder::TagListBuilder tl_builder{builder};
-            tl_builder.add_tag("key1", "val1");
-            tl_builder.add_tag("key2", "val2");
-        }
-
-        osmium::builder::ChangesetDiscussionBuilder disc_builder{builder};
+        osmium::builder::TagListBuilder tl_builder(buffer, &builder);
+        tl_builder.add_tag("key1", "val1");
+        tl_builder.add_tag("key2", "val2");
+    }
+
+    {
+        osmium::builder::ChangesetDiscussionBuilder disc_builder(buffer, &builder);
         disc_builder.add_comment(osmium::Timestamp(300), 10, "user2");
         disc_builder.add_comment_text("foo");
         disc_builder.add_comment(osmium::Timestamp(400), 9, "user");
         disc_builder.add_comment_text("bar");
     }
 
-    const auto& cs = buffer.get<osmium::Changeset>(buffer.commit());
+    buffer.commit();
 
-    REQUIRE(42 == cs.id());
-    REQUIRE(9 == cs.uid());
-    REQUIRE(7 == cs.num_changes());
-    REQUIRE(2 == cs.num_comments());
-    REQUIRE(true == cs.closed());
-    REQUIRE(osmium::Timestamp(100) == cs.created_at());
-    REQUIRE(osmium::Timestamp(200) == cs.closed_at());
-    REQUIRE(2 == cs.tags().size());
-    REQUIRE(std::string("user") == cs.user());
+    REQUIRE(42 == cs1.id());
+    REQUIRE(9 == cs1.uid());
+    REQUIRE(7 == cs1.num_changes());
+    REQUIRE(2 == cs1.num_comments());
+    REQUIRE(true == cs1.closed());
+    REQUIRE(osmium::Timestamp(100) == cs1.created_at());
+    REQUIRE(osmium::Timestamp(200) == cs1.closed_at());
+    REQUIRE(2 == cs1.tags().size());
+    REQUIRE(std::string("user") == cs1.user());
 
-    auto cit = cs.discussion().begin();
+    auto cit = cs1.discussion().begin();
 
-    REQUIRE(cit != cs.discussion().end());
+    REQUIRE(cit != cs1.discussion().end());
     REQUIRE(cit->date() == osmium::Timestamp(300));
     REQUIRE(cit->uid() == 10);
     REQUIRE(std::string("user2") == cit->user());
     REQUIRE(std::string("foo") == cit->text());
 
-    REQUIRE(++cit != cs.discussion().end());
+    REQUIRE(++cit != cs1.discussion().end());
     REQUIRE(cit->date() == osmium::Timestamp(400));
     REQUIRE(cit->uid() == 9);
     REQUIRE(std::string("user") == cit->user());
     REQUIRE(std::string("bar") == cit->text());
 
-    REQUIRE(++cit == cs.discussion().end());
+    REQUIRE(++cit == cs1.discussion().end());
 }
 
diff --git a/test/t/osm/test_crc.cpp b/test/t/basic/test_crc.cpp
similarity index 100%
rename from test/t/osm/test_crc.cpp
rename to test/t/basic/test_crc.cpp
diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp
new file mode 100644
index 0000000..13de94b
--- /dev/null
+++ b/test/t/basic/test_entity_bits.cpp
@@ -0,0 +1,32 @@
+#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::nothing   == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined));
+        REQUIRE(osmium::osm_entity_bits::node      == osmium::osm_entity_bits::from_item_type(osmium::item_type::node));
+        REQUIRE(osmium::osm_entity_bits::way       == osmium::osm_entity_bits::from_item_type(osmium::item_type::way));
+        REQUIRE(osmium::osm_entity_bits::relation  == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation));
+        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/osm/test_location.cpp b/test/t/basic/test_location.cpp
similarity index 78%
rename from test/t/osm/test_location.cpp
rename to test/t/basic/test_location.cpp
index 7abf779..dc5b378 100644
--- a/test/t/osm/test_location.cpp
+++ b/test/t/basic/test_location.cpp
@@ -166,44 +166,41 @@ TEST_CASE("Location hash") {
     }
 }
 
-void C(const char* s, long v, const char* r = "") {
-    std::string strm{"-"};
-    strm += s;
-    REQUIRE(std::atof(strm.c_str() + 1) == Approx( v / 10000000.0));
-    REQUIRE(std::atof(strm.c_str()    ) == Approx(-v / 10000000.0));
-    const char* x = strm.c_str() + 1;
-    const char** data = &x;
-    REQUIRE(osmium::detail::string_to_location_coordinate(data) == v);
-    REQUIRE(std::string{*data} == r);
-    x = strm.c_str();
-    data = &x;
-    REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v);
-    REQUIRE(std::string{*data} == r);
-}
-
-void F(const char* s) {
-    std::string strm{"-"};
-    strm += s;
-    const char* x = strm.c_str();
-    const char** data = &x;
-    REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location);
-    ++x;
-    data = &x;
-    REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location);
-}
+#define CR(s, v, r) { \
+                const char* strm = "-" s; \
+                const char* strp = strm + 1; \
+                REQUIRE(std::atof(strp) == Approx( v / 10000000.0)); \
+                REQUIRE(std::atof(strm) == Approx(-v / 10000000.0)); \
+                const char** data = &strp; \
+                REQUIRE(osmium::detail::string_to_location_coordinate(data) == v); \
+                REQUIRE(std::string{*data} == r); \
+                data = &strm; \
+                REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v); \
+                REQUIRE(std::string{*data} == r); \
+                }
+
+#define C(s, v) CR(s, v, "")
+
+#define F(s) { \
+             const char* strm = "-" s; \
+             const char* strp = strm + 1; \
+             const char** data = &strp; \
+             REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \
+             data = &strm; \
+             REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \
+             }
 
 TEST_CASE("Parsing coordinates from strings") {
     F("x");
     F(".");
-    F(".e2");
     F("--");
     F("");
     F(" ");
     F(" 123");
 
-    C("123 ", 1230000000, " ");
-    C("123x", 1230000000, "x");
-    C("1.2x",   12000000, "x");
+    CR("123 ", 1230000000, " ");
+    CR("123x", 1230000000, "x");
+    CR("1.2x",   12000000, "x");
 
     C("0",              0);
 
@@ -226,19 +223,14 @@ TEST_CASE("Parsing coordinates from strings") {
     F("1234");
     F("1234.");
     F("12345678901234567890");
-    F("1.1234568111111111111111111111111111111");
-    F("112.34568111111111111111111111111111111");
 
     C("0.",             0);
-    C(".0",             0);
     C("0.0",            0);
     C("1.",      10000000);
     C("1.0",     10000000);
     C("1.2",     12000000);
     C("0.1",      1000000);
-    C(".1",       1000000);
     C("0.01",      100000);
-    C(".01",       100000);
     C("0.001",      10000);
     C("0.0001",      1000);
     C("0.00001",      100);
@@ -259,24 +251,6 @@ TEST_CASE("Parsing coordinates from strings") {
     C("179.99999999", 1800000000);
     C("200.123",      2001230000);
 
-    C("8.109E-4" , 8109);
-    C("8.1090E-4" , 8109);
-    C("8.10909E-4" , 8109);
-    C("8.109095E-4" , 8109);
-    C("8.1090959E-4" , 8109);
-    C("8.10909598E-4" , 8109);
-    C("8.109095988E-4" , 8109);
-    C("8.1090959887E-4" , 8109);
-    C("8.10909598870E-4" , 8109);
-    C("8.109095988709E-4" , 8109);
-    C("8.1090959887098E-4" , 8109);
-    C("8.10909598870983E-4" , 8109);
-    C("8.109095988709837E-4" , 8109);
-    C("81.09095988709837E-4" , 81091);
-    C("810.9095988709837E-4" , 810910);
-    C(".8109095988709837E-4" , 811);
-    C(".08109095988709837E-4" , 81);
-
     C("1e2",   1000000000);
     C("1e1",    100000000);
     C("1e0",     10000000);
@@ -289,10 +263,8 @@ TEST_CASE("Parsing coordinates from strings") {
     C("1e-7",           1);
 
     C("1.0e2",   1000000000);
-    C("1.e2",    1000000000);
     C("1.1e1",    110000000);
     C("0.1e1",     10000000);
-    C(".1e1",      10000000);
     C("1.2e0",     12000000);
     C("1.9e-1",     1900000);
     C("2.0e-2",      200000);
@@ -319,35 +291,32 @@ TEST_CASE("Parsing coordinates from strings") {
     F("5.0e2");
     F("3e2");
     F("1e");
-    F("1e-");
-    F("1e1234567");
     F("0.5e");
     F("1e10");
 
-    C("1e2 ",   1000000000, " ");
-    C("1.1e2 ", 1100000000, " ");
-    C("1.1e2x", 1100000000, "x");
-    C("1.1e2:", 1100000000, ":");
+    CR("1e2 ",   1000000000, " ");
+    CR("1.1e2 ", 1100000000, " ");
+    CR("1.1e2x", 1100000000, "x");
+    CR("1.1e2:", 1100000000, ":");
 }
 
-TEST_CASE("Writing zero coordinate into string") {
-    std::string buffer;
-    osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0);
-    REQUIRE(buffer == "0");
-}
+#undef C
+#undef CR
+#undef F
+
+#define CW(v, s) buffer.clear(); \
+                 osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); \
+                 CHECK(buffer == s); \
+                 buffer.clear(); \
+                 osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); \
+                 CHECK(buffer == "-" s);
 
-void CW(long v, const char* s) {
+TEST_CASE("Writing coordinates into string") {
     std::string buffer;
 
-    osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v);
-    REQUIRE(buffer == s);
-    buffer.clear();
-    osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v);
-    REQUIRE(buffer[0] == '-');
-    REQUIRE_FALSE(std::strcmp(buffer.c_str() + 1, s));
-}
+    osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0);
+    CHECK(buffer == "0");
 
-TEST_CASE("Writing coordinate into string") {
     CW(  10000000, "1");
     CW(  90000000, "9");
     CW( 100000000, "10");
@@ -369,6 +338,8 @@ TEST_CASE("Writing coordinate into string") {
     CW(1799999999, "179.9999999");
 }
 
+#undef CW
+
 TEST_CASE("set lon/lat from string") {
     osmium::Location loc;
     loc.set_lon("1.2");
diff --git a/test/t/osm/test_node.cpp b/test/t/basic/test_node.cpp
similarity index 100%
rename from test/t/osm/test_node.cpp
rename to test/t/basic/test_node.cpp
diff --git a/test/t/osm/test_node_ref.cpp b/test/t/basic/test_node_ref.cpp
similarity index 100%
rename from test/t/osm/test_node_ref.cpp
rename to test/t/basic/test_node_ref.cpp
diff --git a/test/t/osm/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp
similarity index 100%
rename from test/t/osm/test_object_comparisons.cpp
rename to test/t/basic/test_object_comparisons.cpp
diff --git a/test/t/osm/test_relation.cpp b/test/t/basic/test_relation.cpp
similarity index 100%
rename from test/t/osm/test_relation.cpp
rename to test/t/basic/test_relation.cpp
diff --git a/test/t/osm/test_timestamp.cpp b/test/t/basic/test_timestamp.cpp
similarity index 100%
rename from test/t/osm/test_timestamp.cpp
rename to test/t/basic/test_timestamp.cpp
diff --git a/test/t/osm/test_types_from_string.cpp b/test/t/basic/test_types_from_string.cpp
similarity index 100%
rename from test/t/osm/test_types_from_string.cpp
rename to test/t/basic/test_types_from_string.cpp
diff --git a/test/t/osm/test_way.cpp b/test/t/basic/test_way.cpp
similarity index 98%
rename from test/t/osm/test_way.cpp
rename to test/t/basic/test_way.cpp
index 21258a4..005ef30 100644
--- a/test/t/osm/test_way.cpp
+++ b/test/t/basic/test_way.cpp
@@ -68,7 +68,7 @@ TEST_CASE("build way with helpers") {
 
     {
         osmium::builder::WayBuilder builder(buffer);
-        builder.set_user("username");
+        builder.add_user("username");
         builder.add_tags({
             {"amenity", "restaurant"},
             {"name", "Zum goldenen Schwanen"}
diff --git a/test/t/memory/test_buffer_basics.cpp b/test/t/buffer/test_buffer_basics.cpp
similarity index 100%
rename from test/t/memory/test_buffer_basics.cpp
rename to test/t/buffer/test_buffer_basics.cpp
diff --git a/test/t/memory/test_buffer_node.cpp b/test/t/buffer/test_buffer_node.cpp
similarity index 50%
rename from test/t/memory/test_buffer_node.cpp
rename to test/t/buffer/test_buffer_node.cpp
index 040cbb8..ba2431b 100644
--- a/test/t/memory/test_buffer_node.cpp
+++ b/test/t/buffer/test_buffer_node.cpp
@@ -3,7 +3,7 @@
 #include <osmium/builder/osm_object_builder.hpp>
 #include <osmium/osm/node.hpp>
 
-void check_node_1(const osmium::Node& node) {
+void check_node_1(osmium::Node& node) {
     REQUIRE(1 == node.id());
     REQUIRE(3 == node.version());
     REQUIRE(true == node.visible());
@@ -11,9 +11,9 @@ void check_node_1(const osmium::Node& node) {
     REQUIRE(21 == node.uid());
     REQUIRE(123 == uint32_t(node.timestamp()));
     REQUIRE(osmium::Location(3.5, 4.7) == node.location());
-    REQUIRE(std::string{"testuser"} == node.user());
+    REQUIRE(std::string("testuser") == node.user());
 
-    for (const osmium::memory::Item& item : node) {
+    for (osmium::memory::Item& item : node) {
         REQUIRE(osmium::item_type::tag_list == item.type());
     }
 
@@ -22,7 +22,7 @@ void check_node_1(const osmium::Node& node) {
     REQUIRE(0 == std::distance(node.tags().begin(), node.tags().end()));
 }
 
-void check_node_2(const osmium::Node& node) {
+void check_node_2(osmium::Node& node) {
     REQUIRE(2 == node.id());
     REQUIRE(3 == node.version());
     REQUIRE(true == node.visible());
@@ -30,9 +30,9 @@ void check_node_2(const osmium::Node& node) {
     REQUIRE(21 == node.uid());
     REQUIRE(123 == uint32_t(node.timestamp()));
     REQUIRE(osmium::Location(3.5, 4.7) == node.location());
-    REQUIRE(std::string{"testuser"} == node.user());
+    REQUIRE(std::string("testuser") == node.user());
 
-    for (const osmium::memory::Item& item : node) {
+    for (osmium::memory::Item& item : node) {
         REQUIRE(osmium::item_type::tag_list == item.type());
     }
 
@@ -61,52 +61,60 @@ TEST_CASE("Node in Buffer") {
     constexpr size_t buffer_size = 10000;
     unsigned char data[buffer_size];
 
-    osmium::memory::Buffer buffer{data, buffer_size, 0};
+    osmium::memory::Buffer buffer(data, buffer_size, 0);
 
     SECTION("Add node to buffer") {
 
         {
             // add node 1
-            osmium::builder::NodeBuilder node_builder{buffer};
-
-            node_builder.set_id(1)
-                .set_version(3)
-                .set_visible(true)
-                .set_changeset(333)
-                .set_uid(21)
-                .set_timestamp(123)
-                .set_location(osmium::Location{3.5, 4.7})
-                .set_user("testuser");
-        }
+            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));
 
-        buffer.commit();
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
 
         {
             // add node 2
-            osmium::builder::NodeBuilder node_builder{buffer};
-
-            node_builder.set_id(2)
-                .set_version(3)
-                .set_visible(true)
-                .set_changeset(333)
-                .set_uid(21)
-                .set_timestamp(123)
-                .set_location(osmium::Location{3.5, 4.7})
-                .set_user("testuser");
-
-            osmium::builder::TagListBuilder tag_builder{node_builder};
-            tag_builder.add_tag("amenity", "bank");
-            tag_builder.add_tag("name", "OSM Savings");
-        }
+            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();
+            buffer.commit();
+        }
 
         REQUIRE(2 == std::distance(buffer.begin(), buffer.end()));
         int item_no = 0;
-        for (const osmium::memory::Item& item : buffer) {
+        for (osmium::memory::Item& item : buffer) {
             REQUIRE(osmium::item_type::node == item.type());
 
-            const osmium::Node& node = static_cast<const osmium::Node&>(item);
+            osmium::Node& node = static_cast<osmium::Node&>(item);
 
             switch (item_no) {
                 case 0:
@@ -129,21 +137,24 @@ TEST_CASE("Node in Buffer") {
 
         {
             // add node 1
-            osmium::builder::NodeBuilder node_builder{buffer};
-
-            node_builder.set_id(1)
-                .set_version(3)
-                .set_visible(true)
-                .set_changeset(333)
-                .set_uid(21)
-                .set_timestamp(123)
-                .set_location(osmium::Location{3.5, 4.7})
-                .set_user("testuser");
-        }
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
 
-        buffer.commit();
+            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));
 
-        osmium::memory::Buffer buffer2{buffer_size, osmium::memory::Buffer::auto_grow::yes};
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes);
 
         buffer2.add_buffer(buffer);
         buffer2.commit();
@@ -158,21 +169,24 @@ TEST_CASE("Node in Buffer") {
 
         {
             // add node 1
-            osmium::builder::NodeBuilder node_builder{buffer};
-
-            node_builder.set_id(1)
-                .set_version(3)
-                .set_visible(true)
-                .set_changeset(333)
-                .set_uid(21)
-                .set_timestamp(123)
-                .set_location(osmium::Location{3.5, 4.7})
-                .set_user("testuser");
-        }
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
 
-        buffer.commit();
+            node.set_id(1);
+            node.set_version(3);
+            node.set_visible(true);
+            node.set_changeset(333);
+            node.set_uid(21);
+            node.set_timestamp(123);
+            node.set_location(osmium::Location(3.5, 4.7));
+
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
 
-        osmium::memory::Buffer buffer2{buffer_size, osmium::memory::Buffer::auto_grow::yes};
+        osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes);
 
         std::copy(buffer.begin(), buffer.end(), std::back_inserter(buffer2));
 
diff --git a/test/t/memory/test_buffer_purge.cpp b/test/t/buffer/test_buffer_purge.cpp
similarity index 60%
rename from test/t/memory/test_buffer_purge.cpp
rename to test/t/buffer/test_buffer_purge.cpp
index e61e08d..a72db1b 100644
--- a/test/t/memory/test_buffer_purge.cpp
+++ b/test/t/buffer/test_buffer_purge.cpp
@@ -17,9 +17,9 @@ struct CallbackClass {
 TEST_CASE("Purge data from buffer") {
 
     constexpr size_t buffer_size = 10000;
-    osmium::memory::Buffer buffer{buffer_size};
 
     SECTION("purge empty buffer") {
+        osmium::memory::Buffer buffer(buffer_size);
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 0);
 
         CallbackClass callback;
@@ -30,13 +30,15 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with one object but nothing to delete") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
         }
         buffer.commit();
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1);
-        const size_t committed = buffer.committed();
+        size_t committed = buffer.committed();
 
         CallbackClass callback;
         buffer.purge_removed(&callback);
@@ -47,10 +49,12 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with one object which gets deleted") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
-            node_builder.set_removed(true);
+            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);
@@ -64,19 +68,21 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with two objects, first gets deleted") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
-        const size_t size1 = buffer.committed();
+        size_t size1 = buffer.committed();
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
         }
         buffer.commit();
-        const size_t size2 = buffer.committed() - size1;
+        size_t size2 = buffer.committed() - size1;
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
 
         CallbackClass callback;
@@ -88,16 +94,18 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with two objects, second gets deleted") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser_longer_name");
+            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.set_user("testuser");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
 
@@ -112,22 +120,24 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with three objects, middle one gets deleted") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser_longer_name");
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser_longer_name");
         }
         buffer.commit();
 
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
 
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("sn");
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("sn");
         }
         buffer.commit();
 
@@ -141,24 +151,26 @@ TEST_CASE("Purge data from buffer") {
     }
 
     SECTION("purge buffer with three objects, all get deleted") {
+        osmium::memory::Buffer buffer(buffer_size);
+
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser_longer_name");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser_longer_name");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
 
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("testuser");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
 
         {
-            osmium::builder::NodeBuilder node_builder{buffer};
-            node_builder.set_user("sn");
-            node_builder.set_removed(true);
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("sn");
+            node_builder.object().set_removed(true);
         }
         buffer.commit();
 
diff --git a/test/t/builder/test_object_builder.cpp b/test/t/builder/test_object_builder.cpp
deleted file mode 100644
index 5b12564..0000000
--- a/test/t/builder/test_object_builder.cpp
+++ /dev/null
@@ -1,444 +0,0 @@
-
-#include "catch.hpp"
-
-#include <osmium/builder/osm_object_builder.hpp>
-#include <osmium/memory/buffer.hpp>
-#include <osmium/osm.hpp>
-
-TEST_CASE("create objects using builder") {
-    osmium::memory::Buffer buffer{1024*10};
-    std::string user;
-
-    SECTION("complete node with tags") {
-        SECTION("user length 0") {
-            user = "";
-        }
-        SECTION("user length 1") {
-            user = "1";
-        }
-        SECTION("user length 2") {
-            user = "12";
-        }
-        SECTION("user length 3") {
-            user = "123";
-        }
-        SECTION("user length 4") {
-            user = "1234";
-        }
-        SECTION("user length 5") {
-            user = "12345";
-        }
-        SECTION("user length 6") {
-            user = "123456";
-        }
-        SECTION("user length 7") {
-            user = "1234567";
-        }
-        SECTION("user length 8") {
-            user = "12345678";
-        }
-        SECTION("user length 9") {
-            user = "123456789";
-        }
-        SECTION("user length 10") {
-            user = "1234567890";
-        }
-        SECTION("user length 11") {
-            user = "12345678901";
-        }
-        SECTION("user length 12") {
-            user = "123456789012";
-        }
-        SECTION("user length 13") {
-            user = "1234567890123";
-        }
-        SECTION("user length 14") {
-            user = "12345678901234";
-        }
-        SECTION("user length 15") {
-            user = "123456789012345";
-        }
-        SECTION("user length 16") {
-            user = "1234567890123456";
-        }
-        SECTION("user length 17") {
-            user = "12345678901234567";
-        }
-        SECTION("user length 18") {
-            user = "123456789012345678";
-        }
-
-        osmium::Location loc{1.2, 3.4};
-
-        {
-            osmium::builder::NodeBuilder builder{buffer};
-
-            builder.set_id(17)
-                .set_visible(true)
-                .set_version(1)
-                .set_changeset(123)
-                .set_uid(555)
-                .set_timestamp("2015-07-01T00:00:01Z")
-                .set_location(loc)
-                .set_user(user);
-
-            builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}});
-        }
-
-        const auto& node = buffer.get<osmium::Node>(buffer.commit());
-
-        REQUIRE(node.id() == 17);
-        REQUIRE(node.version() == 1);
-        REQUIRE(node.changeset() == 123);
-        REQUIRE(node.uid() == 555);
-        REQUIRE(node.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"});
-        REQUIRE(node.location() == loc);
-
-        REQUIRE(user == node.user());
-
-        REQUIRE(node.tags().size() == 2);
-    }
-
-    SECTION("complete way with tags") {
-        SECTION("user length 0") {
-            user = "";
-        }
-        SECTION("user length 1") {
-            user = "1";
-        }
-        SECTION("user length 2") {
-            user = "12";
-        }
-        SECTION("user length 3") {
-            user = "123";
-        }
-        SECTION("user length 4") {
-            user = "1234";
-        }
-        SECTION("user length 5") {
-            user = "12345";
-        }
-        SECTION("user length 6") {
-            user = "123456";
-        }
-        SECTION("user length 7") {
-            user = "1234567";
-        }
-        SECTION("user length 8") {
-            user = "12345678";
-        }
-        SECTION("user length 9") {
-            user = "123456789";
-        }
-        SECTION("user length 10") {
-            user = "1234567890";
-        }
-        SECTION("user length 11") {
-            user = "12345678901";
-        }
-        SECTION("user length 12") {
-            user = "123456789012";
-        }
-        SECTION("user length 13") {
-            user = "1234567890123";
-        }
-        SECTION("user length 14") {
-            user = "12345678901234";
-        }
-        SECTION("user length 15") {
-            user = "123456789012345";
-        }
-        SECTION("user length 16") {
-            user = "1234567890123456";
-        }
-        SECTION("user length 17") {
-            user = "12345678901234567";
-        }
-        SECTION("user length 18") {
-            user = "123456789012345678";
-        }
-
-        {
-            osmium::builder::WayBuilder builder{buffer};
-
-            builder.set_id(17)
-                .set_visible(true)
-                .set_version(1)
-                .set_changeset(123)
-                .set_uid(555)
-                .set_timestamp("2015-07-01T00:00:01Z")
-                .set_user(user);
-
-            builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}});
-        }
-
-        const auto& way = buffer.get<osmium::Way>(buffer.commit());
-
-        REQUIRE(way.id() == 17);
-        REQUIRE(way.version() == 1);
-        REQUIRE(way.changeset() == 123);
-        REQUIRE(way.uid() == 555);
-        REQUIRE(way.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"});
-
-        REQUIRE(user == way.user());
-
-        REQUIRE(way.tags().size() == 2);
-    }
-
-    SECTION("complete relation with tags") {
-        SECTION("user length 0") {
-            user = "";
-        }
-        SECTION("user length 1") {
-            user = "1";
-        }
-        SECTION("user length 2") {
-            user = "12";
-        }
-        SECTION("user length 3") {
-            user = "123";
-        }
-        SECTION("user length 4") {
-            user = "1234";
-        }
-        SECTION("user length 5") {
-            user = "12345";
-        }
-        SECTION("user length 6") {
-            user = "123456";
-        }
-        SECTION("user length 7") {
-            user = "1234567";
-        }
-        SECTION("user length 8") {
-            user = "12345678";
-        }
-        SECTION("user length 9") {
-            user = "123456789";
-        }
-        SECTION("user length 10") {
-            user = "1234567890";
-        }
-        SECTION("user length 11") {
-            user = "12345678901";
-        }
-        SECTION("user length 12") {
-            user = "123456789012";
-        }
-        SECTION("user length 13") {
-            user = "1234567890123";
-        }
-        SECTION("user length 14") {
-            user = "12345678901234";
-        }
-        SECTION("user length 15") {
-            user = "123456789012345";
-        }
-        SECTION("user length 16") {
-            user = "1234567890123456";
-        }
-        SECTION("user length 17") {
-            user = "12345678901234567";
-        }
-        SECTION("user length 18") {
-            user = "123456789012345678";
-        }
-
-        {
-            osmium::builder::RelationBuilder builder{buffer};
-
-            builder.set_id(17)
-                .set_visible(true)
-                .set_version(1)
-                .set_changeset(123)
-                .set_uid(555)
-                .set_timestamp("2015-07-01T00:00:01Z")
-                .set_user(user);
-
-            builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}});
-        }
-
-        const auto& relation = buffer.get<osmium::Relation>(buffer.commit());
-
-        REQUIRE(relation.id() == 17);
-        REQUIRE(relation.version() == 1);
-        REQUIRE(relation.changeset() == 123);
-        REQUIRE(relation.uid() == 555);
-        REQUIRE(relation.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"});
-
-        REQUIRE(user == relation.user());
-
-        REQUIRE(relation.tags().size() == 2);
-    }
-
-    SECTION("complete changeset with tags") {
-        osmium::Location bl{-1.2, -3.4};
-        osmium::Location tr{1.2, 3.4};
-
-        SECTION("user length 0") {
-            user = "";
-        }
-        SECTION("user length 1") {
-            user = "1";
-        }
-        SECTION("user length 2") {
-            user = "12";
-        }
-        SECTION("user length 3") {
-            user = "123";
-        }
-        SECTION("user length 4") {
-            user = "1234";
-        }
-        SECTION("user length 5") {
-            user = "12345";
-        }
-        SECTION("user length 6") {
-            user = "123456";
-        }
-        SECTION("user length 7") {
-            user = "1234567";
-        }
-        SECTION("user length 8") {
-            user = "12345678";
-        }
-        SECTION("user length 9") {
-            user = "123456789";
-        }
-        SECTION("user length 10") {
-            user = "1234567890";
-        }
-        SECTION("user length 11") {
-            user = "12345678901";
-        }
-        SECTION("user length 12") {
-            user = "123456789012";
-        }
-        SECTION("user length 13") {
-            user = "1234567890123";
-        }
-        SECTION("user length 14") {
-            user = "12345678901234";
-        }
-        SECTION("user length 15") {
-            user = "123456789012345";
-        }
-        SECTION("user length 16") {
-            user = "1234567890123456";
-        }
-        SECTION("user length 17") {
-            user = "12345678901234567";
-        }
-        SECTION("user length 18") {
-            user = "123456789012345678";
-        }
-
-        {
-            osmium::builder::ChangesetBuilder builder{buffer};
-
-            builder.set_id(17)
-                .set_uid(222)
-                .set_created_at(osmium::Timestamp{"2016-07-03T01:23:45Z"})
-                .set_closed_at(osmium::Timestamp{"2016-07-03T01:23:48Z"})
-                .set_num_changes(3)
-                .set_num_comments(2)
-                .set_bounds(osmium::Box{bl, tr})
-                .set_user(user);
-        }
-
-        const auto& changeset = buffer.get<osmium::Changeset>(buffer.commit());
-
-        REQUIRE(changeset.id() == 17);
-        REQUIRE(changeset.uid() == 222);
-        REQUIRE(changeset.created_at() == osmium::Timestamp{"2016-07-03T01:23:45Z"});
-        REQUIRE(changeset.closed_at() == osmium::Timestamp{"2016-07-03T01:23:48Z"});
-        REQUIRE(changeset.num_changes() == 3);
-        REQUIRE(changeset.num_comments() == 2);
-
-        const auto& box = changeset.bounds();
-        REQUIRE(box.bottom_left() == bl);
-        REQUIRE(box.top_right() == tr);
-
-        REQUIRE(user == changeset.user());
-    }
-
-}
-
-TEST_CASE("no call to set_user on node") {
-    osmium::memory::Buffer buffer{1024*10};
-
-    {
-        osmium::builder::NodeBuilder builder{buffer};
-    }
-
-    const auto& node = buffer.get<osmium::Node>(buffer.commit());
-
-    REQUIRE(*node.user() == '\0');
-}
-
-TEST_CASE("set_user with length on node") {
-    osmium::memory::Buffer buffer{1024*10};
-    std::string user = "userx";
-
-    {
-        osmium::builder::NodeBuilder builder{buffer};
-        builder.set_user(user.c_str(), 4);
-    }
-
-    const auto& node = buffer.get<osmium::Node>(buffer.commit());
-
-    REQUIRE(std::string{"user"} == node.user());
-}
-
-TEST_CASE("no call to set_user on way") {
-    osmium::memory::Buffer buffer{1024*10};
-
-    {
-        osmium::builder::WayBuilder builder{buffer};
-    }
-
-    const auto& way = buffer.get<osmium::Way>(buffer.commit());
-
-    REQUIRE(*way.user() == '\0');
-}
-
-TEST_CASE("set_user with length on way") {
-    osmium::memory::Buffer buffer{1024*10};
-    std::string user = "userx";
-
-    {
-        osmium::builder::WayBuilder builder{buffer};
-        builder.set_user(user.c_str(), 4);
-    }
-
-    const auto& way = buffer.get<osmium::Way>(buffer.commit());
-
-    REQUIRE(std::string{"user"} == way.user());
-}
-
-TEST_CASE("no call to set_user on changeset") {
-    osmium::memory::Buffer buffer{1024*10};
-
-    {
-        osmium::builder::ChangesetBuilder builder{buffer};
-    }
-
-    const auto& changeset = buffer.get<osmium::Changeset>(buffer.commit());
-
-    REQUIRE(*changeset.user() == '\0');
-}
-
-TEST_CASE("set_user with length on changeset") {
-    osmium::memory::Buffer buffer{1024*10};
-    std::string user = "userx";
-
-    {
-        osmium::builder::ChangesetBuilder builder{buffer};
-        builder.set_user(user.c_str(), 4);
-    }
-
-    const auto& changeset = buffer.get<osmium::Changeset>(buffer.commit());
-
-    REQUIRE(std::string{"user"} == changeset.user());
-}
-
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_crs.cpp b/test/t/geom/test_crs.cpp
index d8f7c36..c8fbc09 100644
--- a/test/t/geom/test_crs.cpp
+++ b/test/t/geom/test_crs.cpp
@@ -5,12 +5,12 @@
 #include <osmium/geom/projection.hpp>
 
 TEST_CASE("CRS") {
-    const osmium::geom::CRS wgs84{4326};
-    const osmium::geom::CRS mercator{3857};
+    osmium::geom::CRS wgs84{4326};
+    osmium::geom::CRS mercator{3857};
 
-    const osmium::geom::Coordinates c{osmium::geom::deg_to_rad(1.2), osmium::geom::deg_to_rad(3.4)};
-    const auto ct = osmium::geom::transform(wgs84, mercator, c);
-    const auto c2 = osmium::geom::transform(mercator, wgs84, ct);
+    osmium::geom::Coordinates c{osmium::geom::deg_to_rad(1.2), osmium::geom::deg_to_rad(3.4)};
+    auto ct = osmium::geom::transform(wgs84, mercator, c);
+    auto c2 = osmium::geom::transform(mercator, wgs84, ct);
 
     REQUIRE(c.x == Approx(c2.x));
     REQUIRE(c.y == Approx(c2.y));
diff --git a/test/t/geom/test_exception.cpp b/test/t/geom/test_exception.cpp
index 4122d17..fe95043 100644
--- a/test/t/geom/test_exception.cpp
+++ b/test/t/geom/test_exception.cpp
@@ -6,9 +6,11 @@
 
 TEST_CASE("Geometry exception") {
 
-    osmium::geometry_error e{"some error message", "node", 17};
-    REQUIRE(e.id() == 17);
-    REQUIRE(std::string{e.what()} == "some error message (node_id=17)");
+    SECTION("geometry_error") {
+        osmium::geometry_error e("some error message", "node", 17);
+        REQUIRE(e.id() == 17);
+        REQUIRE(std::string(e.what()) == "some error message (node_id=17)");
+    }
 
 }
 
diff --git a/test/t/geom/test_factory_with_projection.cpp b/test/t/geom/test_factory_with_projection.cpp
index 08efc03..42fc864 100644
--- a/test/t/geom/test_factory_with_projection.cpp
+++ b/test/t/geom/test_factory_with_projection.cpp
@@ -1,21 +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>
 
-TEST_CASE("Projection using MercatorProjection class to WKT") {
-    osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory{2};
+#include "helper.hpp"
 
-    const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})};
-    REQUIRE(wkt == "POINT(356222.37 467961.14)");
-}
+TEST_CASE("Projection") {
 
-TEST_CASE("Projection using Projection class to WKT") {
-    osmium::geom::WKTFactory<osmium::geom::Projection> factory{osmium::geom::Projection{3857}, 2};
+    SECTION("point_mercator") {
+        osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory(2);
 
-    const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})};
-    REQUIRE(wkt == "POINT(356222.37 467961.14)");
-}
+        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
index 5724936..725be7f 100644
--- a/test/t/geom/test_geojson.cpp
+++ b/test/t/geom/test_geojson.cpp
@@ -5,143 +5,162 @@
 #include "area_helper.hpp"
 #include "wnl_helper.hpp"
 
-TEST_CASE("GeoJSON point geometry") {
+TEST_CASE("GeoJSON_Geometry") {
+
+SECTION("point") {
     osmium::geom::GeoJSONFactory<> factory;
 
-    SECTION("point") {
-        const std::string json{factory.create_point(osmium::Location{3.2, 4.2})};
-        REQUIRE(std::string{"{\"type\":\"Point\",\"coordinates\":[3.2,4.2]}"} == json);
-    }
+    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") {
-        REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location);
-    }
+SECTION("empty_point") {
+    osmium::geom::GeoJSONFactory<> factory;
 
+    REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
 }
 
-TEST_CASE("GeoJSON linestring geometry") {
+SECTION("linestring") {
     osmium::geom::GeoJSONFactory<> factory;
-    osmium::memory::Buffer buffer{1000};
 
-    SECTION("linestring, default") {
-        const auto& wnl = create_test_wnl_okay(buffer);
-        const std::string json{factory.create_linestring(wnl)};
+    osmium::memory::Buffer buffer(1000);
+    auto &wnl = create_test_wnl_okay(buffer);
+
+    {
+        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);
     }
 
-    SECTION("linestring, unique, backwards") {
-        const auto& wnl = create_test_wnl_okay(buffer);
-        const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+    {
+        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);
     }
 
-    SECTION("linestring, all") {
-        const auto& wnl = create_test_wnl_okay(buffer);
-        const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+    {
+        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);
     }
 
-    SECTION("linestring, all, backwards") {
-        const auto& wnl = create_test_wnl_okay(buffer);
-        const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+    {
+        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") {
-        const auto& wnl = create_test_wnl_empty(buffer);
+SECTION("empty_linestring") {
+    osmium::geom::GeoJSONFactory<> factory;
 
-        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);
-    }
+    osmium::memory::Buffer buffer(1000);
+    auto& wnl = create_test_wnl_empty(buffer);
 
-    SECTION("linestring with two same locations") {
-        const auto& wnl = create_test_wnl_same_location(buffer);
+    REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error);
+    REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error);
+    REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error);
+    REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error);
+}
 
-        REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error);
-        REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error);
+SECTION("linestring_with_two_same_locations") {
+    osmium::geom::GeoJSONFactory<> factory;
 
-        {
-            const 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);
-        }
+    osmium::memory::Buffer buffer(1000);
+    auto& wnl = create_test_wnl_same_location(buffer);
 
-        {
-            const 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);
-        }
+    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);
     }
 
-    SECTION("linestring with undefined location") {
-        const auto& wnl = create_test_wnl_undefined_location(buffer);
-        REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location);
+    {
+        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(1000);
+    auto& wnl = create_test_wnl_undefined_location(buffer);
 
+    REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location);
 }
 
-TEST_CASE("GeoJSON area geometry") {
+SECTION("area_1outer_0inner") {
     osmium::geom::GeoJSONFactory<> factory;
-    osmium::memory::Buffer buffer{1000};
 
-    SECTION("area_1outer_0inner") {
-        const osmium::Area& area = create_test_area_1outer_0inner(buffer);
+    osmium::memory::Buffer buffer(1000);
+    const osmium::Area& area = create_test_area_1outer_0inner(buffer);
 
-        REQUIRE(!area.is_multipolygon());
-        REQUIRE(std::distance(area.cbegin(), area.cend()) == 2);
-        REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+    REQUIRE(!area.is_multipolygon());
+    REQUIRE(std::distance(area.cbegin(), area.cend()) == 2);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
 
-        std::string json{factory.create_multipolygon(area)};
+    {
+        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;
 
-    SECTION("area_1outer_1inner") {
-        const osmium::Area& area = create_test_area_1outer_1inner(buffer);
+    osmium::memory::Buffer buffer(1000);
+    const osmium::Area& area = create_test_area_1outer_1inner(buffer);
 
-        REQUIRE(!area.is_multipolygon());
-        REQUIRE(std::distance(area.cbegin(), area.cend()) == 3);
-        REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
-        REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
+    REQUIRE(!area.is_multipolygon());
+    REQUIRE(std::distance(area.cbegin(), area.cend()) == 3);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+    REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
 
-        std::string json{factory.create_multipolygon(area)};
+    {
+        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;
 
-    SECTION("area_2outer_2inner") {
-        const osmium::Area& area = create_test_area_2outer_2inner(buffer);
-
-        REQUIRE(area.is_multipolygon());
-        REQUIRE(std::distance(area.cbegin(), area.cend()) == 5);
-        REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
-        REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
-
-        int outer_ring=0;
-        int inner_ring=0;
-        for (const auto& outer : area.outer_rings()) {
-            if (outer_ring == 0) {
-                REQUIRE(outer.front().ref() == 1);
-            } else if (outer_ring == 1) {
-                REQUIRE(outer.front().ref() == 100);
+    osmium::memory::Buffer buffer(1000);
+    const osmium::Area& area = create_test_area_2outer_2inner(buffer);
+
+    REQUIRE(area.is_multipolygon());
+    REQUIRE(std::distance(area.cbegin(), area.cend()) == 5);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+    REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
+
+    int outer_ring=0;
+    int inner_ring=0;
+    for (const auto& outer : area.outer_rings()) {
+        if (outer_ring == 0) {
+            REQUIRE(outer.front().ref() == 1);
+        } else if (outer_ring == 1) {
+            REQUIRE(outer.front().ref() == 100);
+        } else {
+            REQUIRE(false);
+        }
+        for (const auto& inner : area.inner_rings(outer)) {
+            if (outer_ring == 0 && inner_ring == 0) {
+                REQUIRE(inner.front().ref() == 5);
+            } else if (outer_ring == 0 && inner_ring == 1) {
+                REQUIRE(inner.front().ref() == 10);
             } else {
                 REQUIRE(false);
             }
-            for (const auto& inner : area.inner_rings(outer)) {
-                if (outer_ring == 0 && inner_ring == 0) {
-                    REQUIRE(inner.front().ref() == 5);
-                } else if (outer_ring == 0 && inner_ring == 1) {
-                    REQUIRE(inner.front().ref() == 10);
-                } else {
-                    REQUIRE(false);
-                }
-                ++inner_ring;
-            }
-            inner_ring = 0;
-            ++outer_ring;
+            ++inner_ring;
         }
+        inner_ring = 0;
+        ++outer_ring;
+    }
 
-        std::string json{factory.create_multipolygon(area)};
+    {
+        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
index 8e7fac4..f74027c 100644
--- a/test/t/geom/test_geos.cpp
+++ b/test/t/geom/test_geos.cpp
@@ -1,10 +1,6 @@
-
-#include <osmium/geom/geos.hpp>
-
-#ifdef OSMIUM_WITH_GEOS
-
 #include "catch.hpp"
 
+#include <osmium/geom/geos.hpp>
 #include <osmium/geom/mercator_projection.hpp>
 
 #include "area_helper.hpp"
@@ -13,7 +9,7 @@
 TEST_CASE("GEOS geometry factory - create point") {
     osmium::geom::GEOSFactory<> factory;
 
-    const std::unique_ptr<geos::geom::Point> point{factory.create_point(osmium::Location{3.2, 4.2})};
+    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());
@@ -22,7 +18,7 @@ TEST_CASE("GEOS geometry factory - create point") {
 TEST_CASE("GEOS geometry factory - create point in web mercator") {
     osmium::geom::GEOSFactory<osmium::geom::MercatorProjection> factory;
 
-    const std::unique_ptr<geos::geom::Point> point{factory.create_point(osmium::Location{3.2, 4.2})};
+    std::unique_ptr<geos::geom::Point> point {factory.create_point(osmium::Location(3.2, 4.2))};
     REQUIRE(Approx(356222.3705384755l) == point->getX());
     REQUIRE(Approx(467961.143605213l) == point->getY());
     REQUIRE(3857 == point->getSRID());
@@ -30,9 +26,9 @@ TEST_CASE("GEOS geometry factory - create point in web mercator") {
 
 TEST_CASE("GEOS geometry factory - create point with externally created GEOS factory") {
     geos::geom::GeometryFactory geos_factory;
-    osmium::geom::GEOSFactory<> factory{geos_factory};
+    osmium::geom::GEOSFactory<> factory(geos_factory);
 
-    const std::unique_ptr<geos::geom::Point> point{factory.create_point(osmium::Location{3.2, 4.2})};
+    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(0 == point->getSRID());
@@ -41,45 +37,45 @@ TEST_CASE("GEOS geometry factory - create point with externally created GEOS fac
 TEST_CASE("GEOS geometry factory - can not create from invalid location") {
     osmium::geom::GEOSFactory<> factory;
 
-    REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location);
+    REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
 }
 
 TEST_CASE("GEOS geometry factory - create linestring") {
     osmium::geom::GEOSFactory<> factory;
 
-    osmium::memory::Buffer buffer{10000};
-    const auto& wnl = create_test_wnl_okay(buffer);
+    osmium::memory::Buffer buffer(10000);
+    auto &wnl = create_test_wnl_okay(buffer);
 
     SECTION("from way node list") {
-        const std::unique_ptr<geos::geom::LineString> linestring{factory.create_linestring(wnl)};
+        std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl)};
         REQUIRE(3 == linestring->getNumPoints());
 
-        const auto p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+        std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
         REQUIRE(3.2 == p0->getX());
-        const auto p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
+        std::unique_ptr<geos::geom::Point> p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
         REQUIRE(3.6 == p2->getX());
     }
 
     SECTION("without duplicates and backwards") {
-        const std::unique_ptr<geos::geom::LineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+        std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
         REQUIRE(3 == linestring->getNumPoints());
-        const auto p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+        std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
         REQUIRE(3.6 == p0->getX());
-        const auto p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
+        std::unique_ptr<geos::geom::Point> p2 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(2));
         REQUIRE(3.2 == p2->getX());
     }
 
     SECTION("with duplicates") {
-        const std::unique_ptr<geos::geom::LineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+        std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
         REQUIRE(4 == linestring->getNumPoints());
-        const auto p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+        std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
         REQUIRE(3.2 == p0->getX());
     }
 
     SECTION("with duplicates and backwards") {
-        const std::unique_ptr<geos::geom::LineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+        std::unique_ptr<geos::geom::LineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
         REQUIRE(4 == linestring->getNumPoints());
-        const auto p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
+        std::unique_ptr<geos::geom::Point> p0 = std::unique_ptr<geos::geom::Point>(linestring->getPointN(0));
         REQUIRE(3.6 == p0->getX());
     }
 }
@@ -87,10 +83,10 @@ TEST_CASE("GEOS geometry factory - create linestring") {
 TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings") {
     osmium::geom::GEOSFactory<> factory;
 
-    osmium::memory::Buffer buffer{10000};
+    osmium::memory::Buffer buffer(10000);
     const osmium::Area& area = create_test_area_1outer_0inner(buffer);
 
-    const std::unique_ptr<geos::geom::MultiPolygon> mp{factory.create_multipolygon(area)};
+    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));
@@ -100,17 +96,17 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings
     const geos::geom::LineString* l0e = p0->getExteriorRing();
     REQUIRE(4 == l0e->getNumPoints());
 
-    const auto l0e_p0 = std::unique_ptr<geos::geom::Point>(l0e->getPointN(1));
+    std::unique_ptr<geos::geom::Point> l0e_p0 = std::unique_ptr<geos::geom::Point>(l0e->getPointN(1));
     REQUIRE(3.5 == l0e_p0->getX());
 }
 
 TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring") {
     osmium::geom::GEOSFactory<> factory;
 
-    osmium::memory::Buffer buffer{10000};
+    osmium::memory::Buffer buffer(10000);
     const osmium::Area& area = create_test_area_1outer_1inner(buffer);
 
-    const std::unique_ptr<geos::geom::MultiPolygon> mp{factory.create_multipolygon(area)};
+    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));
@@ -127,10 +123,10 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring
 TEST_CASE("GEOS geometry factory - create area with two outer and two inner rings") {
     osmium::geom::GEOSFactory<> factory;
 
-    osmium::memory::Buffer buffer{10000};
+    osmium::memory::Buffer buffer(10000);
     const osmium::Area& area = create_test_area_2outer_2inner(buffer);
 
-    const std::unique_ptr<geos::geom::MultiPolygon> mp{factory.create_multipolygon(area)};
+    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));
@@ -148,5 +144,3 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring
     REQUIRE(5 == l1e->getNumPoints());
 }
 
-#endif
-
diff --git a/test/t/geom/test_geos_wkb.cpp b/test/t/geom/test_geos_wkb.cpp
new file mode 100644
index 0000000..1fca63b
--- /dev/null
+++ b/test/t/geom/test_geos_wkb.cpp
@@ -0,0 +1,92 @@
+#include "catch.hpp"
+
+#include <osmium/geom/geos.hpp>
+#include <osmium/geom/wkb.hpp>
+
+#include "helper.hpp"
+#include "area_helper.hpp"
+#include "wnl_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 = create_test_wnl_okay(buffer);
+
+    {
+        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);
+    const osmium::Area& area = create_test_area_1outer_0inner(buffer);
+
+    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);
+    const osmium::Area& area = create_test_area_1outer_1inner(buffer);
+
+    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);
+    const osmium::Area& area = create_test_area_2outer_2inner(buffer);
+
+    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_ogr.cpp b/test/t/geom/test_ogr.cpp
index 5e03082..3490c57 100644
--- a/test/t/geom/test_ogr.cpp
+++ b/test/t/geom/test_ogr.cpp
@@ -5,115 +5,121 @@
 #include "area_helper.hpp"
 #include "wnl_helper.hpp"
 
-TEST_CASE("OGR point geometry") {
+TEST_CASE("OGR_Geometry") {
+
+SECTION("point") {
     osmium::geom::OGRFactory<> factory;
 
-    SECTION("point") {
-        std::unique_ptr<OGRPoint> point{factory.create_point(osmium::Location{3.2, 4.2})};
-        REQUIRE(3.2 == point->getX());
-        REQUIRE(4.2 == point->getY());
-    }
+    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") {
-        REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
-    }
+SECTION("empty_point") {
+    osmium::geom::OGRFactory<> factory;
 
+    REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location);
 }
 
-TEST_CASE("OGR linestring geometry") {
+SECTION("linestring") {
     osmium::geom::OGRFactory<> factory;
-    osmium::memory::Buffer buffer{10000};
-    const auto& wnl = create_test_wnl_okay(buffer);
 
-    SECTION("linestring, default") {
-        std::unique_ptr<OGRLineString> linestring{factory.create_linestring(wnl)};
+    osmium::memory::Buffer buffer(10000);
+    auto &wnl = create_test_wnl_okay(buffer);
+
+    {
+        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));
     }
 
-    SECTION("linestring, unique nodes, backwards") {
-        std::unique_ptr<OGRLineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
+    {
+        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));
     }
 
-    SECTION("linestring, all nodes") {
-        std::unique_ptr<OGRLineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
+    {
+        std::unique_ptr<OGRLineString> linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
         REQUIRE(4 == linestring->getNumPoints());
 
         REQUIRE(3.2 == linestring->getX(0));
     }
 
-    SECTION("linestring, all nodes, backwards") {
-        std::unique_ptr<OGRLineString> linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
+    {
+        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));
     }
-
 }
 
-TEST_CASE("OGR area geometry") {
+SECTION("area_1outer_0inner") {
     osmium::geom::OGRFactory<> factory;
-    osmium::memory::Buffer buffer{10000};
 
-    SECTION("area_1outer_0inner") {
-        const osmium::Area& area = create_test_area_1outer_0inner(buffer);
+    osmium::memory::Buffer buffer(10000);
+    const osmium::Area& area = create_test_area_1outer_0inner(buffer);
 
-        std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
-        REQUIRE(1 == mp->getNumGeometries());
+    std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+    REQUIRE(1 == mp->getNumGeometries());
 
-        const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
-        REQUIRE(p0);
-        REQUIRE(0 == p0->getNumInteriorRings());
+    const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
+    REQUIRE(0 == p0->getNumInteriorRings());
 
-        const OGRLineString* l0e = p0->getExteriorRing();
-        REQUIRE(4 == l0e->getNumPoints());
+    const OGRLineString* l0e = p0->getExteriorRing();
+    REQUIRE(4 == l0e->getNumPoints());
 
-        REQUIRE(3.5 == l0e->getX(1));
-    }
+    REQUIRE(3.5 == l0e->getX(1));
+}
 
-    SECTION("area_1outer_1inner") {
-        const osmium::Area& area = create_test_area_1outer_1inner(buffer);
+SECTION("area_1outer_1inner") {
+    osmium::geom::OGRFactory<> factory;
 
-        std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
-        REQUIRE(1 == mp->getNumGeometries());
+    osmium::memory::Buffer buffer(10000);
+    const osmium::Area& area = create_test_area_1outer_1inner(buffer);
 
-        const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
-        REQUIRE(p0);
-        REQUIRE(1 == p0->getNumInteriorRings());
+    std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+    REQUIRE(1 == mp->getNumGeometries());
 
-        const OGRLineString* l0e = p0->getExteriorRing();
-        REQUIRE(5 == l0e->getNumPoints());
+    const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
+    REQUIRE(1 == p0->getNumInteriorRings());
 
-        const OGRLineString* l0i0 = p0->getInteriorRing(0);
-        REQUIRE(5 == l0i0->getNumPoints());
-    }
+    const OGRLineString* l0e = p0->getExteriorRing();
+    REQUIRE(5 == l0e->getNumPoints());
+
+    const OGRLineString* l0i0 = p0->getInteriorRing(0);
+    REQUIRE(5 == l0i0->getNumPoints());
+}
 
-    SECTION("area_2outer_2inner") {
-        const osmium::Area& area = create_test_area_2outer_2inner(buffer);
+SECTION("area_2outer_2inner") {
+    osmium::geom::OGRFactory<> factory;
 
-        std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
-        REQUIRE(2 == mp->getNumGeometries());
+    osmium::memory::Buffer buffer(10000);
+    const osmium::Area& area = create_test_area_2outer_2inner(buffer);
 
-        const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
-        REQUIRE(p0);
-        REQUIRE(2 == p0->getNumInteriorRings());
+    std::unique_ptr<OGRMultiPolygon> mp {factory.create_multipolygon(area)};
+    REQUIRE(2 == mp->getNumGeometries());
 
-        const OGRLineString* l0e = p0->getExteriorRing();
-        REQUIRE(5 == l0e->getNumPoints());
+    const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
+    REQUIRE(2 == p0->getNumInteriorRings());
 
-        const OGRPolygon* p1 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(1));
-        REQUIRE(p1);
-        REQUIRE(0 == p1->getNumInteriorRings());
+    const OGRLineString* l0e = p0->getExteriorRing();
+    REQUIRE(5 == l0e->getNumPoints());
 
-        const OGRLineString* l1e = p1->getExteriorRing();
-        REQUIRE(5 == l1e->getNumPoints());
-    }
+    const OGRPolygon* p1 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(1));
+    REQUIRE(p1);
+    REQUIRE(0 == p1->getNumInteriorRings());
+
+    const OGRLineString* l1e = p1->getExteriorRing();
+    REQUIRE(5 == l1e->getNumPoints());
+}
 
 }
 
diff --git a/test/t/geom/test_ogr_wkb.cpp b/test/t/geom/test_ogr_wkb.cpp
deleted file mode 100644
index 548d143..0000000
--- a/test/t/geom/test_ogr_wkb.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-#include "catch.hpp"
-
-#include <memory>
-#include <sstream>
-#include <string>
-
-#include <osmium/geom/ogr.hpp>
-#include <osmium/geom/wkb.hpp>
-
-#include "area_helper.hpp"
-#include "wnl_helper.hpp"
-
-std::string to_wkb(const OGRGeometry* geometry) {
-    std::string buffer;
-    buffer.resize(geometry->WkbSize());
-
-    geometry->exportToWkb(wkbNDR, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(buffer.data())));
-
-    return buffer;
-}
-
-TEST_CASE("compare WKB point against GDAL/OGR") {
-    osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb};
-    osmium::geom::OGRFactory<> ogr_factory;
-
-    osmium::Location loc{3.2, 4.2};
-    const std::string wkb{wkb_factory.create_point(loc)};
-    const std::unique_ptr<OGRPoint> geometry = ogr_factory.create_point(loc);
-    REQUIRE(to_wkb(geometry.get()) == wkb);
-}
-
-TEST_CASE("compare WKB linestring against GDAL/OGR") {
-    osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb};
-    osmium::geom::OGRFactory<> ogr_factory;
-    osmium::memory::Buffer buffer{10000};
-
-    const auto& wnl = create_test_wnl_okay(buffer);
-
-    SECTION("linestring") {
-        const std::string wkb{wkb_factory.create_linestring(wnl)};
-        const std::unique_ptr<OGRLineString> geometry = ogr_factory.create_linestring(wnl);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-    SECTION("linestring, unique nodes, backwards") {
-        const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)};
-        const std::unique_ptr<OGRLineString> geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-    SECTION("linestring, all nodes, forwards") {
-        const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all)};
-        const std::unique_ptr<OGRLineString> geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::all);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-    SECTION("linestring, all nodes, backwards") {
-        const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)};
-        const std::unique_ptr<OGRLineString> geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-}
-
-TEST_CASE("compare WKB area against GDAL/OGR") {
-    osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb};
-    osmium::geom::OGRFactory<> ogr_factory;
-    osmium::memory::Buffer buffer{10000};
-
-    SECTION("area_1outer_0inner") {
-        const osmium::Area& area = create_test_area_1outer_0inner(buffer);
-
-        const std::string wkb{wkb_factory.create_multipolygon(area)};
-        const std::unique_ptr<OGRMultiPolygon> geometry = ogr_factory.create_multipolygon(area);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-    SECTION("area_1outer_1inner") {
-        const osmium::Area& area = create_test_area_1outer_1inner(buffer);
-
-        const std::string wkb{wkb_factory.create_multipolygon(area)};
-        const std::unique_ptr<OGRMultiPolygon> geometry = ogr_factory.create_multipolygon(area);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-    SECTION("area_2outer_2inner") {
-        const osmium::Area& area = create_test_area_2outer_2inner(buffer);
-
-        const std::string wkb{wkb_factory.create_multipolygon(area)};
-        const std::unique_ptr<OGRMultiPolygon> geometry = ogr_factory.create_multipolygon(area);
-        REQUIRE(to_wkb(geometry.get()) == wkb);
-    }
-
-}
-
-#endif
-
diff --git a/test/t/geom/test_projection.cpp b/test/t/geom/test_projection.cpp
index 14df3bf..5885410 100644
--- a/test/t/geom/test_projection.cpp
+++ b/test/t/geom/test_projection.cpp
@@ -6,121 +6,144 @@
 #include <osmium/geom/mercator_projection.hpp>
 #include <osmium/geom/projection.hpp>
 
-TEST_CASE("Indentity Projection") {
+TEST_CASE("Projection") {
+
+SECTION("identity_projection") {
     osmium::geom::IdentityProjection projection;
     REQUIRE(4326 == projection.epsg());
     REQUIRE("+proj=longlat +datum=WGS84 +no_defs" == projection.proj_string());
 }
 
-TEST_CASE("Projection 4326") {
-    osmium::geom::Projection projection{4326};
+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};
+    const osmium::Location loc(1.0, 2.0);
+    const osmium::geom::Coordinates c {1.0, 2.0};
     REQUIRE(c == projection(loc));
 }
 
-TEST_CASE("Projection 4326 from init string") {
-    osmium::geom::Projection projection{"+init=epsg:4326"};
+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};
+    const osmium::Location loc(1.0, 2.0);
+    const osmium::geom::Coordinates c {1.0, 2.0};
     REQUIRE(c == projection(loc));
 }
 
-TEST_CASE("Creating projection from unknown init string") {
-    REQUIRE_THROWS_AS(osmium::geom::Projection projection{"abc"}, osmium::projection_error);
+SECTION("unknown_projection_string") {
+    REQUIRE_THROWS_AS(osmium::geom::Projection projection("abc"), osmium::projection_error);
 }
 
-TEST_CASE("Creating projection from unknown EPSG code") {
-    REQUIRE_THROWS_AS(osmium::geom::Projection projection{9999999}, osmium::projection_error);
+SECTION("unknown_epsg_code") {
+    REQUIRE_THROWS_AS(osmium::geom::Projection projection(9999999), osmium::projection_error);
 }
 
-TEST_CASE("Projection 3857") {
-    osmium::geom::Projection projection{3857};
+SECTION("project_location_3857") {
+    osmium::geom::Projection projection(3857);
     REQUIRE(3857 == projection.epsg());
     REQUIRE("+init=epsg:3857" == projection.proj_string());
 
-    SECTION("Zero coordinates") {
-        const osmium::Location loc{0.0, 0.0};
-        const osmium::geom::Coordinates c{0.0, 0.0};
+    {
+        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));
     }
-
-    SECTION("Max longitude") {
-        const osmium::Location loc{180.0, 0.0};
-        const osmium::geom::Coordinates c{20037508.34, 0.0};
+    {
+        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));
     }
-
-    SECTION("Min longitude") {
-        const osmium::Location loc{-180.0, 0.0};
-        const osmium::geom::Coordinates c{-20037508.34, 0.0};
+    {
+        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));
     }
-
-    SECTION("Max latitude") {
-        const osmium::Location loc{0.0, 85.0511288};
-        const osmium::geom::Coordinates c{0.0, 20037508.34};
+    {
+        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));
     }
 }
 
-TEST_CASE("MercatorProjection") {
+SECTION("project_location_mercator") {
     osmium::geom::MercatorProjection projection;
 
-    SECTION("Zero coordinates") {
-        const osmium::Location loc{0.0, 0.0};
-        const osmium::geom::Coordinates c{0.0, 0.0};
+    {
+        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));
     }
-
-    SECTION("Max longitude") {
-        const osmium::Location loc{180.0, 0.0};
-        const osmium::geom::Coordinates c{20037508.34, 0.0};
+    {
+        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));
     }
-
-    SECTION("Min longitude") {
-        const osmium::Location loc{-180.0, 0.0};
-        const osmium::geom::Coordinates c{-20037508.34, 0.0};
+    {
+        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));
     }
-
-    SECTION("Max latitude") {
-        const osmium::Location loc{0.0, 85.0511288};
-        const osmium::geom::Coordinates c{0.0, 20037508.34};
+    {
+        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));
     }
 }
 
-TEST_CASE("Compare mercator implementations") {
+SECTION("compare_mercators") {
     osmium::geom::MercatorProjection projection_merc;
-    osmium::geom::Projection projection_3857{3857};
-
-    SECTION("random coordinates") {
-        std::random_device rd;
-        std::mt19937 gen{rd()};
-        std::uniform_real_distribution<> dis_x{-180.0, 180.0};
-        std::uniform_real_distribution<> dis_y{-90.0, 90.0};
-
-        for (int n = 0; n < 10000; ++n) {
-            const osmium::Location loc{dis_x(gen), dis_y(gen)};
-            REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
-            REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
-        }
+    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));
+    }
+}
+
+SECTION("compare_mercators") {
+    osmium::geom::MercatorProjection projection_merc;
+    osmium::geom::Projection projection_3857(3857);
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<> dis_x(-180.0, 180.0);
+    std::uniform_real_distribution<> dis_y(-90.0, 90.0);
+
+    for (int n = 0; n < 100000; ++n) {
+        const osmium::Location loc(dis_x(gen), dis_y(gen));
+        REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+        REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+    }
+}
 
 }
diff --git a/test/t/geom/test_tile.cpp b/test/t/geom/test_tile.cpp
index 953fcc7..5454fed 100644
--- a/test/t/geom/test_tile.cpp
+++ b/test/t/geom/test_tile.cpp
@@ -4,6 +4,8 @@
 
 #include <osmium/geom/tile.hpp>
 
+#include "helper.hpp"
+
 #include "test_tile_data.hpp"
 
 TEST_CASE("Tile from x0.0 y0.0 at zoom 0") {
diff --git a/test/t/geom/test_wkb.cpp b/test/t/geom/test_wkb.cpp
index 66dd42e..d4d9228 100644
--- a/test/t/geom/test_wkb.cpp
+++ b/test/t/geom/test_wkb.cpp
@@ -10,28 +10,28 @@ TEST_CASE("WKB geometry factory (byte-order-dependant), points") {
     const osmium::Location loc{3.2, 4.2};
 
     SECTION("point") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
 
         const std::string wkb{factory.create_point(loc)};
         REQUIRE(wkb == "01010000009A99999999990940CDCCCCCCCCCC1040");
     }
 
     SECTION("point in web mercator") {
-        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
 
         const std::string wkb{factory.create_point(loc)};
         REQUIRE(wkb == "010100000028706E7BF9BD1541B03E0D93E48F1C41");
     }
 
     SECTION("point in ewkb") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
 
         const std::string wkb{factory.create_point(loc)};
         REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040");
     }
 
     SECTION("point in ewkb in web mercator") {
-        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
 
         const std::string wkb{factory.create_point(loc)};
         REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41");
@@ -44,7 +44,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") {
     osmium::memory::Buffer buffer{10000};
 
     SECTION("linestring") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
         const auto& wnl = create_test_wnl_okay(buffer);
 
         {
@@ -69,7 +69,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") {
     }
 
     SECTION("linestring as ewkb") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
 
         const auto& wnl = create_test_wnl_okay(buffer);
 
@@ -78,7 +78,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") {
     }
 
     SECTION("linestring with two same locations") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
 
         const auto& wnl = create_test_wnl_same_location(buffer);
 
@@ -102,7 +102,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") {
     }
 
     SECTION("linestring with undefined location") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+        osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
 
         const auto& wnl = create_test_wnl_undefined_location(buffer);
 
@@ -115,7 +115,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") {
 
 TEST_CASE("WKB geometry (byte-order-independent)") {
 
-    osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+    osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
 
     SECTION("empty point") {
         REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location);
diff --git a/test/t/geom/wnl_helper.hpp b/test/t/geom/wnl_helper.hpp
index 68de4c3..91ac114 100644
--- a/test/t/geom/wnl_helper.hpp
+++ b/test/t/geom/wnl_helper.hpp
@@ -6,7 +6,7 @@
 using namespace osmium::builder::attr;
 
 inline const osmium::WayNodeList& create_test_wnl_okay(osmium::memory::Buffer& buffer) {
-    const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
+    auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
         {1, {3.2, 4.2}},
         {3, {3.5, 4.7}},
         {4, {3.5, 4.7}},
@@ -25,7 +25,7 @@ inline const osmium::WayNodeList& create_test_wnl_empty(osmium::memory::Buffer&
 }
 
 inline const osmium::WayNodeList& create_test_wnl_same_location(osmium::memory::Buffer& buffer) {
-    const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
+    auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
         {1, {3.5, 4.7}},
         {2, {3.5, 4.7}}
     }));
@@ -34,7 +34,7 @@ inline const osmium::WayNodeList& create_test_wnl_same_location(osmium::memory::
 }
 
 inline const osmium::WayNodeList& create_test_wnl_undefined_location(osmium::memory::Buffer& buffer) {
-    const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
+    auto pos = osmium::builder::add_way_node_list(buffer, _nodes({
         {1, {3.5, 4.7}},
         {2, osmium::Location()}
     }));
diff --git a/test/t/index/test_id_set.cpp b/test/t/index/test_id_set.cpp
deleted file mode 100644
index 4c24447..0000000
--- a/test/t/index/test_id_set.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-
-#include "catch.hpp"
-
-#include <osmium/index/id_set.hpp>
-#include <osmium/osm/types.hpp>
-
-TEST_CASE("Basic functionality of IdSetDense") {
-    osmium::index::IdSetDense<osmium::unsigned_object_id_type> s;
-
-    REQUIRE_FALSE(s.get(17));
-    REQUIRE_FALSE(s.get(28));
-    REQUIRE(s.empty());
-    REQUIRE(s.size() == 0);
-
-    s.set(17);
-    REQUIRE(s.get(17));
-    REQUIRE_FALSE(s.get(28));
-    REQUIRE_FALSE(s.empty());
-    REQUIRE(s.size() == 1);
-
-    s.set(28);
-    REQUIRE(s.get(17));
-    REQUIRE(s.get(28));
-    REQUIRE_FALSE(s.empty());
-    REQUIRE(s.size() == 2);
-
-    s.set(17);
-    REQUIRE(s.get(17));
-    REQUIRE(s.size() == 2);
-
-    REQUIRE_FALSE(s.check_and_set(17));
-    REQUIRE(s.get(17));
-    REQUIRE(s.size() == 2);
-
-    s.unset(17);
-    REQUIRE_FALSE(s.get(17));
-    REQUIRE(s.size() == 1);
-
-    REQUIRE(s.check_and_set(32));
-    REQUIRE(s.get(32));
-    REQUIRE(s.size() == 2);
-
-    s.clear();
-    REQUIRE(s.empty());
-    REQUIRE(s.size() == 0);
-}
-
-TEST_CASE("Iterating over IdSetDense") {
-    osmium::index::IdSetDense<osmium::unsigned_object_id_type> s;
-    s.set(7);
-    s.set(35);
-    s.set(35);
-    s.set(20);
-    s.set(1LL << 33);
-    s.set(21);
-    s.set((1LL << 27) + 13);
-
-    REQUIRE(s.size() == 6);
-
-    auto it = s.begin();
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 7);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 20);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 21);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 35);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == (1LL << 27) + 13);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 1LL << 33);
-    ++it;
-    REQUIRE(it == s.end());
-}
-
-TEST_CASE("Test with larger Ids") {
-    osmium::index::IdSetDense<osmium::unsigned_object_id_type> s;
-
-    const osmium::unsigned_object_id_type start = 25;
-    const osmium::unsigned_object_id_type end = 100000000;
-    const osmium::unsigned_object_id_type step = 123456;
-
-    for (osmium::unsigned_object_id_type i = start; i < end; i += step) {
-        s.set(i);
-    }
-
-    for (osmium::unsigned_object_id_type i = start; i < end; i += step) {
-        REQUIRE(s.get(i));
-        REQUIRE_FALSE(s.get(i + 1));
-    }
-}
-
-TEST_CASE("Large gap") {
-    osmium::index::IdSetDense<osmium::unsigned_object_id_type> s;
-
-    s.set(3);
-    s.set(1 << 30);
-
-    REQUIRE(s.get(1 << 30));
-    REQUIRE_FALSE(s.get(1 << 29));
-}
-
-TEST_CASE("Basic functionality of IdSetSmall") {
-    osmium::index::IdSetSmall<osmium::unsigned_object_id_type> s;
-
-    REQUIRE_FALSE(s.get(17));
-    REQUIRE_FALSE(s.get(28));
-    REQUIRE(s.empty());
-
-    s.set(17);
-    REQUIRE(s.get(17));
-    REQUIRE_FALSE(s.get(28));
-    REQUIRE_FALSE(s.empty());
-
-    s.set(28);
-    REQUIRE(s.get(17));
-    REQUIRE(s.get(28));
-    REQUIRE_FALSE(s.empty());
-
-    s.clear();
-    REQUIRE(s.empty());
-}
-
-TEST_CASE("Iterating over IdSetSmall") {
-    osmium::index::IdSetSmall<osmium::unsigned_object_id_type> s;
-    s.set(7);
-    s.set(35);
-    s.set(35);
-    s.set(20);
-    s.set(1LL << 33);
-    s.set(21);
-    s.set((1LL << 27) + 13);
-
-    // needs to be called before size() and iterator will work properly
-    s.sort_unique();
-
-    REQUIRE(s.size() == 6);
-
-    auto it = s.begin();
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 7);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 20);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 21);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 35);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == (1LL << 27) + 13);
-    ++it;
-    REQUIRE(it != s.end());
-    REQUIRE(*it == 1LL << 33);
-    ++it;
-    REQUIRE(it == s.end());
-}
-
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
index 36fc074..810ef3b 100644
--- a/test/t/index/test_id_to_location.cpp
+++ b/test/t/index/test_id_to_location.cpp
@@ -15,14 +15,12 @@
 
 #include <osmium/index/node_locations_map.hpp>
 
-static_assert(osmium::index::empty_value<osmium::Location>() == osmium::Location{}, "Empty value for location is wrong");
-
 template <typename TIndex>
 void test_func_all(TIndex& index) {
-    const osmium::unsigned_object_id_type id1 = 12;
-    const osmium::unsigned_object_id_type id2 = 3;
-    const osmium::Location loc1{1.2, 4.5};
-    const osmium::Location loc2{3.5, -7.2};
+    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);
 
@@ -35,16 +33,14 @@ void test_func_all(TIndex& index) {
     REQUIRE_THROWS_AS(index.get(1), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(5), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
-    REQUIRE_THROWS_WITH(index.get(0), "id 0 not found");
-    REQUIRE_THROWS_WITH(index.get(1), "id 1 not found");
 }
 
 template <typename TIndex>
 void test_func_real(TIndex& index) {
-    const osmium::unsigned_object_id_type id1 = 12;
-    const osmium::unsigned_object_id_type id2 = 3;
-    const osmium::Location loc1{1.2, 4.5};
-    const osmium::Location loc2{3.5, -7.2};
+    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);
@@ -70,116 +66,115 @@ void test_func_real(TIndex& index) {
     REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
 }
 
-TEST_CASE("Map Id to location: Dummy") {
-    using index_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
+TEST_CASE("IdToLocation") {
 
-    index_type index1;
+    SECTION("Dummy") {
+        using index_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
+        index_type index1;
 
-    test_func_all<index_type>(index1);
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
-}
+        test_func_all<index_type>(index1);
 
-TEST_CASE("Map Id to location: DenseMemArray") {
-    using index_type = osmium::index::map::DenseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
+    }
 
-    index_type index1;
-    index1.reserve(1000);
-    test_func_all<index_type>(index1);
+    SECTION("DenseMemArray") {
+        using index_type = osmium::index::map::DenseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
 
-    index_type index2;
-    index2.reserve(1000);
-    test_func_real<index_type>(index2);
-}
+        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__
-TEST_CASE("Map Id to location: DenseMmapArray") {
-    using index_type = osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location>;
+    SECTION("DenseMmapArray") {
+        using index_type = osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location>;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 #else
 # pragma message("not running 'DenseMapMmap' test case on this machine")
 #endif
 
-TEST_CASE("Map Id to location: DenseFileArray") {
-    using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
+    SECTION("DenseFileArray") {
+        using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
 #ifdef OSMIUM_WITH_SPARSEHASH
 
-TEST_CASE("Map Id to location: SparseMemTable") {
-    using index_type = osmium::index::map::SparseMemTable<osmium::unsigned_object_id_type, osmium::Location>;
+    SECTION("SparseMemTable") {
+        using index_type = osmium::index::map::SparseMemTable<osmium::unsigned_object_id_type, osmium::Location>;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
 #endif
 
-TEST_CASE("Map Id to location: SparseMemMap") {
-    using index_type = osmium::index::map::SparseMemMap<osmium::unsigned_object_id_type, osmium::Location>;
+    SECTION("SparseMemMap") {
+        using index_type = osmium::index::map::SparseMemMap<osmium::unsigned_object_id_type, osmium::Location>;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
-
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-TEST_CASE("Map Id to location: SparseMemArray") {
-    using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
-    index_type index1;
+    SECTION("SparseMemArray") {
+        using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
+        index_type index1;
 
-    test_func_all<index_type>(index1);
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
 
-    REQUIRE(2 == index1.size());
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        REQUIRE(2 == index1.size());
 
-TEST_CASE("Map Id to location: Dynamic map choice") {
-    using map_type = osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
-    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
-    const std::vector<std::string> map_type_names = map_factory.map_types();
-    REQUIRE(map_type_names.size() >= 5);
+    SECTION("Dynamic map choice") {
+        using map_type = osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
+        const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
 
-    REQUIRE_THROWS_AS(map_factory.create_map(""), osmium::map_factory_error);
-    REQUIRE_THROWS_AS(map_factory.create_map("does not exist"), osmium::map_factory_error);
-    REQUIRE_THROWS_WITH(map_factory.create_map(""), "Need non-empty map type name");
-    REQUIRE_THROWS_WITH(map_factory.create_map("does not exist"), "Support for map type 'does not exist' not compiled into this binary");
+        const 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);
+        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);
+            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/io/test_compression_factory.cpp b/test/t/io/test_compression_factory.cpp
deleted file mode 100644
index 5428d82..0000000
--- a/test/t/io/test_compression_factory.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-
-#include "catch.hpp"
-
-#include <osmium/io/compression.hpp>
-
-TEST_CASE("Compression factory") {
-    const auto& factory = osmium::io::CompressionFactory::instance();
-
-    SECTION("compressor") {
-        REQUIRE(factory.create_compressor(osmium::io::file_compression::none, -1, osmium::io::fsync::no));
-    }
-
-    SECTION("decompressor") {
-        REQUIRE(factory.create_decompressor(osmium::io::file_compression::none, nullptr, 0));
-    }
-
-    SECTION("fail on undefined compression") {
-        REQUIRE_THROWS_AS({
-            factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no);
-        }, osmium::unsupported_file_format_error);
-        REQUIRE_THROWS_WITH({
-            factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no);
-        }, "Support for compression 'gzip' not compiled into this binary");
-    }
-
-}
-
diff --git a/test/t/io/test_reader_with_mock_parser.cpp b/test/t/io/test_reader_with_mock_parser.cpp
index b1076cc..c5c9975 100644
--- a/test/t/io/test_reader_with_mock_parser.cpp
+++ b/test/t/io/test_reader_with_mock_parser.cpp
@@ -24,9 +24,9 @@ public:
     MockParser(osmium::io::detail::future_string_queue_type& input_queue,
                osmium::io::detail::future_buffer_queue_type& output_queue,
                std::promise<osmium::io::Header>& header_promise,
-               osmium::io::detail::reader_options options,
+               osmium::osm_entity_bits::type read_types,
                const std::string& fail_in) :
-        Parser(input_queue, output_queue, header_promise, options),
+        Parser(input_queue, output_queue, header_promise, read_types),
         m_fail_in(fail_in) {
     }
 
@@ -59,8 +59,8 @@ TEST_CASE("Test Reader using MockParser") {
         [&](osmium::io::detail::future_string_queue_type& input_queue,
             osmium::io::detail::future_buffer_queue_type& output_queue,
             std::promise<osmium::io::Header>& header_promise,
-            osmium::io::detail::reader_options options) {
-        return std::unique_ptr<osmium::io::detail::Parser>(new MockParser(input_queue, output_queue, header_promise, options, fail_in));
+            osmium::osm_entity_bits::type read_which_entities) {
+        return std::unique_ptr<osmium::io::detail::Parser>(new MockParser(input_queue, output_queue, header_promise, read_which_entities, fail_in));
     });
 
     SECTION("no failure") {
diff --git a/test/t/osm/test_entity_bits.cpp b/test/t/osm/test_entity_bits.cpp
deleted file mode 100644
index a124fa3..0000000
--- a/test/t/osm/test_entity_bits.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "catch.hpp"
-
-#include <osmium/osm/entity_bits.hpp>
-
-static_assert((osmium::osm_entity_bits::node
-              |osmium::osm_entity_bits::way
-              |osmium::osm_entity_bits::relation)
-              == osmium::osm_entity_bits::nwr, "entity_bits nwr failed");
-
-static_assert((osmium::osm_entity_bits::node
-              |osmium::osm_entity_bits::way
-              |osmium::osm_entity_bits::relation
-              |osmium::osm_entity_bits::area)
-              == osmium::osm_entity_bits::nwra, "entity_bits nwra failed");
-
-static_assert((osmium::osm_entity_bits::nwra
-              |osmium::osm_entity_bits::changeset)
-              == osmium::osm_entity_bits::all, "entity_bits all failed");
-
-static_assert((osmium::osm_entity_bits::all
-              &osmium::osm_entity_bits::node)
-              == osmium::osm_entity_bits::node, "entity_bits node failed");
-
-static_assert((~osmium::osm_entity_bits::all) == osmium::osm_entity_bits::nothing, "entity_bits nothing is the inverse of all");
-static_assert((~osmium::osm_entity_bits::nothing) == osmium::osm_entity_bits::all, "entity_bits all is the inverse of nothing");
-static_assert((~osmium::osm_entity_bits::changeset) == osmium::osm_entity_bits::nwra, "entity_bits nwra is the inverse of changeset");
-
-TEST_CASE("Bitwise 'and' and 'or' on entity bits") {
-    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_FALSE((entities & osmium::osm_entity_bits::changeset));
-
-    entities &= osmium::osm_entity_bits::node;
-    REQUIRE((entities & osmium::osm_entity_bits::node));
-    REQUIRE_FALSE((entities & osmium::osm_entity_bits::way));
-    REQUIRE(entities == osmium::osm_entity_bits::node);
-}
-
-TEST_CASE("Bitwise 'not' on entity bits") {
-    REQUIRE(~osmium::osm_entity_bits::all == osmium::osm_entity_bits::nothing);
-    REQUIRE(~osmium::osm_entity_bits::nothing == osmium::osm_entity_bits::all);
-    REQUIRE(~osmium::osm_entity_bits::node == (osmium::osm_entity_bits::way | osmium::osm_entity_bits::relation | osmium::osm_entity_bits::area | osmium::osm_entity_bits::changeset));
-    REQUIRE(~osmium::osm_entity_bits::nwr == (osmium::osm_entity_bits::area | osmium::osm_entity_bits::changeset));
-    REQUIRE(~osmium::osm_entity_bits::nwra == osmium::osm_entity_bits::changeset);
-}
-
-TEST_CASE("Converting item types to entity bits") {
-    REQUIRE(osmium::osm_entity_bits::nothing   == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined));
-    REQUIRE(osmium::osm_entity_bits::node      == osmium::osm_entity_bits::from_item_type(osmium::item_type::node));
-    REQUIRE(osmium::osm_entity_bits::way       == osmium::osm_entity_bits::from_item_type(osmium::item_type::way));
-    REQUIRE(osmium::osm_entity_bits::relation  == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation));
-    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));
-}
-

-- 
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