#ifndef ASYNC_MQTT5_BASE_ENCODERS_HPP #define ASYNC_MQTT5_BASE_ENCODERS_HPP #include #include #include #include #include namespace async_mqtt5::encoders { namespace basic { inline void to_variable_bytes(std::string& s, int32_t val) { if (val > 0xfffffff) return; while (val > 127) { s.push_back(char((val & 0b01111111) | 0b10000000)); val >>= 7; } s.push_back(val & 0b01111111); } inline size_t variable_length(int32_t val) { if (val > 0xfffffff) return 0; size_t rv = 1; for (; val > 127; ++rv) val >>= 7; return rv; } struct encoder {}; template class flag_def : public encoder { template using least_type = std::conditional_t< num_bits <= 8, uint8_t, std::conditional_t< num_bits <= 16, uint16_t, std::conditional_t< num_bits <= 32, uint32_t, std::conditional_t > > >; template friend class flag_def; repr _val { 0 }; public: flag_def(repr val) : _val(val) {} flag_def() = default; template requires (is_optional) auto operator()(T&& value, projection proj = {}) const { if constexpr (std::is_same_v) { repr val = value.has_value(); return flag_def { val }; } else { repr val = value.has_value() ? static_cast(std::invoke(proj, *value)) : 0; return flag_def { val }; } } template requires (!is_optional) auto operator()(T&& value, projection proj = {}) const { auto val = static_cast(std::invoke(proj, value)); return flag_def { val }; } size_t byte_size() const { return sizeof(repr); } template auto operator|(const flag_def& rhs) const { using res_repr = least_type; auto val = static_cast((_val << rhs_bits) | rhs._val); return flag_def { val }; } std::string& encode(std::string& s) const { using namespace boost::endian; size_t sz = s.size(); s.resize(sz + sizeof(repr)); auto p = reinterpret_cast(s.data() + sz); endian_store(p, _val); return s; } }; template constexpr auto flag = flag_def{}; template class int_val : public encoder { T _val; public: int_val(T val) : _val(val) {} size_t byte_size() const { if constexpr (is_optional) { if (_val) return val_length(*_val); return 0; } else return val_length(_val); } std::string& encode(std::string& s) const { if constexpr (is_optional) { if (_val) return encode_val(s, *_val); return s; } else return encode_val(s, _val); } private: template static size_t val_length(U&& val) { if constexpr (std::is_same_v) return variable_length(int32_t(val)); else return sizeof(Repr); } template static std::string& encode_val(std::string& s, U&& val) { using namespace boost::endian; if constexpr (std::is_same_v) { to_variable_bytes(s, int32_t(val)); return s; } else { size_t sz = s.size(); s.resize(sz + sizeof(Repr)); auto p = reinterpret_cast(s.data() + sz); endian_store(p, val); return s; } } }; template class int_def { public: template auto operator()(T&& val) const { return int_val { std::forward(val) }; } template auto operator()(T&& val, projection proj) const { if constexpr (is_optional) { using rv_type = std::invoke_result_t< projection, typename std::remove_cvref_t::value_type >; if (val.has_value()) return (*this)(std::invoke(proj, *val)); return int_val { rv_type {} }; } else { using rv_type = std::invoke_result_t; return int_val { std::invoke(proj, val) }; } } }; constexpr auto byte_ = int_def{}; constexpr auto int16_ = int_def{}; constexpr auto int32_ = int_def{}; constexpr auto varlen_ = int_def{}; template class array_val : public encoder { T _val; bool _with_length; public: array_val(T val, bool with_length) : _val(val), _with_length(with_length) { static_assert( std::is_reference_v || std::is_same_v ); } size_t byte_size() const { if constexpr (is_optional) return _val ? _with_length * 2 + val_length(*_val) : 0; else return _with_length * 2 + val_length(_val); } std::string& encode(std::string& s) const { if constexpr (is_optional) { if (_val) return encode_val(s, *_val); return s; } else return encode_val(s, _val); } private: template static size_t val_length(U&& val) { if constexpr (std::same_as, const char*>) return std::strlen(val); if constexpr (requires { val.size(); }) return val.size(); else // fallback to type const char (&)[N] (substract 1 for trailing 0) return sizeof(val) - 1; } template std::string& encode_val(std::string& s, U&& u) const { using namespace boost::endian; auto byte_len = val_length(std::forward(u)); if (byte_len == 0 && !_with_length) return s; if (_with_length) { size_t sz = s.size(); s.resize(sz + 2); auto p = reinterpret_cast(s.data() + sz); endian_store( p, int16_t(byte_len) ); } s.append(std::begin(u), std::begin(u) + byte_len); return s; } }; template class array_def { public: template auto operator()(T&& val) const { return array_val { std::forward(val), with_length }; } template auto operator()(T&& val, projection proj) const { if constexpr (is_optional) { using rv_type = std::invoke_result_t< projection, typename std::remove_cvref_t::value_type >; if (val.has_value()) return (*this)(std::invoke(proj, *val)); return array_val { rv_type {}, false }; } else { const auto& av = std::invoke(proj, val); return array_val { av, true }; } } }; using utf8_def = array_def; constexpr auto utf8_ = utf8_def{}; constexpr auto binary_ = array_def{}; // for now constexpr auto verbatim_ = array_def{}; template class composed_val : public encoder { T _lhs; U _rhs; public: composed_val(T lhs, U rhs) : _lhs(std::forward(lhs)), _rhs(std::forward(rhs)) {} size_t byte_size() const { return _lhs.byte_size() + _rhs.byte_size(); } std::string& encode(std::string& s) const { _lhs.encode(s); return _rhs.encode(s); } }; template requires ( std::derived_from, encoder> && std::derived_from, encoder> ) inline auto operator&(T&& t, U&& u) { return composed_val(std::forward(t), std::forward(u)); } template requires (std::derived_from, encoder>) std::string& operator<<(std::string& s, T&& t) { return t.encode(s); } } // end namespace basic namespace detail { namespace pp = async_mqtt5::prop; template constexpr bool match_v = std::is_same_v< std::integral_constant, typename std::tuple_element_t::key >; template < pp::property_type p, typename Tuple, typename Idxs = std::make_index_sequence> > struct type_index; template < pp::property_type p, template typename Tuple, typename... Args, std::size_t... Is > struct type_index, std::index_sequence> : std::integral_constant< std::size_t, ((Is * match_v>)+... + 0) > { static_assert( 1 == (match_v> + ... + 0), "T doesn't appear once in tuple" ); }; } // end namespace detail namespace prop { namespace pp = async_mqtt5::prop; template struct prop_encoder_type { using key = std::integral_constant; using value = T; }; using encoder_types = std::tuple< prop_encoder_type>, prop_encoder_type>, prop_encoder_type>, prop_encoder_type, prop_encoder_type, prop_encoder_type, prop_encoder_type>, prop_encoder_type>, prop_encoder_type, prop_encoder_type>, prop_encoder_type, prop_encoder_type, prop_encoder_type>, prop_encoder_type>, prop_encoder_type>, prop_encoder_type, prop_encoder_type, prop_encoder_type, prop_encoder_type>, prop_encoder_type>, prop_encoder_type>, prop_encoder_type>, prop_encoder_type>, prop_encoder_type, prop_encoder_type>, prop_encoder_type>, prop_encoder_type> >; template constexpr auto encoder_for_prop = typename std::tuple_element_t< detail::type_index::value, encoder_types >::value {}; template class prop_val; template requires (!is_vector && is_optional) class prop_val : public basic::encoder { // T is always std::optional using opt_type = typename std::remove_cvref_t::value_type; // allows T to be reference type to std::optional static inline std::optional nulltype; T _val; public: prop_val(T val) : _val(val) { static_assert(std::is_reference_v); } prop_val() : _val(nulltype) {} size_t byte_size() const { if (!_val) return 0; auto sval = encoder_for_prop

