MIP_SDK  latest-2-g34f3e39
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 
51 {
52 public:
53  SerializerBase() = default;
54  SerializerBase(uint8_t* ptr, size_t capacity, size_t offset=0) : m_ptr(ptr), m_size(capacity), m_offset(offset) {}
55  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) {}
56  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) {}
57 
58  size_t capacity() const { return m_size; }
59  size_t offset() const { return m_offset; }
60  size_t usedLength() const { return offset(); }
61  int remaining() const { return int(m_size - m_offset); }
62 
63  bool isOverrun() const { return m_offset > m_size; }
64  bool isOk() const { return !isOverrun(); }
65  bool isFinished() const { return m_offset == m_size; }
66  bool hasRemaining(size_t count=1) const { return m_offset+count <= m_size; }
67 
68  uint8_t* basePointer() { return m_ptr; }
69  const uint8_t* basePointer() const { return m_ptr; }
70 
75  uint8_t* getPointer(size_t required_size) { return hasRemaining(required_size) ? (m_ptr+m_offset) : nullptr; }
76  const uint8_t* getPointer(size_t required_size) const { return hasRemaining(required_size) ? (m_ptr+m_offset) : nullptr; }
77 
88  uint8_t* getPtrAndAdvance(size_t size) { uint8_t* ptr = hasRemaining(size) ? (m_ptr+m_offset) : nullptr; m_offset += size; return ptr; }
89 
92  void invalidate() { m_offset = m_size+1; }
93 
99  size_t setOffset(size_t offset) { std::swap(m_offset, offset); return offset; }
100 
101 private:
102  uint8_t* m_ptr = nullptr;
103  size_t m_size = 0;
104  size_t m_offset = 0;
105 };
106 
107 
124 template<serialization::Endian E>
126 {
127 public:
129 
130  static const serialization::Endian ENDIAN = E;
131 
132  template<typename... Ts> bool insert (const Ts&... values);
133  template<typename... Ts> bool extract(Ts&... values);
134 
135  template<class T, class S> bool extract_count(T& count, S max_count);
136  template<class T, class S> bool extract_count(T* count, S max_count) { return extract_count(*count, max_count); }
137 };
138 
141 
142 
143 //
144 //
145 // Non-member functions which the user may overload
146 //
147 //
148 
150 //
151 // LEVEL 1 SERIALIZATION
152 //
154 
155 //
156 // Built-in types (bool, int, float, ...)
157 //
158 
170 template<serialization::Endian E, class T>
171 typename std::enable_if<std::is_arithmetic<T>::value, size_t>::type
172 /*size_t*/ insert(Serializer<E>& buffer, T value)
173 {
174  if(auto ptr = buffer.getPtrAndAdvance(sizeof(T)))
175  serialization::write<E>(ptr, value);
176 
177  return sizeof(T);
178 }
179 
191 template<serialization::Endian E, class T>
192 typename std::enable_if<std::is_arithmetic<T>::value, size_t>::type
193 /*size_t*/ extract(Serializer<E>& buffer, T& value)
194 {
195  if(auto ptr = buffer.getPtrAndAdvance(sizeof(T)))
196  serialization::read<E>(ptr, value);
197 
198  return sizeof(T);
199 }
200 
201 
202 //
203 // Enums
204 //
205 
219 template<serialization::Endian E, class T>
220 typename std::enable_if<std::is_enum<T>::value, size_t>::type
221 /*size_t*/ insert(Serializer<E>& buffer, T value)
222 {
223  using BaseType = typename std::underlying_type<T>::type;
224 
225  if(auto ptr = buffer.getPtrAndAdvance(sizeof(BaseType)))
226  serialization::write<E>(ptr, static_cast<BaseType>(value));
227 
228  return sizeof(BaseType);
229 }
230 
244 template<serialization::Endian E, class T>
245 typename std::enable_if<std::is_enum<T>::value, size_t>::type
246 /*size_t*/ extract(Serializer<E>& buffer, T& value)
247 {
248  using BaseType = typename std::underlying_type<T>::type;
249 
250  if(auto ptr = buffer.getPtrAndAdvance(sizeof(BaseType)))
251  {
252  BaseType base;
253  serialization::read<E>(ptr, base);
254  value = static_cast<T>(base);
255  }
256 
257  return sizeof(BaseType);
258 }
259 
260 
261 //
262 // Classes - if they have member functions "insert" and "extract"
263 //
264 
297 template<serialization::Endian E, class T, decltype(&T::insert) = nullptr>
298 typename std::enable_if<std::is_class<T>::value , size_t>::type
299 /*size_t*/ insert(microstrain::Serializer<E>& serializer, const T& object)
300 {
301  size_t offset = serializer.offset();
302  object.insert(serializer);
303  return serializer.offset() - offset;
304 }
305 
338 template<serialization::Endian E, class T, decltype(&T::extract) = nullptr>
339 typename std::enable_if<std::is_class<T>::value , size_t>::type
340 /*size_t*/ extract(microstrain::Serializer<E>& serializer, T& object)
341 {
342  size_t offset = serializer.offset();
343  object.extract(serializer);
344  return serializer.offset() - offset;
345 }
346 
347 
349 //
350 // LEVEL 2 SERIALIZATION
351 //
353 
354 //
355 // std::tuple - only supported if std::apply can be used
356 //
357 #if __cpp_lib_apply >= 201603L
358 
372 template<serialization::Endian E, class... Ts>
373 size_t insert(Serializer<E>& serializer, const std::tuple<Ts...>& values)
374 {
375  auto lambda = [&serializer](const Ts&... args) {
376  return insert(serializer, args...);
377  };
378 
379  return std::apply(lambda, values);
380 }
381 
395 template<serialization::Endian E, class... Ts>
396 size_t extract(Serializer<E>& serializer, const std::tuple<std::reference_wrapper<Ts>...>& values)
397 {
398  auto lambda = [&serializer](auto&... args) {
399  return extract(serializer, args...);
400  };
401 
402  return std::apply(lambda, values);
403 }
404 
405 #endif
406 
407 
408 
409 //
410 // Arrays of runtime length
411 //
412 
425 template<serialization::Endian E, class T>
426 size_t insert(Serializer<E>& serializer, const T* values, size_t count)
427 {
428  // For arithmetic types the size is fixed so it can be optimized.
429  IF_CONSTEXPR(std::is_arithmetic<T>::value)
430  {
431  const size_t size = sizeof(T)*count;
432  if(auto ptr = serializer.getPtrAndAdvance(size))
433  {
434  for(size_t i=0; i<count; i++)
435  serialization::write<E>(ptr+i*sizeof(T), values[i]);
436  }
437  return size;
438  }
439  else // Unknown size, have to check length every time.
440  {
441  size_t offset = serializer.offset();
442  for(size_t i=0; i<count; i++)
443  serializer.insert(values[i]);
444  return serializer.offset() - offset;
445  }
446 }
447 
460 template<serialization::Endian E, class T>
461 size_t extract(Serializer<E>& serializer, T* values, size_t count)
462 {
463  // For arithmetic types the size is fixed so it can be optimized.
464  IF_CONSTEXPR(std::is_arithmetic<T>::value)
465  {
466  const size_t size = sizeof(T)*count;
467  if(auto ptr = serializer.getPtrAndAdvance(size))
468  {
469  for(size_t i=0; i<count; i++)
470  serialization::read<E>(ptr+i*sizeof(T), values[i]);
471  }
472  return size;
473  }
474  else // Unknown size, have to check length every time.
475  {
476  size_t offset = serializer.offset();
477  for(size_t i=0; i<count; i++)
478  serializer.extract(values[i]);
479  return serializer.offset() - offset;
480  }
481 }
482 
494 template<serialization::Endian E, class T>
496 {
497  return insert(serializer, values.data(), values.size());
498 }
499 
511 template<serialization::Endian E, class T>
513 {
514  return extract(serializer, values.data(), values.size());
515 }
516 
517 
518 //
519 // Arrays of fixed size
520 //
521 
533 template<serialization::Endian E, class T, size_t N>
534 size_t insert(Serializer<E>& serializer, const T(&values)[N])
535 {
536  return insert(serializer, values, N);
537 }
538 
550 template<serialization::Endian E, class T, size_t N>
551 size_t extract(Serializer<E>& serializer, T(&values)[N])
552 {
553  return extract(serializer, values, N);
554 }
555 
556 
557 
569 template<serialization::Endian E, class T, size_t N>
570 size_t insert(Serializer<E>& serializer, const std::array<T,N>& values)
571 {
572  return insert(serializer, values.data(), values.size());
573 }
574 
586 template<serialization::Endian E, class T, size_t N>
587 size_t extract(Serializer<E>& serializer, const std::array<T,N>& values)
588 {
589  return extract(serializer, values.data(), values.size());
590 }
591 
592 //
593 // Multiple values at once - more efficient since it avoids multiple size checks
594 //
595 
612 #if __cpp_fold_expressions >= 201603L && __cpp_if_constexpr >= 201606L
613 template<serialization::Endian E, typename... Ts>
614 typename std::enable_if<(sizeof...(Ts) > 1), size_t>::type
615 /*size_t*/ insert(Serializer<E>& serializer, Ts... values)
616 {
617  if constexpr( (std::is_arithmetic<Ts>::value && ...) )
618  {
619  const size_t size = ( ... + sizeof(Ts) );
620 
621  if(uint8_t* ptr = serializer.getPtrAndAdvance(size))
622  {
623  size_t offset = 0;
624  ( ..., (offset += serialization::write<E>(ptr+offset, values)) );
625  return offset;
626  }
627 
628  return size;
629  }
630  else // Class types may not have fixed sizes, can't optimize them
631  return ( ... + insert(serializer, values) );
632 }
633 #else
634 template<serialization::Endian E, typename T0, typename T1, typename... Ts>
635 size_t insert(Serializer<E>& serializer, const T0& value0, const T1& value1, Ts... values)
636 {
637  return insert(serializer, value0) + insert(serializer, value1, values...);
638 }
639 #endif
640 
641 
658 #if __cpp_fold_expressions >= 201603L && __cpp_if_constexpr >= 201606L
659 template<serialization::Endian E, typename... Ts>
660 typename std::enable_if<(sizeof...(Ts) > 1), size_t>::type
661 /*size_t*/ extract(Serializer<E>& serializer, Ts&... values)
662 {
663  if constexpr( (std::is_arithmetic<Ts>::value && ...) )
664  {
665  const size_t size = ( ... + sizeof(Ts) );
666 
667  if(uint8_t* ptr = serializer.getPtrAndAdvance(size))
668  {
669  size_t offset = 0;
670  ( ..., (offset += serialization::read<E>(ptr+offset, values)) );
671  return offset;
672  }
673 
674  return size;
675  }
676  else // Class types may not have fixed sizes, can't optimize them
677  return ( ... + extract(serializer, values) );
678 }
679 #else
680 template<serialization::Endian E, typename T0, typename T1, typename... Ts>
681 size_t extract(Serializer<E>& serializer, T0& value0, T1& value1, Ts&... values)
682 {
683  return extract(serializer, value0) + extract(serializer, value1, values...);
684 }
685 #endif
686 
687 
688 //
689 // Raw buffer - avoids the need to create a serializer yourself.
690 //
691 
721 template<serialization::Endian E, class T>
722 bool insert(const T& value, uint8_t* buffer, size_t buffer_length, size_t offset=0, bool exact_size=false)
723 {
724  Serializer<E> serializer(buffer, buffer_length, offset);
725  serializer.insert(value);
726  return exact_size ? serializer.isFinished() : serializer.isOk();
727 }
728 
759 template<serialization::Endian E, class T>
760 bool extract(T& value, const uint8_t* buffer, size_t buffer_length, size_t offset=0, bool exact_size=false)
761 {
762  Serializer<E> serializer(buffer, buffer_length, offset);
763  extract(serializer, value);
764  return exact_size ? serializer.isFinished() : serializer.isOk();
765 }
766 
767 
768 //
769 // Raw buffer - Span version
770 //
771 
790 template<serialization::Endian E, class T>
791 bool insert(T value, microstrain::Span<uint8_t> buffer, size_t offset=0, bool exact_size=false)
792 {
793  return insert<E,T>(value, buffer.data(), buffer.size(), offset, exact_size);
794 }
795 
814 template<serialization::Endian E, class T>
815 bool extract(T& value, microstrain::Span<const uint8_t> buffer, size_t offset=0, bool exact_size=false)
816 {
817  return extract<E,T>(value, buffer.data(), buffer.size(), offset, exact_size);
818 }
819 
820 //
821 // Special Deserialization
822 //
823 
824 #ifdef MICROSTRAIN_HAS_OPTIONAL
825 template<class T, serialization::Endian E>
838 std::optional<T> extract(Serializer<E>& serializer)
839 {
840  T value;
841  if(extract<E,T>(serializer, value))
842  return value;
843  else
844  return std::nullopt;
845 }
846 
864 template<class T, serialization::Endian E>
865 std::optional<T> extract(const uint8_t* buffer, size_t length, size_t offset, bool exact_size=false)
866 {
867  T value;
868  if(extract<E,T>(value, buffer, length, offset, exact_size))
869  return value;
870  else
871  return std::nullopt;
872 }
873 
890 template<class T, serialization::Endian E>
891 std::optional<T> extract(microstrain::Span<const uint8_t> buffer, size_t offset, bool exact_size=false)
892 {
893  T value;
894  if(extract<E,T>(value, buffer.data(), buffer.size(), offset, exact_size))
895  return value;
896  else
897  return std::nullopt;
898 }
899 #endif
900 
901 
903 //
904 // Serializer member functions which depend on the above overloads.
905 //
907 
921 template<serialization::Endian E>
922 template<typename... Ts>
923 bool Serializer<E>::insert(const Ts&... values)
924 {
925  // Prevents infinite recursion but allows ADL
926  // https://stackoverflow.com/questions/13407205/calling-nonmember-instead-of-member-function
927  using microstrain::insert;
928 
929  insert(*this, values...);
930 
931  return isOk();
932 }
933 
947 template<serialization::Endian E>
948 template<typename... Ts>
949 bool Serializer<E>::extract(Ts&... values)
950 {
951  // Prevents infinite recursion but allows ADL
952  // https://stackoverflow.com/questions/13407205/calling-nonmember-instead-of-member-function
953  using microstrain::extract;
954 
955  extract(*this, values...);
956 
957  return isOk();
958 }
959 
978 template<serialization::Endian E>
979 template<class T, class S>
980 bool Serializer<E>::extract_count(T& count, S max_count)
981 {
982  if( this->extract(count) )
983  {
984  if( count <= max_count )
985  return true;
986 
987  invalidate();
988  }
989 
990  count = 0;
991 
992  return false;
993 }
994 
995 
996 } // namespace microstrain
microstrain::Span::data
constexpr pointer data() const noexcept
Definition: span.hpp:63
microstrain::Serializer::ENDIAN
static const serialization::Endian ENDIAN
Definition: serializer.hpp:130
microstrain::Serializer
Serializes or deserializes data to/from a byte buffer.
Definition: serializer.hpp:125
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:99
microstrain::Serializer::extract_count
bool extract_count(T &count, S max_count)
Deserializes an integer with maximum permissible value.
Definition: serializer.hpp:980
IF_CONSTEXPR
#define IF_CONSTEXPR
Definition: platform.hpp:21
microstrain::Span::size
constexpr size_t size() const noexcept
Definition: span.hpp:65
microstrain::Span
Implementation of std::span from C++20.
Definition: span.hpp:40
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:88
microstrain::Serializer::extract
bool extract(Ts &... values)
Deserializes one or more values.
Definition: serializer.hpp:949
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:75
microstrain::SerializerBase::SerializerBase
SerializerBase()=default
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:172
microstrain::Serializer::extract_count
bool extract_count(T *count, S max_count)
Definition: serializer.hpp:136
microstrain::SerializerBase::SerializerBase
SerializerBase(microstrain::Span< const uint8_t > buffer, size_t offset=0)
Definition: serializer.hpp:56
microstrain::SerializerBase::SerializerBase
SerializerBase(uint8_t *ptr, size_t capacity, size_t offset=0)
Definition: serializer.hpp:54
microstrain::SerializerBase
Represents a view of a buffer of bytes of known capacity.
Definition: serializer.hpp:50
microstrain::SerializerBase::invalidate
void invalidate()
Marks the buffer as invalid, i.e. overrun/error state. All further accesses via pointer(),...
Definition: serializer.hpp:92
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:193
microstrain::SerializerBase::SerializerBase
SerializerBase(const uint8_t *ptr, size_t size, size_t offset=0)
Definition: serializer.hpp:55
microstrain::Serializer::insert
bool insert(const Ts &... values)
Serializes one or more values.
Definition: serializer.hpp:923
microstrain
Definition: embedded_time.h:8
microstrain::serialization::Endian
Endian
Definition: readwrite.hpp:38
readwrite.hpp