[libosmium] 01/04: Imported Upstream version 2.6.1

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Thu Feb 25 19:28:57 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 e7709959e84dda3dca58cb628c84baa082a6a422
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Thu Feb 25 09:25:04 2016 +0100

    Imported Upstream version 2.6.1
---
 CHANGELOG.md                                    |  19 ++-
 CMakeLists.txt                                  |   9 +-
 examples/osmium_area_test.cpp                   |  12 +-
 include/osmium/area/assembler.hpp               |  30 ++--
 include/osmium/area/detail/proto_ring.hpp       |   8 +
 include/osmium/area/detail/segment_list.hpp     |  27 +++-
 include/osmium/io/detail/pbf_output_format.hpp  |  55 +++----
 include/osmium/io/detail/protobuf_tags.hpp      |   2 +-
 include/osmium/relations/collector.hpp          |  95 ++++++-----
 include/osmium/relations/detail/member_meta.hpp |  15 --
 include/osmium/util/iterator.hpp                |  74 +++++++++
 include/osmium/util/memory.hpp                  |   6 +-
 include/protozero/config.hpp                    |   4 +-
 include/protozero/exception.hpp                 |   8 +-
 include/protozero/pbf_builder.hpp               |  14 +-
 include/protozero/pbf_message.hpp               |   2 +-
 include/protozero/pbf_reader.hpp                |  40 +++--
 include/protozero/pbf_writer.hpp                | 202 ++++++++++++++++++++++--
 include/protozero/{pbf_types.hpp => types.hpp}  |   8 +-
 include/protozero/varint.hpp                    |   4 +-
 include/protozero/version.hpp                   |   6 +-
 test/t/basic/test_node_ref.cpp                  |  52 ++++++
 22 files changed, 517 insertions(+), 175 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ca6518..3946448 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,22 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [2.6.1] - 2016-02-22
+
+### Added
+
+- Add `WITH_PROFILING` option to CMake config. When enabled, this sets the
+  `-fno-omit-frame-pointer` compiler option.
+
+### Changed
+
+- Massive speed improvements when building multipolygons.
+- Uses (and includes) new version 1.3.0 of protozero library.
+- Removed dependency on Boost Iterator for PBF writer.
+- Example program `osmium_area_test` now uses `cerr` instead of `cout` for
+  debug output.
+
+
 ## [2.6.0] - 2016-02-04
 
 ### Added
@@ -260,7 +276,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   Doxygen (up to version 1.8.8). This version contains a workaround to fix
   this.
 
