mirror of
https://github.com/systemed/tilemaker
synced 2025-02-22 06:24:08 +01:00
398 lines
14 KiB
C++
398 lines
14 KiB
C++
#ifndef VTZERO_PROPERTY_VALUE_HPP
|
|
#define VTZERO_PROPERTY_VALUE_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 property_value.hpp
|
|
*
|
|
* @brief Contains the property_value class.
|
|
*/
|
|
|
|
#include "exception.hpp"
|
|
#include "types.hpp"
|
|
|
|
#include <protozero/pbf_message.hpp>
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <utility>
|
|
|
|
namespace vtzero {
|
|
|
|
/**
|
|
* A view of a vector tile property value.
|
|
*
|
|
* Doesn't hold any data itself.
|
|
*/
|
|
class property_value {
|
|
|
|
data_view m_value{};
|
|
|
|
static bool check_tag_and_type(protozero::pbf_tag_type tag, protozero::pbf_wire_type type) noexcept {
|
|
static constexpr const std::array<protozero::pbf_wire_type, 7> types{{
|
|
string_value_type::wire_type,
|
|
float_value_type::wire_type,
|
|
double_value_type::wire_type,
|
|
int_value_type::wire_type,
|
|
uint_value_type::wire_type,
|
|
sint_value_type::wire_type,
|
|
bool_value_type::wire_type,
|
|
}};
|
|
|
|
if (tag < 1 || tag > types.size()) {
|
|
return false;
|
|
}
|
|
|
|
return types[tag - 1] == type; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
|
}
|
|
|
|
static data_view get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, string_value_type /* dummy */) {
|
|
return value_message.get_view();
|
|
}
|
|
|
|
static float get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, float_value_type /* dummy */) {
|
|
return value_message.get_float();
|
|
}
|
|
|
|
static double get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, double_value_type /* dummy */) {
|
|
return value_message.get_double();
|
|
}
|
|
|
|
static int64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, int_value_type /* dummy */) {
|
|
return value_message.get_int64();
|
|
}
|
|
|
|
static uint64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, uint_value_type /* dummy */) {
|
|
return value_message.get_uint64();
|
|
}
|
|
|
|
static int64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, sint_value_type /* dummy */) {
|
|
return value_message.get_sint64();
|
|
}
|
|
|
|
static bool get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, bool_value_type /* dummy */) {
|
|
return value_message.get_bool();
|
|
}
|
|
|
|
template <typename T>
|
|
typename T::type get_value() const {
|
|
vtzero_assert(valid());
|
|
protozero::pbf_message<detail::pbf_value> value_message{m_value};
|
|
|
|
typename T::type result{};
|
|
bool has_result = false;
|
|
while (value_message.next(T::pvtype, T::wire_type)) {
|
|
result = get_value_impl(value_message, T());
|
|
has_result = true;
|
|
}
|
|
|
|
if (has_result) {
|
|
return result;
|
|
}
|
|
|
|
throw type_exception{};
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* The default constructor creates an invalid (empty) property_value.
|
|
*/
|
|
constexpr property_value() noexcept = default;
|
|
|
|
/**
|
|
* Create a (valid) property_value from a data_view.
|
|
*/
|
|
explicit constexpr property_value(const data_view value) noexcept :
|
|
m_value(value) {
|
|
}
|
|
|
|
/**
|
|
* Is this a valid property value? Property values are valid if they
|
|
* were constructed using the non-default constructor.
|
|
*/
|
|
constexpr bool valid() const noexcept {
|
|
return m_value.data() != nullptr;
|
|
}
|
|
|
|
/**
|
|
* Get the type of this property.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws format_exception if the encoding is invalid
|
|
*/
|
|
property_value_type type() const {
|
|
vtzero_assert(valid());
|
|
protozero::pbf_message<detail::pbf_value> value_message{m_value};
|
|
if (value_message.next()) {
|
|
const auto tag_val = static_cast<protozero::pbf_tag_type>(value_message.tag());
|
|
if (!check_tag_and_type(tag_val, value_message.wire_type())) {
|
|
throw format_exception{"illegal property value type"};
|
|
}
|
|
return value_message.tag();
|
|
}
|
|
throw format_exception{"missing tag value"};
|
|
}
|
|
|
|
/**
|
|
* Get the internal data_view this object was constructed with.
|
|
*/
|
|
constexpr data_view data() const noexcept {
|
|
return m_value;
|
|
}
|
|
|
|
/**
|
|
* Get string value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than string.
|
|
*/
|
|
data_view string_value() const {
|
|
return get_value<string_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get float value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than float.
|
|
*/
|
|
float float_value() const {
|
|
return get_value<float_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get double value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than double.
|
|
*/
|
|
double double_value() const {
|
|
return get_value<double_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get int value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than int.
|
|
*/
|
|
std::int64_t int_value() const {
|
|
return get_value<int_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get uint value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than uint.
|
|
*/
|
|
std::uint64_t uint_value() const {
|
|
return get_value<uint_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get sint value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than sint.
|
|
*/
|
|
std::int64_t sint_value() const {
|
|
return get_value<sint_value_type>();
|
|
}
|
|
|
|
/**
|
|
* Get bool value of this object.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
* @throws type_exception if the type of this property value is
|
|
* something other than bool.
|
|
*/
|
|
bool bool_value() const {
|
|
return get_value<bool_value_type>();
|
|
}
|
|
|
|
}; // class property_value
|
|
|
|
/// property_values are equal if they contain the same data.
|
|
inline constexpr bool operator==(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() == rhs.data();
|
|
}
|
|
|
|
/// property_values are unequal if they do not contain the same data.
|
|
inline constexpr bool operator!=(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() != rhs.data();
|
|
}
|
|
|
|
/// property_values are ordered in the same way as the underlying data
|
|
inline bool operator<(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() < rhs.data();
|
|
}
|
|
|
|
/// property_values are ordered in the same way as the underlying data
|
|
inline bool operator<=(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() <= rhs.data();
|
|
}
|
|
|
|
/// property_values are ordered in the same way as the underlying data
|
|
inline bool operator>(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() > rhs.data();
|
|
}
|
|
|
|
/// property_values are ordered in the same way as the underlying data
|
|
inline bool operator>=(const property_value lhs, const property_value rhs) noexcept {
|
|
return lhs.data() >= rhs.data();
|
|
}
|
|
|
|
/**
|
|
* Apply the value to a visitor.
|
|
*
|
|
* The visitor must have an overloaded call operator taking a single
|
|
* argument of each of the types data_view, float, double, int64_t,
|
|
* uint64_t, and bool. All call operators must return the same type which
|
|
* will be the return type of this function.
|
|
*/
|
|
template <typename V>
|
|
decltype(std::declval<V>()(data_view{})) apply_visitor(V&& visitor, const property_value value) {
|
|
switch (value.type()) {
|
|
case property_value_type::string_value:
|
|
return std::forward<V>(visitor)(value.string_value());
|
|
case property_value_type::float_value:
|
|
return std::forward<V>(visitor)(value.float_value());
|
|
case property_value_type::double_value:
|
|
return std::forward<V>(visitor)(value.double_value());
|
|
case property_value_type::int_value:
|
|
return std::forward<V>(visitor)(value.int_value());
|
|
case property_value_type::uint_value:
|
|
return std::forward<V>(visitor)(value.uint_value());
|
|
case property_value_type::sint_value:
|
|
return std::forward<V>(visitor)(value.sint_value());
|
|
default: // case property_value_type::bool_value:
|
|
return std::forward<V>(visitor)(value.bool_value());
|
|
}
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
template <typename TVariant, typename TMapping>
|
|
struct convert_visitor {
|
|
|
|
TVariant operator()(data_view value) const {
|
|
return TVariant(static_cast<typename TMapping::string_type>(value));
|
|
}
|
|
|
|
TVariant operator()(float value) const {
|
|
return TVariant(static_cast<typename TMapping::float_type>(value));
|
|
}
|
|
|
|
TVariant operator()(double value) const {
|
|
return TVariant(static_cast<typename TMapping::double_type>(value));
|
|
}
|
|
|
|
TVariant operator()(int64_t value) const {
|
|
return TVariant(static_cast<typename TMapping::int_type>(value));
|
|
}
|
|
|
|
TVariant operator()(uint64_t value) const {
|
|
return TVariant(static_cast<typename TMapping::uint_type>(value));
|
|
}
|
|
|
|
TVariant operator()(bool value) const {
|
|
return TVariant(static_cast<typename TMapping::bool_type>(value));
|
|
}
|
|
|
|
}; // struct convert_visitor
|
|
|
|
} // namespace detail
|
|
|
|
/**
|
|
* Default mapping between the different types of a property_value to
|
|
* the types needed for a variant. Derive from this class, overwrite
|
|
* the types you want and use that class as second template parameter
|
|
* in the convert_property_value class.
|
|
*/
|
|
struct property_value_mapping {
|
|
|
|
/// mapping for string type
|
|
using string_type = std::string;
|
|
|
|
/// mapping for float type
|
|
using float_type = float;
|
|
|
|
/// mapping for double type
|
|
using double_type = double;
|
|
|
|
/// mapping for int type
|
|
using int_type = int64_t;
|
|
|
|
/// mapping for uint type
|
|
using uint_type = uint64_t;
|
|
|
|
/// mapping for bool type
|
|
using bool_type = bool;
|
|
|
|
}; // struct property_value_mapping
|
|
|
|
/**
|
|
* Convert a property_value to a different (usually variant-based)
|
|
* class.
|
|
*
|
|
* Usage: If you have a variant type like
|
|
*
|
|
* @code
|
|
* using variant_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
|
* @endcode
|
|
*
|
|
* you can use
|
|
* @code
|
|
* property_value x = ...;
|
|
* auto v = convert_property_value<variant_type>(x);
|
|
* @endcode
|
|
*
|
|
* to convert the data.
|
|
*
|
|
* Usually your variant type has to support all of the following types:
|
|
* std::string, float, double, int64_t, uint64_t, and bool. If your type
|
|
* doesn't, you can add a second template parameter with a struct
|
|
* containing the mapping between the vtzero types and your types:
|
|
*
|
|
* @code
|
|
* struct mapping : vtzero::property_value_mapping {
|
|
* using float_type = double; // convert all floats to doubles
|
|
* using bool_type = int; // convert all bools to ints
|
|
* // use default types for the rest
|
|
* // see the class vtzero::property_value_mapping for the defaults
|
|
* };
|
|
* property_value x = ...;
|
|
* auto v = convert_property_value<variant_type, mapping>(x);
|
|
* @endcode
|
|
*
|
|
* @tparam TVariant The variant type to convert to.
|
|
* @tparam TMapping A struct derived from property_value_mapping with the
|
|
* mapping for the types.
|
|
* @param value The property value to convert.
|
|
*
|
|
*/
|
|
template <typename TVariant, typename TMapping = property_value_mapping>
|
|
TVariant convert_property_value(const property_value value) {
|
|
return apply_visitor(detail::convert_visitor<TVariant, TMapping>{}, value);
|
|
}
|
|
|
|
} // namespace vtzero
|
|
|
|
#endif // VTZERO_PROPERTY_VALUE_HPP
|