MIP_SDK  v3.0.0-502-gc890c1c
MicroStrain Communications Library for embedded systems
serializer.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "readwrite.hpp"
4 
5 #include "../span.hpp"
6 
7 #include <array>
8 
9 #include <stdint.h>
10 #include <stddef.h>
11 
12 #ifdef MICROSTRAIN_HAS_OPTIONAL
13 #include <optional>
14 #endif
15 
16 #if __cpp_lib_apply >= 201603L
17 #include <functional>
18 #endif
19 
20 namespace microstrain
21 {
22 
32 
61 {
62 public:
63  SerializerBase() = default;
64  SerializerBase(uint8_t* ptr, size_t capacity, size_t offset=0) : m_ptr(ptr), m_size(capacity), m_offset(offset) {}
65  SerializerBase(const uint8_t* ptr, size_t size, size_t offset=0) : m_ptr(const_cast<uint8_t*>(ptr)), m_size(size), m_offset(offset) {}
66  SerializerBase(microstrain::Span<const uint8_t> buffer, size_t offset=0) : m_ptr(const_cast<uint8_t*>(buffer.data())), m_size(buffer.size()), m_offset(offset) {}
67 
68  size_t capacity() const { return m_size; }
69  size_t offset() const { return m_offset; }
70  size_t usedLength() const { return offset(); }
71  int remaining() const { return int(m_size - m_offset); }
72 
73  bool isOverrun() const { return m_offset > m_size; }
74  bool isOk() const { return !isOverrun(); }
75  bool isFinished() const { return m_offset == m_size; }
76  bool hasRemaining(size_t count=1) const { return m_offset+count <= m_size; }
77 
78  uint8_t* basePointer() { return m_ptr; }
79  const uint8_t* basePointer() const { return m_ptr; }
80 
85  uint8_t* getPointer(size_t required_size) { return hasRemaining(required_size) ? (m_ptr+m_offset) : nullptr; }
86  const uint8_t* getPointer(size_t required_size) const { return hasRemaining(required_size) ? (m_ptr+m_offset) : nullptr; }
87 
98  uint8_t* getPtrAndAdvance(size_t size) { uint8_t* ptr = hasRemaining(size) ? (m_ptr+m_offset) : nullptr; m_offset += size; return ptr; }
99 
102  void invalidate() { m_offset = m_size+1; }
103 
109  size_t setOffset(size_t offset) { std::swap(m_offset, offset); return offset; }
110 
111 private:
112  uint8_t* m_ptr = nullptr;
113  size_t m_size = 0;
114  size_t m_offset = 0;
115 };
116 
117 
134 template<serialization::Endian E>
136 {
137 public:
139 
140  static const serialization::Endian ENDIAN = E;
141 
142  template<typename... Ts> bool insert (const Ts&... values);
143  template<typename... Ts> bool extract(Ts&... values);
144 
145  template<class T, class S> bool extract_count(T& count, S max_count);
146  template<class T, class S> bool extract_count(T* count, S max_count) { return extract_count(*count, max_count); }
147 };
148 
151 
152 
153 //
154 //
155 // Non-member functions which the user may overload
156 //
157 //
158 
160 //
161 // LEVEL 1 SERIALIZATION
162 //
164 
165 //
166 // Built-in types (bool, int, float, ...)
167 //
168 
180 template<serialization::Endian E, class T>
181 typename std::enable_if<std::is_arithmetic<T>::value, size_t>::type
182 /*size_t*/ insert(Serializer<E>& buffer, T value)
183 {
184  if(auto ptr = buffer.getPtrAndAdvance(sizeof(T)))
185  serialization::write<E>(ptr, value);
186 
187  return sizeof(T);
188 }
189 
201 template<serialization::Endian E, class T>
202 typename std::enable_if<std::is_arithmetic<T>::value, size_t>::type
203 /*size_t*/ extract(Serializer<E>& buffer, T& value)
204 {
205  if(auto ptr = buffer.getPtrAndAdvance(sizeof(T)))
206  serialization::read<E>(ptr, value);
207 
208  return sizeof(T);
209 }
210 
211 
212 //
213 // Enums
214 //
215 
229 template<serialization::Endian E, class T>
230 typename std::enable_if<std::is_enum<T>::value, size_t>::type
231 /*size_t*/ insert(Serializer<E>& buffer, T value)
232 {
233  using BaseType = typename std::underlying_type<T>::type;
234 
235  if(auto ptr = buffer.getPtrAndAdvance(sizeof(BaseType)))
236  serialization::write<E>(ptr, static_cast<BaseType>(value));
237 
238  return sizeof(BaseType);
239 }
240 
254 template<serialization::Endian E, class T>
255 typename std::enable_if<std::is_enum<T>::value, size_t>::type
256 /*size_t*/ extract(Serializer<E>& buffer, T& value)
257 {
258  using BaseType = typename std::underlying_type<T>::type;
259 
260  if(auto ptr = buffer.getPtrAndAdvance(sizeof(BaseType)))
261  {
262  BaseType base;
263  serialization::read<E>(ptr, base);
264  value = static_cast<T>(base);
265  }
266 
267  return sizeof(BaseType);
268 }
269 
270 
271 //
272 // Classes - if they have member functions "insert" and "extract"
273 //
274 
307 template<serialization::Endian E, class T, decltype(&T::insert) = nullptr>
308 typename std::enable_if<std::is_class<T>::value , size_t>::type
309 /*size_t*/ insert(microstrain::Serializer<E>& serializer, const T& object)
310 {
311  size_t offset = serializer.offset();
312  object.insert(serializer);
313  return serializer.offset() - offset;
314 }
315 
348 template<serialization::Endian E, class T, decltype(&T::extract) = nullptr>
349 typename std::enable_if<std::is_class<T>::value , size_t>::type
350 /*size_t*/ extract(microstrain::Serializer<E>& serializer, T& object)
351 {
352  size_t offset = serializer.offset();
353  object.extract(serializer);
354  return serializer.offset() - offset;
355 }
356 
357 
359 //
360 // LEVEL 2 SERIALIZATION
361 //
363 
364 //
365 // std::tuple - only supported if std::apply can be used
366 //
367 #if __cpp_lib_apply >= 201603L
368 
382 template<serialization::Endian E, class... Ts>
383 size_t insert(Serializer<E>& serializer, const std::tuple<Ts...>& values)
384 {
385  auto lambda = [&serializer](const Ts&... args) {
386  return insert(serializer, args...);
387  };
388 
389  return std::apply(lambda, values);
390 }
391 
405 template<serialization::Endian E, class... Ts>
406 size_t extract(Serializer<E>& serializer, const std::tuple<std::reference_wrapper<Ts>...>& values)
407 {
408  auto lambda = [&serializer](auto&... args) {
409  return extract(serializer, args...);
410  };
411 
412  return std::apply(lambda, values);
413 }
414 
415 #endif
416 
417 
418 
419 //
420 // Arrays of runtime length
421 //
422 
435 template<serialization::Endian E, class T>
436 size_t insert(Serializer<E>& serializer, const T* values, size_t count)
437 {
438  // For arithmetic types the size is fixed so it can be optimized.
439  IF_CONSTEXPR(std::is_arithmetic<T>::value)
440  {
441  const size_t size = sizeof(T)*count;
442  if(auto ptr = serializer.getPtrAndAdvance(size))
443  {
444  for(size_t i=0; i<count; i++)
445  serialization::write<E>(ptr+i*sizeof(T), values[i]);
446  }
447  return size;
448  }
449  else // Unknown size, have to check length every time.
450  {
451  size_t offset = serializer.offset();
452  for(size_t i=0; i<count; i++)
453  serializer.insert(values[i]);
454  return serializer.offset() - offset;
455  }
456 }
457 
470 template<serialization::Endian E, class T>
471 size_t extract(Serializer<E>& serializer, T* values, size_t count)
472 {
473  // For arithmetic types the size is fixed so it can be optimized.
474  IF_CONSTEXPR(std::is_arithmetic<T>::value)
475  {
476  const size_t size = sizeof(T)*count;
477  if(auto ptr = serializer.getPtrAndAdvance(size))
478  {
479  for(size_t i=0; i<count; i++)
480  serialization::read<E>(ptr+i*sizeof(T), values[i]);
481  }
482  return size;
483  }
484  else // Unknown size, have to check length every time.
485  {
486  size_t offset = serializer.offset();
487  for(size_t i=0; i<count; i++)
488  serializer.extract(values[i]);
489  return serializer.offset() - offset;
490  }
491 }
492 
504 template<serialization::Endian E, class T>
506 {
507  return insert(serializer, values.data(), values.size());
508 }
509 
521 template<serialization::Endian E, class T>
523 {
524  return extract(serializer, values.data(), values.size());
525 }
526 
527 
528 //
529 // Arrays of fixed size
530 //
531 
543 template<serialization::Endian E, class T, size_t N>
544 size_t insert(Serializer<E>& serializer, const T(&values)[N])
545 {
546  return insert(serializer, values, N);
547 }
548 
560 template<serialization::Endian E, class T, size_t N>
561 size_t extract(Serializer<E>& serializer, T(&values)[N])
562 {
563  return extract(serializer, values, N);
564 }
565 
566 
567 
579 template<serialization::Endian E, class T, size_t N>
580 size_t insert(Serializer<E>& serializer, const std::array<T,N>& values)
581 {
582  return insert(serializer, values.data(), values.size());
583 }
584 
596 template<serialization::Endian E, class T, size_t N>
597 size_t extract(Serializer<E>& serializer, const std::array<T,N>& values)
598 {
599  return extract(serializer, values.data(), values.size());
600 }
601 
602 //
603 // Multiple values at once - more efficient since it avoids multiple size checks
604 //
605 
622 #if __cpp_fold_expressions >= 201603L && __cpp_if_constexpr >= 201606L
623 template<serialization::Endian E, typename... Ts>
624 typename std::enable_if<(sizeof...(Ts) > 1), size_t>::type
625 /*size_t*/ insert(Serializer<E>& serializer, Ts... values)
626 {
627  if constexpr( (std::is_arithmetic<Ts>::value && ...) )
628  {
629  const size_t size = ( ... + sizeof(Ts) );
630 
631  if(uint8_t* ptr = serializer.getPtrAndAdvance(size))
632  {
633  size_t offset = 0;
634  ( ..., (offset += serialization::write<E>(ptr+offset, values)) );
635  return offset;
636  }
637 
638  return size;
639  }
640  else // Class types may not have fixed sizes, can't optimize them
641  return ( ... + insert(serializer, values) );
642 }
643 #else
644 template<serialization::Endian E, typename T0, typename T1, typename... Ts>
645 size_t insert(Serializer<E>& serializer, const T0& value0, const T1& value1, Ts... values)
646 {
647  return insert(serializer, value0) + insert(serializer, value1, values...);
648 }
649 #endif
650 
651 
668 #if __cpp_fold_expressions >= 201603L && __cpp_if_constexpr >= 201606L
669 template<serialization::Endian E, typename... Ts>
670 typename std::enable_if<(sizeof...(Ts) > 1), size_t>::type
671 /*size_t*/ extract(Serializer<E>& serializer, Ts&... values)
672 {
673  if constexpr( (std::is_arithmetic<Ts>::value && ...) )
674  {
675  const size_t size = ( ... + sizeof(Ts) );
676 
677  if(uint8_t* ptr = serializer.getPtrAndAdvance(size))
678  {
679  size_t offset = 0;
680  ( ..., (offset += serialization::read<E>(ptr+offset, values)) );
681  return offset;
682  }
683 
684  return size;
685  }
686  else // Class types may not have fixed sizes, can't optimize them
687  return ( ... + extract(serializer, values) );
688 }
689 #else
690 template<serialization::Endian E, typename T0, typename T1, typename... Ts>
691 size_t extract(Serializer<E>& serializer, T0& value0, T1& value1, Ts&... values)
692 {
693  return extract(serializer, value0) + extract(serializer, value1, values...);
694 }
695 #endif
696 
697 
698 //
699 // Raw buffer - avoids the need to create a serializer yourself.
700 //
701 
731 template<serialization::Endian E, class T>
732 bool insert(const T& value, uint8_t* buffer, size_t buffer_length, size_t offset=0, bool exact_size=false)
733 {
734  Serializer<E> serializer(buffer, buffer_length, offset);
735  serializer.insert(value);
736  return exact_size ? serializer.isFinished() : serializer.isOk();
737 }
738 
769 template<serialization::Endian E, class T>
770 bool extract(T& value, const uint8_t* buffer, size_t buffer_length, size_t offset=0, bool exact_size=false)
771 {
772  Serializer<E> serializer(buffer, buffer_length, offset);
773  extract(serializer, value);
774  return exact_size ? serializer.isFinished() : serializer.isOk();
775 }
776 
777 
778 //
779 // Raw buffer - Span version
780 //
781 
800 template<serialization::Endian E, class T>
801 bool insert(T value, microstrain::Span<uint8_t> buffer, size_t offset=0, bool exact_size=false)
802 {
803  return insert<E,T>(value, buffer.data(), buffer.size(), offset, exact_size);
804 }
805 
824 template<serialization::Endian E, class T>
825 bool extract(T& value, microstrain::Span<const uint8_t> buffer, size_t offset=0, bool exact_size=false)
826 {
827  return extract<E,T>(value, buffer.data(), buffer.size(), offset, exact_size);
828 }
829 
830 //
831 // Special Deserialization
832 //
833 
834 #ifdef MICROSTRAIN_HAS_OPTIONAL
835 template<class T, serialization::Endian E>
848 std::optional<T> extract(Serializer<E>& serializer)
849 {
850  T value;
851  if(extract<E,T>(serializer, value))
852  return value;
853  else
854  return std::nullopt;
855 }
856 
874 template<class T, serialization::Endian E>
875 std::optional<T> extract(const uint8_t* buffer, size_t length, size_t offset, bool exact_size=false)
876 {
877  T value;
878  if(extract<E,T>(value, buffer, length, offset, exact_size))
879  return value;
880  else
881  return std::nullopt;
882 }
883 
900 template<class T, serialization::Endian E>
901 std::optional<T> extract(microstrain::Span<const uint8_t> buffer, size_t offset, bool exact_size=false)
902 {
903  T value;
904  if(extract<E,T>(value, buffer.data(), buffer.size(), offset, exact_size))
905  return value;
906  else
907  return std::nullopt;
908 }
909 #endif
910 
911 
913 //
914 // Serializer member functions which depend on the above overloads.
915 //
917 
931 template<serialization::Endian E>
932 template<typename... Ts>
933 bool Serializer<E>::insert(const Ts&... values)
934 {
935  // Prevents infinite recursion but allows ADL
936  // https://stackoverflow.com/questions/13407205/calling-nonmember-instead-of-member-function
937  using microstrain::insert;
938 
939  insert(*this, values...);
940 
941  return isOk();
942 }
943 
957 template<serialization::Endian E>
958 template<typename... Ts>
959 bool Serializer<E>::extract(Ts&... values)
960 {
961  // Prevents infinite recursion but allows ADL
962  // https://stackoverflow.com/questions/13407205/calling-nonmember-instead-of-member-function
963  using microstrain::extract;
964 
965  extract(*this, values...);
966 
967  return isOk();
968 }
969 
988 template<serialization::Endian E>
989 template<class T, class S>
990 bool Serializer<E>::extract_count(T& count, S max_count)
991 {
992  if( this->extract(count) )
993  {
994  if( count <= max_count )
995  return true;
996 
997  invalidate();
998  }
999 
1000  count = 0;
1001 
1002  return false;
1003 }
1004 
1005 
1009 
1010 } // namespace microstrain
microstrain::Span::data
constexpr pointer data() const noexcept
Definition: span.hpp:67
microstrain::extract
std::enable_if< std::is_arithmetic< T >::value, size_t >::type extract(Serializer< E > &buffer, T &value)
Reads a numeric value from a Serializer.
Definition: serializer.hpp:203
microstrain::Serializer::ENDIAN
static const serialization::Endian ENDIAN
Definition: serializer.hpp:140
microstrain::Serializer::extract_count
bool extract_count(T &count, S max_count)
Deserializes an integer with maximum permissible value.
Definition: serializer.hpp:990
microstrain::Serializer
Serializes or deserializes data to/from a byte buffer.
Definition: serializer.hpp:135
microstrain::SerializerBase::setOffset
size_t setOffset(size_t offset)
Sets a new offset and returns the old value. This can be used to save/restore the current offset....
Definition: serializer.hpp:109
IF_CONSTEXPR
#define IF_CONSTEXPR
Definition: platform.hpp:21
microstrain::Span::size
constexpr size_t size() const noexcept
Definition: span.hpp:69
microstrain::Serializer::insert
bool insert(const Ts &... values)
Serializes one or more values.
Definition: serializer.hpp:933
microstrain::Span
Implementation of std::span from C++20.
Definition: span.hpp:42
microstrain::SerializerBase::getPtrAndAdvance
uint8_t * getPtrAndAdvance(size_t size)
Obtains a pointer to the current offset for reading/writing a value of specified size,...
Definition: serializer.hpp:98
microstrain::SerializerBase::getPointer
uint8_t * getPointer(size_t required_size)
Obtains a pointer to the current offset for reading/writing a value of the specified size....
Definition: serializer.hpp:85
microstrain::SerializerBase::SerializerBase
SerializerBase()=default
microstrain::Serializer::extract_count
bool extract_count(T *count, S max_count)
Definition: serializer.hpp:146
microstrain::SerializerBase::SerializerBase
SerializerBase(microstrain::Span< const uint8_t > buffer, size_t offset=0)
Definition: serializer.hpp:66
microstrain::SerializerBase::SerializerBase
SerializerBase(uint8_t *ptr, size_t capacity, size_t offset=0)
Definition: serializer.hpp:64
microstrain::SerializerBase
Represents a view of a buffer of bytes of known capacity.
Definition: serializer.hpp:60
microstrain::insert
std::enable_if< std::is_arithmetic< T >::value, size_t >::type insert(Serializer< E > &buffer, T value)
Inserts a numeric value to a Serializer.
Definition: serializer.hpp:182
microstrain::Serializer::extract
bool extract(Ts &... values)
Deserializes one or more values.
Definition: serializer.hpp:959
microstrain::SerializerBase::invalidate
void invalidate()
Marks the buffer as invalid, i.e. overrun/error state. All further accesses via pointer(),...
Definition: serializer.hpp:102
microstrain::SerializerBase::SerializerBase
SerializerBase(const uint8_t *ptr, size_t size, size_t offset=0)
Definition: serializer.hpp:65
microstrain
Definition: embedded_time.h:8
microstrain::serialization::Endian
Endian
Definition: readwrite.hpp:38
readwrite.hpp