#ifndef VTZERO_BUILDER_IMPL_HPP #define VTZERO_BUILDER_IMPL_HPP /***************************************************************************** vtzero - Tiny and fast vector tile decoder and encoder in C++. This file is from https://github.com/mapbox/vtzero where you can find more documentation. *****************************************************************************/ /** * @file builder_impl.hpp * * @brief Contains classes internal to the builder. */ #include "encoded_property_value.hpp" #include "property_value.hpp" #include "types.hpp" #include #include #include #include #include #include namespace vtzero { namespace detail { class layer_builder_impl { // If this layer is copied from an existing layer, this points // to the data of the original layer. For newly built layers, // this is empty. data_view m_data_view{}; // Buffer containing the encoded layer metadata and features std::string m_data; // Buffer containing the encoded keys table std::string m_keys_data; // Buffer containing the encoded values table std::string m_values_data; protozero::pbf_builder m_pbf_message_layer{}; protozero::pbf_builder m_pbf_message_keys{}; protozero::pbf_builder m_pbf_message_values{}; // The number of features in the layer std::size_t m_num_features = 0; // Vector tile spec version uint32_t m_version = 0; // The number of keys in the keys table uint32_t m_num_keys = 0; // The number of values in the values table uint32_t m_num_values = 0; // Below this value, no index will be used to find entries in the // key/value tables. This number is based on some initial // benchmarking but probably needs some tuning. // See also https://github.com/mapbox/vtzero/issues/30 static constexpr const uint32_t max_entries_flat = 20; using map_type = std::unordered_map; map_type m_keys_index; map_type m_values_index; static index_value find_in_table(const data_view text, const std::string& data) { uint32_t index = 0; protozero::pbf_message pbf_table{data}; while (pbf_table.next()) { const auto v = pbf_table.get_view(); if (v == text) { return index_value{index}; } ++index; } return index_value{}; } // Read the key or value table and populate an index from its // entries. This is done once the table becomes too large to do // linear search in it. static void populate_index(const std::string& data, map_type& map) { uint32_t index = 0; protozero::pbf_message pbf_table{data}; while (pbf_table.next()) { map[pbf_table.get_string()] = index++; } } index_value add_value_without_dup_check(const data_view text) { m_pbf_message_values.add_string(detail::pbf_layer::values, text); return m_num_values++; } index_value add_value(const data_view text) { const auto index = find_in_values_table(text); if (index.valid()) { return index; } return add_value_without_dup_check(text); } index_value find_in_keys_table(const data_view text) { if (m_num_keys < max_entries_flat) { return find_in_table(text, m_keys_data); } if (m_keys_index.empty()) { populate_index(m_keys_data, m_keys_index); } auto& v = m_keys_index[std::string(text)]; if (!v.valid()) { v = add_key_without_dup_check(text); } return v; } index_value find_in_values_table(const data_view text) { if (m_num_values < max_entries_flat) { return find_in_table(text, m_values_data); } if (m_values_index.empty()) { populate_index(m_values_data, m_values_index); } auto& v = m_values_index[std::string(text)]; if (!v.valid()) { v = add_value_without_dup_check(text); } return v; } public: // This layer should be a copy of an existing layer explicit layer_builder_impl(const data_view data) : m_data_view(data) { } // This layer is being created from scratch template layer_builder_impl(TString&& name, uint32_t version, uint32_t extent) : m_pbf_message_layer(m_data), m_pbf_message_keys(m_keys_data), m_pbf_message_values(m_values_data), m_version(version) { m_pbf_message_layer.add_uint32(detail::pbf_layer::version, version); m_pbf_message_layer.add_string(detail::pbf_layer::name, std::forward(name)); m_pbf_message_layer.add_uint32(detail::pbf_layer::extent, extent); } ~layer_builder_impl() noexcept = default; layer_builder_impl(const layer_builder_impl&) = delete; layer_builder_impl& operator=(const layer_builder_impl&) = delete; layer_builder_impl(layer_builder_impl&&) = default; layer_builder_impl& operator=(layer_builder_impl&&) = default; uint32_t version() const noexcept { return m_version; } index_value add_key_without_dup_check(const data_view text) { m_pbf_message_keys.add_string(detail::pbf_layer::keys, text); return m_num_keys++; } index_value add_key(const data_view text) { const auto index = find_in_keys_table(text); if (index.valid()) { return index; } return add_key_without_dup_check(text); } index_value add_value_without_dup_check(const property_value value) { return add_value_without_dup_check(value.data()); } index_value add_value_without_dup_check(const encoded_property_value& value) { return add_value_without_dup_check(value.data()); } index_value add_value(const property_value value) { return add_value(value.data()); } index_value add_value(const encoded_property_value& value) { return add_value(value.data()); } const std::string& data() const noexcept { return m_data; } const std::string& keys_data() const noexcept { return m_keys_data; } const std::string& values_data() const noexcept { return m_values_data; } protozero::pbf_builder& message() noexcept { return m_pbf_message_layer; } void increment_feature_count() noexcept { ++m_num_features; } std::size_t estimated_size() const { if (m_data_view.data()) { // This is a layer created as copy from an existing layer constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8; return m_data_view.size() + estimated_overhead_for_pbf_encoding; } // This is a layer created from scratch constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8; return data().size() + keys_data().size() + values_data().size() + estimated_overhead_for_pbf_encoding; } template void build(protozero::basic_pbf_builder& pbf_tile_builder) const { if (m_data_view.data()) { // This is a layer created as copy from an existing layer pbf_tile_builder.add_bytes(detail::pbf_tile::layers, m_data_view); return; } // This is a layer created from scratch if (m_num_features > 0) { pbf_tile_builder.add_bytes_vectored(detail::pbf_tile::layers, data(), keys_data(), values_data()); } } }; // class layer_builder_impl } // namespace detail } // namespace vtzero #endif // VTZERO_BUILDER_IMPL_HPP