Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 08:51:36

0001 //
0002 // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
0003 //
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MQTT5_BASE_ENCODERS_HPP
0009 #define BOOST_MQTT5_BASE_ENCODERS_HPP
0010 
0011 #include <boost/mqtt5/property_types.hpp>
0012 
0013 #include <boost/mqtt5/detail/traits.hpp>
0014 
0015 #include <boost/core/identity.hpp>
0016 #include <boost/endian/conversion.hpp>
0017 #include <boost/type_traits/is_detected_exact.hpp>
0018 #include <boost/type_traits/remove_cv_ref.hpp>
0019 
0020 #include <cstddef>
0021 #include <cstdint>
0022 #include <string>
0023 #include <type_traits>
0024 #include <utility>
0025 
0026 namespace boost::mqtt5::encoders {
0027 
0028 namespace basic {
0029 
0030 using varint_t = int*;
0031 
0032 inline void to_variable_bytes(std::string& s, int32_t val) {
0033     if (val > 0xfffffff) return;
0034     while (val > 127) {
0035         s.push_back(char((val & 0b01111111) | 0b10000000));
0036         val >>= 7;
0037     }
0038     s.push_back(val & 0b01111111);
0039 }
0040 
0041 inline size_t variable_length(int32_t val) {
0042     if (val > 0xfffffff) return 0;
0043     size_t rv = 1;
0044     for (; val > 127; ++rv) val >>= 7;
0045     return rv;
0046 }
0047 
0048 struct encoder {};
0049 
0050 template <size_t bits, typename repr = uint8_t>
0051 class flag_def : public encoder {
0052     template <size_t num_bits>
0053     using least_type = std::conditional_t<
0054         num_bits <= 8, uint8_t,
0055         std::conditional_t<
0056             num_bits <= 16, uint16_t,
0057             std::conditional_t<
0058                 num_bits <= 32, uint32_t,
0059                 std::conditional_t<num_bits <= 64, uint64_t, void>
0060             >
0061         >
0062     >;
0063 
0064     template <size_t oth_bits, typename oth_repr>
0065     friend class flag_def;
0066 
0067     repr _val { 0 };
0068 
0069 public:
0070     flag_def(repr val) : _val(val) {}
0071     flag_def() = default;
0072 
0073     template <
0074         typename T,
0075         typename projection = boost::identity,
0076         std::enable_if_t<detail::is_optional<T>, bool> = true
0077     >
0078     auto operator()(T&& value, projection proj = {}) const {
0079         if constexpr (std::is_same_v<projection, boost::identity>) {
0080             repr val = value.has_value();
0081             return flag_def<bits, repr> { val };
0082         }
0083         else {
0084             repr val = value.has_value() ?
0085                 static_cast<repr>(std::invoke(proj, *value)) : 0;
0086             return flag_def<bits, repr> { val };
0087         }
0088     }
0089 
0090     template <
0091         typename T,
0092         typename projection = boost::identity,
0093         std::enable_if_t<!detail::is_optional<T>, bool> = true
0094     >
0095     auto operator()(T&& value, projection proj = {}) const {
0096         auto val = static_cast<repr>(std::invoke(proj, value));
0097         return flag_def<bits, repr> { val };
0098     }
0099 
0100     size_t byte_size() const { return sizeof(repr); }
0101 
0102     template <size_t rhs_bits, typename rhs_repr>
0103     auto operator|(const flag_def<rhs_bits, rhs_repr>& rhs) const {
0104         using res_repr = least_type<bits + rhs_bits>;
0105         auto val = static_cast<res_repr>((_val << rhs_bits) | rhs._val);
0106         return flag_def<bits + rhs_bits, res_repr> { val };
0107     }
0108 
0109     std::string& encode(std::string& s) const {
0110         using namespace boost::endian;
0111         size_t sz = s.size(); s.resize(sz + sizeof(repr));
0112         auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
0113         endian_store<repr, sizeof(repr), order::big>(p, _val);
0114         return s;
0115     }
0116 };
0117 
0118 template <size_t bits, typename repr = uint8_t>
0119 constexpr auto flag = flag_def<bits, repr>{};
0120 
0121 
0122 template <typename T, typename Repr>
0123 class int_val : public encoder {
0124     T _val;
0125 public:
0126     int_val(T val) : _val(val) {}
0127 
0128     size_t byte_size() const {
0129         if constexpr (detail::is_optional<T>) {
0130             if (_val) return val_length(*_val);
0131             return 0;
0132         }
0133         else
0134             return val_length(_val);
0135     }
0136 
0137     std::string& encode(std::string& s) const {
0138         if constexpr (detail::is_optional<T>) {
0139             if (_val) return encode_val(s, *_val);
0140             return s;
0141         }
0142         else
0143             return encode_val(s, _val);
0144     }
0145 private:
0146     template <typename U>
0147     static size_t val_length(U&& val) {
0148         if constexpr (std::is_same_v<Repr, varint_t>)
0149             return variable_length(int32_t(val));
0150         else
0151             return sizeof(Repr);
0152     }
0153 
0154     template <typename U>
0155     static std::string& encode_val(std::string& s, U&& val) {
0156         using namespace boost::endian;
0157         if constexpr (std::is_same_v<Repr, varint_t>) {
0158             to_variable_bytes(s, int32_t(val));
0159             return s;
0160         }
0161         else {
0162             size_t sz = s.size(); s.resize(sz + sizeof(Repr));
0163             auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
0164             endian_store<Repr, sizeof(Repr), order::big>(p, val);
0165             return s;
0166         }
0167     }
0168 };
0169 
0170 template <typename Repr>
0171 class int_def {
0172 public:
0173     template <typename T>
0174     auto operator()(T&& val) const {
0175         return int_val<T, Repr> { std::forward<T>(val) };
0176     }
0177 
0178     template <typename T, typename projection>
0179     auto operator()(T&& val, projection proj) const {
0180         if constexpr (detail::is_optional<T>) {
0181             using rv_type = std::invoke_result_t<
0182                 projection, typename boost::remove_cv_ref_t<T>::value_type
0183             >;
0184             if (val.has_value())
0185                 return (*this)(std::invoke(proj, *val));
0186             return int_val<rv_type, Repr> { rv_type {} };
0187         }
0188         else {
0189             using rv_type = std::invoke_result_t<projection, T>;
0190             return int_val<rv_type, Repr> { std::invoke(proj, val) };
0191         }
0192     }
0193 };
0194 
0195 constexpr auto byte_ = int_def<uint8_t> {};
0196 constexpr auto int16_ = int_def<uint16_t> {};
0197 constexpr auto int32_ = int_def<uint32_t> {};
0198 constexpr auto varlen_ = int_def<varint_t> {};
0199 
0200 
0201 template <typename T>
0202 class array_val : public encoder {
0203     T _val;
0204     bool _with_length;
0205 public:
0206     array_val(T val, bool with_length) :
0207         _val(val), _with_length(with_length)
0208     {
0209         static_assert(
0210             std::is_reference_v<T> || std::is_same_v<T, std::string_view>
0211         );
0212     }
0213 
0214     size_t byte_size() const {
0215         if constexpr (detail::is_optional<T>)
0216             return _val ? _with_length * 2 + val_length(*_val) : 0;
0217         else
0218             return _with_length * 2 + val_length(_val);
0219     }
0220 
0221     std::string& encode(std::string& s) const {
0222         if constexpr (detail::is_optional<T>) {
0223             if (_val) return encode_val(s, *_val);
0224             return s;
0225         }
0226         else
0227             return encode_val(s, _val);
0228     }
0229 
0230 private:
0231     template <typename V>
0232     using has_size = decltype(std::declval<V&>().size());
0233 
0234     template <typename U>
0235     static size_t val_length(U&& val) {
0236         if constexpr (std::is_same_v<boost::remove_cv_ref_t<U>, const char*>)
0237             return std::strlen(val);
0238 
0239         if constexpr (boost::is_detected_exact_v<size_t, has_size, U>)
0240             return val.size();
0241         else // fallback to type const char (&)[N] (substract 1 for trailing 0)
0242             return sizeof(val) - 1;
0243     }
0244 
0245     template <typename U>
0246     std::string& encode_val(std::string& s, U&& u) const {
0247         using namespace boost::endian;
0248         auto byte_len = val_length(std::forward<U>(u));
0249         if (byte_len == 0 && !_with_length) return s;
0250         if (_with_length) {
0251             size_t sz = s.size(); s.resize(sz + 2);
0252             auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
0253             endian_store<int16_t, sizeof(int16_t), order::big>(
0254                 p, int16_t(byte_len)
0255             );
0256         }
0257         s.append(std::begin(u), std::begin(u) + byte_len);
0258         return s;
0259     }
0260 };
0261 
0262 template <bool with_length = true>
0263 class array_def {
0264 public:
0265     template <typename T>
0266     auto operator()(T&& val) const {
0267         return array_val<T> { std::forward<T>(val), with_length };
0268     }
0269 
0270     template <typename T, typename projection>
0271     auto operator()(T&& val, projection proj) const {
0272         if constexpr (detail::is_optional<T>) {
0273             using rv_type = std::invoke_result_t<
0274                 projection, typename boost::remove_cv_ref_t<T>::value_type
0275             >;
0276             if (val.has_value())
0277                 return (*this)(std::invoke(proj, *val));
0278             return array_val<rv_type> { rv_type {}, false };
0279         }
0280         else {
0281             const auto& av = std::invoke(proj, val);
0282             return array_val<T> { av, true };
0283         }
0284     }
0285 };
0286 
0287 using utf8_def = array_def<true>;
0288 
0289 constexpr auto utf8_ = utf8_def {};
0290 constexpr auto binary_ = array_def<true> {}; // for now
0291 constexpr auto verbatim_ = array_def<false> {};
0292 
0293 
0294 template <typename T, typename U>
0295 class composed_val : public encoder {
0296     T _lhs; U _rhs;
0297 public:
0298     composed_val(T lhs, U rhs) :
0299         _lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs))
0300     {}
0301 
0302     size_t byte_size() const {
0303         return _lhs.byte_size() + _rhs.byte_size();
0304     }
0305 
0306     std::string& encode(std::string& s) const {
0307         _lhs.encode(s);
0308         return _rhs.encode(s);
0309     }
0310 };
0311 
0312 template <
0313     typename T, typename U,
0314     std::enable_if_t<
0315         std::is_base_of_v<encoder, std::decay_t<T>> &&
0316         std::is_base_of_v<encoder, std::decay_t<U>>,
0317         bool
0318     > = true
0319 >
0320 inline auto operator&(T&& t, U&& u) {
0321     return composed_val(std::forward<T>(t), std::forward<U>(u));
0322 }
0323 
0324 template <
0325     typename T,
0326     std::enable_if_t<std::is_base_of_v<encoder, std::decay_t<T>>, bool> = true
0327 >
0328 std::string& operator<<(std::string& s, T&& t) {
0329     return t.encode(s);
0330 }
0331 
0332 } // end namespace basic
0333 
0334 namespace prop {
0335 
0336 namespace pp = boost::mqtt5::prop;
0337 
0338 template <typename T>
0339 auto encoder_for_prop_value(const T& val) {
0340     if constexpr (std::is_same_v<T, uint8_t>)
0341         return basic::int_def<uint8_t>{}(val);
0342     else if constexpr (std::is_same_v<T, uint16_t>)
0343         return basic::int_def<uint16_t>{}(val);
0344     else if constexpr (std::is_same_v<T, int32_t>)
0345         return basic::int_def<basic::varint_t>{}(val);
0346     else if constexpr (std::is_same_v<T, uint32_t>)
0347         return basic::int_def<uint32_t>{}(val);
0348     else if constexpr (std::is_same_v<T, std::string>)
0349         return basic::utf8_def{}(val);
0350     else if constexpr (detail::is_pair<T>)
0351         return encoder_for_prop_value(val.first) &
0352             encoder_for_prop_value(val.second);
0353 }
0354 
0355 template <typename T, pp::property_type p, typename Enable = void>
0356 class prop_val;
0357 
0358 template <
0359     typename T, pp::property_type p
0360 >
0361 class prop_val<
0362     T, p,
0363     std::enable_if_t<!detail::is_vector<T> && detail::is_optional<T>>
0364 > : public basic::encoder {
0365     // allows T to be reference type to std::optional
0366     static inline boost::remove_cv_ref_t<T> nulltype;
0367     T _val;
0368 public:
0369     prop_val(T val) : _val(val) {
0370         static_assert(std::is_reference_v<T>);
0371     }
0372     prop_val() : _val(nulltype) {}
0373 
0374     size_t byte_size() const {
0375         if (!_val) return 0;
0376         return 1 + encoder_for_prop_value(*_val).byte_size();
0377     }
0378 
0379     std::string& encode(std::string& s) const {
0380         if (!_val)
0381             return s;
0382         s.push_back(p);
0383         return encoder_for_prop_value(*_val).encode(s);
0384     }
0385 };
0386 
0387 template <
0388     typename T, pp::property_type p
0389 >
0390 class prop_val<
0391     T, p,
0392     std::enable_if_t<detail::is_vector<T> || detail::is_small_vector<T>>
0393 > : public basic::encoder {
0394     // allows T to be reference type to std::vector
0395     static inline boost::remove_cv_ref_t<T> nulltype;
0396     T _val;
0397 public:
0398     prop_val(T val) : _val(val) {
0399         static_assert(std::is_reference_v<T>);
0400     }
0401 
0402     prop_val() : _val(nulltype) { }
0403 
0404     size_t byte_size() const {
0405         if (_val.empty()) return 0;
0406 
0407         size_t total_size = 0;
0408         for (const auto& elem : _val)
0409             total_size += 1 + encoder_for_prop_value(elem).byte_size();
0410 
0411         return total_size;
0412     }
0413 
0414     std::string& encode(std::string& s) const {
0415         if (_val.empty())
0416             return s;
0417 
0418         for (const auto& elem: _val) {
0419             s.push_back(p);
0420             encoder_for_prop_value(elem).encode(s);
0421         }
0422 
0423         return s;
0424     }
0425 };
0426 
0427 
0428 template <typename Props>
0429 class props_val : public basic::encoder {
0430     static inline std::decay_t<Props> nulltype;
0431 
0432     template <pp::property_type P, typename T>
0433     static auto to_prop_val(const T& val) {
0434         return prop_val<const T&, P>(val);
0435     }
0436 
0437     template <pp::property_type ...Ps>
0438     static auto to_prop_vals(const pp::properties<Ps...>& props) {
0439         return std::make_tuple(
0440             to_prop_val<Ps>(
0441                 props[std::integral_constant<pp::property_type, Ps> {}]
0442             )...
0443         );
0444     }
0445 
0446     template <typename Func>
0447     auto apply_each(Func&& func) const {
0448         return std::apply([&func](const auto&... props) {
0449             return (std::invoke(func, props), ...);
0450         }, _prop_vals);
0451     }
0452 
0453     decltype(to_prop_vals(std::declval<Props>())) _prop_vals;
0454     bool _may_omit;
0455 
0456 public:
0457     props_val(Props val, bool may_omit) :
0458         _prop_vals(to_prop_vals(val)), _may_omit(may_omit)
0459     {
0460         static_assert(std::is_reference_v<Props>);
0461     }
0462     props_val(bool may_omit) :
0463         _prop_vals(to_prop_vals(nulltype)), _may_omit(may_omit)
0464     {}
0465 
0466     size_t byte_size() const {
0467         size_t psize = props_size();
0468         if (_may_omit && psize == 0) return 0;
0469         return psize + basic::varlen_(psize).byte_size();
0470     }
0471 
0472     std::string& encode(std::string& s) const {
0473         size_t psize = props_size();
0474         if (_may_omit && psize == 0) return s;
0475         basic::varlen_(psize).encode(s);
0476         apply_each([&s](const auto& pv) { return pv.encode(s); });
0477         return s;
0478     }
0479 private:
0480     size_t props_size() const {
0481         size_t retval = 0;
0482         apply_each([&retval](const auto& pv) {
0483             return retval += pv.byte_size();
0484         });
0485         return retval;
0486     }
0487 };
0488 
0489 template <bool may_omit>
0490 class props_def {
0491 public:
0492     template <typename T>
0493     auto operator()(T&& prop_container) const {
0494         if constexpr (detail::is_optional<T>) {
0495             if (prop_container.has_value())
0496                 return (*this)(*prop_container);
0497             return props_val<
0498                 const typename boost::remove_cv_ref_t<T>::value_type&
0499             >(true);
0500         }
0501         else {
0502             return props_val<T> { prop_container, may_omit };
0503         }
0504     }
0505 };
0506 
0507 constexpr auto props_ = props_def<false> {};
0508 constexpr auto props_may_omit_ = props_def<true> {};
0509 
0510 } // end namespace prop
0511 
0512 } // end namespace boost::mqtt5::encoders
0513 
0514 #endif // !BOOST_MQTT5_BASE_ENCODERS_HPP