diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp index 7c8fc99f..b03cfb2f 100644 --- a/include/osmium/io/detail/pbf_decoder.hpp +++ b/include/osmium/io/detail/pbf_decoder.hpp @@ -81,31 +81,50 @@ namespace osmium { using protozero::data_view; - class varint_range { + template + class values_access { const char* m_data = nullptr; const char* m_end = nullptr; + T m_value = 0; + int m_num_values = 0; + + protected: std::uint64_t next() { return protozero::decode_varint(&m_data, m_end); } + bool has_single_value() const noexcept { + return m_num_values > 0; + } + + T get_and_clear_value() noexcept { + m_num_values = 0; + return m_value; + } + public: - varint_range() = default; + values_access() = default; - explicit varint_range(const data_view& data) : + explicit values_access(const data_view& data) : m_data(data.data()), m_end(data.data() + data.size()) { } + explicit values_access(T value) : + m_value(value), + m_num_values(1) { + } + bool empty() const noexcept { - return m_data == m_end; + return m_data == m_end && m_num_values == 0; } std::size_t size() const noexcept { if (!m_data) { - return 0; + return m_num_values; } // We know that each varint contains exactly one byte with the most @@ -116,23 +135,67 @@ namespace osmium { }); } - std::int32_t next_int32() { + }; // class values_access + + class values_access_int32 : public values_access { + + public: + + using values_access::values_access; + + int32_t next_int32() { + if (has_single_value()) { + return get_and_clear_value(); + } return static_cast(next()); } - std::uint32_t next_uint32() { + }; // class values_access_int32 + + class values_access_uint32 : public values_access { + + public: + + using values_access::values_access; + + uint32_t next_uint32() { + if (has_single_value()) { + return get_and_clear_value(); + } return static_cast(next()); } - std::int32_t next_sint32() { + }; // class values_access_uint32 + + class values_access_sint32 : public values_access { + + public: + + using values_access::values_access; + + int32_t next_sint32() { + if (has_single_value()) { + return get_and_clear_value(); + } return protozero::decode_zigzag32(static_cast(next())); } - std::int64_t next_sint64() { + }; // class values_access_sint32 + + class values_access_sint64 : public values_access { + + public: + + using values_access::values_access; + + int64_t next_sint64() { + if (has_single_value()) { + return get_and_clear_value(); + } return protozero::decode_zigzag64(next()); } - }; // class varint_range + }; // class values_access_sint64 using osm_string_len_type = std::pair; @@ -300,7 +363,7 @@ namespace osmium { return user; } - void build_tag_list(osmium::builder::Builder& parent, varint_range& keys, varint_range& vals) { + void build_tag_list(osmium::builder::Builder& parent, values_access_uint32& keys, values_access_uint32& vals) { if (keys.empty() || vals.empty()) { return; } @@ -325,8 +388,8 @@ namespace osmium { osmium::builder::NodeBuilder builder{m_buffer}; osmium::Node& node = builder.object(); - varint_range keys; - varint_range vals; + values_access_uint32 keys; + values_access_uint32 vals; int64_t lon = std::numeric_limits::max(); int64_t lat = std::numeric_limits::max(); @@ -339,10 +402,16 @@ namespace osmium { node.set_id(pbf_node.get_sint64()); break; case protozero::tag_and_type(OSMFormat::Node::packed_uint32_keys, protozero::pbf_wire_type::length_delimited): - keys = varint_range{pbf_node.get_view()}; + keys = values_access_uint32{pbf_node.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Node::packed_uint32_keys, protozero::pbf_wire_type::varint): + keys = values_access_uint32{pbf_node.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Node::packed_uint32_vals, protozero::pbf_wire_type::length_delimited): - vals = varint_range{pbf_node.get_view()}; + vals = values_access_uint32{pbf_node.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Node::packed_uint32_vals, protozero::pbf_wire_type::varint): + vals = values_access_uint32{pbf_node.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Node::optional_Info_info, protozero::pbf_wire_type::length_delimited): if (m_read_metadata == osmium::io::read_meta::yes) { @@ -381,11 +450,11 @@ namespace osmium { void decode_way(const data_view& data) { osmium::builder::WayBuilder builder{m_buffer}; - varint_range keys; - varint_range vals; - varint_range refs; - varint_range lats; - varint_range lons; + values_access_uint32 keys; + values_access_uint32 vals; + values_access_sint64 refs; + values_access_sint64 lats; + values_access_sint64 lons; osm_string_len_type user{"", 0}; @@ -396,10 +465,16 @@ namespace osmium { builder.object().set_id(pbf_way.get_int64()); break; case protozero::tag_and_type(OSMFormat::Way::packed_uint32_keys, protozero::pbf_wire_type::length_delimited): - keys = varint_range{pbf_way.get_view()}; + keys = values_access_uint32{pbf_way.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Way::packed_uint32_keys, protozero::pbf_wire_type::varint): + keys = values_access_uint32{pbf_way.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Way::packed_uint32_vals, protozero::pbf_wire_type::length_delimited): - vals = varint_range{pbf_way.get_view()}; + vals = values_access_uint32{pbf_way.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Way::packed_uint32_vals, protozero::pbf_wire_type::varint): + vals = values_access_uint32{pbf_way.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Way::optional_Info_info, protozero::pbf_wire_type::length_delimited): if (m_read_metadata == osmium::io::read_meta::yes) { @@ -409,13 +484,22 @@ namespace osmium { } break; case protozero::tag_and_type(OSMFormat::Way::packed_sint64_refs, protozero::pbf_wire_type::length_delimited): - refs = varint_range{pbf_way.get_view()}; + refs = values_access_sint64{pbf_way.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Way::packed_sint64_refs, protozero::pbf_wire_type::varint): + refs = values_access_sint64{pbf_way.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::Way::packed_sint64_lat, protozero::pbf_wire_type::length_delimited): - lats = varint_range{pbf_way.get_view()}; + lats = values_access_sint64{pbf_way.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Way::packed_sint64_lat, protozero::pbf_wire_type::varint): + lats = values_access_sint64{pbf_way.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::Way::packed_sint64_lon, protozero::pbf_wire_type::length_delimited): - lons = varint_range{pbf_way.get_view()}; + lons = values_access_sint64{pbf_way.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Way::packed_sint64_lon, protozero::pbf_wire_type::varint): + lons = values_access_sint64{pbf_way.get_sint64()}; break; default: pbf_way.skip(); @@ -450,11 +534,11 @@ namespace osmium { void decode_relation(const data_view& data) { osmium::builder::RelationBuilder builder{m_buffer}; - varint_range keys; - varint_range vals; - varint_range roles; - varint_range refs; - varint_range types; + values_access_uint32 keys; + values_access_uint32 vals; + values_access_int32 roles; + values_access_sint64 refs; + values_access_int32 types; osm_string_len_type user{"", 0}; @@ -465,10 +549,16 @@ namespace osmium { builder.object().set_id(pbf_relation.get_int64()); break; case protozero::tag_and_type(OSMFormat::Relation::packed_uint32_keys, protozero::pbf_wire_type::length_delimited): - keys = varint_range{pbf_relation.get_view()}; + keys = values_access_uint32{pbf_relation.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Relation::packed_uint32_keys, protozero::pbf_wire_type::varint): + keys = values_access_uint32{pbf_relation.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Relation::packed_uint32_vals, protozero::pbf_wire_type::length_delimited): - vals = varint_range{pbf_relation.get_view()}; + vals = values_access_uint32{pbf_relation.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Relation::packed_uint32_vals, protozero::pbf_wire_type::varint): + vals = values_access_uint32{pbf_relation.get_uint32()}; break; case protozero::tag_and_type(OSMFormat::Relation::optional_Info_info, protozero::pbf_wire_type::length_delimited): if (m_read_metadata == osmium::io::read_meta::yes) { @@ -478,13 +568,22 @@ namespace osmium { } break; case protozero::tag_and_type(OSMFormat::Relation::packed_int32_roles_sid, protozero::pbf_wire_type::length_delimited): - roles = varint_range{pbf_relation.get_view()}; + roles = values_access_int32{pbf_relation.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Relation::packed_int32_roles_sid, protozero::pbf_wire_type::varint): + roles = values_access_int32{pbf_relation.get_int32()}; break; case protozero::tag_and_type(OSMFormat::Relation::packed_sint64_memids, protozero::pbf_wire_type::length_delimited): - refs = varint_range{pbf_relation.get_view()}; + refs = values_access_sint64{pbf_relation.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Relation::packed_sint64_memids, protozero::pbf_wire_type::varint): + refs = values_access_sint64{pbf_relation.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::Relation::packed_MemberType_types, protozero::pbf_wire_type::length_delimited): - types = varint_range{pbf_relation.get_view()}; + types = values_access_int32{pbf_relation.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::Relation::packed_MemberType_types, protozero::pbf_wire_type::varint): + types = values_access_int32{pbf_relation.get_int32()}; break; default: pbf_relation.skip(); @@ -514,7 +613,7 @@ namespace osmium { build_tag_list(builder, keys, vals); } - void build_tag_list_from_dense_nodes(osmium::builder::NodeBuilder& builder, varint_range& tags) { + void build_tag_list_from_dense_nodes(osmium::builder::NodeBuilder& builder, values_access_int32& tags) { osmium::builder::TagListBuilder tl_builder{builder}; while (!tags.empty()) { const auto idx = tags.next_int32(); @@ -531,25 +630,37 @@ namespace osmium { } void decode_dense_nodes_without_metadata(const data_view& data) { - varint_range ids; - varint_range lats; - varint_range lons; - varint_range tags; + values_access_sint64 ids; + values_access_sint64 lats; + values_access_sint64 lons; + values_access_int32 tags; protozero::pbf_message pbf_dense_nodes{data}; while (pbf_dense_nodes.next()) { switch (pbf_dense_nodes.tag_and_type()) { case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_id, protozero::pbf_wire_type::length_delimited): - ids = varint_range{pbf_dense_nodes.get_view()}; + ids = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_id, protozero::pbf_wire_type::varint): + ids = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lat, protozero::pbf_wire_type::length_delimited): - lats = varint_range{pbf_dense_nodes.get_view()}; + lats = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lat, protozero::pbf_wire_type::varint): + lats = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lon, protozero::pbf_wire_type::length_delimited): - lons = varint_range{pbf_dense_nodes.get_view()}; + lons = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lon, protozero::pbf_wire_type::varint): + lons = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_int32_keys_vals, protozero::pbf_wire_type::length_delimited): - tags = varint_range{pbf_dense_nodes.get_view()}; + tags = values_access_int32{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_int32_keys_vals, protozero::pbf_wire_type::varint): + tags = values_access_int32{pbf_dense_nodes.get_int32()}; break; default: pbf_dense_nodes.skip(); @@ -592,22 +703,25 @@ namespace osmium { void decode_dense_nodes(const data_view& data) { bool has_info = false; - varint_range ids; - varint_range lats; - varint_range lons; - varint_range tags; - varint_range versions; - varint_range timestamps; - varint_range changesets; - varint_range uids; - varint_range user_sids; - varint_range visibles; + values_access_sint64 ids; + values_access_sint64 lats; + values_access_sint64 lons; + values_access_int32 tags; + values_access_int32 versions; + values_access_sint64 timestamps; + values_access_sint64 changesets; + values_access_sint32 uids; + values_access_sint32 user_sids; + values_access_int32 visibles; protozero::pbf_message pbf_dense_nodes{data}; while (pbf_dense_nodes.next()) { switch (pbf_dense_nodes.tag_and_type()) { case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_id, protozero::pbf_wire_type::length_delimited): - ids = varint_range{pbf_dense_nodes.get_view()}; + ids = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_id, protozero::pbf_wire_type::varint): + ids = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::optional_DenseInfo_denseinfo, protozero::pbf_wire_type::length_delimited): { @@ -616,22 +730,40 @@ namespace osmium { while (pbf_dense_info.next()) { switch (pbf_dense_info.tag_and_type()) { case protozero::tag_and_type(OSMFormat::DenseInfo::packed_int32_version, protozero::pbf_wire_type::length_delimited): - versions = varint_range{pbf_dense_info.get_view()}; + versions = values_access_int32{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_int32_version, protozero::pbf_wire_type::varint): + versions = values_access_int32{pbf_dense_info.get_int32()}; break; case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint64_timestamp, protozero::pbf_wire_type::length_delimited): - timestamps = varint_range{pbf_dense_info.get_view()}; + timestamps = values_access_sint64{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint64_timestamp, protozero::pbf_wire_type::varint): + timestamps = values_access_sint64{pbf_dense_info.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint64_changeset, protozero::pbf_wire_type::length_delimited): - changesets = varint_range{pbf_dense_info.get_view()}; + changesets = values_access_sint64{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint64_changeset, protozero::pbf_wire_type::varint): + changesets = values_access_sint64{pbf_dense_info.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint32_uid, protozero::pbf_wire_type::length_delimited): - uids = varint_range{pbf_dense_info.get_view()}; + uids = values_access_sint32{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint32_uid, protozero::pbf_wire_type::varint): + uids = values_access_sint32{pbf_dense_info.get_sint32()}; break; case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint32_user_sid, protozero::pbf_wire_type::length_delimited): - user_sids = varint_range{pbf_dense_info.get_view()}; + user_sids = values_access_sint32{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_sint32_user_sid, protozero::pbf_wire_type::varint): + user_sids = values_access_sint32{pbf_dense_info.get_sint32()}; break; case protozero::tag_and_type(OSMFormat::DenseInfo::packed_bool_visible, protozero::pbf_wire_type::length_delimited): - visibles = varint_range{pbf_dense_info.get_view()}; + visibles = values_access_int32{pbf_dense_info.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseInfo::packed_bool_visible, protozero::pbf_wire_type::varint): + visibles = values_access_int32{pbf_dense_info.get_int32()}; break; default: pbf_dense_info.skip(); @@ -640,13 +772,22 @@ namespace osmium { } break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lat, protozero::pbf_wire_type::length_delimited): - lats = varint_range{pbf_dense_nodes.get_view()}; + lats = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lat, protozero::pbf_wire_type::varint): + lats = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lon, protozero::pbf_wire_type::length_delimited): - lons = varint_range{pbf_dense_nodes.get_view()}; + lons = values_access_sint64{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_sint64_lon, protozero::pbf_wire_type::varint): + lons = values_access_sint64{pbf_dense_nodes.get_sint64()}; break; case protozero::tag_and_type(OSMFormat::DenseNodes::packed_int32_keys_vals, protozero::pbf_wire_type::length_delimited): - tags = varint_range{pbf_dense_nodes.get_view()}; + tags = values_access_int32{pbf_dense_nodes.get_view()}; + break; + case protozero::tag_and_type(OSMFormat::DenseNodes::packed_int32_keys_vals, protozero::pbf_wire_type::varint): + tags = values_access_int32{pbf_dense_nodes.get_int32()}; break; default: pbf_dense_nodes.skip();