buffer_ref.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /////////////////////////////////////////////////////////////////////////////
  2. /// @file buffer_ref.h
  3. /// Buffer reference type for the Paho MQTT C++ library.
  4. /// @date April 18, 2017
  5. /// @author Frank Pagliughi
  6. /////////////////////////////////////////////////////////////////////////////
  7. /*******************************************************************************
  8. * Copyright (c) 2017-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_buffer_ref_h
  23. #define __mqtt_buffer_ref_h
  24. #include <cstring>
  25. #include <iostream>
  26. #include "mqtt/types.h"
  27. namespace mqtt {
  28. /////////////////////////////////////////////////////////////////////////////
  29. /**
  30. * A reference object for holding immutable data buffers, with cheap copy
  31. * semantics and lifetime management.
  32. *
  33. * Each object of this class contains a reference-counted pointer to an
  34. * immutable data buffer. Objects can be copied freely and easily, even
  35. * across threads, since all instances promise not to modify the contents
  36. * of the buffer.
  37. *
  38. * The buffer is immutable but the reference itself acts like a normal
  39. * variable. It can be reassigned to point to a different buffer.
  40. *
  41. * If no value has been assigned to a reference, then it is in a default
  42. * "null" state. It is not safe to call any member functions on a null
  43. * reference, other than to check if the object is null or empty.
  44. * @verbatim
  45. * string_ref sr;
  46. * if (!sr)
  47. * cout << "null reference" << endl;
  48. * else
  49. * cout.write(sr.data(), sr.size());
  50. * @endverbatim
  51. */
  52. template <typename T>
  53. class buffer_ref
  54. {
  55. public:
  56. /**
  57. * The underlying type for the buffer.
  58. * Normally byte-wide data (char or uint8_t) for Paho.
  59. */
  60. using value_type = T;
  61. /**
  62. * The type for the buffer.
  63. * We use basic_string for compatibility with string data.
  64. */
  65. using blob = std::basic_string<value_type>;
  66. /**
  67. * The pointer we use.
  68. * Note that it is a pointer to a _const_ blob.
  69. */
  70. using pointer_type = std::shared_ptr<const blob>;
  71. private:
  72. /** Our data is a shared pointer to a const buffer */
  73. pointer_type data_;
  74. public:
  75. /**
  76. * Default constructor creates a null reference.
  77. */
  78. buffer_ref() = default;
  79. /**
  80. * Copy constructor only copies a shared pointer.
  81. * @param buf Another buffer reference.
  82. */
  83. buffer_ref(const buffer_ref& buf) = default;
  84. /**
  85. * Move constructor only moves a shared pointer.
  86. * @param buf Another buffer reference.
  87. */
  88. buffer_ref(buffer_ref&& buf) = default;
  89. /**
  90. * Creates a reference to a new buffer by copying data.
  91. * @param b A string from which to create a new buffer.
  92. */
  93. buffer_ref(const blob& b) : data_{std::make_shared<blob>(b)} {}
  94. /**
  95. * Creates a reference to a new buffer by moving a string into the
  96. * buffer.
  97. * @param b A string from which to create a new buffer.
  98. */
  99. buffer_ref(blob&& b) : data_{std::make_shared<blob>(std::move(b))} {}
  100. /**
  101. * Creates a reference to an existing buffer by copying the shared
  102. * pointer.
  103. * Note that it is up to the caller to insure that there are no mutable
  104. * references to the buffer.
  105. * @param p A shared pointer to a string.
  106. */
  107. buffer_ref(const pointer_type& p) : data_(p) {}
  108. /**
  109. * Creates a reference to an existing buffer by moving the shared
  110. * pointer.
  111. * Note that it is up to the caller to insure that there are no mutable
  112. * references to the buffer.
  113. * @param p A shared pointer to a string.
  114. */
  115. buffer_ref(pointer_type&& p) : data_(std::move(p)) {}
  116. /**
  117. * Creates a reference to a new buffer containing a copy of the data.
  118. * @param buf The memory to copy
  119. * @param n The number of bytes to copy.
  120. */
  121. buffer_ref(const value_type* buf, size_t n) : data_{std::make_shared<blob>(buf, n)} {}
  122. /**
  123. * Creates a reference to a new buffer containing a copy of the
  124. * NUL-terminated char array.
  125. * @param buf A NUL-terminated char array (C string).
  126. */
  127. buffer_ref(const char* buf)
  128. : buffer_ref(reinterpret_cast<const value_type*>(buf), std::strlen(buf)) {
  129. static_assert(
  130. sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers"
  131. );
  132. }
  133. /**
  134. * Copy the reference to the buffer.
  135. * @param rhs Another buffer
  136. * @return A reference to this object
  137. */
  138. buffer_ref& operator=(const buffer_ref& rhs) = default;
  139. /**
  140. * Move a reference to a buffer.
  141. * @param rhs The other reference to move.
  142. * @return A reference to this object.
  143. */
  144. buffer_ref& operator=(buffer_ref&& rhs) = default;
  145. /**
  146. * Copy a string into this object, creating a new buffer.
  147. * Modifies the reference for this object, pointing it to a
  148. * newly-created buffer. Other references to the old object remain
  149. * unchanges, so this follows copy-on-write semantics.
  150. * @param b A new blob/string to copy.
  151. * @return A reference to this object.
  152. */
  153. buffer_ref& operator=(const blob& b) {
  154. data_.reset(new blob(b));
  155. return *this;
  156. }
  157. /**
  158. * Move a string into this object, creating a new buffer.
  159. * Modifies the reference for this object, pointing it to a
  160. * newly-created buffer. Other references to the old object remain
  161. * unchanges, so this follows copy-on-write semantics.
  162. * @param b A new blob/string to move.
  163. * @return A reference to this object.
  164. */
  165. buffer_ref& operator=(blob&& b) {
  166. data_.reset(new blob(std::move(b)));
  167. return *this;
  168. }
  169. /**
  170. * Copy a NUL-terminated C char array into a new buffer
  171. * @param cstr A NUL-terminated C string.
  172. * @return A reference to this object
  173. */
  174. buffer_ref& operator=(const char* cstr) {
  175. static_assert(
  176. sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers"
  177. );
  178. data_.reset(new blob(reinterpret_cast<const value_type*>(cstr), strlen(cstr)));
  179. return *this;
  180. }
  181. /**
  182. * Copy another type of buffer reference to this one.
  183. * This can copy a buffer of different types, provided that the size of
  184. * the data elements are the same. This is typically used to convert
  185. * from char to byte, where the data is the same, but the interpretation
  186. * is different. Note that this copies the underlying buffer.
  187. * @param rhs A reference to a different type of buffer.
  188. * @return A reference to this object.
  189. */
  190. template <typename OT>
  191. buffer_ref& operator=(const buffer_ref<OT>& rhs) {
  192. static_assert(
  193. sizeof(OT) == sizeof(T), "Can only assign buffers if values the same size"
  194. );
  195. data_.reset(new blob(reinterpret_cast<const value_type*>(rhs.data()), rhs.size()));
  196. return *this;
  197. }
  198. /**
  199. * Clears the reference to nil.
  200. */
  201. void reset() { data_.reset(); }
  202. /**
  203. * Determines if the reference is valid.
  204. * If the reference is invalid then it is not safe to call @em any
  205. * member functions other than @ref is_null() and @ref empty()
  206. * @return @em true if referring to a valid buffer, @em false if the
  207. * reference (pointer) is null.
  208. */
  209. explicit operator bool() const { return bool(data_); }
  210. /**
  211. * Determines if the reference is invalid.
  212. * If the reference is invalid then it is not safe to call @em any
  213. * member functions other than @ref is_null() and @ref empty()
  214. * @return @em true if the reference is null, @em false if it is
  215. * referring to a valid buffer,
  216. */
  217. bool is_null() const { return !data_; }
  218. /**
  219. * Determines if the buffer is empty.
  220. * @return @em true if the buffer is empty or the reference is null,
  221. * @em false if the buffer contains data.
  222. */
  223. bool empty() const { return !data_ || data_->empty(); }
  224. /**
  225. * Gets a const pointer to the data buffer.
  226. * @return A pointer to the data buffer.
  227. */
  228. const value_type* data() const { return data_->data(); }
  229. /**
  230. * Gets the size of the data buffer.
  231. * @return The size of the data buffer.
  232. */
  233. size_t size() const { return data_->size(); }
  234. /**
  235. * Gets the size of the data buffer.
  236. * @return The size of the data buffer.
  237. */
  238. size_t length() const { return data_->length(); }
  239. /**
  240. * Gets the data buffer as a string.
  241. * @return The data buffer as a string.
  242. */
  243. const blob& str() const { return *data_; }
  244. /**
  245. * Gets the data buffer as a string.
  246. * @return The data buffer as a string.
  247. */
  248. const blob& to_string() const { return str(); }
  249. /**
  250. * Gets the data buffer as NUL-terminated C string.
  251. * Note that the reference must be set to call this function.
  252. * @return The data buffer as a string.
  253. */
  254. const char* c_str() const { return data_->c_str(); }
  255. /**
  256. * Gets a shared pointer to the (const) data buffer.
  257. * @return A shared pointer to the (const) data buffer.
  258. */
  259. const pointer_type& ptr() const { return data_; }
  260. /**
  261. * Gets elemental access to the data buffer (read only)
  262. * @param i The index into the buffer.
  263. * @return The value at the specified index.
  264. */
  265. const value_type& operator[](size_t i) const { return (*data_)[i]; }
  266. };
  267. /**
  268. * Stream inserter for a buffer reference.
  269. * This does a binary write of the data in the buffer.
  270. * @param os The output stream.
  271. * @param buf The buffer reference to write.
  272. * @return A reference to the output stream.
  273. */
  274. template <typename T>
  275. std::ostream& operator<<(std::ostream& os, const buffer_ref<T>& buf) {
  276. if (!buf.empty())
  277. os.write(buf.data(), buf.size());
  278. return os;
  279. }
  280. /////////////////////////////////////////////////////////////////////////////
  281. /**
  282. * A reference to a text buffer.
  283. */
  284. using string_ref = buffer_ref<char>;
  285. /**
  286. * A reference to a binary buffer.
  287. * Note that we're using char for the underlying data type to allow
  288. * efficient moves to and from std::string's. Using a separate type
  289. * indicates that the data may be arbitrary binary.
  290. */
  291. using binary_ref = buffer_ref<char>;
  292. /////////////////////////////////////////////////////////////////////////////
  293. } // namespace mqtt
  294. #endif // __mqtt_buffer_ref_h