Alchemy  1.0
A framework to robustly process network messages and structured data
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
msg_buffer.h
Go to the documentation of this file.
1 /// @file Hg/msg_buffer.h
2 ///
3 /// A packet buffer structure suitable for processing network
4 /// and interprocess communication messages.
5 ///
6 /// The MIT License(MIT)
7 /// @copyright 2014 Paul M Watt
8 // ****************************************************************************
9 #ifndef MSG_BUFFER_H_INCLUDED
10 #define MSG_BUFFER_H_INCLUDED
11 // Includes *******************************************************************
12 #include <Pb/meta_fwd.h>
13 #include <Pb/byte_order.h>
14 #include <Pb/meta_foreach.h>
15 #include <Hg/storage_policy.h>
16 
17 #include <cstddef>
18 #include <algorithm>
19 
20 
21 namespace Hg
22 {
23 
24 // ****************************************************************************
25 /// MsgBuffer Template Definition.
26 /// This abstracts the behavior of the buffer with respect to the Byte-Order of the message.
27 ///
28 /// @tparam StorageT [typename] The storage_t that manages the
29 /// type of object used in the underlying buffer for
30 /// the packet data. This policy also provides
31 /// the capabilities to read/write to the buffer.
32 ///
33 template< typename StorageT>
34 class MsgBuffer
35 {
36 public:
37  // Typedefs *****************************************************************
38  typedef StorageT storage_type;
39  typedef typename storage_type::data_type data_type;
40  typedef typename storage_type::s_pointer s_pointer;
41  typedef typename storage_type::w_pointer w_pointer;
42 
43  typedef data_type* pointer;
44  typedef const data_type* const_pointer;
45 
46  // Construction *************************************************************
47  // **************************************************************************
48  /// Default Constructor
49  ///
51  : m_offset(0)
52  {
53  // No current operations
54  }
55 
56  // **************************************************************************
57  /// Fill Constructor
58  ///
59  /// @param n The number of bytes to allocate for the buffer.
60  /// @param val The value to fill each item in the allocated buffer if supplied.
61  ///
62  explicit
63  MsgBuffer(size_t n)
64  : m_data(n)
65  , m_offset(0)
66  {
67  // No current operations
68  }
69 
70  MsgBuffer(size_t n, byte_t val)
71  : m_offset(0)
72  {
73  m_data.resize(n, val);
74  }
75 
76 
77  // **************************************************************************
78  /// Copy Constructor
79  ///
80  /// @param rhs The input object to be copied.
81  /// @note This object's copy constructor only performs a shallow
82  /// copy of the message buffer. Use the clone operation
83  /// to make a separate copy of the buffer.
84  ///
85  MsgBuffer(const MsgBuffer& rhs)
86  : m_offset(rhs.offset())
87  {
88  assign(rhs.data(), rhs.size());
89  }
90 
91  // **************************************************************************
92  /// Destructor
93  ///
95  {
96  m_data.clear();
97  }
98 
99  // Operators ****************************************************************
100  // **************************************************************************
101  /// Assignment Operator
102  ///
103  /// @param rhs (right-hand side) The input object to be copied.
104  /// @note This object's copy constructor only performs a shallow
105  /// copy of the message buffer. Use the clone operation
106  /// to make a separate copy of the buffer.
107  ///
109  {
110  assign(rhs.data(), rhs.size());
111  return *this;
112  }
113 
114  // Status *******************************************************************
115  // **************************************************************************
116  /// Indicates if memory is currently managed by this buffer.
117  ///
118  /// @return true - The buffer is empty.
119  /// false- There is memory allocated and managed by this buffer.
120  ///
121  bool empty() const
122  {
123  return m_data.empty();
124  }
125 
126  // **************************************************************************
127  /// Return the number of bytes reserved for use by this buffer.
128  ///
129  /// @return The allocated buffer size is returned. This packet buffer
130  /// must be valid for a non-zero value to be returned.
131  ///
132  size_t capacity() const
133  {
134  return m_data.capacity();
135  }
136 
137  // **************************************************************************
138  /// Reports the current size of the buffer in use.
139  ///
140  /// @return The number of bytes in use in the allocated buffer.
141  /// If there is no internal buffer, 0 is returned.
142  ///
143  size_t size() const
144  {
145  return m_data.size();
146  }
147 
148  // Methods ******************************************************************
149  // **************************************************************************
150  /// Releases any resources associated with this object.
151  ///
152  void clear()
153  {
154  m_data.clear();
155  m_offset = 0;
156  }
157 
158  // TODO: When offsets are considered, how should this affect items like resize.
159  // **************************************************************************
160  /// Resizes the buffer to contain n elements.
161  ///
162  /// @param n The number of elements to allocate for the buffer.
163  /// @param val Optional value to be copied into each element allocated.
164  ///
165  void resize(size_t n)
166  {
167  m_data.resize(n);
168  }
169 
170  // **************************************************************************
171  void resize(size_t n, byte_t val)
172  {
173  m_data.resize(n, val);
174  }
175 
176  // **************************************************************************
177  /// Provides access to the packed memory buffer.
178  ///
179  /// @return Returns a constant pointer to the buffer that contains the
180  /// packed memory.
181  /// 0 is returned if there is no memory associated with the buffer.
182  ///
183  const_pointer data() const
184  {
185  if (m_data.empty())
186  {
187  return 0;
188  }
189 
190  return const_cast<const_pointer>(&m_data[0]);
191  }
192 
193  // **************************************************************************
194  /// Zeroes the contents of the buffer if they have been assigned.
195  ///
196  void zero()
197  {
198  std::fill(m_data.begin(), m_data.end(), 0);
199  }
200 
201  // **************************************************************************
202  /// Assigns the contents of an incoming raw memory buffer to the message buffer.
203  ///
204  /// @param pBuffer A memory buffer whose contents will be assigned to
205  /// this message object. The values of the buffer are
206  /// copied into the message.
207  /// @param n The number of bytes held in p_buffer.
208  ///
209  void assign(const_pointer pBuffer, size_t n)
210  {
211  // Verify inputs.
212  if ( !pBuffer
213  || 0 == n)
214  {
215  m_data.clear();
216  return;
217  }
218 
219  // Assign the input values to the internal buffer.
220  const_pointer pFirst = pBuffer;
221  const_pointer pLast = &pBuffer[n];
222 
223  m_data.assign(pFirst, pLast);
224  }
225 
226  // **************************************************************************
227  /// Returns the offset used to access the buffer.
228  ///
229  /// Returns the offset used to access the buffer relative to the first index
230  /// in the buffer. The default offset is zero. Alternate offsets are allowed
231  /// to facilitate nested packet structures as well as dynamically formatted
232  /// structures.
233  ///
234  /// @return A number of bytes greater than 0 is returned.
235  ///
236  std::ptrdiff_t offset() const
237  {
238  return m_offset;
239  }
240 
241  // **************************************************************************
242  /// Assigns a new base offset for memory access to this object.
243  ///
244  /// @param new_offset The offset from the beginning of the supplied buffer.
245  ///
246  void offset(std::ptrdiff_t new_offset)
247  {
248  m_offset = new_offset;
249  }
250 
251  // **************************************************************************
252  /// Parameterized function that returns data from a specified offset in the buffer.
253  ///
254  /// @tparam T [typename] The parameterized type to be returned
255  /// to the caller.
256  /// @param value Accepts the value read from the packet buffer.
257  /// @param pos The offset from the beginning of the buffer to
258  /// read the output value.
259  ///
260  /// @return The number of bytes read from the buffer is returned.
261  /// It is possible for a successful case to return 0.
262  ///
263  template <typename T>
264  size_t get_data(T& value, std::ptrdiff_t pos) const
265  {
266  if (empty())
267  {
268  return 0;
269  }
270 
271  // Read from the user supplied offset as well as the base offset
272  // configured for this Packet Buffer.
273  std::ptrdiff_t total_offset = offset() + pos;
274 
275  // Verify the requested data can be read from the buffer.
276  size_t bytes_read = 0;
277  if ( total_offset >= 0
278  && total_offset + sizeof(value) <= size())
279  {
280  bytes_read =
281  storage_type::read( data(),
282  &value,
283  sizeof(T),
284  total_offset)
285  ? sizeof(T)
286  : 0;
287  }
288 
289  return bytes_read;
290  }
291 
292  // **************************************************************************
293  /// Parameterized function that reads a range of data from the buffer.
294  ///
295  /// @tparam T [typename] The parameterized type to be read
296  /// by the caller.
297  /// @param pBuffer Pointer to the first element in the array to
298  /// be written into.
299  ///
300  /// @param length The number of bytes to read in.
301  /// pBuffer must contain at least length number of
302  /// bytes.
303  ///
304  /// @param pos The offset from the beginning of the buffer to
305  /// read the input value.
306  ///
307  /// @return The number of bytes read from the buffer is returned.
308  /// It is possible for a successful case to return 0.
309  ///
310  size_t get_range(void* pBuffer, size_t length, size_t pos) const
311  {
312  if ( empty()
313  || 0 == pBuffer
314  || 0 == length)
315  {
316  return 0;
317  }
318 
319  // Read from the user supplied offset as well as the base offset
320  // configured for this Packet Buffer.
321  size_t total_offset = static_cast<size_t>(offset()) + pos;
322 
323  // Verify the data can be safely written within the bounds of the buffer.
324  size_t bytes_read = 0;
325  size_t total_size = this->size();
326  if ( (total_offset >= 0)
327  && (total_offset + length) <= total_size)
328  {
329  bytes_read =
330  storage_type::read( data(),
331  pBuffer,
332  length,
333  total_offset)
334  ? length
335  : 0;
336  }
337 
338  return bytes_read;
339  }
340 
341  // **************************************************************************
342  /// Parameterized function that writes data to a specified offset in the buffer.
343  ///
344  /// @tparam T [typename] The parameterized type to be written
345  /// by the caller.
346  /// @param value Contains the value to write to the packet buffer.
347  /// @param pos The offset from the beginning of the buffer to
348  /// write the input value.
349  ///
350  /// @return The number of bytes written to the buffer is returned.
351  /// It is possible for a successful case to return 0.
352  ///
353  template <typename T>
354  size_t set_data(const T& value, size_t pos)
355  {
356  if (empty())
357  {
358  return 0;
359  }
360 
361  // Read from the user supplied offset as well as the base offset
362  // configured for this Packet Buffer.
363  size_t total_offset = static_cast<size_t>(offset()) + pos;
364 
365  // Verify the data can be safely written within the bounds of the buffer.
366  size_t bytes_written = 0;
367  size_t total_size = size();
368  if ( (total_offset >= 0)
369  && (total_offset + Hg::SizeOf<T>::value) <= total_size)
370  {
371  bytes_written =
372  storage_type::write ( raw_data(),
373  &value,
375  total_offset)
377  : 0;
378  }
379 
380  return bytes_written;
381  }
382 
383  // **************************************************************************
384  /// Parameterized function that writes a range of data to the buffer.
385  ///
386  /// @tparam T [typename] The parameterized type to be written
387  /// by the caller.
388  /// @param first Contains the first item to write to the buffer.
389  ///
390  /// @param last Contains the item one past the last item to write.
391  /// This item may point to an element that is not
392  /// valid to de-reference. Similar to C+++ Standard Library.
393  ///
394  /// Last must be greater than than the first time,
395  /// and they must be part of the same range of elements
396  /// otherwise the behavior is undefined.
397  ///
398  /// @param pos The offset from the beginning of the buffer to
399  /// write the input value.
400  ///
401  /// @return The number of bytes written to the buffer is returned.
402  /// It is possible for a successful case to return 0.
403  ///
404  template <typename InputIt>
405  size_t set_range(InputIt first, InputIt last, size_t pos)
406  {
407  if (empty())
408  {
409  return 0;
410  }
411 
412  // Return if first and last are the same.
413  if (first == last)
414  {
415  return 0;
416  }
417 
418  // Read from the user supplied offset as well as the base offset
419  // configured for this Packet Buffer.
420  size_t total_offset = static_cast<size_t>(offset()) + pos;
421  size_t range_size = std::distance(first, last);
422 
423  // Verify the data can be safely written within the bounds of the buffer.
424  size_t bytes_written = 0;
425  size_t total_size = size();
426  if ( (total_offset >= 0)
427  && (total_offset + range_size) <= total_size)
428  {
429  bytes_written =
430  storage_type::write ( raw_data(),
431  first,
432  range_size,
433  total_offset)
434  ? range_size
435  : 0;
436  }
437 
438  return bytes_written;
439  }
440 
441  // **************************************************************************
442  /// Creates a full copy of the message buffer.
443  ///
444  /// @return A deep copy of this objects packet buffer.
445  ///
446  /// @note clone performs a buffer allocation according to the
447  /// storage policy provided to the MsgBuffer. Therefore a
448  /// new memory buffer is created for the cloned copy to exist
449  /// within.
450  ///
451  MsgBuffer clone() const
452  {
453  MsgBuffer retBuffer;
454  if (empty())
455  {
456  return retBuffer;
457  }
458 
459  // Create a new buffer to accept a clone of the data.
460  // Allocate a buffer that matches this buffers capacity.
461  retBuffer.assign(data(), size());
462  retBuffer.m_offset = m_offset;
463 
464  return retBuffer;
465  }
466 
467 private:
468  // Typedefs *****************************************************************
469  typedef std::vector<data_type> DataVector;
470 
471 
472  // Private Member Data *******************************************************
473  DataVector m_data; ///< The storage medium managed by
474  /// this message buffer.
475  std::ptrdiff_t m_offset; ///< The number of bytes from the
476  /// beginning of the buffer that
477  /// all data reads should occur from.
478 
479  // **************************************************************************
480  // Provides access to the packed memory buffer.
481  //
482  // @return Returns a constant pointer to the buffer that contains the
483  // packed memory.
484  // 0 is returned if there is no memory associated with the buffer.
485  //
486  pointer raw_data()
487  {
488  return &m_data[0];
489  }
490 
491 
492 
493  // Let all message buffer objects be friends for data transfer.
494  template< typename T>
495  friend
496  class MsgBuffer;
497 };
498 
499 
500 // ****************************************************************************
501 template <typename pkt_buffer_t>
502 pkt_buffer_t& make_nil_buffer()
503 {
504  static pkt_buffer_t nilBuffer;
505  return nilBuffer;
506 }
507 
508 } // namespace Hg
509 
510 
511 #endif