(_val); return 1 + sval.byte_size(); } std::string& encode(std::string& s) const { if (!_val) return s; s.push_back(p); auto sval = encoder_for_prop

(_val); return sval.encode(s); } }; template requires (is_vector) class prop_val : public basic::encoder { // allows T to be reference type to std::vector static inline std::remove_cvref_t nulltype; T _val; public: prop_val(T val) : _val(val) { static_assert(std::is_reference_v); } prop_val() : _val(nulltype) { } size_t byte_size() const { if (_val.empty()) return 0; size_t total_size = 0; for (const auto& pr: _val) { auto sval = encoder_for_prop

(pr); size_t prop_size = sval.byte_size(); if (prop_size) total_size += 1 + prop_size; } return total_size; } std::string& encode(std::string& s) const { if (_val.empty()) return s; for (const auto& pr: _val) { auto sval = encoder_for_prop

(pr); s.push_back(p); sval.encode(s); } return s; } }; template class props_val : public basic::encoder { static inline std::decay_t nulltype; template static auto to_prop_val(const T& val) { return prop_val(val); } template static auto to_prop_vals(const pp::properties& props) { return std::make_tuple( to_prop_val( props[std::integral_constant{}] )... ); } template auto apply_each(Func&& func) const { return std::apply([&func](const auto&... props) { return (std::invoke(func, props), ...); }, _prop_vals); } decltype(to_prop_vals(std::declval())) _prop_vals; bool _may_omit; public: props_val(Props val, bool may_omit) : _prop_vals(to_prop_vals(val)), _may_omit(may_omit) { static_assert(std::is_reference_v); } props_val(bool may_omit) : _prop_vals(to_prop_vals(nulltype)), _may_omit(may_omit) {} size_t byte_size() const { size_t psize = props_size(); if (_may_omit && psize == 0) return 0; return psize + basic::varlen_(psize).byte_size(); } std::string& encode(std::string& s) const { size_t psize = props_size(); if (_may_omit && psize == 0) return s; basic::varlen_(psize).encode(s); apply_each([&s](const auto& pv) { return pv.encode(s); }); return s; } private: size_t props_size() const { size_t retval = 0; apply_each([&retval](const auto& pv) { return retval += pv.byte_size(); }); return retval; } }; template class props_def { public: template auto operator()(T&& prop_container) const { if constexpr (is_optional) { if (prop_container.has_value()) return (*this)(*prop_container); return props_val< const typename std::remove_cvref_t::value_type& >(true); } else { return props_val { prop_container, may_omit }; } } }; constexpr auto props_ = props_def{}; constexpr auto props_may_omit_ = props_def{}; } // end namespace prop } // end namespace async_mqtt5::encoders #endif // !ASYNC_MQTT5_BASE_ENCODERS_HPP