base_encoders.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. //
  2. // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MQTT5_BASE_ENCODERS_HPP
  8. #define BOOST_MQTT5_BASE_ENCODERS_HPP
  9. #include <boost/mqtt5/property_types.hpp>
  10. #include <boost/mqtt5/detail/traits.hpp>
  11. #include <boost/core/identity.hpp>
  12. #include <boost/endian/conversion.hpp>
  13. #include <boost/type_traits/is_detected_exact.hpp>
  14. #include <boost/type_traits/remove_cv_ref.hpp>
  15. #include <cstddef>
  16. #include <cstdint>
  17. #include <string>
  18. #include <type_traits>
  19. #include <utility>
  20. namespace boost::mqtt5::encoders {
  21. namespace basic {
  22. using varint_t = int*;
  23. inline void to_variable_bytes(std::string& s, int32_t val) {
  24. if (val > 0xfffffff) return;
  25. while (val > 127) {
  26. s.push_back(char((val & 0b01111111) | 0b10000000));
  27. val >>= 7;
  28. }
  29. s.push_back(val & 0b01111111);
  30. }
  31. inline size_t variable_length(int32_t val) {
  32. if (val > 0xfffffff) return 0;
  33. size_t rv = 1;
  34. for (; val > 127; ++rv) val >>= 7;
  35. return rv;
  36. }
  37. struct encoder {};
  38. template <size_t bits, typename repr = uint8_t>
  39. class flag_def : public encoder {
  40. template <size_t num_bits>
  41. using least_type = std::conditional_t<
  42. num_bits <= 8, uint8_t,
  43. std::conditional_t<
  44. num_bits <= 16, uint16_t,
  45. std::conditional_t<
  46. num_bits <= 32, uint32_t,
  47. std::conditional_t<num_bits <= 64, uint64_t, void>
  48. >
  49. >
  50. >;
  51. template <size_t oth_bits, typename oth_repr>
  52. friend class flag_def;
  53. repr _val { 0 };
  54. public:
  55. flag_def(repr val) : _val(val) {}
  56. flag_def() = default;
  57. template <
  58. typename T,
  59. typename projection = boost::identity,
  60. std::enable_if_t<detail::is_optional<T>, bool> = true
  61. >
  62. auto operator()(T&& value, projection proj = {}) const {
  63. if constexpr (std::is_same_v<projection, boost::identity>) {
  64. repr val = value.has_value();
  65. return flag_def<bits, repr> { val };
  66. }
  67. else {
  68. repr val = value.has_value() ?
  69. static_cast<repr>(std::invoke(proj, *value)) : 0;
  70. return flag_def<bits, repr> { val };
  71. }
  72. }
  73. template <
  74. typename T,
  75. typename projection = boost::identity,
  76. std::enable_if_t<!detail::is_optional<T>, bool> = true
  77. >
  78. auto operator()(T&& value, projection proj = {}) const {
  79. auto val = static_cast<repr>(std::invoke(proj, value));
  80. return flag_def<bits, repr> { val };
  81. }
  82. size_t byte_size() const { return sizeof(repr); }
  83. template <size_t rhs_bits, typename rhs_repr>
  84. auto operator|(const flag_def<rhs_bits, rhs_repr>& rhs) const {
  85. using res_repr = least_type<bits + rhs_bits>;
  86. auto val = static_cast<res_repr>((_val << rhs_bits) | rhs._val);
  87. return flag_def<bits + rhs_bits, res_repr> { val };
  88. }
  89. std::string& encode(std::string& s) const {
  90. using namespace boost::endian;
  91. size_t sz = s.size(); s.resize(sz + sizeof(repr));
  92. auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
  93. endian_store<repr, sizeof(repr), order::big>(p, _val);
  94. return s;
  95. }
  96. };
  97. template <size_t bits, typename repr = uint8_t>
  98. constexpr auto flag = flag_def<bits, repr>{};
  99. template <typename T, typename Repr>
  100. class int_val : public encoder {
  101. T _val;
  102. public:
  103. int_val(T val) : _val(val) {}
  104. size_t byte_size() const {
  105. if constexpr (detail::is_optional<T>) {
  106. if (_val) return val_length(*_val);
  107. return 0;
  108. }
  109. else
  110. return val_length(_val);
  111. }
  112. std::string& encode(std::string& s) const {
  113. if constexpr (detail::is_optional<T>) {
  114. if (_val) return encode_val(s, *_val);
  115. return s;
  116. }
  117. else
  118. return encode_val(s, _val);
  119. }
  120. private:
  121. template <typename U>
  122. static size_t val_length(U&& val) {
  123. if constexpr (std::is_same_v<Repr, varint_t>)
  124. return variable_length(int32_t(val));
  125. else
  126. return sizeof(Repr);
  127. }
  128. template <typename U>
  129. static std::string& encode_val(std::string& s, U&& val) {
  130. using namespace boost::endian;
  131. if constexpr (std::is_same_v<Repr, varint_t>) {
  132. to_variable_bytes(s, int32_t(val));
  133. return s;
  134. }
  135. else {
  136. size_t sz = s.size(); s.resize(sz + sizeof(Repr));
  137. auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
  138. endian_store<Repr, sizeof(Repr), order::big>(p, val);
  139. return s;
  140. }
  141. }
  142. };
  143. template <typename Repr>
  144. class int_def {
  145. public:
  146. template <typename T>
  147. auto operator()(T&& val) const {
  148. return int_val<T, Repr> { std::forward<T>(val) };
  149. }
  150. template <typename T, typename projection>
  151. auto operator()(T&& val, projection proj) const {
  152. if constexpr (detail::is_optional<T>) {
  153. using rv_type = std::invoke_result_t<
  154. projection, typename boost::remove_cv_ref_t<T>::value_type
  155. >;
  156. if (val.has_value())
  157. return (*this)(std::invoke(proj, *val));
  158. return int_val<rv_type, Repr> { rv_type {} };
  159. }
  160. else {
  161. using rv_type = std::invoke_result_t<projection, T>;
  162. return int_val<rv_type, Repr> { std::invoke(proj, val) };
  163. }
  164. }
  165. };
  166. constexpr auto byte_ = int_def<uint8_t> {};
  167. constexpr auto int16_ = int_def<uint16_t> {};
  168. constexpr auto int32_ = int_def<uint32_t> {};
  169. constexpr auto varlen_ = int_def<varint_t> {};
  170. template <typename T>
  171. class array_val : public encoder {
  172. T _val;
  173. bool _with_length;
  174. public:
  175. array_val(T val, bool with_length) :
  176. _val(val), _with_length(with_length)
  177. {
  178. static_assert(
  179. std::is_reference_v<T> || std::is_same_v<T, std::string_view>
  180. );
  181. }
  182. size_t byte_size() const {
  183. if constexpr (detail::is_optional<T>)
  184. return _val ? _with_length * 2 + val_length(*_val) : 0;
  185. else
  186. return _with_length * 2 + val_length(_val);
  187. }
  188. std::string& encode(std::string& s) const {
  189. if constexpr (detail::is_optional<T>) {
  190. if (_val) return encode_val(s, *_val);
  191. return s;
  192. }
  193. else
  194. return encode_val(s, _val);
  195. }
  196. private:
  197. template <typename V>
  198. using has_size = decltype(std::declval<V&>().size());
  199. template <typename U>
  200. static size_t val_length(U&& val) {
  201. if constexpr (std::is_same_v<boost::remove_cv_ref_t<U>, const char*>)
  202. return std::strlen(val);
  203. if constexpr (boost::is_detected_exact_v<size_t, has_size, U>)
  204. return val.size();
  205. else // fallback to type const char (&)[N] (substract 1 for trailing 0)
  206. return sizeof(val) - 1;
  207. }
  208. template <typename U>
  209. std::string& encode_val(std::string& s, U&& u) const {
  210. using namespace boost::endian;
  211. auto byte_len = val_length(std::forward<U>(u));
  212. if (byte_len == 0 && !_with_length) return s;
  213. if (_with_length) {
  214. size_t sz = s.size(); s.resize(sz + 2);
  215. auto p = reinterpret_cast<uint8_t*>(s.data() + sz);
  216. endian_store<int16_t, sizeof(int16_t), order::big>(
  217. p, int16_t(byte_len)
  218. );
  219. }
  220. s.append(std::begin(u), std::begin(u) + byte_len);
  221. return s;
  222. }
  223. };
  224. template <bool with_length = true>
  225. class array_def {
  226. public:
  227. template <typename T>
  228. auto operator()(T&& val) const {
  229. return array_val<T> { std::forward<T>(val), with_length };
  230. }
  231. template <typename T, typename projection>
  232. auto operator()(T&& val, projection proj) const {
  233. if constexpr (detail::is_optional<T>) {
  234. using rv_type = std::invoke_result_t<
  235. projection, typename boost::remove_cv_ref_t<T>::value_type
  236. >;
  237. if (val.has_value())
  238. return (*this)(std::invoke(proj, *val));
  239. return array_val<rv_type> { rv_type {}, false };
  240. }
  241. else {
  242. const auto& av = std::invoke(proj, val);
  243. return array_val<T> { av, true };
  244. }
  245. }
  246. };
  247. using utf8_def = array_def<true>;
  248. constexpr auto utf8_ = utf8_def {};
  249. constexpr auto binary_ = array_def<true> {}; // for now
  250. constexpr auto verbatim_ = array_def<false> {};
  251. template <typename T, typename U>
  252. class composed_val : public encoder {
  253. T _lhs; U _rhs;
  254. public:
  255. composed_val(T lhs, U rhs) :
  256. _lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs))
  257. {}
  258. size_t byte_size() const {
  259. return _lhs.byte_size() + _rhs.byte_size();
  260. }
  261. std::string& encode(std::string& s) const {
  262. _lhs.encode(s);
  263. return _rhs.encode(s);
  264. }
  265. };
  266. template <
  267. typename T, typename U,
  268. std::enable_if_t<
  269. std::is_base_of_v<encoder, std::decay_t<T>> &&
  270. std::is_base_of_v<encoder, std::decay_t<U>>,
  271. bool
  272. > = true
  273. >
  274. inline auto operator&(T&& t, U&& u) {
  275. return composed_val(std::forward<T>(t), std::forward<U>(u));
  276. }
  277. template <
  278. typename T,
  279. std::enable_if_t<std::is_base_of_v<encoder, std::decay_t<T>>, bool> = true
  280. >
  281. std::string& operator<<(std::string& s, T&& t) {
  282. return t.encode(s);
  283. }
  284. } // end namespace basic
  285. namespace prop {
  286. namespace pp = boost::mqtt5::prop;
  287. template <typename T>
  288. auto encoder_for_prop_value(const T& val) {
  289. if constexpr (std::is_same_v<T, uint8_t>)
  290. return basic::int_def<uint8_t>{}(val);
  291. else if constexpr (std::is_same_v<T, uint16_t>)
  292. return basic::int_def<uint16_t>{}(val);
  293. else if constexpr (std::is_same_v<T, int32_t>)
  294. return basic::int_def<basic::varint_t>{}(val);
  295. else if constexpr (std::is_same_v<T, uint32_t>)
  296. return basic::int_def<uint32_t>{}(val);
  297. else if constexpr (std::is_same_v<T, std::string>)
  298. return basic::utf8_def{}(val);
  299. else if constexpr (detail::is_pair<T>)
  300. return encoder_for_prop_value(val.first) &
  301. encoder_for_prop_value(val.second);
  302. }
  303. template <typename T, pp::property_type p, typename Enable = void>
  304. class prop_val;
  305. template <
  306. typename T, pp::property_type p
  307. >
  308. class prop_val<
  309. T, p,
  310. std::enable_if_t<!detail::is_vector<T> && detail::is_optional<T>>
  311. > : public basic::encoder {
  312. // allows T to be reference type to std::optional
  313. static inline boost::remove_cv_ref_t<T> nulltype;
  314. T _val;
  315. public:
  316. prop_val(T val) : _val(val) {
  317. static_assert(std::is_reference_v<T>);
  318. }
  319. prop_val() : _val(nulltype) {}
  320. size_t byte_size() const {
  321. if (!_val) return 0;
  322. return 1 + encoder_for_prop_value(*_val).byte_size();
  323. }
  324. std::string& encode(std::string& s) const {
  325. if (!_val)
  326. return s;
  327. s.push_back(p);
  328. return encoder_for_prop_value(*_val).encode(s);
  329. }
  330. };
  331. template <
  332. typename T, pp::property_type p
  333. >
  334. class prop_val<
  335. T, p,
  336. std::enable_if_t<detail::is_vector<T> || detail::is_small_vector<T>>
  337. > : public basic::encoder {
  338. // allows T to be reference type to std::vector
  339. static inline boost::remove_cv_ref_t<T> nulltype;
  340. T _val;
  341. public:
  342. prop_val(T val) : _val(val) {
  343. static_assert(std::is_reference_v<T>);
  344. }
  345. prop_val() : _val(nulltype) { }
  346. size_t byte_size() const {
  347. if (_val.empty()) return 0;
  348. size_t total_size = 0;
  349. for (const auto& elem : _val)
  350. total_size += 1 + encoder_for_prop_value(elem).byte_size();
  351. return total_size;
  352. }
  353. std::string& encode(std::string& s) const {
  354. if (_val.empty())
  355. return s;
  356. for (const auto& elem: _val) {
  357. s.push_back(p);
  358. encoder_for_prop_value(elem).encode(s);
  359. }
  360. return s;
  361. }
  362. };
  363. template <typename Props>
  364. class props_val : public basic::encoder {
  365. static inline std::decay_t<Props> nulltype;
  366. template <pp::property_type P, typename T>
  367. static auto to_prop_val(const T& val) {
  368. return prop_val<const T&, P>(val);
  369. }
  370. template <pp::property_type ...Ps>
  371. static auto to_prop_vals(const pp::properties<Ps...>& props) {
  372. return std::make_tuple(
  373. to_prop_val<Ps>(
  374. props[std::integral_constant<pp::property_type, Ps> {}]
  375. )...
  376. );
  377. }
  378. template <typename Func>
  379. auto apply_each(Func&& func) const {
  380. return std::apply([&func](const auto&... props) {
  381. return (std::invoke(func, props), ...);
  382. }, _prop_vals);
  383. }
  384. decltype(to_prop_vals(std::declval<Props>())) _prop_vals;
  385. bool _may_omit;
  386. public:
  387. props_val(Props val, bool may_omit) :
  388. _prop_vals(to_prop_vals(val)), _may_omit(may_omit)
  389. {
  390. static_assert(std::is_reference_v<Props>);
  391. }
  392. props_val(bool may_omit) :
  393. _prop_vals(to_prop_vals(nulltype)), _may_omit(may_omit)
  394. {}
  395. size_t byte_size() const {
  396. size_t psize = props_size();
  397. if (_may_omit && psize == 0) return 0;
  398. return psize + basic::varlen_(psize).byte_size();
  399. }
  400. std::string& encode(std::string& s) const {
  401. size_t psize = props_size();
  402. if (_may_omit && psize == 0) return s;
  403. basic::varlen_(psize).encode(s);
  404. apply_each([&s](const auto& pv) { return pv.encode(s); });
  405. return s;
  406. }
  407. private:
  408. size_t props_size() const {
  409. size_t retval = 0;
  410. apply_each([&retval](const auto& pv) {
  411. return retval += pv.byte_size();
  412. });
  413. return retval;
  414. }
  415. };
  416. template <bool may_omit>
  417. class props_def {
  418. public:
  419. template <typename T>
  420. auto operator()(T&& prop_container) const {
  421. if constexpr (detail::is_optional<T>) {
  422. if (prop_container.has_value())
  423. return (*this)(*prop_container);
  424. return props_val<
  425. const typename boost::remove_cv_ref_t<T>::value_type&
  426. >(true);
  427. }
  428. else {
  429. return props_val<T> { prop_container, may_omit };
  430. }
  431. }
  432. };
  433. constexpr auto props_ = props_def<false> {};
  434. constexpr auto props_may_omit_ = props_def<true> {};
  435. } // end namespace prop
  436. } // end namespace boost::mqtt5::encoders
  437. #endif // !BOOST_MQTT5_BASE_ENCODERS_HPP