properties.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /////////////////////////////////////////////////////////////////////////////
  2. /// @file properties.h
  3. /// Declaration of MQTT properties class
  4. /// @date July 7, 2019
  5. /// @author Frank Pagliughi
  6. /////////////////////////////////////////////////////////////////////////////
  7. /*******************************************************************************
  8. * Copyright (c) 2019-2024 Frank Pagliughi <fpagliughi@mindspring.com>
  9. *
  10. * All rights reserved. This program and the accompanying materials
  11. * are made available under the terms of the Eclipse Public License v2.0
  12. * and Eclipse Distribution License v1.0 which accompany this distribution.
  13. *
  14. * The Eclipse Public License is available at
  15. * http://www.eclipse.org/legal/epl-v20.html
  16. * and the Eclipse Distribution License is available at
  17. * http://www.eclipse.org/org/documents/edl-v10.php.
  18. *
  19. * Contributors:
  20. * Frank Pagliughi - initial implementation and documentation
  21. *******************************************************************************/
  22. #ifndef __mqtt_properties_h
  23. #define __mqtt_properties_h
  24. extern "C" {
  25. #include "MQTTProperties.h"
  26. }
  27. #include <initializer_list>
  28. #include <iostream>
  29. #include <map>
  30. #include <stdexcept>
  31. #include <string_view>
  32. #include <tuple>
  33. #include <typeinfo>
  34. #include "mqtt/buffer_ref.h"
  35. #include "mqtt/exception.h"
  36. #include "mqtt/platform.h"
  37. #include "mqtt/types.h"
  38. namespace mqtt {
  39. /** A pair of strings as a tuple. */
  40. using string_pair = std::tuple<string, string>;
  41. /////////////////////////////////////////////////////////////////////////////
  42. /**
  43. * A single MQTT v5 property.
  44. */
  45. class property
  46. {
  47. /** The underlying Paho C property struct. */
  48. MQTTProperty prop_;
  49. // Make a deep copy of the property struct into this one.
  50. // For string properties, this allocates memory and copied the string(s)
  51. void copy(const MQTTProperty& other);
  52. friend class properties;
  53. property() {}
  54. public:
  55. /**
  56. * The integer codes for the different v5 properties.
  57. */
  58. enum code {
  59. PAYLOAD_FORMAT_INDICATOR = 1,
  60. MESSAGE_EXPIRY_INTERVAL = 2,
  61. CONTENT_TYPE = 3,
  62. RESPONSE_TOPIC = 8,
  63. CORRELATION_DATA = 9,
  64. SUBSCRIPTION_IDENTIFIER = 11,
  65. SESSION_EXPIRY_INTERVAL = 17,
  66. ASSIGNED_CLIENT_IDENTIFIER = 18,
  67. SERVER_KEEP_ALIVE = 19,
  68. AUTHENTICATION_METHOD = 21,
  69. AUTHENTICATION_DATA = 22,
  70. REQUEST_PROBLEM_INFORMATION = 23,
  71. WILL_DELAY_INTERVAL = 24,
  72. REQUEST_RESPONSE_INFORMATION = 25,
  73. RESPONSE_INFORMATION = 26,
  74. SERVER_REFERENCE = 28,
  75. REASON_STRING = 31,
  76. RECEIVE_MAXIMUM = 33,
  77. TOPIC_ALIAS_MAXIMUM = 34,
  78. TOPIC_ALIAS = 35,
  79. MAXIMUM_QOS = 36,
  80. RETAIN_AVAILABLE = 37,
  81. USER_PROPERTY = 38,
  82. MAXIMUM_PACKET_SIZE = 39,
  83. WILDCARD_SUBSCRIPTION_AVAILABLE = 40,
  84. SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,
  85. SHARED_SUBSCRIPTION_AVAILABLE = 42
  86. };
  87. /** The names of the different types of properties */
  88. PAHO_MQTTPP_EXPORT static const std::map<code, std::string_view> TYPE_NAME;
  89. /**
  90. * Create a numeric property.
  91. * This can be a byte, or 2-byte, 4-byte, or variable byte integer.
  92. * @param c The property code
  93. * @param val The integer value for the property
  94. */
  95. property(code c, int32_t val);
  96. /**
  97. * Create a numeric property.
  98. * This can be a byte, or 2-byte, 4-byte, or variable byte integer.
  99. * @param c The property code
  100. * @param val The integer value for the property
  101. */
  102. property(code c, uint32_t val) : property(c, int32_t(val)) {}
  103. /**
  104. * Create a string or binary property.
  105. * @param c The property code
  106. * @param val The value for the property
  107. */
  108. property(code c, string_ref val);
  109. /**
  110. * Create a string pair property.
  111. * @param c The property code
  112. * @param name The string name for the property
  113. * @param val The string value for the property
  114. */
  115. property(code c, string_ref name, string_ref val);
  116. /**
  117. * Creates a property from a C struct.
  118. * @param cprop A C struct for a property list.
  119. */
  120. explicit property(const MQTTProperty& cprop) { copy(cprop); }
  121. /**
  122. * Moves a C struct into this property list.
  123. * This takes ownership of any memory that the C struct is holding.
  124. * @param cprop A C struct for a property list.
  125. */
  126. explicit property(MQTTProperty&& cprop) : prop_(cprop) {
  127. memset(&cprop, 0, sizeof(MQTTProperty));
  128. }
  129. /**
  130. * Copy constructor
  131. * @param other The other property to copy into this one.
  132. */
  133. property(const property& other) { copy(other.prop_); }
  134. /**
  135. * Move constructor.
  136. * @param other The other property that is moved into this one.
  137. */
  138. property(property&& other);
  139. /**
  140. * Destructor
  141. */
  142. ~property();
  143. /**
  144. * Copy assignment.
  145. * @param rhs Another property list to copy into this one.
  146. * @return A reference to this object.
  147. */
  148. property& operator=(const property& rhs);
  149. /**
  150. * Move assignment.
  151. * @param rhs Another property list to move into this one.
  152. * @return A reference to this object.
  153. */
  154. property& operator=(property&& rhs);
  155. /**
  156. * Gets the underlying C property struct.
  157. * @return A const reference to the underlying C property
  158. * struct.
  159. */
  160. const MQTTProperty& c_struct() const { return prop_; }
  161. /**
  162. * Gets the property type (identifier).
  163. * @return The code for the property type.
  164. */
  165. code type() const { return code(prop_.identifier); }
  166. /**
  167. * Gets a printable name for the property type.
  168. * @return A printable name for the property type.
  169. */
  170. std::string_view type_name() const;
  171. /**
  172. * Gets the typeid for the value contained in the property.
  173. * @return The typeid for the value contained in the property.
  174. */
  175. const std::type_info& value_type_id();
  176. };
  177. std::ostream& operator<<(std::ostream& os, const property& prop);
  178. /**
  179. * Extracts the value from the property as the specified type.
  180. * @return The value from the property as the specified type.
  181. */
  182. template <typename T>
  183. inline T get(const property&) {
  184. throw bad_cast();
  185. }
  186. /**
  187. * Extracts the value from the property as an unsigned 8-bit integer.
  188. * @return The value from the property as an unsigned 8-bit integer.
  189. */
  190. template <>
  191. inline uint8_t get<uint8_t>(const property& prop) {
  192. return uint8_t(prop.c_struct().value.byte);
  193. }
  194. /**
  195. * Extracts the value from the property as an unsigned 16-bit integer.
  196. * @return The value from the property as an unsigned 16-bit integer.
  197. */
  198. template <>
  199. inline uint16_t get<uint16_t>(const property& prop) {
  200. return uint16_t(prop.c_struct().value.integer2);
  201. }
  202. /**
  203. * Extracts the value from the property as a signed 16-bit integer.
  204. * @return The value from the property as a signed 16-bit integer.
  205. * @deprecated All integer properties are unsigned. Use
  206. * `get<uint16_t>()`
  207. */
  208. template <>
  209. [[deprecated("Integer properties are unsigned. Use get<uint16_t>()")]] inline int16_t
  210. get<int16_t>(const property& prop) {
  211. return int16_t(prop.c_struct().value.integer2);
  212. }
  213. /**
  214. * Extracts the value from the property as an unsigned 32-bit integer.
  215. * @return The value from the property as an unsigned 32-bit integer.
  216. */
  217. template <>
  218. inline uint32_t get<uint32_t>(const property& prop) {
  219. return uint32_t(prop.c_struct().value.integer4);
  220. }
  221. /**
  222. * Extracts the value from the property as a signed 32-bit integer.
  223. * @return The value from the property as a signed 32-bit integer.
  224. * @deprecated All integer properties are unsigned. Use
  225. * `get<uint32_t>()`
  226. */
  227. template <>
  228. [[deprecated("Integer properties are unsigned. Use get<uint32_t>()")]] inline int32_t
  229. get<int32_t>(const property& prop) {
  230. return int32_t(prop.c_struct().value.integer4);
  231. }
  232. /**
  233. * Extracts the value from the property as a string.
  234. * @return The value from the property as a string.
  235. */
  236. template <>
  237. inline string get<string>(const property& prop) {
  238. return (!prop.c_struct().value.data.data)
  239. ? string()
  240. : string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
  241. }
  242. /**
  243. * Extracts the value from the property as a pair of strings.
  244. * @return The value from the property as a pair of strings.
  245. */
  246. template <>
  247. inline string_pair get<string_pair>(const property& prop) {
  248. string name =
  249. (!prop.c_struct().value.data.data)
  250. ? string()
  251. : string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
  252. string value =
  253. (!prop.c_struct().value.value.data)
  254. ? string()
  255. : string(prop.c_struct().value.value.data, prop.c_struct().value.value.len);
  256. return std::make_tuple(std::move(name), std::move(value));
  257. }
  258. /////////////////////////////////////////////////////////////////////////////
  259. /**
  260. * MQTT v5 property list.
  261. *
  262. * A collection of properties that can be added to outgoing packets or
  263. * retrieved from incoming packets.
  264. */
  265. class properties
  266. {
  267. /** The default C struct */
  268. static constexpr MQTTProperties DFLT_C_STRUCT MQTTProperties_initializer;
  269. /** The underlying C properties struct */
  270. MQTTProperties props_{DFLT_C_STRUCT};
  271. template <typename T>
  272. friend T get(const properties& props, property::code propid, size_t idx);
  273. template <typename T>
  274. friend T get(const properties& props, property::code propid);
  275. public:
  276. /** A const iterator for the properties list */
  277. class const_iterator
  278. {
  279. const MQTTProperty* curr_;
  280. mutable property prop_;
  281. friend properties;
  282. const_iterator(const MQTTProperty* curr) : curr_{curr} {}
  283. public:
  284. /**
  285. * Gets a reference to the current value.
  286. * @return A reference to the current value.
  287. */
  288. const property& operator*() const {
  289. prop_ = property{*curr_};
  290. return prop_;
  291. }
  292. /**
  293. * Postfix increment operator.
  294. * @return An iterator pointing to the previous matching item.
  295. */
  296. const_iterator operator++(int) noexcept {
  297. auto tmp = *this;
  298. curr_++;
  299. return tmp;
  300. }
  301. /**
  302. * Prefix increment operator.
  303. * @return An iterator pointing to the next matching item.
  304. */
  305. const_iterator& operator++() noexcept {
  306. ++curr_;
  307. return *this;
  308. }
  309. /**
  310. * Compares two iterators to see if they don't refer to the same
  311. * node.
  312. *
  313. * @param other The other iterator to compare against this one.
  314. * @return @em true if they don't match, @em false if they do
  315. */
  316. bool operator!=(const const_iterator& other) const noexcept {
  317. return curr_ != other.curr_;
  318. }
  319. };
  320. /**
  321. * Default constructor.
  322. * Creates an empty properties list.
  323. */
  324. properties() {}
  325. /**
  326. * Copy constructor.
  327. * @param other The property list to copy.
  328. */
  329. properties(const properties& other) : props_(::MQTTProperties_copy(&other.props_)) {}
  330. /**
  331. * Move constructor.
  332. * @param other The property list to move to this one.
  333. */
  334. properties(properties&& other) : props_(other.props_) {
  335. std::memset(&other.props_, 0, sizeof(MQTTProperties));
  336. }
  337. /**
  338. * Creates a list of properties from a C struct.
  339. * @param cprops The c struct of properties
  340. */
  341. properties(const MQTTProperties& cprops) { props_ = ::MQTTProperties_copy(&cprops); }
  342. /**
  343. * Constructs from a list of property objects.
  344. * @param props An initializer list of property objects.
  345. */
  346. properties(std::initializer_list<property> props);
  347. /**
  348. * Destructor.
  349. */
  350. ~properties() { ::MQTTProperties_free(&props_); }
  351. /**
  352. * Gets a reference to the underlying C properties structure.
  353. * @return A const reference to the underlying C properties structure.
  354. */
  355. const MQTTProperties& c_struct() const { return props_; }
  356. /**
  357. * Copy assignment.
  358. * @param rhs The other property list to copy into this one
  359. * @return A reference to this object.
  360. */
  361. properties& operator=(const properties& rhs);
  362. /**
  363. * Move assignment.
  364. * @param rhs The property list to move to this one.
  365. * @return A reference to this object.
  366. */
  367. properties& operator=(properties&& rhs);
  368. /**
  369. * Determines if the property list is empty.
  370. * @return @em true if there are no properties in the list, @em false if
  371. * the list contains any items.
  372. */
  373. bool empty() const { return props_.count == 0; }
  374. /**
  375. * Gets the property at the specified index in the collection.
  376. * @param i The index
  377. * @return The property at the specified index.
  378. */
  379. const property operator[](size_t i) const { return property{props_.array[i]}; }
  380. /**
  381. * Gets the property at the specified index in the collection.
  382. * @param i The index
  383. * @return The property at the specified index.
  384. */
  385. const property at(size_t i) const {
  386. if (i < size_t(props_.count))
  387. return property{props_.array[i]};
  388. throw std::out_of_range{"property index"};
  389. }
  390. /**
  391. * Gets the numbers of property items in the list.
  392. * @return The number of property items in the list.
  393. */
  394. size_t size() const { return size_t(props_.count); }
  395. /**
  396. * Gets a const iterator to the full collection of properties.
  397. * @return A const iterator to the full collection of properties.
  398. */
  399. const_iterator begin() const { return const_iterator{props_.array}; }
  400. /**
  401. * Gets a const iterator to the full collection of properties.
  402. * @return A const iterator to the full collection of properties.
  403. */
  404. const_iterator cbegin() const { return begin(); }
  405. /**
  406. * Gets a const iterator to the end of the collection of properties.
  407. * @return A const iterator to the end of collection of properties.
  408. */
  409. const_iterator end() const { return const_iterator{props_.array + size()}; }
  410. /**
  411. * Gets a const iterator to the end of the collection of properties.
  412. * @return A const iterator to the end of collection of properties.
  413. */
  414. const_iterator cend() const { return end(); }
  415. /**
  416. * Adds a property to the list.
  417. * @param prop The property to add to the list.
  418. */
  419. void add(const property& prop) { ::MQTTProperties_add(&props_, &prop.c_struct()); }
  420. /**
  421. * Removes all the items from the property list.
  422. */
  423. void clear() { ::MQTTProperties_free(&props_); }
  424. /**
  425. * Determines if the list contains a specific property.
  426. * @param propid The property ID (code).
  427. * @return @em true if the list contains the property, @em false if not.
  428. */
  429. bool contains(property::code propid) const {
  430. return ::MQTTProperties_hasProperty(
  431. const_cast<MQTTProperties*>(&props_), MQTTPropertyCodes(propid)
  432. ) != 0;
  433. }
  434. /**
  435. * Get the number of properties in the list with the specified property
  436. * ID.
  437. *
  438. * Most properties can exist only once. User properties and subscription
  439. * ID's can exist more than once.
  440. *
  441. * @param propid The property ID (code).
  442. * @return The number of properties in the list with the specified ID.
  443. */
  444. size_t count(property::code propid) const {
  445. return size_t(
  446. ::MQTTProperties_propertyCount(
  447. const_cast<MQTTProperties*>(&props_), MQTTPropertyCodes(propid)
  448. )
  449. );
  450. }
  451. /**
  452. * Gets the property with the specified ID.
  453. *
  454. * @param propid The property ID (code).
  455. * @param idx Which instance of the property to retrieve, if there are
  456. * more than one.
  457. * @return The requested property
  458. */
  459. property get(property::code propid, size_t idx = 0) const;
  460. };
  461. // --------------------------------------------------------------------------
  462. /**
  463. * Retrieves a single value from a property list for when there may be
  464. * multiple identical property ID's.
  465. * @tparam T The type of the value to retrieve
  466. * @param props The property list
  467. * @param propid The property ID code for the desired value.
  468. * @param idx Index of the desired property ID
  469. * @return The requested value of type T
  470. */
  471. template <typename T>
  472. inline T get(const properties& props, property::code propid, size_t idx) {
  473. MQTTProperty* prop = MQTTProperties_getPropertyAt(
  474. const_cast<MQTTProperties*>(&props.c_struct()), MQTTPropertyCodes(propid), int(idx)
  475. );
  476. if (!prop)
  477. throw bad_cast();
  478. return get<T>(property(*prop));
  479. }
  480. /**
  481. * Retrieves a single value from a property list.
  482. * @tparam T The type of the value to retrieve
  483. * @param props The property list
  484. * @param propid The property ID code for the desired value.
  485. * @return The requested value of type T
  486. */
  487. template <typename T>
  488. inline T get(const properties& props, property::code propid) {
  489. return get<T>(props, propid, 0);
  490. }
  491. /////////////////////////////////////////////////////////////////////////////
  492. } // namespace mqtt
  493. #endif // __mqtt_properties_h