-[unreleased]: https://github.com/osmcode/libosmium/compare/v2.6.0...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.6.1...HEAD
+[2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1
 [2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0
 [2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4
 [2.5.3]: https://github.com/osmcode/libosmium/compare/v2.5.2...v2.5.3
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7339467..bc9b12e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@ project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
 set(LIBOSMIUM_VERSION_MINOR 6)
-set(LIBOSMIUM_VERSION_PATCH 0)
+set(LIBOSMIUM_VERSION_PATCH 1)
 
 set(LIBOSMIUM_VERSION
     "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
@@ -58,6 +58,8 @@ option(INSTALL_GDALCPP   "also install gdalcpp headers" OFF)
 option(INSTALL_PROTOZERO "also install protozero headers" OFF)
 option(INSTALL_UTFCPP    "also install utfcpp headers" OFF)
 
+option(WITH_PROFILING    "add flags needed for profiling" OFF)
+
 
 #-----------------------------------------------------------------------------
 #
@@ -205,12 +207,17 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}"
     CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds."
     FORCE)
 
+if(WITH_PROFILING)
+    add_definitions(-fno-omit-frame-pointer)
+endif()
+
 
 #-----------------------------------------------------------------------------
 #
 #  Build Type
 #
 #-----------------------------------------------------------------------------
+
 # In 'Dev' mode: compile with very strict warnings and turn them into errors.
 if(CMAKE_BUILD_TYPE STREQUAL "Dev")
     if(NOT MSVC)
diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp
index f072c5e..e9b7a18 100644
--- a/examples/osmium_area_test.cpp
+++ b/examples/osmium_area_test.cpp
@@ -99,13 +99,13 @@ int main(int argc, char* argv[]) {
     osmium::area::Assembler::config_type assembler_config;
     osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
 
-    std::cout << "Pass 1...\n";
+    std::cerr << "Pass 1...\n";
     osmium::io::Reader reader1(infile, osmium::osm_entity_bits::relation);
     collector.read_relations(reader1);
     reader1.close();
-    std::cout << "Pass 1 done\n";
+    std::cerr << "Pass 1 done\n";
 
-    std::cout << "Memory:\n";
+    std::cerr << "Memory:\n";
     collector.used_memory();
 
     index_pos_type index_pos;
@@ -113,15 +113,15 @@ int main(int argc, char* argv[]) {
     location_handler_type location_handler(index_pos, index_neg);
     location_handler.ignore_errors(); // XXX
 
-    std::cout << "Pass 2...\n";
+    std::cerr << "Pass 2...\n";
     osmium::io::Reader reader2(infile);
     osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) {
         osmium::apply(buffer, handler);
     }));
     reader2.close();
-    std::cout << "Pass 2 done\n";
+    std::cerr << "Pass 2 done\n";
 
-    std::cout << "Memory:\n";
+    std::cerr << "Memory:\n";
     collector.used_memory();
 
     std::vector<const osmium::Relation*> incomplete_relations = collector.get_incomplete_relations();
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index 1d9b0df..a2d1c8e 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -232,7 +232,7 @@ namespace osmium {
                     if (!ring.closed()) {
                         open_rings = true;
                         if (m_config.problem_reporter) {
-                            m_config.problem_reporter->report_ring_not_closed(ring.get_segment_front().first().location(), ring.get_segment_back().second().location());
+                            m_config.problem_reporter->report_ring_not_closed(ring.get_node_ref_front().location(), ring.get_node_ref_back().location());
                         }
                     }
                 }
@@ -248,14 +248,14 @@ namespace osmium {
              * true.
              */
             bool possibly_combine_rings_back(ProtoRing& ring) {
-                const osmium::NodeRef& nr = ring.get_segment_back().second();
+                const osmium::NodeRef& nr = ring.get_node_ref_back();
 
                 if (debug()) {
                     std::cerr << "      possibly_combine_rings_back()\n";
                 }
                 for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
                     if (&*it != &ring && !it->closed()) {
-                        if (has_same_location(nr, it->get_segment_front().first())) {
+                        if (has_same_location(nr, it->get_node_ref_front())) {
                             if (debug()) {
                                 std::cerr << "      ring.last=it->first\n";
                             }
@@ -263,7 +263,7 @@ namespace osmium {
                             m_rings.erase(it);
                             return true;
                         }
-                        if (has_same_location(nr, it->get_segment_back().second())) {
+                        if (has_same_location(nr, it->get_node_ref_back())) {
                             if (debug()) {
                                 std::cerr << "      ring.last=it->last\n";
                             }
@@ -284,14 +284,14 @@ namespace osmium {
              * true.
              */
             bool possibly_combine_rings_front(ProtoRing& ring) {
-                const osmium::NodeRef& nr = ring.get_segment_front().first();
+                const osmium::NodeRef& nr = ring.get_node_ref_front();
 
                 if (debug()) {
                     std::cerr << "      possibly_combine_rings_front()\n";
                 }
                 for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
                     if (&*it != &ring && !it->closed()) {
-                        if (has_same_location(nr, it->get_segment_back().second())) {
+                        if (has_same_location(nr, it->get_node_ref_back())) {
                             if (debug()) {
                                 std::cerr << "      ring.first=it->last\n";
                             }
@@ -300,7 +300,7 @@ namespace osmium {
                             m_rings.erase(it);
                             return true;
                         }
-                        if (has_same_location(nr, it->get_segment_front().first())) {
+                        if (has_same_location(nr, it->get_node_ref_front())) {
                             if (debug()) {
                                 std::cerr << "      ring.first=it->first\n";
                             }
@@ -368,7 +368,7 @@ namespace osmium {
                 }
 
                 osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size());
-                std::copy(ring.segments().begin(), ring.segments().end(), segments.begin());
+                std::copy(ring.segments().cbegin(), ring.segments().cend(), segments.begin());
                 std::sort(segments.begin(), segments.end());
                 const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) {
                     return has_same_location(s1.first(), s2.first());
@@ -433,14 +433,14 @@ namespace osmium {
                     }
                     {
                         osmium::builder::OuterRingBuilder ring_builder(builder.buffer(), &builder);
-                        ring_builder.add_node_ref(ring->get_segment_front().first());
+                        ring_builder.add_node_ref(ring->get_node_ref_front());
                         for (const auto& segment : ring->segments()) {
                             ring_builder.add_node_ref(segment.second());
                         }
                     }
                     for (ProtoRing* inner : ring->inner_rings()) {
                         osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder);
-                        ring_builder.add_node_ref(inner->get_segment_front().first());
+                        ring_builder.add_node_ref(inner->get_node_ref_front());
                         for (const auto& segment : inner->segments()) {
                             ring_builder.add_node_ref(segment.second());
                         }
@@ -459,21 +459,21 @@ namespace osmium {
                             std::cerr << " => ring CLOSED\n";
                         }
                     } else {
-                        if (has_same_location(ring.get_segment_back().second(), segment.first())) {
+                        if (has_same_location(ring.get_node_ref_back(), segment.first())) {
                             combine_rings_back(segment, ring);
                             return true;
                         }
-                        if (has_same_location(ring.get_segment_back().second(), segment.second())) {
+                        if (has_same_location(ring.get_node_ref_back(), segment.second())) {
                             segment.swap_locations();
                             combine_rings_back(segment, ring);
                             return true;
                         }
-                        if (has_same_location(ring.get_segment_front().first(), segment.first())) {
+                        if (has_same_location(ring.get_node_ref_front(), segment.first())) {
                             segment.swap_locations();
                             combine_rings_front(segment, ring);
                             return true;
                         }
-                        if (has_same_location(ring.get_segment_front().first(), segment.second())) {
+                        if (has_same_location(ring.get_node_ref_front(), segment.second())) {
                             combine_rings_front(segment, ring);
                             return true;
                         }
@@ -696,7 +696,7 @@ namespace osmium {
                 }
 
                 // Now create the Area object and add the attributes and tags
-                // from the relation.
+                // from the way.
                 {
                     osmium::builder::AreaBuilder builder(out_buffer);
                     builder.initialize_from_object(way);
diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp
index 59478dc..c4edf40 100644
--- a/include/osmium/area/detail/proto_ring.hpp
+++ b/include/osmium/area/detail/proto_ring.hpp
@@ -118,6 +118,10 @@ namespace osmium {
                     return m_segments.front();
                 }
 
+                const NodeRef& get_node_ref_front() const {
+                    return get_segment_front().first();
+                }
+
                 const NodeRefSegment& get_segment_back() const {
                     return m_segments.back();
                 }
@@ -126,6 +130,10 @@ namespace osmium {
                     return m_segments.back();
                 }
 
+                const NodeRef& get_node_ref_back() const {
+                    return get_segment_back().second();
+                }
+
                 bool closed() const {
                     return m_segments.front().first().location() == m_segments.back().second().location();
                 }
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 29eec58..05e0cd8 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -58,7 +58,7 @@ namespace osmium {
              */
             class SegmentList {
 
-                typedef std::vector<NodeRefSegment> slist_type;
+                using slist_type = std::vector<NodeRefSegment>;
 
                 slist_type m_segments;
 
@@ -67,10 +67,11 @@ namespace osmium {
             public:
 
                 explicit SegmentList(bool debug) noexcept :
+                    m_segments(),
                     m_debug(debug) {
                 }
 
-                ~SegmentList() = default;
+                ~SegmentList() noexcept = default;
 
                 SegmentList(const SegmentList&) = delete;
                 SegmentList(SegmentList&&) = delete;
@@ -88,6 +89,15 @@ namespace osmium {
                 }
 
                 typedef slist_type::const_iterator const_iterator;
+                typedef slist_type::iterator iterator;
+
+                iterator begin() noexcept {
+                    return m_segments.begin();
+                }
+
+                iterator end() noexcept {
+                    return m_segments.end();
+                }
 
                 const_iterator begin() const noexcept {
                     return m_segments.begin();
@@ -98,8 +108,8 @@ namespace osmium {
                 }
 
                 /**
-                 * Enable or disable debug output to stderr. This is for Osmium
-                 * developers only.
+                 * Enable or disable debug output to stderr. This is used
+                 * for debugging libosmium itself.
                  */
                 void enable_debug_output(bool debug = true) noexcept {
                     m_debug = debug;
@@ -148,9 +158,9 @@ namespace osmium {
 
                 /**
                  * Find duplicate segments (ie same start and end point) in the
-                 * list and remove them. This will always remove pairs of the same
-                 * segment. So if there are three, for instance, two will be
-                 * removed and one will be left.
+                 * list and remove them. This will always remove pairs of the
+                 * same segment. So if there are three, for instance, two will
+                 * be removed and one will be left.
                  */
                 void erase_duplicate_segments() {
                     while (true) {
@@ -168,7 +178,8 @@ namespace osmium {
                 /**
                  * Find intersection between segments.
                  *
-                 * @param problem_reporter Any intersections found are reported to this object.
+                 * @param problem_reporter Any intersections found are
+                 *                         reported to this object.
                  * @returns true if there are intersections.
                  */
                 bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 02b543f..878d7b4 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -44,10 +44,6 @@ DEALINGS IN THE SOFTWARE.
 #include <time.h>
 #include <utility>
 
-// needed for older boost libraries
-#define BOOST_RESULT_OF_USE_DECLTYPE
-#include <boost/iterator/transform_iterator.hpp>
-
 #include <protozero/pbf_builder.hpp>
 
 #include <osmium/handler.hpp>
@@ -441,22 +437,19 @@ namespace osmium {
 
                 template <typename T>
                 void add_meta(const osmium::OSMObject& object, T& pbf_object) {
-                    const osmium::TagList& tags = object.tags();
-
-                    auto map_tag_key = [this](const osmium::Tag& tag) -> uint32_t {
-                        return m_primitive_block.store_in_stringtable(tag.key());
-                    };
-                    auto map_tag_value = [this](const osmium::Tag& tag) -> uint32_t {
-                        return m_primitive_block.store_in_stringtable(tag.value());
-                    };
-
-                    pbf_object.add_packed_uint32(T::enum_type::packed_uint32_keys,
-                        boost::make_transform_iterator(tags.begin(), map_tag_key),
-                        boost::make_transform_iterator(tags.end(), map_tag_key));
+                    {
+                        protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_keys)};
+                        for (const auto& tag : object.tags()) {
+                            field.add_element(m_primitive_block.store_in_stringtable(tag.key()));
+                        }
+                    }
 
-                    pbf_object.add_packed_uint32(T::enum_type::packed_uint32_vals,
-                        boost::make_transform_iterator(tags.begin(), map_tag_value),
-                        boost::make_transform_iterator(tags.end(), map_tag_value));
+                    {
+                        protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_vals)};
+                        for (const auto& tag : object.tags()) {
+                            field.add_element(m_primitive_block.store_in_stringtable(tag.value()));
+                        }
+                    }
 
                     if (m_options.add_metadata) {
                         protozero::pbf_builder<OSMFormat::Info> pbf_info(pbf_object, T::enum_type::optional_Info_info);
@@ -596,12 +589,12 @@ namespace osmium {
                     pbf_relation.add_int64(OSMFormat::Relation::required_int64_id, relation.id());
                     add_meta(relation, pbf_relation);
 
-                    auto map_member_role = [this](const osmium::RelationMember& member) -> uint32_t {
-                        return m_primitive_block.store_in_stringtable(member.role());
-                    };
-                    pbf_relation.add_packed_int32(OSMFormat::Relation::packed_int32_roles_sid,
-                        boost::make_transform_iterator(relation.members().begin(), map_member_role),
-                        boost::make_transform_iterator(relation.members().end(), map_member_role));
+                    {
+                        protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_int32_roles_sid)};
+                        for (const auto& member : relation.members()) {
+                            field.add_element(m_primitive_block.store_in_stringtable(member.role()));
+                        }
+                    }
 
                     static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type {
                         return member->ref();
@@ -612,12 +605,12 @@ namespace osmium {
                     it_type last { members.cend(), members.cend(), map_member_ref };
                     pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last);
 
-                    static auto map_member_type = [](const osmium::RelationMember& member) noexcept -> int32_t {
-                        return int32_t(osmium::item_type_to_nwr_index(member.type()));
-                    };
-                    pbf_relation.add_packed_int32(OSMFormat::Relation::packed_MemberType_types,
-                        boost::make_transform_iterator(relation.members().begin(), map_member_type),
-                        boost::make_transform_iterator(relation.members().end(), map_member_type));
+                    {
+                        protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)};
+                        for (const auto& member : relation.members()) {
+                            field.add_element(int32_t(osmium::item_type_to_nwr_index(member.type())));
+                        }
+                    }
                 }
 
             }; // class PBFOutputFormat
diff --git a/include/osmium/io/detail/protobuf_tags.hpp b/include/osmium/io/detail/protobuf_tags.hpp
index 1106f03..bdaabba 100644
--- a/include/osmium/io/detail/protobuf_tags.hpp
+++ b/include/osmium/io/detail/protobuf_tags.hpp
@@ -33,7 +33,7 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 
 namespace osmium {
 
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index af9d63c..7d7d14d 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -49,6 +49,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/types.hpp>
 #include <osmium/handler.hpp>
 #include <osmium/memory/buffer.hpp>
+#include <osmium/util/iterator.hpp>
 #include <osmium/visitor.hpp>
 
 #include <osmium/relations/detail/relation_meta.hpp>
@@ -61,6 +62,17 @@ namespace osmium {
      */
     namespace relations {
 
+        namespace detail {
+
+            template <typename R>
+            inline typename std::iterator_traits<typename R::iterator>::difference_type count_not_removed(const R& range) {
+                return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) {
+                    return !mm.removed();
+                });
+            }
+
+        } // namespace detail
+
         /**
          * The Collector class collects members of a relation. This is a generic
          * base class that can be used to assemble all kinds of relations. It has numerous
@@ -175,7 +187,9 @@ namespace osmium {
              * One vector each for nodes, ways, and relations containing all
              * mappings from member ids to their relations.
              */
-            std::vector<MemberMeta> m_member_meta[3];
+            using mm_vector_type = std::vector<MemberMeta>;
+            using mm_iterator = mm_vector_type::iterator;
+            mm_vector_type m_member_meta[3];
 
             int m_count_complete = 0;
 
@@ -184,6 +198,11 @@ namespace osmium {
 
             static constexpr size_t initial_buffer_size = 1024 * 1024;
 
+            iterator_range<mm_iterator> find_member_meta(osmium::item_type type, osmium::object_id_type id) {
+                auto& mmv = member_meta(type);
+                return iterator_range<mm_iterator>{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))};
+            }
+
         public:
 
             /**
@@ -367,10 +386,9 @@ namespace osmium {
              *          relation and false otherwise
              */
             bool find_and_add_object(const osmium::OSMObject& object) {
-                auto& mmv = member_meta(object.type());
-                auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id()));
+                auto range = find_member_meta(object.type(), object.id());
 
-                if (osmium::relations::count_not_removed(range.first, range.second) == 0) {
+                if (detail::count_not_removed(range) == 0) {
                     // nothing found
                     return false;
                 }
@@ -379,13 +397,12 @@ namespace osmium {
                     members_buffer().add_item(object);
                     const size_t member_offset = members_buffer().commit();
 
-                    for (auto it = range.first; it != range.second; ++it) {
-                        it->set_buffer_offset(member_offset);
+                    for (auto& member_meta : range) {
+                        member_meta.set_buffer_offset(member_offset);
                     }
                 }
 
-                for (auto it = range.first; it != range.second; ++it) {
-                    MemberMeta& member_meta = *it;
+                for (auto& member_meta : range) {
                     if (member_meta.removed()) {
                         break;
                     }
@@ -405,11 +422,6 @@ namespace osmium {
                     }
                 }
 
-                // Remove MemberMetas that were marked as removed.
-                mmv.erase(std::remove_if(mmv.begin(), mmv.end(), [](MemberMeta& mm) {
-                    return mm.removed();
-                }), mmv.end());
-
                 return true;
             }
 
@@ -417,19 +429,18 @@ namespace osmium {
                 const osmium::Relation& relation = get_relation(relation_meta);
                 for (const auto& member : relation.members()) {
                     if (member.ref() != 0) {
-                        auto& mmv = member_meta(member.type());
-                        auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(member.ref()));
-                        assert(range.first != range.second);
+                        auto range = find_member_meta(member.type(), member.ref());
+                        assert(!range.empty());
 
                         // if this is the last time this object was needed
                         // then mark it as removed
-                        if (osmium::relations::count_not_removed(range.first, range.second) == 1) {
-                            get_member(range.first->buffer_offset()).set_removed(true);
+                        if (detail::count_not_removed(range) == 1) {
+                            get_member(range.begin()->buffer_offset()).set_removed(true);
                         }
 
-                        for (auto it = range.first; it != range.second; ++it) {
-                            if (!it->removed() && relation.id() == get_relation(it->relation_pos()).id()) {
-                                it->remove();
+                        for (auto& member_meta : range) {
+                            if (!member_meta.removed() && relation.id() == get_relation(member_meta.relation_pos()).id()) {
+                                member_meta.remove();
                                 break;
                             }
                         }
@@ -446,24 +457,24 @@ namespace osmium {
                 const uint64_t relations_buffer_capacity = m_relations_buffer.capacity();
                 const uint64_t members_buffer_capacity = m_members_buffer.capacity();
 
-                std::cout << "  nR  = m_relations.capacity() ........... = " << std::setw(12) << m_relations.capacity() << "\n";
-                std::cout << "  nMN = m_member_meta[NODE].capacity() ... = " << std::setw(12) << m_member_meta[0].capacity() << "\n";
-                std::cout << "  nMW = m_member_meta[WAY].capacity() .... = " << std::setw(12) << m_member_meta[1].capacity() << "\n";
-                std::cout << "  nMR = m_member_meta[RELATION].capacity() = " << std::setw(12) << m_member_meta[2].capacity() << "\n";
-                std::cout << "  nM  = m_member_meta[*].capacity() ...... = " << std::setw(12) << nmembers << "\n";
+                std::cerr << "  nR  = m_relations.capacity() ........... = " << std::setw(12) << m_relations.capacity() << "\n";
+                std::cerr << "  nMN = m_member_meta[NODE].capacity() ... = " << std::setw(12) << m_member_meta[0].capacity() << "\n";
+                std::cerr << "  nMW = m_member_meta[WAY].capacity() .... = " << std::setw(12) << m_member_meta[1].capacity() << "\n";
+                std::cerr << "  nMR = m_member_meta[RELATION].capacity() = " << std::setw(12) << m_member_meta[2].capacity() << "\n";
+                std::cerr << "  nM  = m_member_meta[*].capacity() ...... = " << std::setw(12) << nmembers << "\n";
 
-                std::cout << "  sRM = sizeof(RelationMeta) ............. = " << std::setw(12) << sizeof(RelationMeta) << "\n";
-                std::cout << "  sMM = sizeof(MemberMeta) ............... = " << std::setw(12) << sizeof(MemberMeta) << "\n\n";
+                std::cerr << "  sRM = sizeof(RelationMeta) ............. = " << std::setw(12) << sizeof(RelationMeta) << "\n";
+                std::cerr << "  sMM = sizeof(MemberMeta) ............... = " << std::setw(12) << sizeof(MemberMeta) << "\n\n";
 
-                std::cout << "  nR * sRM ............................... = " << std::setw(12) << relations << "\n";
-                std::cout << "  nM * sMM ............................... = " << std::setw(12) << members << "\n";
-                std::cout << "  relations_buffer_capacity .............. = " << std::setw(12) << relations_buffer_capacity << "\n";
-                std::cout << "  members_buffer_capacity ................ = " << std::setw(12) << members_buffer_capacity << "\n";
+                std::cerr << "  nR * sRM ............................... = " << std::setw(12) << relations << "\n";
+                std::cerr << "  nM * sMM ............................... = " << std::setw(12) << members << "\n";
+                std::cerr << "  relations_buffer_capacity .............. = " << std::setw(12) << relations_buffer_capacity << "\n";
+                std::cerr << "  members_buffer_capacity ................ = " << std::setw(12) << members_buffer_capacity << "\n";
 
                 const uint64_t total = relations + members + relations_buffer_capacity + members_buffer_capacity;
 
-                std::cout << "  total .................................. = " << std::setw(12) << total << "\n";
-                std::cout << "  =======================================================\n";
+                std::cerr << "  total .................................. = " << std::setw(12) << total << "\n";
+                std::cerr << "  =======================================================\n";
 
                 return relations_buffer_capacity + members_buffer_capacity + relations + members;
             }
@@ -481,10 +492,9 @@ namespace osmium {
             }
 
             size_t get_offset(osmium::item_type type, osmium::object_id_type id) {
-                const auto& mmv = member_meta(type);
-                const auto range = std::equal_range(mmv.cbegin(), mmv.cend(), MemberMeta(id));
-                assert(range.first != range.second);
-                return range.first->buffer_offset();
+                const auto range = find_member_meta(type, id);
+                assert(!range.empty());
+                return range.begin()->buffer_offset();
             }
 
             template <typename TIter>
@@ -502,11 +512,10 @@ namespace osmium {
 
             void moving_in_buffer(size_t old_offset, size_t new_offset) {
                 const osmium::OSMObject& object = m_members_buffer.get<osmium::OSMObject>(old_offset);
-                auto& mmv = member_meta(object.type());
-                auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id()));
-                for (auto it = range.first; it != range.second; ++it) {
-                    assert(it->buffer_offset() == old_offset);
-                    it->set_buffer_offset(new_offset);
+                auto range = find_member_meta(object.type(), object.id());
+                for (auto& member_meta : range) {
+                    assert(member_meta.buffer_offset() == old_offset);
+                    member_meta.set_buffer_offset(new_offset);
                 }
             }
 
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
index a540f43..f0e9c36 100644
--- a/include/osmium/relations/detail/member_meta.hpp
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -136,21 +136,6 @@ namespace osmium {
             return out;
         }
 
-        /**
-         * Count the number of MemberMeta objects in the iterator range
-         * that are not marked as removed.
-         *
-         * @tparam TIter Iterator that dereferences to a MemberMeta
-         * @param begin Begin of iterator range
-         * @param end End of iterator range
-         */
-        template <typename TIter>
-        inline typename std::iterator_traits<TIter>::difference_type count_not_removed(TIter begin, TIter end) {
-            return std::count_if(begin, end, [](MemberMeta& mm) {
-                return !mm.removed();
-            });
-        }
-
     } // namespace relations
 
 } // namespace osmium
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/iterator.hpp
new file mode 100644
index 0000000..4cef519
--- /dev/null
+++ b/include/osmium/util/iterator.hpp
@@ -0,0 +1,74 @@
+#ifndef OSMIUM_UTIL_ITERATOR_HPP
+#define OSMIUM_UTIL_ITERATOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstddef>
+#include <utility>
+
+namespace osmium {
+
+    template <typename It, typename P = std::pair<It, It>>
+    struct iterator_range : public P {
+
+        using iterator = It;
+
+        iterator_range(P&& p) :
+            P(std::forward<P>(p)) {
+        }
+
+        It begin() {
+            return this->first;
+        }
+
+        It end() {
+            return this->second;
+        }
+
+        It begin() const {
+            return this->first;
+        }
+
+        It end() const {
+            return this->second;
+        }
+
+        size_t empty() const {
+            return begin() == end();
+        }
+
+    };
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_ITERATOR_HPP
diff --git a/include/osmium/util/memory.hpp b/include/osmium/util/memory.hpp
index 7ef445b..777a6e0 100644
--- a/include/osmium/util/memory.hpp
+++ b/include/osmium/util/memory.hpp
@@ -45,9 +45,9 @@ namespace osmium {
         int m_peak = 0;
 
 #ifdef __linux__
-        int parse_number(const std::string& line) {
-            int f = line.find_first_of("0123456789");
-            int l = line.find_last_of("0123456789");
+        static int parse_number(const std::string& line) {
+            const auto f = line.find_first_of("0123456789");
+            const auto l = line.find_last_of("0123456789");
             return std::atoi(line.substr(f, l-f+1).c_str());
         }
 #endif
diff --git a/include/protozero/config.hpp b/include/protozero/config.hpp
index 4086994..8465c96 100644
--- a/include/protozero/config.hpp
+++ b/include/protozero/config.hpp
@@ -40,7 +40,9 @@ documentation.
 // in this case.
 #if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
 # if !defined(__arm__) && !defined(_M_ARM)
-#  define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+#  ifndef PROTOZERO_DO_NOT_USE_BARE_POINTER
+#   define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+#  endif
 # endif
 #endif
 
diff --git a/include/protozero/exception.hpp b/include/protozero/exception.hpp
index 1229f7d..5c7ab54 100644
--- a/include/protozero/exception.hpp
+++ b/include/protozero/exception.hpp
@@ -29,7 +29,7 @@ namespace protozero {
  */
 struct exception : std::exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "pbf exception"; }
+    const char *what() const noexcept override { return "pbf exception"; }
 };
 
 /**
@@ -38,7 +38,7 @@ struct exception : std::exception {
  */
 struct varint_too_long_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "varint too long exception"; }
+    const char *what() const noexcept override { return "varint too long exception"; }
 };
 
 /**
@@ -47,7 +47,7 @@ struct varint_too_long_exception : exception {
  */
 struct unknown_pbf_wire_type_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "unknown pbf field type exception"; }
+    const char *what() const noexcept override { return "unknown pbf field type exception"; }
 };
 
 /**
@@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception {
  */
 struct end_of_buffer_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "end of buffer exception"; }
+    const char *what() const noexcept override { return "end of buffer exception"; }
 };
 
 } // end namespace protozero
diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp
index 063fa9c..548f4ce 100644
--- a/include/protozero/pbf_builder.hpp
+++ b/include/protozero/pbf_builder.hpp
@@ -18,7 +18,7 @@ documentation.
 
 #include <type_traits>
 
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/pbf_writer.hpp>
 
 namespace protozero {
@@ -26,8 +26,10 @@ namespace protozero {
 /**
  * The pbf_builder is used to write PBF formatted messages into a buffer. It
  * is based on the pbf_writer class and has all the same methods. The
- * difference is that whereever the pbf_writer class takes an integer tag,
- * this template class takes a tag of the template type T.
+ * difference is that while the pbf_writer class takes an integer tag,
+ * this template class takes a tag of the template type T. The idea is that
+ * T will be an enumeration value and this helps reduce the possibility of
+ * programming errors.
  *
  * Almost all methods in this class can throw an std::bad_alloc exception if
  * the std::string used as a buffer wants to resize.
@@ -77,7 +79,7 @@ public:
 #undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
 /// @endcond
 
-    inline void add_bytes(T tag, const char* value, size_t size) {
+    inline void add_bytes(T tag, const char* value, std::size_t size) {
         pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
     }
 
@@ -85,7 +87,7 @@ public:
         pbf_writer::add_bytes(pbf_tag_type(tag), value);
     }
 
-    inline void add_string(T tag, const char* value, size_t size) {
+    inline void add_string(T tag, const char* value, std::size_t size) {
         pbf_writer::add_string(pbf_tag_type(tag), value, size);
     }
 
@@ -97,7 +99,7 @@ public:
         pbf_writer::add_string(pbf_tag_type(tag), value);
     }
 
-    inline void add_message(T tag, const char* value, size_t size) {
+    inline void add_message(T tag, const char* value, std::size_t size) {
         pbf_writer::add_message(pbf_tag_type(tag), value, size);
     }
 
diff --git a/include/protozero/pbf_message.hpp b/include/protozero/pbf_message.hpp
index 7fef06f..45f01c1 100644
--- a/include/protozero/pbf_message.hpp
+++ b/include/protozero/pbf_message.hpp
@@ -19,7 +19,7 @@ documentation.
 #include <type_traits>
 
 #include <protozero/pbf_reader.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 
 namespace protozero {
 
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
index aced901..58b3884 100644
--- a/include/protozero/pbf_reader.hpp
+++ b/include/protozero/pbf_reader.hpp
@@ -25,7 +25,7 @@ documentation.
 
 #include <protozero/config.hpp>
 #include <protozero/exception.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/varint.hpp>
 
 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
@@ -94,11 +94,12 @@ class pbf_reader {
 #ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
 
     template <typename T>
-    inline std::pair<const T*, const T*> packed_fixed() {
-        protozero_assert(tag() != 0 && "call next() before accessing field value");
-        auto len = get_len_and_skip();
-        protozero_assert(len % sizeof(T) == 0);
-        return std::make_pair(reinterpret_cast<const T*>(m_data-len), reinterpret_cast<const T*>(m_data));
+    using const_fixed_iterator = const T*;
+
+    template <typename T>
+    inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
+        return std::make_pair(reinterpret_cast<const T*>(first),
+                              reinterpret_cast<const T*>(last));
     }
 
 #else
@@ -157,16 +158,21 @@ class pbf_reader {
     }; // class const_fixed_iterator
 
     template <typename T>
+    inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
+        return std::make_pair(const_fixed_iterator<T>(first, last),
+                              const_fixed_iterator<T>(last, last));
+    }
+
+#endif
+
+    template <typename T>
     inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> packed_fixed() {
         protozero_assert(tag() != 0 && "call next() before accessing field value");
         auto len = get_len_and_skip();
         protozero_assert(len % sizeof(T) == 0);
-        return std::make_pair(const_fixed_iterator<T>(m_data-len, m_data),
-                              const_fixed_iterator<T>(m_data, m_data));
+        return create_fixed_iterator_pair<T>(m_data-len, m_data);
     }
 
-#endif
-
     template <typename T> inline T get_varint();
     template <typename T> inline T get_svarint();
 
@@ -187,7 +193,7 @@ public:
      *
      * @post There is no current field.
      */
-    inline pbf_reader(const char *data, size_t length) noexcept;
+    inline pbf_reader(const char *data, std::size_t length) noexcept;
 
     /**
      * Construct a pbf_reader message from a data pointer and a length. The pointer
@@ -198,7 +204,7 @@ public:
      *
      * @post There is no current field.
      */
-    inline pbf_reader(std::pair<const char *, size_t> data) noexcept;
+    inline pbf_reader(std::pair<const char *, std::size_t> data) noexcept;
 
     /**
      * Construct a pbf_reader message from a std::string. A pointer to the string
@@ -247,8 +253,8 @@ public:
      * buffer. Of course you have to know reasonably well what data to expect
      * and how it is encoded for this number to have any meaning.
      */
-    size_t length() const noexcept {
-        return size_t(m_end - m_data);
+    std::size_t length() const noexcept {
+        return std::size_t(m_end - m_data);
     }
 
     /**
@@ -832,14 +838,14 @@ public:
 
 }; // class pbf_reader
 
-pbf_reader::pbf_reader(const char *data, size_t length) noexcept
+pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept
     : m_data(data),
       m_end(data + length),
       m_wire_type(pbf_wire_type::unknown),
       m_tag(0) {
 }
 
-pbf_reader::pbf_reader(std::pair<const char *, size_t> data) noexcept
+pbf_reader::pbf_reader(std::pair<const char *, std::size_t> data) noexcept
     : m_data(data.first),
       m_end(data.first + data.second),
       m_wire_type(pbf_wire_type::unknown),
@@ -935,7 +941,7 @@ void pbf_reader::skip() {
             skip_bytes(4);
             break;
         default:
-            throw unknown_pbf_wire_type_exception();
+            protozero_assert(false && "can not be here because next() should have thrown already");
     }
 }
 
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
index 2b78cb8..422e147 100644
--- a/include/protozero/pbf_writer.hpp
+++ b/include/protozero/pbf_writer.hpp
@@ -24,7 +24,7 @@ documentation.
 #include <string>
 
 #include <protozero/config.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/varint.hpp>
 
 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
@@ -33,6 +33,14 @@ documentation.
 
 namespace protozero {
 
+namespace detail {
+
+    template <typename T> class packed_field_varint;
+    template <typename T> class packed_field_svarint;
+    template <typename T> class packed_field_fixed;
+
+} // end namespace detail
+
 /**
  * The pbf_writer is used to write PBF formatted messages into a buffer.
  *
@@ -41,9 +49,24 @@ namespace protozero {
  */
 class pbf_writer {
 
+    // A pointer to a string buffer holding the data already written to the
+    // PBF message. For default constructed writers or writers that have been
+    // rolled back, this is a nullptr.
     std::string* m_data;
+
+    // A pointer to a parent writer object if this is a submessage. If this
+    // is a top-level writer, it is a nullptr.
     pbf_writer* m_parent_writer;
-    size_t m_pos = 0;
+
+    // This is usually 0. If there is an open submessage, this is set in the
+    // parent to the rollback position, ie. the last position before the
+    // submessage was started. This is the position where the header of the
+    // submessage starts.
+    std::size_t m_rollback_pos = 0;
+
+    // This is usually 0. If there is an open submessage, this is set in the
+    // parent to the position where the data of the submessage is written to.
+    std::size_t m_pos = 0;
 
     inline void add_varint(uint64_t value) {
         protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
@@ -94,7 +117,9 @@ class pbf_writer {
             return;
         }
 
-        add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last)));
+        auto length = std::distance(first, last);
+        add_length_varint(tag, sizeof(T) * pbf_length_type(length));
+        reserve(sizeof(T) * std::size_t(length));
 
         while (first != last) {
             add_fixed<T>(*first++);
@@ -132,16 +157,37 @@ class pbf_writer {
     // and a varint needs 8 bit for every 7 bit.
     static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
 
-    inline void open_submessage(pbf_tag_type tag) {
+    // If m_rollpack_pos is set to this special value, it means that when
+    // the submessage is closed, nothing needs to be done, because the length
+    // of the submessage has already been written correctly.
+    static const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
+
+    inline void open_submessage(pbf_tag_type tag, std::size_t size) {
         protozero_assert(m_pos == 0);
         protozero_assert(m_data);
-        add_field(tag, pbf_wire_type::length_delimited);
-        m_data->append(size_t(reserve_bytes), '\0');
+        if (size == 0) {
+            m_rollback_pos = m_data->size();
+            add_field(tag, pbf_wire_type::length_delimited);
+            m_data->append(std::size_t(reserve_bytes), '\0');
+        } else {
+            m_rollback_pos = size_is_known;
+            add_length_varint(tag, pbf_length_type(size));
+            reserve(size);
+        }
         m_pos = m_data->size();
     }
 
-    inline void close_submessage() {
+    inline void rollback_submessage() {
         protozero_assert(m_pos != 0);
+        protozero_assert(m_rollback_pos != size_is_known);
+        protozero_assert(m_data);
+        m_data->resize(m_rollback_pos);
+        m_pos = 0;
+    }
+
+    inline void commit_submessage() {
+        protozero_assert(m_pos != 0);
+        protozero_assert(m_rollback_pos != size_is_known);
         protozero_assert(m_data);
         auto length = pbf_length_type(m_data->size() - m_pos);
 
@@ -152,6 +198,18 @@ class pbf_writer {
         m_pos = 0;
     }
 
+    inline void close_submessage() {
+        protozero_assert(m_data);
+        if (m_pos == 0 || m_rollback_pos == size_is_known) {
+            return;
+        }
+        if (m_data->size() - m_pos == 0) {
+            rollback_submessage();
+        } else {
+            commit_submessage();
+        }
+    }
+
     inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
         add_field(tag, pbf_wire_type::length_delimited);
         add_varint(length);
@@ -161,7 +219,8 @@ public:
 
     /**
      * Create a writer using the given string as a data store. The pbf_writer
-     * stores a reference to that string and adds all data to it.
+     * stores a reference to that string and adds all data to it. The string
+     * doesn't have to be empty. The pbf_writer will just append data.
      */
     inline explicit pbf_writer(std::string& data) noexcept :
         m_data(&data),
@@ -185,12 +244,15 @@ public:
      *
      * @param parent_writer The pbf_writer
      * @param tag Tag (field number) of the field that will be written
+     * @param size Optional size of the submessage in bytes (use 0 for unknown).
+     *        Setting this allows some optimizations but is only possible in
+     *        a few very specific cases.
      */
-    inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) :
+    inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
         m_data(parent_writer.m_data),
         m_parent_writer(&parent_writer),
         m_pos(0) {
-        m_parent_writer->open_submessage(tag);
+        m_parent_writer->open_submessage(tag, size);
     }
 
     /// A pbf_writer object can be copied
@@ -211,6 +273,26 @@ public:
         }
     }
 
+    /**
+     * Reserve size bytes in the underlying message store in addition to
+     * whatever the message store already holds. So unlike
+     * the `std::string::reserve()` method this is not an absolute size,
+     * but additional memory that should be reserved.
+     *
+     * @param size Number of bytes to reserve in underlying message store.
+     */
+    void reserve(std::size_t size) {
+        protozero_assert(m_data);
+        m_data->reserve(m_data->size() + size);
+    }
+
+    inline void rollback() {
+        protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
+        protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
+        m_parent_writer->rollback_submessage();
+        m_data = nullptr;
+    }
+
     ///@{
     /**
      * @name Scalar field writer functions
@@ -372,7 +454,7 @@ public:
      * @param value Pointer to value to be written
      * @param size Number of bytes to be written
      */
-    inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
         protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
         protozero_assert(m_data);
         protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
@@ -397,7 +479,7 @@ public:
      * @param value Pointer to value to be written
      * @param size Number of bytes to be written
      */
-    inline void add_string(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
         add_bytes(tag, value, size);
     }
 
@@ -429,7 +511,7 @@ public:
      * @param value Pointer to message to be written
      * @param size Length of the message
      */
-    inline void add_message(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
         add_bytes(tag, value, size);
     }
 
@@ -654,8 +736,102 @@ public:
 
     ///@}
 
+    template <typename T> friend class detail::packed_field_varint;
+    template <typename T> friend class detail::packed_field_svarint;
+    template <typename T> friend class detail::packed_field_fixed;
+
 }; // class pbf_writer
 
+namespace detail {
+
+    class packed_field {
+
+    protected:
+
+        pbf_writer m_writer;
+
+    public:
+
+        packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
+            m_writer(parent_writer, tag) {
+        }
+
+        packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
+            m_writer(parent_writer, tag, size) {
+        }
+
+        void rollback() {
+            m_writer.rollback();
+        }
+
+    }; // class packed_field
+
+    template <typename T>
+    class packed_field_fixed : public packed_field {
+
+    public:
+
+        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
+            packed_field(parent_writer, tag, size * sizeof(T)) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_fixed<T>(value);
+        }
+
+    }; // class packed_field_fixed
+
+    template <typename T>
+    class packed_field_varint : public packed_field {
+
+    public:
+
+        packed_field_varint(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_varint(uint64_t(value));
+        }
+
+    }; // class packed_field_varint
+
+    template <typename T>
+    class packed_field_svarint : public packed_field {
+
+    public:
+
+        packed_field_svarint(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_varint(encode_zigzag64(value));
+        }
+
+    }; // class packed_field_svarint
+
+} // end namespace detail
+
+using packed_field_bool     = detail::packed_field_varint<bool>;
+using packed_field_enum     = detail::packed_field_varint<int32_t>;
+using packed_field_int32    = detail::packed_field_varint<int32_t>;
+using packed_field_sint32   = detail::packed_field_svarint<int32_t>;
+using packed_field_uint32   = detail::packed_field_varint<uint32_t>;
+using packed_field_int64    = detail::packed_field_varint<int64_t>;
+using packed_field_sint64   = detail::packed_field_svarint<int64_t>;
+using packed_field_uint64   = detail::packed_field_varint<uint64_t>;
+using packed_field_fixed32  = detail::packed_field_fixed<uint32_t>;
+using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
+using packed_field_fixed64  = detail::packed_field_fixed<uint64_t>;
+using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
+using packed_field_float    = detail::packed_field_fixed<float>;
+using packed_field_double   = detail::packed_field_fixed<double>;
+
 } // end namespace protozero
 
 #endif // PROTOZERO_PBF_WRITER_HPP
diff --git a/include/protozero/pbf_types.hpp b/include/protozero/types.hpp
similarity index 91%
rename from include/protozero/pbf_types.hpp
rename to include/protozero/types.hpp
index 9f38584..6856b3d 100644
--- a/include/protozero/pbf_types.hpp
+++ b/include/protozero/types.hpp
@@ -1,5 +1,5 @@
-#ifndef PROTOZERO_PBF_TYPES_HPP
-#define PROTOZERO_PBF_TYPES_HPP
+#ifndef PROTOZERO_TYPES_HPP
+#define PROTOZERO_TYPES_HPP
 
 /*****************************************************************************
 
@@ -11,7 +11,7 @@ documentation.
 *****************************************************************************/
 
 /**
- * @file pbf_types.hpp
+ * @file types.hpp
  *
  * @brief Contains the declaration of low-level types used in the pbf format.
  */
@@ -46,4 +46,4 @@ namespace protozero {
 
 } // end namespace protozero
 
-#endif // PROTOZERO_PBF_TYPES_HPP
+#endif // PROTOZERO_TYPES_HPP
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
index 27536fd..4242df9 100644
--- a/include/protozero/varint.hpp
+++ b/include/protozero/varint.hpp
@@ -25,13 +25,13 @@ namespace protozero {
 /**
  * The maximum length of a 64bit varint.
  */
-const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
+constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
 
 // from https://github.com/facebook/folly/blob/master/folly/Varint.h
 /**
  * Decode a 64bit varint.
  *
- * String exception guarantee: if there is an exception the data pointer will
+ * Strong exception guarantee: if there is an exception the data pointer will
  * not be changed.
  *
  * @param[in,out] data Pointer to pointer to the input data. After the function
diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp
index 4f129ac..7b60e2e 100644
--- a/include/protozero/version.hpp
+++ b/include/protozero/version.hpp
@@ -11,12 +11,12 @@ documentation.
 *****************************************************************************/
 
 #define PROTOZERO_VERSION_MAJOR 1
-#define PROTOZERO_VERSION_MINOR 2
-#define PROTOZERO_VERSION_PATCH 3
+#define PROTOZERO_VERSION_MINOR 3
+#define PROTOZERO_VERSION_PATCH 0
 
 #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
 
-#define PROTOZERO_VERSION_STRING "1.2.3"
+#define PROTOZERO_VERSION_STRING "1.3.0"
 
 
 #endif // PROTOZERO_VERSION_HPP
diff --git a/test/t/basic/test_node_ref.cpp b/test/t/basic/test_node_ref.cpp
index ac7ccbf..9932ff5 100644
--- a/test/t/basic/test_node_ref.cpp
+++ b/test/t/basic/test_node_ref.cpp
@@ -1,6 +1,9 @@
 #include "catch.hpp"
 
+#include <osmium/builder/attr.hpp>
+#include <osmium/memory/buffer.hpp>
 #include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/node_ref_list.hpp>
 
 TEST_CASE("NodeRef") {
 
@@ -55,3 +58,52 @@ TEST_CASE("NodeRef") {
 
 }
 
+TEST_CASE("WayNodeList") {
+    osmium::memory::Buffer buffer(1024);
+
+    SECTION("Empty list") {
+        {
+            osmium::builder::WayNodeListBuilder builder(buffer);
+        }
+        REQUIRE(buffer.commit() == 0);
+        REQUIRE(buffer.committed( )> 0);
+
+        const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
+        REQUIRE(nrl.empty());
+        REQUIRE(nrl.size() == 0);
+    }
+
+    SECTION("Small area") {
+        osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({
+            { 1, {0, 0}},
+            { 2, {0, 1}},
+            { 3, {1, 1}},
+            { 4, {1, 0}},
+            { 1, {0, 0}},
+        }));
+
+        const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
+        REQUIRE_FALSE(nrl.empty());
+        REQUIRE(nrl.size() == 5);
+        REQUIRE(nrl.is_closed());
+        REQUIRE(nrl.ends_have_same_id());
+        REQUIRE(nrl.ends_have_same_location());
+    }
+
+    SECTION("Not an area") {
+        osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({
+            { 1, {0, 0}},
+            { 2, {1, 0}},
+            { 1, {0, 0}},
+        }));
+
+        const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
+        REQUIRE_FALSE(nrl.empty());
+        REQUIRE(nrl.size() == 3);
+        REQUIRE(nrl.is_closed());
+        REQUIRE(nrl.ends_have_same_id());
+        REQUIRE(nrl.ends_have_same_location());
+    }
+
+}
+

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